LoginSignup
12
16

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-09-26

cloudpack大阪の佐々木です。

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

元ネタはこれなんですが、このサンプルだと、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が止まっても本番環境には影響なく安心ですね。

12
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
16