本記事は 夏休みクラウド自由研究 8/18付の記事です。 |
こんにちは。SCSKのふくちーぬです。
2024/7/11に Amazon ECS のアップデートが発表されました。今回は、Amazon ECS においてローリングアップデート時のソフトウェアの一貫性が保証されるようになりましたので紹介します。
Amazon ECS でコンテナ化されたアプリケーションにソフトウェアバージョンの一貫性が強制されるようになりました
以前まではECSにおいてlatestタグを利用していた場合、latestタグの更新・タスクのスケールアウトのタイミング次第で、latestタグが参照するコンテナイメージが異なるため、サービス内でコンテンツが異なるタスクがデプロイされてしまう事象が発生していました。
2024/7/11のアップデートにより、イメージタグが更新された場合において、サービス内のすべてのタスクが同一であることを保証し、ユーザーに一貫性のあるアプリケーションを提供することが可能です。
以前までのデプロイ方式
実際にどのようなデプロイ方式であったか説明します。
以前の方式では、各タスク内でその都度コンテナイメージタグのイメージダイジェスト解決をしていました。
そのため、例えば以下のようなシナリオにおいてスケールアウトのタイミングでコンテンツが異なるタスクがユーザーに公開される可能性があります。
①タスク定義内の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テンプレート
以下のテンプレートを使用して、デプロイします。
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 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!”が表示されます。コンテナイメージとしては、古いのバージョンのものを参照していますね。タスクの詳細を確認してみます。
新規に起動したタスクのイメージURIを確認すると、古いバージョンのもの(以前のイメージダイジェスト)を参照していることが分かります。
ソフトウェアバージョンの一貫性の挙動を確かめました。これによってユーザーには、同一のコンテンツを提供することが可能になります。
注意事項
・実環境でECSプラットフォームバージョン1.3.0についても、ソフトウェアバージョンの一貫性がサポートされていました。つまり、現AWS環境ではECSプラットフォームバージョン1.3.0,1.4.0が利用できるため、全てソフトウェアバージョンの一貫性がサポートされることに注意してください。
※2024/8/13にて、本検証を行ったところ、1.3.0以上でもサポートされている挙動を確認したため、AWSサポートに問い合わせ、AWSドキュメントを修正いただいています。日本語ドキュメントでは、1.4.0以上でのみサポートされていると記述されておりますが、英語ドキュメントでは1.3.0以上でサポートしている旨に修正されています。
最後に
いかがだったでしょうか。
コンテナでlatestタグを利用している場合でも、ECSでのデプロイがより快適なものになりました。
最新アップデートを鵜吞みにするだけではなくきちんと動作検証をすることで、AWSドキュメントの不備にも気づくことができました。
本記事が皆様のお役にたてば幸いです。
ではサウナラ~🔥