Terraformでtask-definisionを消したらエラーがで続けた…

task-definitionをコンソールから停止

tfファイルを更新してもtask-definitionが更新されないので試しに現状動いているものを停止してみた

error.sh
$ terraform apply
aws_ecs_service.registry: ServiceNotActiveException: Service was not ACTIVE.
    status code: 400, request id:*****

後に更新されない理由が判明したがエラーが消えてくれない

原因

ECSのtaskは停止しても削除されず、INACTIVE状態で残るためにtfstateファイルから同じものを見続けることが判明
一時的に参照するtask-definitionを動いてるものに指定 => apply => 元に戻す でも効果なし

ecs.tf
resource "aws_ecs_service" "service_redash_blt" {
  name = "${terraform.env}-****"
  cluster = "${aws_ecs_cluster.****.id}"
  task_definition = "${aws_ecs_task_definition.****.arn}"
  desired_count = 1
  deployment_minimum_healthy_percent = 50
  deployment_maximum_percent = 100
  iam_role = "${aws_iam_role.****.arn}"

  load_balancer {
    elb_name = "${aws_elb.****.name}"
    container_name = "nginx"
    container_port = 80
  }
}

解決策

結局task-definitionのfamily自体を変更
terraformで作ったものをコンソールからいじってはいけないと痛感…

続きを読む

Elastic BeanStalkの環境別のタグ付を忘れたときにどうすんの?って話

1年ぐらい前からBeanStalk使ってます。
なんつうのか、この辺のAWSのマネージドサービスっていうの?ってほんとすごいですよね。
インフラエンジニアなんか必要なくなりますよね。
AWSの機能をガリガリ使って一昔前の3-tier systemなんて、ネットワークの知識もインフラ系のIeasの知識もなくアプリ屋さんがポチポチやってればいつでも復元できるシステム!が出来上がっちゃうんですもんね。
しかもはやりのECSつかったDockerコンテナの仕組みもつかって・・・
「あ〜〜Dockerとか最近ちゃってもう最近開発とか楽でさ〜〜、インフラ屋?いるの?仕事おせえよあいつら」とかほざけるわけです。

俺なんか無職まっしぐらです。

Beanstalkってなんぞや?

さて、まぁ今更ですがBeanStalkってなんぞやって人に簡単に説明しておきますと、
要は初見殺しとも言えるAWSの「CloudFomartion」を”比較的”わかりやすいUIで設定できるというマネージメントシステムです。
基本はネットワーク層+3tier+インスタンススケーリング何かをある程度コントロールしてくれます
更にアプリケーションのデプロイはソースをまとめておくとECR上にコンテナイメージとして作成してくれたり、そこまで必要ない人でもPHP用のAMIを使ってちょっとPHPで動作させたいコンテンツを作ってデプロイしてみたいな事ができます。
この辺、コンソールでポチポチやってると、たしかに時間的にそれなりのリソースを取られますしで開発したアプリケーションのバージョン管理もソレナリにしてくれるので何度も言いますがとても便利なサービスだと思っています。

BeanStalkのアレなところ

こんなに便利そうなBeanStalkなんですが、サーバサイド(アプリケーション開発者)にはなり針を振り気味なサービスなので、少しでも込み入ったことを始めるととたんに出来ないことが出てきます。
そこで、「.ebextentions」というファイルを作ってアプリ層のレポジトリに拡張設定をおいて設定を入れたりする訳です。
まぁ、デフォルトで設定されるCloudFormationの設定項目をアプリ層に置くようにするわけで、インフラ屋のいない会社なんかだと違和感ないというかとても理にかなっているようにみえるんですが、タスクをサーバサイド・インフラサイドと別けているチームなんかだと

インフラ屋がアプリ屋に土下座をしてネットワーク設定なんかを開発アプリレポジトリに入れてもらう

なんていう状態が発生してしまい、なんとなく自分の存在意義を感じられなくなってきたりもします。

そこで、インフラ屋さんは「EBとかつかってらんねぇよ、せめてCloudFormationでやろうぜ・・・(JSON書きたくないけど)」とか言い始めるわけです。

