本記事は TechHarmony Advent Calendar 2024 12/1付の記事です。 | 
こんにちは。SCSKのふくちーぬです。本記事がアップされる頃には、成田空港を出発して、ラスベガスに向かっていることでしょうか✈
12月のアドベントカレンダーということで、re:Invent 2024での最新アップデートを紹介したいところです。しかし、本日は12/1ということで、発表前日となります?
最新アップデートについては、明日以降の皆さんにお任せするとして、私はre:Invent 2024に予選落ちしたアップデート内容を紹介します。
以前、Amazon ECS においてローリングアップデート時のソフトウェアの一貫性手法をご紹介しました。こちらの記事を読んでいない方は、是非ご一読ください。
今回は、Amazon ECSにおいてソフトウェアバージョンの一貫性を自由に設定できるようになりましたので紹介します。
Amazon ECSにおいてソフトウェアバージョンの一貫性を自由に設定できるようになりました
2024/7/11のアップデートにより、イメージタグが更新された場合において、サービス内のすべてのタスクが同一であり、統一されたイメージダイジェストで起動されることが強制されました。これによりlatestタグを利用している場合でも、同一のコンテンツを提供することが可能になりました。
2024/11/19のアップデートにより、一貫性の設定が”強制”ではなく”任意”に選択することができるようになりました。
一貫性がオフの場合
各タスク内でその都度コンテナイメージタグのイメージダイジェスト解決をします。
そのため、例えば以下のようなシナリオにおいてスケールアウトのタイミングでコンテンツが異なるタスクがユーザーに公開される可能性があります。
①タスク定義内のlatestタグに従い,2つのタスクが起動
②開発者がlatestタグを更新(latestタグを新しいコンテナイメージへ参照先を変更する)
③タスクのスケールアウトが発生し,タスク定義内のlatestタグに従い,1つのタスクが新規起動
このように、サービス内でタスク間で異なるイメージダイジェストを参照することで、意図しないWebコンテンツの公開が行われてしまいます。
一貫性がオンの場合
一連のデプロイメントにおいて、ECSサービス内でコンテナイメージタグがイメージダイジェストに解決されて保存されます。よって、同じタスクをユーザーに公開することが可能になります。
先程と同様のシナリオで違いをみてみます。
①タスク定義内のlatestタグに従い,2つのタスクが起動
②開発者がlatestタグを更新(latestタグを新しいコンテナイメージへ参照先を変更する)
③タスクのスケールアウトが発生し,タスク定義内のlatestタグに従い,1つのタスクが新規起動
検証
今回のアップデートを早速検証してみます。一貫性をオフに設定して、一連のデプロイでタスク間で異なるイメージダイジェストを参照していることを確認することがゴールとなります。
事前準備
VPC、サブネット、ルートテーブル、インターネットゲートウェイ、ネットワークACL、ECRが作成済みであることを確認してください。
ECRへコンテナイメージをpush
nginx-helloリポジトリにイメージをpushします。
下記のdockerfileを利用して、コンテナイメージを作成します。
# ベースイメージとしてnginxの公式イメージを使用 FROM nginx:latest # "Hello, world!" を返すHTMLファイルを作成 RUN echo "Hello, world!" > /usr/share/nginx/html/index.html #80番ポートで公開 EXPOSE 80
Cloud9やCloudShell等のdockerインストール済みのサーバを用意して、上記のdockerfileをビルドします。
サーバのIAMロールには、下記の権限を付与しておいてください。
・arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser
docker build . -t <AWSアカウントId>.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-hello:latest
ECRへログインした後に、ECRのリポジトリにコンテナイメージをpushします。
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin <AWSアカウントId>.dkr.ecr.ap-northeast-1.amazonaws.com docker push <AWSアカウントId>.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-hello:latest
ECRのリポジトリ内にコンテナイメージが1つ格納されます。
CloudFormationテンプレート
versionConsistencyプロパティを設定します。enabled/disabledを選択することが可能です。
今回は、無効化するために”disabled”を設定します。
以下のテンプレートを使用して、デプロイします。
AWSTemplateFormatVersion: 2010-09-09
# ---------------------------------
# パラメータ
# ---------------------------------
Parameters: 
  Env:
    Type: String 
  VpcId:
    Type: String    
  PublicSubnetA:
    Type: String  
  PublicSubnetC:
    Type: String  
  MyIp:
    Type: String    
Resources:
  # ================================
  # ECS (Cluster)
  # ================================
  ECSCluster:
    Type: "AWS::ECS::Cluster"
    Properties:
      ClusterName: !Sub "ECS-${Env}-helloworld-cluster"
      ServiceConnectDefaults:
        Namespace: !Sub "ECS-${Env}-helloworld-cluster"
      CapacityProviders:
        - FARGATE
  # ================================
  # ECS (Task Difinition)
  # ================================          
  ECSTaskDefinition:
    Type: "AWS::ECS::TaskDefinition"
    UpdateReplacePolicy: Retain
    Properties:
      Cpu: 256
      ExecutionRoleArn: !Sub "arn:aws:iam::${AWS::AccountId}:role/ecsTaskExecutionRole"
      Family: !Sub "ECS-${Env}-helloworld-taskdef"
      Memory: 512
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE
      ContainerDefinitions:
        - Name: helloworld
      versionConsistency: disabled
          Image: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/nginx-hello:latest"
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Sub "/ecs/ECS-${Env}-helloworld-service" 
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: latest
          PortMappings:
            - AppProtocol: http
              HostPort: 80
              Protocol: tcp
              ContainerPort: 80
              Name: helloworld-8080-tcp
          ReadonlyRootFilesystem: false
      RuntimePlatform: 
        CpuArchitecture: X86_64
        OperatingSystemFamily: LINUX
