![]() |
こんにちは。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ドキュメントの不備にも気づくことができました。
本記事が皆様のお役にたてば幸いです。
ではサウナラ~🔥