あ、個人的にはterraformに逃げましたw

本題の環境ごとのタグの調べ方について

なんか、閑話休題のボリュームがいつもまとまらず多めになってしまう訳ですが
本当に書きたかったEBで作った環境に対してのタグの後付とかに関してです。

EBで環境を作るとEBにおけるタグを設定しておけば、そのアプリケーションで起動したEC2インスタンスなどには一応Nameタグがついてくれてどの環境かわかりやすくなるのですが、SecurityGroup・ELB/ALBなどには最初にタグを付け忘れたりすると、どの環境で使っているSG・LBなのかを探すのが非常に困難になります。

具体的には例えばSecurityGroupなんかだと下記のようなタグが付くことになります
– awseb-a-3xxxxx2xxxx-stack-AWSEBSecurityGroup-HOGEFUGE1HOGE

まぁ訳わんないですよね。SGに関してはまだNameのタグを設定しておけば自分で任意に環境名をつけられますが
ELBとかだとName=「ユニークな文字列:DNS名」になっているのでAWSのコンソールやawscliコマンドの返り値だけでは一見どの環境用セット用の機材なのかが全くわからない状態になってしまっています。

また、タグ自体も起動段階で設定していないと、環境をすべてEB的に再構築しない限り(つまり環境内のすべてを捨ててもう一度構築し直す処理)を入れないと追加したNameタグなどは設定されない状況に陥ります。

これをあとでチクチクと手作業で入れることになるんですが、
– awseb-a-3xxxxx2xxxx-stack-AWSEBSecurityGroup-HOGEFUGE1HOGE

この文字列はEBの中身を見ているだけだと判別がつかない物なのです。
なぜか
この文字列自体がCloudformationで作られている物だからですwww。
なのでこのELBがどのEBアプリケーションで使われているのか?というのを調べるためには

  • まず、上記の文字列のうち、「awseb-a-3xxxxx2xxxx-stack」までの部分を抜き出し
  • CloudFormationのスタック一覧のFilterをその文字列に入れ
  • 説明欄にちゃんと記載されている「AWS Elastic Beanstalk environment (Name: ‘hogefuge-app’)」を確認するという作業が必要になります。

まぁ調べる方法がある分マシですがね・・・この辺もうデフォルトでNameダグとしてすべての環境に入れてしまってもいいと思うんですけどね

以上、ちょっとしたボヤキでした。
ちなみに、サーバサイドの方の「構築の手間が楽になるからEB使うことを強く推したい」という要望であるプロジェクトでEB使いましたがもう本サービス運用では二度と使いたくありません・・・・

続きを読む

ECSでBlue/Greenデプロイ【cloudpack大阪ブログ】

cloudpack大阪の佐々木です。

今扱っているECS環境では、デプロイをCloudFormationでやっています。実際やってみると、アップデート途中で止まるってことが割と頻繁にあって、インプレイスでアップデートするのは怖いなということで、Blue/Greenデプロイ環境をつくってみました。

https://aws.amazon.com/jp/blogs/news/bluegreen-deployments-with-amazon-ecs/

元ネタはこれなんですが、このサンプルだと、CodePipelineとか使って ややこしい いい感じので、もう少し簡単に。

初期設定

まず、Blueを本番、Greenをステージングとして、下記のように設定します。

ALB設定

  • ダイナミックポートマッピングで、Blue、Greenそれぞれのターゲットグループを作成
  • Blue用ターゲットグループをTCP/80(本番用ポート)にマッピング
  • Green用ターゲットグループをTCP/8080(ステージング用ポート)にマッピング

ECS設定

  • Blue用のサービスをつくってBlue用のターゲットグループにマッピング
  • Green用のサービスをつくってGreen用のターゲットグループにマッピング

CFnのテンプレートはこんな感じになります。

elb.yml
Parameters:
  VpcId:
    Type: String

  Subnets:
    Type: List<AWS::EC2::Subnet::Id>