# ------------------------------------------------------------#
#  Security Group
# ------------------------------------------------------------#        
  ECSServiceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      GroupDescription: ecs security group 
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: !Ref MyIp
      SecurityGroupEgress:
        - IpProtocol: -1
          FromPort: -1
          ToPort: -1
          CidrIp: "0.0.0.0/0"     
# ------------------------------------------------------------#
#  ECS Service
# ------------------------------------------------------------#
  ECSService:
    Type: AWS::ECS::Service
    Properties:
      Cluster: !Ref ECSCluster
      DesiredCount: 2
      DeploymentConfiguration: 
        DeploymentCircuitBreaker: 
          Enable: TRUE
          Rollback: TRUE
        MaximumPercent: 200
        MinimumHealthyPercent: 100
      DeploymentController:
        Type: ECS
      LaunchType: FARGATE
      NetworkConfiguration:
        AwsvpcConfiguration:
            AssignPublicIp: ENABLED
            SecurityGroups:
              - !Ref ECSServiceSecurityGroup
            Subnets:
              - !Ref PublicSubnetA 
              - !Ref PublicSubnetC 
      PlatformVersion: 1.4.0
      ServiceName: !Sub "ECS-${Env}-helloworld-service"
      TaskDefinition: !Ref ECSTaskDefinition  
# ------------------------------------------------------------#
#  ECS LogGroup
# ------------------------------------------------------------#
  ECSLogGroup:
    Type: "AWS::Logs::LogGroup"
    Properties:
      LogGroupName: !Sub "/ecs/ECS-${Env}-helloworld-service"
Webサーバのコンテンツを確認
デプロイ済みのタスクのパブリックIPアドレスにアクセスして、コンテンツを確認します。
2つのコンテナどちらも”Hello, world!”が表示されます。
ECRへコンテナイメージを再push
下記のdockerfileを利用して、コンテナイメージを作成します。
# ベースイメージとしてnginxの公式イメージを使用 FROM nginx:latest # "Hello, world! Welcome" を返すHTMLファイルを作成 RUN echo "Hello, world! Welcome" > /usr/share/nginx/html/index.html #80番ポートで公開 EXPOSE 80
上記のdockerfileをビルドします。
docker build . -t <AWSアカウントId>.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-hello:latest
ECRのリポジトリにコンテナイメージをpushします。
docker push <AWSアカウントId>.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-hello:latest
以下のように、ECRのリポジトリ内のlatestタグの参照先が更新されます。
タスクの再起動
今回は検証目的のため、タスクを手動停止することで新規タスクを起動させます。もしAuto Scalingが設定されていれば、スケールアウトでも同様に新規タスクを起動させることができます。
タスクを1つ選択後に、「選択されたものを停止」を押下します。続いて、「停止」を押下します。
すぐにタスクが停止され、新規タスクの再起動が始まります。
Webサーバのコンテンツを再確認
新規に起動したタスクのパブリックIPアドレスにアクセスして、コンテンツを確認します。
片方のコンテナでは”Hello, world! Welcome”が表示されます。コンテナイメージとしては、新しいバージョンのものを参照していますね。タスクの詳細を確認してみます。
新規に起動したタスクのイメージURIを確認すると、新しいバージョンのもの(新しいイメージダイジェスト)を参照していることが分かります。
ソフトウェアバージョンの一貫性がないことを確認できました。このようにタイミングによって、ユーザーには異なるコンテンツが提供されてしまいます。
まとめ
一貫性の設定をオフにすると、latestタグ運用時の問題が発生しますので本番環境への適用は控えてください。
メリット
- ECSタスク定義・サービスを更新して、再デプロイするという手間がなくなる。
 - 「新しいデプロイの強制」をすることで、すぐに最新のコンテナイメージの内容がECSタスクに反映される。
 
デメリット
- コンテナイメージのpushのタイミングとECSのスケーリングのタイミングによって、異なるコンテンツを提供してしまう可能性がある。
 - ECRに格納されたイメージとECSタスクで起動しているイメージに差異が発生し、エラー特定が困難になる。
 - 上記に伴い、切り戻し作業ができなくなる。
 
最後に
いかがだったでしょうか。
latestタグの運用は非推奨の為、注意してください。
とはいいつつ、一貫性を任意に設定できることで、試用環境での利用には使えるのではないでしょうか。
まさにユーザーの意見を取り入れてサービスに反映させるAWSの「地球上で最もお客様を大事にする」理念というところでしょうか。
やっぱり選択肢が増えるっていいことですね!
本記事が皆様のお役にたてば幸いです。
ではサウナラ~?

  
  
  
  
















