概要
対象読者
- 今回は、Amazon ECS + Fargate のハンズオンです。対象読者は、Amazon Elastic Container Service (Amazon ECS) および AWS Fargate の初学者、Docker の初学者になります。
今回のテーマ
- 今回のハンズオンで経験できることは、以下となります。
- CloudFormation を使って、VPC および関連リソースを作成します。このVPC が、Fargate (コンテナ)を実行する環境になります。
- AWS Cloud9 を使って、nginx のDocker イメージを作成し、Amazon ECR (リポジトリ) にプッシュします。Cloud9 はクラウドベースの統合開発環境 (IDE) です。Cloud9の実行環境は、先に作成した VPC のパブリックサブネットに配置します。
- ECS クラスタおよびタスク定義を作成し、サービス/タスクをデプロイします。タスク定義に指定したコンテナが起動します。タスクは、先に作成した VPC のプライベートサブネットに配置します。今回コンテナへのアクセスは、ALB 経由で行います。このALB およびALB用のセキュリティグループは、CloudFormation によって作成されます。
- ALB にアクセスし、nginxコンテナから “Hello ECS.” が返ることをテストします。
- 最後に、ECS 関連のリソースを削除、Cloud9の実行環境の削除、VPC および関連リソースの削除を行います。特に、NAT Gateway、ALB は時間単位で費用が掛かります。勉強に使ったら、使わないリソースは削除しましょう。
ECS on Fargateの概念
- 今回のハンズオンの様に、コンテナをサーバーレスで実行する際に、ECS on Fargate の構成を選択します。Amazon Elastic Container Service (Amazon ECS)はコンテナのオーケストレーションになり、AWS Fargate はコンテナの実行環境になります。
- コンテナの実行環境にAWS Fargate を使用することで、コンテナホストがマネージド(サーバーレス)になり、実行環境の管理が不要、スケールもシームレスに行われます。
- ECS on Fargate の概念は、AWS Black Belt の資料も参考にしてください。
演習に利用するシステムの構成図
ハンズオン1:VPC および関連リソースの作成
CloudFormation を使ったVPC および関連リソースの作成
- CloudFormation を使って、VPC および関連リソースを作成します。AWSマネジメントコンソールからCloudFormationを選択します。
- 画面右上の「スタックの作成」から「新しいリソースを使用 (標準)」を選択します。
- 「テンプレートの準備完了」を選択します。「テンプレートの指定」から「テンプレートファイルのアップロード」を選択、「ファイルの選択」を選択し、CFnテンプレートファイルを選択します。
- 今回のテンプレートファイルは、以下の YAMLを使用します。この YAMLは、こちらの AWSドキュメントに記載されているAWS CloudFormation VPC テンプレートをカスタマイズし、ALB およびALB用のセキュリティグループの定義を追加したものです。
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | Description: This template deploys a VPC, with a pair of public and private subnets spread across two Availability Zones. It deploys an internet gateway, with a default route on the public subnets. It deploys a pair of NAT gateways (one in each AZ), and default routes for them in the private subnets. Parameters: EnvironmentName: Description: An environment name that is prefixed to resource names Type: String VpcCIDR: Description: Please enter the IP range (CIDR notation) for this VPC Type: String Default: 10.192.0.0 /16 PublicSubnet1CIDR: Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone Type: String Default: 10.192.10.0 /24 PublicSubnet2CIDR: Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone Type: String Default: 10.192.11.0 /24 PrivateSubnet1CIDR: Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone Type: String Default: 10.192.20.0 /24 PrivateSubnet2CIDR: Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone Type: String Default: 10.192.21.0 /24 Resources: VPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VpcCIDR EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Sub ${EnvironmentName}-vpc InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub ${EnvironmentName}-igw InternetGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref InternetGateway VpcId: !Ref VPC PublicSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 0, !GetAZs '' ] CidrBlock: !Ref PublicSubnet1CIDR MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${EnvironmentName}-sntpub1 PublicSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 1, !GetAZs '' ] CidrBlock: !Ref PublicSubnet2CIDR MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${EnvironmentName}-sntpub2 PrivateSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 0, !GetAZs '' ] CidrBlock: !Ref PrivateSubnet1CIDR MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub ${EnvironmentName}-sntpri1 PrivateSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 1, !GetAZs '' ] CidrBlock: !Ref PrivateSubnet2CIDR MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub ${EnvironmentName}-sntpri2 NatGateway1EIP: Type: AWS::EC2::EIP DependsOn: InternetGatewayAttachment Properties: Domain: vpc NatGateway2EIP: Type: AWS::EC2::EIP DependsOn: InternetGatewayAttachment Properties: Domain: vpc NatGateway1: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt NatGateway1EIP.AllocationId SubnetId: !Ref PublicSubnet1 NatGateway2: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt NatGateway2EIP.AllocationId SubnetId: !Ref PublicSubnet2 PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub ${EnvironmentName}-rtbpub DefaultPublicRoute: Type: AWS::EC2::Route DependsOn: InternetGatewayAttachment Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0 /0 GatewayId: !Ref InternetGateway PublicSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PublicRouteTable SubnetId: !Ref PublicSubnet1 PublicSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PublicRouteTable SubnetId: !Ref PublicSubnet2 PrivateRouteTable1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub ${EnvironmentName}-rtbpri1 DefaultPrivateRoute1: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTable1 DestinationCidrBlock: 0.0.0.0 /0 NatGatewayId: !Ref NatGateway1 PrivateSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTable1 SubnetId: !Ref PrivateSubnet1 PrivateRouteTable2: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub ${EnvironmentName}-rtbpri2 DefaultPrivateRoute2: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTable2 DestinationCidrBlock: 0.0.0.0 /0 NatGatewayId: !Ref NatGateway2 PrivateSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTable2 SubnetId: !Ref PrivateSubnet2 LoadBalancerSecurityGroup: Type: 'AWS::EC2::SecurityGroup' Properties: GroupDescription: !Sub ${EnvironmentName}-sgalb1 GroupName: !Sub ${EnvironmentName}-sgalb1 VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: '80' ToPort: '80' CidrIp: 0.0.0.0 /0 ALB: Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' Properties: LoadBalancerAttributes: - Key: access_logs.s3.enabled Value: 'false' - Key: deletion_protection.enabled Value: 'false' - Key: idle_timeout.timeout_seconds Value: '60' Name: !Sub ${EnvironmentName}-alb1 Scheme: internet-facing SecurityGroups: - !Ref LoadBalancerSecurityGroup Subnets: - !Ref PublicSubnet1 - !Ref PublicSubnet2 Tags: - Key: Name Value: !Sub ${EnvironmentName}-alb1 Outputs: VPC: Description: A reference to the created VPC Value: !Ref VPC PublicSubnets: Description: A list of the public subnets Value: !Join [ "," , [ !Ref PublicSubnet1, !Ref PublicSubnet2 ]] PrivateSubnets: Description: A list of the private subnets Value: !Join [ "," , [ !Ref PrivateSubnet1, !Ref PrivateSubnet2 ]] PublicSubnet1: Description: A reference to the public subnet in the 1st Availability Zone Value: !Ref PublicSubnet1 PublicSubnet2: Description: A reference to the public subnet in the 2nd Availability Zone Value: !Ref PublicSubnet2 PrivateSubnet1: Description: A reference to the private subnet in the 1st Availability Zone Value: !Ref PrivateSubnet1 PrivateSubnet2: Description: A reference to the private subnet in the 2nd Availability Zone Value: !Ref PrivateSubnet2 LoadBalancerSecurityGroup: Description: Security Group for ALB Value: !Ref LoadBalancerSecurityGroup ALB: Description: ALB (internet facing) Value: !Ref ALB |
- スタックの名前を入力します。
- パラメータを指定します。EnvironmentName に任意の名前を指定し、その他のパラメータ (VPC,Subnet のCIDR) は変更しなくても構いません。
- スタックオプションは特に指定しません。
- レビューを確認し、「送信」を押します。
- スタック一覧より、該当のスタックの「ステータス」が「CREATE_COMPLETE」と表示されたことを確認します。スタックの実行完了には、しばらく時間が掛かります。
ハンズオン2:Cloud9 を使ってDocker イメージを作成
ECR のリポジトリ作成
- AWSマネジメントコンソールからECS(Elastic Container Service) を選択します。続いて、Amazon ECR のリポジトリを選択します。
- 「リポジトリを作成」を選択します。
- 可視性設定に「プライベート」を選択、リポジトリ名を指定し、「リポジトリを作成」を押します。
- リポジトリが作成されました。
Cloud9 のIDE (開発環境) を作成
- ECR のリポジトリに Dockerイメージを登録するため、Cloud9 を使用します。先ず、Cloud9 のIDE (開発環境) を作成します。
- AWSマネジメントコンソールからCloud9 を選択します。
- 続いて、「環境を作成」を選択します。
- 環境の名前を入力、環境タイプに「新しい EC2 インスタンス」を指定します。
- 新しい EC2 インスタンスのインスタンスタイプに「t2.micro (1 GiB GiB RAM + 1 vCPU)」、プラットフォームに「Amazon Linux 2」、タイムアウトに「30 分」を指定します。
- ネットワーク設定の接続に「AWS Systems Manager (SSM)」を選択、VPCに先ほど作成したVPC([EnvironmentName]-vpc)を選択、サブネットに先ほど作成したパブリックサブネット([EnvironmentName]-sntpub1)を選択します。「作成」を押します。
- Cloud9 のIDE (開発環境) が作成されました。
Cloud9 でDockerイメージを作成してリポジトリにプッシュ
- 作成されたCloud9 IDE のリンクを押します。以下の通り、IDE が起動しました。
- Cloud9 のターミナルから下記コマンドを実行します。
1 2 3 | mkdir ecs_hello cd ecs_hello/ mkdir conf |
- ecs_hello/ 配下に、Dockerfile ファイルを作成します。
- Dockerfile ファイルは、コンテナイメージを管理するための定義ファイルです。
- ベースとして使用する既存のイメージの指定(→ FROM コマンド)、イメージの作成プロセス時に実行されるコマンド(→ ADD コマンド)、コンテナイメージの新しいインスタンスが展開されるときに実行されるコマンド(→ RUN コマンド)などの定義が含まれます。
- 今回使用するDockerfile ファイルは、下記となります。
1 2 3 4 | FROM nginx:latest ADD conf /nginx .conf /etc/nginx/ RUN echo "Hello ECS." > /usr/share/nginx/html/index .html |
- ecs_hello/conf/ 配下に、nginx.conf を作成します。
- 今回使用する nginx.conf は、下記となります。
- デフォルトから変更している箇所は、worker_processes を1にしています。autoindex on を指定しています。
- 公開ディレクトリは、/usr/share/nginx/html です。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | # For more information on configuration, see: # * Official English Documentation: http://nginx.org/en/docs/ # * Official Russian Documentation: http://nginx.org/ru/docs/ user nginx; worker_processes 1; error_log /var/log/nginx/error .log; pid /run/nginx .pid; # Load dynamic modules. See /usr/share/doc/nginx/README.dynamic. include /usr/share/nginx/modules/ *.conf; events { worker_connections 1024; } http { log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"' ; access_log /var/log/nginx/access .log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 4096; include /etc/nginx/mime .types; default_type application /octet-stream ; # Load modular configuration files from the /etc/nginx/conf.d directory. # See http://nginx.org/en/docs/ngx_core_module.html#include # for more information. include /etc/nginx/conf .d/*.conf; server { listen 80; listen [::]:80; server_name _; root /usr/share/nginx/html ; # Load configuration files for the default server block. include /etc/nginx/default .d/*.conf; location / { autoindex on; } error_page 404 /404 .html; location = /404 .html { } error_page 500 502 503 504 /50x .html; location = /50x .html { } } } |
- 以下は、エディタの画面となります。
- Docker イメージの作成、プッシュを行うコマンドを確認するため、ECR のコンソールに戻り、作成したECR リポジトリを選択します。
- 「プッシュコマンドの表示」ボタンを押します。下記の画面が表示されます。
- Cloud9 のターミナルに戻り、Docker イメージの作成、ECR リポジトリへプッシュを行います。先ほどECR リポジトリの画面で確認したコマンド 1~4を使用します。(以下コマンドの111111111111 にはアカウント番号が入ります。リージョンはお使いの環境に合わせて、修正ください。)
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | $ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 111111111111.dkr.ecr.ap-northeast-1.amazonaws.com WARNING! Your password will be stored unencrypted in /home/ec2-user/ .docker /config .json. Configure a credential helper to remove this warning. See https: //docs .docker.com /engine/reference/commandline/login/ #credentials-store Login Succeeded $ $ docker build -t niikawa-testenv . Sending build context to Docker daemon 5.12kB Step 1 /3 : FROM nginx:latest latest: Pulling from library /nginx f03b40093957: Pull complete eed12bbd6494: Pull complete fa7eb8c8eee8: Pull complete 7ff3b2b12318: Pull complete 0f67c7de5f2c: Pull complete 831f51541d38: Pull complete Digest: sha256:af296b188c7b7df1234567890abcdefg1234567890abcdefg1234567890abcde Status: Downloaded newer image for nginx:latest ---> f9c14fe76d50 Step 2 /3 : ADD conf /nginx .conf /ec/nginx/ ---> 099789c1113a Step 3 /3 : RUN echo "Hello ECS." > /usr/share/nginx/html/index .html ---> Running in ac1cfb8392fa Removing intermediate container ac1cfb8392fa ---> d2a54dd451ac Successfully built d2a54dd451ac Successfully tagged niikawa-testenv:latest $ $ docker tag niikawa-testenv:latest 111111111111.dkr.ecr.ap-northeast-1.amazonaws.com /niikawa-testenv :latest $ docker push 111111111111.dkr.ecr.ap-northeast-1.amazonaws.com /niikawa-testenv :latest The push refers to repository [111111111111.dkr.ecr.ap-northeast-1.amazonaws.com /niikawa-testenv ] 4648e8b920c2: Pushed 72944a72048c: Pushed 4fd834341303: Pushed 5e099cf3f3c8: Pushed 7daac92f43be: Pushed e60266289ce4: Pushed 4b8862fe7056: Pushed 8cbe4b54fa88: Pushed latest: digest: sha256:e72417f8fbbc47f1234567890abcdefg1234567890abcdefg1234567890abcde size: 1985 |
- ECR リポジトリに、イメージがプッシュがされました。ダイジェストが docker pushコマンドの結果と一致します。
ハンズオン3:ECS によるコンテナのデプロイ
ECS クラスタの作成
- AWSマネジメントコンソールからECS(Elastic Container Service) を選択します。続いて、クラスターを選択します。
- 「クラスターの作成」を選択します。
- クラスター名を入力します。
- ネットワーキングのVPCに先ほど作成したVPC([EnvironmentName]-vpc)を選択、サブネットに先ほど作成したプライベートサブネット([EnvironmentName]-sntpri1, [EnvironmentName]-sntpri2)を選択します。
- インフラストラクチャは、デフォルトを使用します。
- これは、キャパシティープロバイダーに関する設定であり、詳細はこちらのドキュメントを参照ください。
- ECS クラスタが作成されました。
ECS タスク定義の作成
- AWSマネジメントコンソールからECS(Elastic Container Service) を選択します。続いて、タスク定義を選択します。
- 「新しいタスク定義の作成」を選択します。
- タスク定義ファミリーの名前を入力、コンテナの名前を入力、コンテナイメージの指定(ECR からイメージURL のコピー/貼りつけ)を行います。
- ポートマッピングはデフォルトのコンテナポート 80 を使用します。awsvpc ネットワークモードを使用するタスク定義では、コンテナポートのみを指定します。
- タスクサイズのCPU、メモリに、Linuxでサポートされる最小の0.25 vCPU、0.5 GBを選択します。タスクサイズの詳細は、こちらのドキュメントを参照ください。
- タスク実行ロールは、コンテナに付与する権限です。
- ログ収集はデフォルト設定を使用して、CloudWatch Logs にログを送信する様ににタスク内のコンテナを設定します。
- タスク定義の設定値を確認して、問題なければ「作成」を押します。
- 以下の通り、新規にタスク定義が作成されました。最初のタスク定義は、リビジョン 1 になります。
サービスの作成
- タスク定義の「デプロイ」ボタンを押し、「サービスの作成」を選択します。
- 既存のクラスターから作成済みのクラスタを選択します。
- サービス名を入力、必要なタスクの数に「1」を指定します。
- ネットワーキングのVPCに先ほど作成したVPC([EnvironmentName]-vpc)を選択、サブネットに先ほど作成したプライベートサブネット([EnvironmentName]-sntpri1, [EnvironmentName]-sntpri2)を2つ選択します。
- セキュリティグループは、「新しいセキュリティグループの作成」を選択します。タスクのENI に割り当てるセキュリティグループの名前、説明を入力、インバウンドルールを登録します。このインバウンドルールには、CloudFormationにて作成したALB 用のセキュリティグループ([EnvironmentName]-sgalb1)を指定します。(ALB から転送されるリクエストのみ許可するため)
- パブリックIP は、オフに設定します。
- ロードバランシング – オプションを設定します。
- ロードバランサーの種類に「Application Load Balancer」を選択します。Application Load Balancerに「既存のロードバランサーを使用」を指定し、ロードバランサーにCloudFormationにて作成したALB ([EnvironmentName]-alb1)を指定します。
- リスナーは「新しいリスナーを作成」を選択します。ポートは「80」を指定、プロトコルは「HTTP」を選択します。
- ターゲットグループに「新しいターゲットグループの作成」を選択します。ターゲットグループ名を入力します。プロトコルは「HTTP」を選択します。ヘルスチェックの設定はデフォルトを使用します。
- サービスのその他の設定はデフォルトを使用します。最後に「作成」を選択します。
- サービス/タスクがデプロイされ、タスクが起動しました。
- サービス/タスクが正常に動作しているかを確認します。サービスの正常性とメトリクスを選択します。以下の通り、ステータスは正常、タスク 1個が実行中、ALB のヘルスチェックも成功しています。
- ターゲットグループを選択します。ターゲットに、先ほどデプロイされたタスクに割り当てられたENI のIPアドレスが登録されています。
nginxコンテナの疎通テスト
- ブラウザからALB にアクセスし、nginxコンテナから “Hello ECS.” が返ることをテストします。
ハンズオン4:後片付け
- 演習の後片付けを行います。先ずECS のタスクを停止させます。対象サービスの「サービスを更新」を選択し、デプロイ設定の必要なタスクの数を「0」に変更します。
- 次に、対象サービスの「サービスを削除」を選択し、サービスの削除を行います。
- 続いて、ECSクラスタを削除します。対象クラスタの「クラスターの削除」を選択し、ECSクラスタの削除を行います。
- Cloud9 の開発環境を削除します。EC2 のインスタンス(aws-cloud9-xxxxxxxxxx) が起動している場合は、インスタンスを選択、「インスタンスの終了」を選択します。(Cloud9 の開発環境にEC2 を使用しているため、VPC および関連リソースを削除する前にインスタンスを削除する必要があります)
- 最後に、CloudFormation のスタックを削除します。スタックを選択し、「削除」を選択します。事前に、VPC/サブネット上にデプロイされていたリソースが削除されていれば、10分程度でDELETE_COMPLETE になります。EC2 のインスタンス(aws-cloud9-xxxxxxxxxx) が残っている場合、VPC/サブネットが削除されずに残ります。その場合は個別にリソースの削除等を行い、再度CloudFormation のスタック削除を行ってください。
- これで、費用に関わるリソースの後片付けが出来ました。ECS のタスク定義、ECR リポジトリは残りますが、不要であれば削除してください。