Resources:
  WebSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: web-sg
      SecurityGroupIngress:
        - CidrIp: "0.0.0.0/0"
          IpProtocol: "TCP"
          FromPort: 80
          ToPort: 80
        - CidrIp: "0.0.0.0/0"
          IpProtocol: "TCP"
          FromPort: 8080
          ToPort: 8080
      VpcId: !Ref VpcId


### ALBを作成 ###
  LoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: ecstest-elb
      Subnets: !Ref Subnets
      SecurityGroups:
        - !Ref WebSecurityGroup

### Blue環境用TargetGroup ###
  BlueTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    DependsOn: LoadBalancer
    Properties:
      Name: target-blue
      VpcId: !Ref VpcId
      Port: 80
      Protocol: HTTP

### Green環境用TargetGroup ###
  GreenTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    DependsOn: LoadBalancer
    Properties:
      Name: target-green
      VpcId: !Ref VpcId
      Port: 80
      Protocol: HTTP

### 本番環境用Listner(TCP/80)
  ListenerProd:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref LoadBalancer
      Port: 80
      Protocol: HTTP
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref BlueTargetGroup # <- 本番にBlue環境を紐付け

### ステージング環境用Listner(TCP/8080)
  ListenerStg:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref LoadBalancer
      Port: 8080
      Protocol: HTTP
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref GreenTargetGroup # <- ステージングにGreen環境を紐付け
blue.yml
Parameters:

  Cluster:
    Type: String

  BlueTargetGroupARN:
    Type: String

Resources:

### Role作成 ###
  ECSServiceRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      RoleName: role-blue
      AssumeRolePolicyDocument: |
        {
            "Statement": [{
                "Effect": "Allow",
                "Principal": { "Service": [ "ecs.amazonaws.com" ]},
                "Action": [ "sts:AssumeRole" ]
            }]
        }
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole

### Blue用サービス ###
  Service:
    Type: AWS::ECS::Service
    Properties:
      ServiceName: service-blue
      Cluster: !Ref Cluster
      Role: !Ref ECSServiceRole
      DesiredCount: 1
      TaskDefinition: !Ref TaskDefinition
      LoadBalancers:
        - ContainerName: nginx
          ContainerPort: 80
          TargetGroupArn: !Ref BlueTargetGroupARN # <- BlueのTargetGroupを指定

### Blue用タスク定義 ###
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: ecstest # ← Familyを同じ値にすることでRevisionの変更が可能
      ContainerDefinitions:
        - Name: nginx
          Image: nginx
          Memory: 128
          PortMappings:
            - ContainerPort: 80

green.yml
Parameters:

  Cluster:
    Type: String

  GreenTargetGroupARN:
    Type: String

Resources:

### Role作成 ###
  ECSServiceRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      RoleName: role-green
      AssumeRolePolicyDocument: |
        {
            "Statement": [{
                "Effect": "Allow",
                "Principal": { "Service": [ "ecs.amazonaws.com" ]},
                "Action": [ "sts:AssumeRole" ]
            }]
        }
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole

### Green用サービス ###
  Service:
    Type: AWS::ECS::Service
    Properties:
      ServiceName: service-green
      Cluster: !Ref Cluster
      Role: !Ref ECSServiceRole
      DesiredCount: 1
      TaskDefinition: !Ref TaskDefinition
      LoadBalancers:
        - ContainerName: nginx
          ContainerPort: 80
          TargetGroupArn: !Ref GreenTargetGroupARN # <- BlueのTargetGroupを指定

### Green用タスク定義 ###
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: ecstest # ← Familyを同じ値にすることでRevisionの変更が可能
      ContainerDefinitions:
        - Name: nginx
          Image: nginx
          Memory: 128
          PortMappings:
            - ContainerPort: 80

ELBのリスナーを確認すると、BlueがTCP/80、GreenがTCP/8080になっています。

Kobito.ZPP2rK.png

タスク定義のFamilyの値をBlue/Greenで同じにしておけば、同じタスク定義でRevisionの更新ができます。

Kobito.D5emlt.png

