Amazon ECS でソフトウェアバージョンの一貫性を自由に設定できるようになりました[Amazon ECS + AWS CloudFormation]

本記事は 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の「地球上で最もお客様を大事にする」理念というところでしょうか。

やっぱり選択肢が増えるっていいことですね!

本記事が皆様のお役にたてば幸いです。

ではサウナラ~🔥

著者について
ふくちーぬ(福地孝哉)

◆所属:クラウドサービス部
◆経歴:
 2022 ANGEL Dojo ANGEL賞 2位
 2023 Japan AWS All Certifications Engineers
 2024 Japan AWS All Certifications Engineers
 2024 Japan AWS Jr. Champions
◆資格:
 熱波師,サウナ・スパ健康アドバイザー,Azure,Terraform,情報処理安全確保支援士合格
◆好きなAWSサービス:Amazon EventBridge
◆好きなサウナの温度:120℃
◆好きな水風呂の温度:16.5℃

ふくちーぬ(福地孝哉)をフォローする

クラウドに強いによるエンジニアブログです。

SCSKクラウドサービス(AWS)は、企業価値の向上につながるAWS 導入を全面支援するオールインワンサービスです。AWS最上位パートナーとして、多種多様な業界のシステム構築実績を持つSCSKが、お客様のDX推進を強力にサポートします。

AWSクラウド
シェアする
タイトルとURLをコピーしました