デプロイ

デプロイの時は、こんな感じです。

ECS設定

  • Green用のサービスをアップデートする

ALB設定

  • ポートのマッピングをBlue/Greenで入れ替える

という手順になります。

CFnでアップデートする場合は、下記のようなテンプレートでUpdate Stackします。

elb.yml
Parameters:
  VpcId:
    Type: String

  Subnets:
    Type: List<AWS::EC2::Subnet::Id>

Resources:
  WebSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: web-sg
      SecurityGroupIngress:
        - CidrIp: "0.0.0.0/0"
          IpProtocol: "TCP"
          FromPort: 80
          ToPort: 80
        - CidrIp: "0.0.0.0/0"
          IpProtocol: "TCP"
          FromPort: 8080
          ToPort: 8080
      VpcId: !Ref VpcId


### ALBを作成 ###
  LoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: ecstest-elb
      Subnets: !Ref Subnets
      SecurityGroups:
        - !Ref WebSecurityGroup

### Blue環境用TargetGroup ###
  BlueTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    DependsOn: LoadBalancer
    Properties:
      Name: target-blue
      VpcId: !Ref VpcId
      Port: 80
      Protocol: HTTP

### Green環境用TargetGroup ###
  GreenTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    DependsOn: LoadBalancer
    Properties:
      Name: target-green
      VpcId: !Ref VpcId
      Port: 80
      Protocol: HTTP

### 本番環境用Listner(TCP/80)
  ListenerProd:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref LoadBalancer
      Port: 80
      Protocol: HTTP
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref GreenTargetGroup # <- 本番にGreen環境を紐付け

### ステージング環境用Listner(TCP/8080)
  ListenerStg:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref LoadBalancer
      Port: 8080
      Protocol: HTTP
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref BlueTargetGroup # <- ステージングにBlue環境を紐付け

リスナーのターゲットグループを入れ替えます。

Kobito.LyimFg.png

入れ替わってますね。

まとめ

ALB(or NLB)のターゲットグループを使えばBlue/Green環境が1つのECSクラスタでできるようになります。
これでCFnが止まっても本番環境には影響なく安心ですね。

続きを読む

AWS サービス別 APIプロトコル一覧

Pythonのbotocoreに入っているメタデータから持ってきた。

サービス名 API protocol
acm json
apigateway rest-json
application-autoscaling json
appstream json
athena json
autoscaling query
batch rest-json
budgets json
clouddirectory rest-json
cloudformation query
cloudfront rest-xml
cloudhsm json
cloudsearch query
cloudsearchdomain rest-json
cloudtrail json
cloudwatch query
codebuild json
codecommit json
codedeploy json
codepipeline json
codestar json
cognito-identity json
cognito-idp json
cognito-sync rest-json
config json
cur json
datapipeline json
dax json
devicefarm json
directconnect json
discovery json
dms json
ds json
dynamodb json
dynamodbstreams json
ec2 ec2
ecr json
ecs json
efs rest-json
elasticache query
elasticbeanstalk query
elastictranscoder rest-json
elb query
elbv2 query
emr json
es rest-json
events json
firehose json
gamelift json
glacier rest-json
greengrass rest-json
health json
iam query
importexport query
inspector json
iot rest-json
iot-data rest-json
kinesis json
kinesisanalytics json
kms json
lambda rest-json
lex-models rest-json
lex-runtime rest-json
lightsail json
logs json
machinelearning json
marketplace-entitlement json
marketplacecommerceanalytics json
meteringmarketplace json
mturk json
opsworks json
opsworkscm json
organizations json
pinpoint rest-json
polly rest-json
rds query
redshift query
rekognition json
resourcegroupstaggingapi json
route53 rest-xml
route53domains json
s3 rest-xml
sdb query
servicecatalog json
ses query
shield json
sms json
snowball json
sns query
sqs query
ssm json
stepfunctions json
storagegateway json
sts query
support json
swf json
waf json
waf-regional json
workdocs rest-json
workspaces json
xray rest-json

続きを読む