Amazon ECR + ECS CLI ハンズオン

(2020/12/26 加筆修正：CentOS6が終了しyum update利用不能となったためAmazonLinuxコンテナへ変更。その他現行環境へ対応しました）

ECR とは
======
AWSのマネージドサービスなdocker互換コンテナレジストリです。(EC2 Container Registry)
デフォルトでは自アカウントのみアクセス可能になっており、外に置きたくないコンテナイメージをAWS内で管理するのに適しています。
今回は、Webサーバコンテナを作成してECRレポジトリに登録し、ECSクラスタでWebサービスを構築します。
コンテナを強制停止しても自動的に新しいコンテナが起動し、サービスが継続されることを確認します。
みんな大好きAWSCLIでやってみましょう。


前提条件
========

2013年12月5日以降に作成したアカウントであること。これより古いアカウント（デフォルトVPCが存在しないクラシックEC2アカウント）には非対応です。
AWSアカウントはメールアドレスさえ別ならば同一住所氏名電話番号クレジットカードでも無料で複数作成可能です。新しくアカウントを作ってください。

作業端末
--------------
AWS CLIがインストールされていること。
jsonlint, jq コマンドがインストールされていること。

実行権限
--------------
EC2インスタンスを新しく起動できること。
ECRの操作権限があること。
IAMの操作権限があること。

AWS CLIのバージョン
-------------------

以下のバージョンで動作確認済

* AWS CLI 1.16.188

```bash:コマンド:
aws --version
```
```text:結果(例):
      aws-cli/1.16.188 Python/2.6.6 Linux/2.6.32-754.35.1.el6.x86_64 botocore/1.12.178
```

注意: 基本的に AWS CLI V2 でも動作しますが、画面出力結果が例と異なる事があります。
また、手順3.1. 3.3. のECRへのログイン周りで操作が失敗します。
これは `aws ecr get-login` コマンドが削除されたことによるものです。
よくわからない方は V1 を使って下さい。


0. 事前作業
===========

0.1. リージョンの決定
---------------------

東京リージョンで利用可能となりましたので利用リージョンを東京リージョンに設定します。
(カレントユーザが利用するカレントリージョンも切り変わります。)
<STRIKE>ECRは現在のところ us-east-1(バージニア)、us-west-2(オレゴン)リージョンにしかありません。(2016年3月オレゴンが追加されたので追記)
今回はus-east-1リージョン使うように設定します。</STRIKE>

```bash:変数の設定:
export AWS_DEFAULT_REGION='ap-northeast-1'
```

0.2. 変数の確認
---------------

プロファイルとリージョンが想定のものになっていることを確認します。

```bash:コマンド
aws configure list
```
```text:結果(例):
            Name                    Value             Type    Location
            ----                    -----             ----    --------
         profile                <not set>             None    None
      access_key     ****************M73Q shared-credentials-file
      secret_key     ****************YWg/ shared-credentials-file
          region           ap-northeast-1              env    AWS_DEFAULT_REGION
```



1. レポジトリの構築
===========

ecr-handson-httpd というレポジトリを作成してみます。

1.1. レポジトリの作成
---------------------

```bash:コマンド
ECR_REPOSITORY=ecr-handson-httpd
aws ecr create-repository --repository-name ${ECR_REPOSITORY}
```
```text:結果(例):
      {
          "repository": {
              "registryId": "639395043347",
              "repositoryName": "ecr-handson-httpd",
              "repositoryArn": "arn:aws:ecr:ap-northeast-1:639395043347:repository/ecr-handson-httpd",
              "createdAt": 1609053056.0,
              "repositoryUri": "639395043347.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-handson-httpd"
          }
      }
```

1.2. レポジトリの確認
---------------------

```bash:コマンド
aws ecr describe-repositories
```
```text:結果(例):
      {
          "repositories": [
              {
                  "registryId": "639395043347",
                  "repositoryName": "ecr-handson-httpd",
                  "repositoryArn": "arn:aws:ecr:ap-northeast-1:639395043347:repository/ecr-handson-httpd",
                  "createdAt": 1609053056.0,
                  "repositoryUri": "639395043347.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-handson-httpd"
              }
          ]
      }
```

1.3. （参考）レポジトリのアクセス権付与
---------------------

（この項目は権限付与の例です。ハンズオンの動作には不要なので飛ばしても構いません）
ECRは作成した状態では自アカウントのみアクセスできるようになっています。
他のアカウントからアクセスできるようにする場合は、JSONでポリシーを書いて登録します。これは誰でもpullのみできるように設定する例です。
まずはポリシーをJSONファイルで作成します。

```bash:コマンド
ECR_POLICY_NAME=ecr-handson-policy
cat << EOF > ${ECR_POLICY_NAME}.json
{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": "*",
      "Action": [
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage",
        "ecr:BatchCheckLayerAvailability"
      ]
    }
  ]
}
EOF
```
```text:結果:
      返り値なし
```

JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。

```bash:コマンド:
jsonlint -q ${ECR_POLICY_NAME}.json
```

```text:結果:
      返り値なし
```

レポジトリへポリシーを設定します。

```bash:コマンド:
aws ecr set-repository-policy --repository-name ${ECR_REPOSITORY} --policy-text file://${ECR_POLICY_NAME}.json
```
```text:結果(例):
      {
          "policyText": "{\n  \"Version\" : \"2008-10-17\",\n  \"Statement\" : [ {\n    \"Sid\" : \"\",\n    \"Effect\" : \"Allow\",\n    \"Principal\" : \"*\",\n    \"Action\" : [ \"ecr:GetDownloadUrlForLayer\", \"ecr:BatchGetImage\", \"ecr:BatchCheckLayerAvailability\" ]\n  } ]\n}",
          "repositoryName": "ecr-handson-httpd",
          "registryId": "639395043347"
      }
```

設定した場合は get-repository-policy で現在の設定状況を確認できます。

```bash:コマンド:
aws ecr get-repository-policy --repository-name ${ECR_REPOSITORY}
```
```text:結果(例):
      {
          "policyText": "{\n  \"Version\" : \"2008-10-17\",\n  \"Statement\" : [ {\n    \"Sid\" : \"\",\n    \"Effect\" : \"Allow\",\n    \"Principal\" : \"*\",\n    \"Action\" : [ \"ecr:GetDownloadUrlForLayer\", \"ecr:BatchGetImage\", \"ecr:BatchCheckLayerAvailability\" ]\n  } ]\n}",
          "repositoryName": "ecr-handson-httpd",
          "registryId": "639395043347"
      }
```

設定コマンドの出力と同じ内容が出力されれば正しく設定されています。
なお、何も設定していない場合はエラーとなります。（自分のみアクセス可能）


2. コンテナインスタンスの作成
===========

2.1. IAMロールの作成
-----------------

```bash:コマンド:
ROLE_NAME="ecr-handson-role"
aws iam create-role --role-name ${ROLE_NAME} --assume-role-policy-document '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com"}, "Action": "sts:AssumeRole" } ] }'
```
```text:結果(例):
      {
          "Role": {
              "AssumeRolePolicyDocument": {
                  "Version": "2012-10-17",
                  "Statement": [
                      {
                          "Action": "sts:AssumeRole",
                          "Effect": "Allow",
                          "Principal": {
                              "Service": "ec2.amazonaws.com"
                          }
                      }
                  ]
              },
              "RoleId": "AROAZJXXKDAJRVR4E2S6Y",
              "CreateDate": "2020-12-27T07:17:11Z",
              "RoleName": "ecr-handson-role",
              "Path": "/",
              "Arn": "arn:aws:iam::639395043347:role/ecr-handson-role"
          }
      }
```

2.2. IAMロールへPolicyを追加
-----------------

ECSに必要なポリシーである AmazonEC2ContainerServiceforEC2Role をIAMロールへ付与します。

```bash:コマンド:
POLICY_ARN="arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
aws iam attach-role-policy --role-name ${ROLE_NAME} --policy-arn ${POLICY_ARN}
```
```text:結果:
      返り値なし
```

2.3. インスタンスプロファイルの作成
-----------------

```bash:コマンド:
INSTANCE_PROFILE_NAME="ecr-handson-profile"
aws iam create-instance-profile --instance-profile-name ${INSTANCE_PROFILE_NAME}
```
```text:結果(例):
      {
          "InstanceProfile": {
              "InstanceProfileId": "AIPAZJXXKDAJX4PUSSJRZ",
              "Roles": [],
              "CreateDate": "2020-12-27T07:21:10Z",
              "InstanceProfileName": "ecr-handson-profile",
              "Path": "/",
              "Arn": "arn:aws:iam::639395043347:instance-profile/ecr-handson-profile"
          }
      }
```

2.4. インスタンスプロファイルへIAMロールを追加
-----------------

```bash:コマンド:
aws iam add-role-to-instance-profile --instance-profile-name ${INSTANCE_PROFILE_NAME} --role-name ${ROLE_NAME}
```
```text:結果:
      返り値なし
```

2.5. 鍵ペアの作成
---------------

鍵ペア virginia を作成します。
秘密鍵ファイルは ~/.ssh/virginia.pem に保存されます。

```bash:コマンド
EC2_KEY_NAME="virginia"
FILE_SSH_KEY="${HOME}/.ssh/${EC2_KEY_NAME}.pem"
aws ec2 create-key-pair --key-name ${EC2_KEY_NAME} --query 'KeyMaterial' --output text > ${FILE_SSH_KEY} && cat ${FILE_SSH_KEY} && chmod 600 ${FILE_SSH_KEY}
```
```text:結果(例):
      -----BEGIN RSA PRIVATE KEY-----
      MIIEowIBAAKCAQEAg4AFnT934UdMmuSADElYSWzOQ/XpXUc0570zKwtDB3RXS9Fi8YP8J5jv6QRl
      （中略）
      MfRletfQDp2Hbth3SnNu2MaFQs8C+ODyx7XTx/13LW247AGVsSfq63jovntOL7PRuFA=
      -----END RSA PRIVATE KEY-----
```

2.6. セキュリティグループの作成
-----------------------

セキュリティグループ ecr-handson-sg を作成します。

```bash:コマンド:
SG_NAME="ecr-handson-sg"
aws ec2 create-security-group --group-name ${SG_NAME} --description ${SG_NAME}
SG_ID=`aws ec2 describe-security-groups --filter Name=group-name,Values=${SG_NAME} --query 'SecurityGroups[].GroupId' --output text` && echo ${SG_ID}
```
```text:結果(例):
      {
          "GroupId": "sg-0aefee00ff317678c"
      }

      sg-0aefee00ff317678c
```

外部からのHTTP接続をセキュリティグループに追加。

```bash:コマンド:
aws ec2 authorize-security-group-ingress --group-id ${SG_ID} --protocol 'tcp' --port 80 --cidr 0.0.0.0/0
```
```text:結果:
      (返り値なし)
```

作業中端末のIPアドレスをSSHログインを追加します。

```bash:コマンド:
TERM_IP=`curl -s http://ip-api.com/json | jq -r '.query'` && echo ${TERM_IP}
aws ec2 authorize-security-group-ingress --group-id ${SG_ID} --protocol 'tcp' --port 22 --cidr ${TERM_IP}/32
```
```text:結果(例):
      54.32.10.999
```

2.7. コンテナインスタンスの起動
-----------------------

AMI IDの取得

```bash:コマンド:
AMZLINUX_VERSION='2.0.20201209'
EC2_IMAGE_NAME="amzn2-ami-ecs-hvm-${AMZLINUX_VERSION}-x86_64-ebs"
EC2_IMAGE_ID=`aws ec2 describe-images --filters Name=name,Values="${EC2_IMAGE_NAME}" --query 'Images[].ImageId' --output text` && echo ${EC2_IMAGE_ID}
```
```text:結果:
      ami-0b60185623255ce57
```

インスタンスの起動

```bash:コマンド:
EC2_INSTANCE_TYPE='t2.micro'
aws ec2 run-instances --image-id ${EC2_IMAGE_ID} --instance-type ${EC2_INSTANCE_TYPE} --security-group-ids ${SG_ID} --key-name ${EC2_KEY_NAME} --associate-public-ip-address --iam-instance-profile Name=${INSTANCE_PROFILE_NAME}
```
```text:結果(例):
      {
          "Instances": [
              {
                  "Monitoring": {
                      "State": "disabled"
                  },
                  "PublicDnsName": "",
                  "StateReason": {
                      "Message": "pending",
                      "Code": "pending"
                  },
                  "State": {
                      "Code": 0,
                      "Name": "pending"
                  },
                  "EbsOptimized": false,
                  "LaunchTime": "2020-12-27T07:24:48.000Z",
                  "PrivateIpAddress": "172.31.38.99",
                  "ProductCodes": [],
                  "VpcId": "vpc-b97c9adf",
                  "CpuOptions": {
                      "CoreCount": 1,
                      "ThreadsPerCore": 1
                  },
                  "StateTransitionReason": "",
                  "InstanceId": "i-0a5bc15f73529ddee",
                  "EnaSupport": true,
                  "ImageId": "ami-0b60185623255ce57",
                  "PrivateDnsName": "ip-172-31-38-99.ap-northeast-1.compute.internal",
      （中略）
                  "Hypervisor": "xen",
                  "BlockDeviceMappings": [],
                  "Architecture": "x86_64",
                  "RootDeviceType": "ebs",
                  "IamInstanceProfile": {
                      "Id": "AIPAZJXXKDAJX4PUSSJRZ",
                      "Arn": "arn:aws:iam::639395043347:instance-profile/ecr-handson-profile"
                  },
                  "RootDeviceName": "/dev/xvda",
                  "VirtualizationType": "hvm",
                  "AmiLaunchIndex": 0
              }
          ],
          "ReservationId": "r-001f47d80d58fe75e",
          "Groups": [],
          "OwnerId": "639395043347"
      }
```

2.8. インスタンスIDの取得
-----------------------

```bash:コマンド:
EC2_INSTANCE_ID=`aws ec2 describe-instances --filters Name=instance-state-name,Values=pending --query 'Reservations[].Instances[].InstanceId' --output text` && echo ${EC2_INSTANCE_ID}
```
```text:結果(例):
      i-0a769c87e822e522a
```

結果が例のように１つだけあることを確認します。
何らかの原因で何も表示されなかったり複数表示された場合はうまくいきません。
この場合は2.7.での出力結果26行目あたりに
```"InstanceId": "i-0a5bc15f73529ddee",```
と InstanceId が表示された項目があるので、この値を手動で
```EC2_INSTANCE_ID="i-0a5bc15f73529ddee"```
のように手動でコマンドを打ち環境変数へ格納してください。

2.9. パブリックIPアドレスの取得
-----------------------

```bash:コマンド:
EC2_PUBLIC_IP=`aws ec2 describe-instances --instance-id ${EC2_INSTANCE_ID} --query "Reservations[].Instances[].PublicIpAddress" --output text` && echo ${EC2_PUBLIC_IP}
```
```text:結果(例):
      54.32.10.999
```


3. コンテナの構築・ECRレポジトリへのpush
===========

3.1. ECRログイン情報の取得
---------------------

ECRへの接続にはログインが必要です。
Docker互換のレポジトリですが本家のように簡単にid/pwでは接続出来ないため専用のコマンドが用意されています。

```bash:コマンド
aws ecr get-login --no-include-email
```
```text:結果(例):
      docker login -u AWS -p eyJwYXlsb2FkIjoib2cxK3k4bklYN3hRQWcrQU1sczVycVJQSDZ3dWJyZUNnb01FZzdjMU1YaGRsbUVrWUl1TU5UUmhJallCZmxTQndPYU5zOWYzVXV6SytOZ3Vqd21iV2RXV01RZ1JEeUlITDFXdVNFZTdGSkRqcCthT0h1UkNEOGZOc0ZKNjJRWTFiMHZGUGJadlZQbStLWGNNdjk1SHdJK3ZRQW5xWUFQSUFVaG4vZFRObjkzSEhLamxKYzdDdDBBZDFwZ2ZZQytUbStYQTkybVgwYWJBUEJpcXpFcGFQUCtrTkQ3ZE5hdVZlZXdTMFdLUlBHSHNMcFQwZzk2NVNlclJqb1YxVWVtb043UkV0M0V3YmxZZFVzUlkwM0tLd0h6Z2VHRjYwSklFMC9aaHNqVG1lbmxuUU5xSzYvL282azZXK2ZxQW5UMU5FWUUzOHZJWEp3TlBsTHhpV2RST01PVkhlQkdxN0E1ZFNIRExMWVcyeXdpdkdKbGtOT2dWeU1hUjN0eGU1d0dYUUV1ZThuRmhhK0Vnd2NEK095MWFmMElKME9yREUydEFqakRFVURFbGFMR09HNSt4bFE4WkpTWGJ4RTNiUFFmM1FUeVZnRnJISjdlSlFVWEwvK3lxdS9Va3QwbVhrYzRiWWNyR3FCcEV6K3pWbWdsYnZqSzhZY1ZXNm5BV2oyZDdQWWNURlVaWHZuWUN1Y0dCL0txVmF3dmFiMUdRWDBsUmVLMWdPdUJKNUpyRFc4dFdWUTFZOVhzU05iTzl3R25nQndkd0hYcXhMQW1WVGRuSXNPaDRpR2VCTUgvOFZVUGl2NEZJc3NqVS9YVk5GN3A5bmZKc1ZZS2NQRUgveG10TXlvZFdmOGtBdk1pbUVhMWFiS1liMGhvcmFOQTU1b1BoQk5hZ0F0SUgvZ1VBanppTVh0bzBQUzZYZzB1UnlUbUNrVlRLTDVGeWZjWFZZMWNwU01XOHpvZkFUUVU0L3hRekpud1FGRzd3Rk1QTUVoZmc0RTlkcm1TQllwQVNVZWVGRmoxd21zTEhkNzI0Y2V3ZTR4VlRKV2xTK0FSUDdZcjVyeTVrS2cxSEw0UjdxcVVCRWgyNm5DUk96cEdEVzJhbXc2SG44VjREcmZ2dHl4TUVxbXNTOHZpTUM3c0ZHR0pwblNwNTFER1pkV2NMeUhXZGl1QVpZMzlLMUg1QmsxWnNWVkwrR0hmRzI5NzVhanNSU2FtYUNPZHBjZWZUUFJuRXJHNVVwSEhXbG9SMUE5U3JleGJTN3R3ejBMWGFCZ1NLam9wYkxPUW5xS0xDWlpWUTJ4RU01RDhyUmZxNDl6NDROOWswYkhMd0p4dm9LNno4ZGExWEM5VUNSNzZsMm5SQjlTYzZLNU8zaWVaN05xbkJwbWg1OVN4OTRxekllWmdSQWw2ZHJhU1NXdlhJa3RsUHVHMXRKK3owK3J3UHdQcG01U2dxMHVEZ1l3UTQwMTlQblZTaW8zVnRFUVRoOVdXM0dRdTRJMmRnelZ6WjcvZ0NZc3Y5b1Z3SEw3RzNseVdrM1VRRENSWVB3dVlBdUVRTkthOU5iYng4NnQweXRnZHkzclk9IiwiZGF0YWtleSI6IkFRRUJBSGdBTWZLRGxJb3BDNnpzMGJNZFJyWVNIYS9DMzlrQ3JjUDhrVnByRTlmK2tRQUFBSDR3ZkFZSktvWklodmNOQVFjR29HOHdiUUlCQURCb0Jna3Foa2lHOXcwQkJ3RXdIZ1lKWUlaSUFXVURCQUV1TUJFRURPMVlpb2FxbkdVZnNHamw3Z0lCRUlBN0t6OStYcWZhTTdWRWVlOTB2SFdHcVc1cTB3K0pMNysxbm9tOERUWUNKcTBuTmNScEVoYUthZjhQdzZFclNwTVlxUndOQ0RIRVI4YTVuTkU9IiwidmVyc2lvbiI6IjIiLCJ0eXBlIjoiREFUQV9LRVkiLCJleHBpcmF0aW9uIjoxNjA5MDk3NDEyfQ== https://639395043347.dkr.ecr.ap-northeast-1.amazonaws.com
```

非常に長いですがこれで１行です。
この後で使うのでメモ帳などにコピーしておいてください。

3.2. コンテナインスタンスへのログイン
---------------------

```bash:コマンド:
ssh -l ec2-user -i ${FILE_SSH_KEY} ${EC2_PUBLIC_IP}
```
（何か聞かれたら yes と答える）

```text:結果(例):
         __|  __|  __|
         _|  (   \__ \   Amazon Linux 2 (ECS Optimized)
       ____|\___|____/
      
      For documentation, visit http://aws.amazon.com/documentation/ecs
      No packages needed for security; 6 packages available
      Run "sudo yum update" to apply all updates.
```

以下、この章はコンテナインスタンス上で作業を行います。

3.3. ECRへログイン
---------------------

先ほど 3.1. で取得したログイン情報（非常に長い１行）をペーストして実行します。

```bash:コマンド(例):
docker login -u AWS -p eyJwYXlsb2FkIjoib2cxK3k4bklYN3hRQWcrQU1sczVycVJQSDZ3dWJyZUNnb01FZzdjMU1YaGRsbUVrWUl1TU5UUmhJallCZmxTQndPYU5zOWYzVXV6SytOZ3Vqd21iV2RXV01RZ1JEeUlITDFXdVNFZTdGSkRqcCthT0h1UkNEOGZOc0ZKNjJRWTFiMHZGUGJadlZQbStLWGNNdjk1SHdJK3ZRQW5xWUFQSUFVaG4vZFRObjkzSEhLamxKYzdDdDBBZDFwZ2ZZQytUbStYQTkybVgwYWJBUEJpcXpFcGFQUCtrTkQ3ZE5hdVZlZXdTMFdLUlBHSHNMcFQwZzk2NVNlclJqb1YxVWVtb043UkV0M0V3YmxZZFVzUlkwM0tLd0h6Z2VHRjYwSklFMC9aaHNqVG1lbmxuUU5xSzYvL282azZXK2ZxQW5UMU5FWUUzOHZJWEp3TlBsTHhpV2RST01PVkhlQkdxN0E1ZFNIRExMWVcyeXdpdkdKbGtOT2dWeU1hUjN0eGU1d0dYUUV1ZThuRmhhK0Vnd2NEK095MWFmMElKME9yREUydEFqakRFVURFbGFMR09HNSt4bFE4WkpTWGJ4RTNiUFFmM1FUeVZnRnJISjdlSlFVWEwvK3lxdS9Va3QwbVhrYzRiWWNyR3FCcEV6K3pWbWdsYnZqSzhZY1ZXNm5BV2oyZDdQWWNURlVaWHZuWUN1Y0dCL0txVmF3dmFiMUdRWDBsUmVLMWdPdUJKNUpyRFc4dFdWUTFZOVhzU05iTzl3R25nQndkd0hYcXhMQW1WVGRuSXNPaDRpR2VCTUgvOFZVUGl2NEZJc3NqVS9YVk5GN3A5bmZKc1ZZS2NQRUgveG10TXlvZFdmOGtBdk1pbUVhMWFiS1liMGhvcmFOQTU1b1BoQk5hZ0F0SUgvZ1VBanppTVh0bzBQUzZYZzB1UnlUbUNrVlRLTDVGeWZjWFZZMWNwU01XOHpvZkFUUVU0L3hRekpud1FGRzd3Rk1QTUVoZmc0RTlkcm1TQllwQVNVZWVGRmoxd21zTEhkNzI0Y2V3ZTR4VlRKV2xTK0FSUDdZcjVyeTVrS2cxSEw0UjdxcVVCRWgyNm5DUk96cEdEVzJhbXc2SG44VjREcmZ2dHl4TUVxbXNTOHZpTUM3c0ZHR0pwblNwNTFER1pkV2NMeUhXZGl1QVpZMzlLMUg1QmsxWnNWVkwrR0hmRzI5NzVhanNSU2FtYUNPZHBjZWZUUFJuRXJHNVVwSEhXbG9SMUE5U3JleGJTN3R3ejBMWGFCZ1NLam9wYkxPUW5xS0xDWlpWUTJ4RU01RDhyUmZxNDl6NDROOWswYkhMd0p4dm9LNno4ZGExWEM5VUNSNzZsMm5SQjlTYzZLNU8zaWVaN05xbkJwbWg1OVN4OTRxekllWmdSQWw2ZHJhU1NXdlhJa3RsUHVHMXRKK3owK3J3UHdQcG01U2dxMHVEZ1l3UTQwMTlQblZTaW8zVnRFUVRoOVdXM0dRdTRJMmRnelZ6WjcvZ0NZc3Y5b1Z3SEw3RzNseVdrM1VRRENSWVB3dVlBdUVRTkthOU5iYng4NnQweXRnZHkzclk9IiwiZGF0YWtleSI6IkFRRUJBSGdBTWZLRGxJb3BDNnpzMGJNZFJyWVNIYS9DMzlrQ3JjUDhrVnByRTlmK2tRQUFBSDR3ZkFZSktvWklodmNOQVFjR29HOHdiUUlCQURCb0Jna3Foa2lHOXcwQkJ3RXdIZ1lKWUlaSUFXVURCQUV1TUJFRURPMVlpb2FxbkdVZnNHamw3Z0lCRUlBN0t6OStYcWZhTTdWRWVlOTB2SFdHcVc1cTB3K0pMNysxbm9tOERUWUNKcTBuTmNScEVoYUthZjhQdzZFclNwTVlxUndOQ0RIRVI4YTVuTkU9IiwidmVyc2lvbiI6IjIiLCJ0eXBlIjoiREFUQV9LRVkiLCJleHBpcmF0aW9uIjoxNjA5MDk3NDEyfQ== https://639395043347.dkr.ecr.ap-northeast-1.amazonaws.com
```
```text:結果:
      WARNING! Using --password via the CLI is insecure. Use --password-stdin.
      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
```

コピーペーストした長い長いコマンドの一番最後に https://....で始まる文字列があります。
このhttps://の後ろの、数字で始まる部分から最後まで（この例では 639395043347.dkr.ecr.ap-northeast-1.amazonaws.com ）をコピーペーストして変数へ格納します。
これは自分のECRレジストリです。（レポジトリが置かれている場所）

```bash:コマンド(例):
ECR_REGISTRY=639395043347.dkr.ecr.ap-northeast-1.amazonaws.com
```
```text:結果:
      返り値なし
```

このコマンド(`ECR_REGISTRY=639395043347.dkr.ecr.ap-northeast-1.amazonaws.com`)は後でもう一度実行するのでメモ帳などにコピーしておいてください。

3.4. Dockerfile ファイルの作成
---------------------

ハンズオン用にWebサーバのコンテナを作成します。
今回はCentOS6をベースにapache httpdのコンテナを作成します。
まずは Dockerfile を作成します。
注意：コマンド１行目のECR_REPOSITORYは 1.1. と必ず同一内容にしてください

```bash:コマンド:
ECR_REPOSITORY=ecr-handson-httpd
mkdir ${ECR_REPOSITORY}
cat << EOF > ${ECR_REPOSITORY}/Dockerfile
FROM amazonlinux
RUN yum -y install httpd
RUN systemctl disable httpd.service
CMD ["/usr/sbin/apachectl", "-DFOREGROUND"]
EOF
```
```text:結果:
      返り値なし
```

3.5. コンテナイメージのビルド
---------------------

Dockerファイルからコンテナイメージをビルドします。
（この処理は大量の画面出力がされ、少々時間がかかります。）

```bash:コマンド:
docker build -t ${ECR_REGISTRY}/${ECR_REPOSITORY} ${ECR_REPOSITORY}
```
```text:結果(例):
      Sending build context to Docker daemon  2.048kB
      Step 1/4 : FROM amazonlinux
      latest: Pulling from library/amazonlinux
      4232b9ee675b: Pull complete
      Digest: sha256:420a6503505c73ea05e1254acd4d1a5e73305dab8ec3fa70833e112cc6980624
      Status: Downloaded newer image for amazonlinux:latest
       ---> 9917dfe117ca
      Step 2/4 : RUN yum -y install httpd
       ---> Running in 2239e8886fb8
      Loaded plugins: ovl, priorities
      Resolving Dependencies
      --> Running transaction check
      ---> Package httpd.x86_64 0:2.4.46-1.amzn2 will be installed
      --> Processing Dependency: httpd-tools = 2.4.46-1.amzn2 for package: httpd-2.4.46-1.amzn2.x86_64
      --> Processing Dependency: httpd-filesystem = 2.4.46-1.amzn2 for package: httpd-2.4.46-1.amzn2.x86_64
      --> Processing Dependency: systemd-units for package: httpd-2.4.46-1.amzn2.x86_64
      --> Processing Dependency: systemd-units for package: httpd-2.4.46-1.amzn2.x86_64
      --> Processing Dependency: systemd-units for package: httpd-2.4.46-1.amzn2.x86_64
      --> Processing Dependency: system-logos-httpd for package: httpd-2.4.46-1.amzn2.x86_64
      --> Processing Dependency: mod_http2 for package: httpd-2.4.46-1.amzn2.x86_64
      --> Processing Dependency: libsystemd.so.0(LIBSYSTEMD_209)(64bit) for package: httpd-2.4.46-1.amzn2.x86_64
      --> Processing Dependency: httpd-filesystem for package: httpd-2.4.46-1.amzn2.x86_64
      --> Processing Dependency: /etc/mime.types for package: httpd-2.4.46-1.amzn2.x86_64
      --> Processing Dependency: libsystemd.so.0()(64bit) for package: httpd-2.4.46-1.amzn2.x86_64
      --> Processing Dependency: libaprutil-1.so.0()(64bit) for package: httpd-2.4.46-1.amzn2.x86_64
      --> Processing Dependency: libapr-1.so.0()(64bit) for package: httpd-2.4.46-1.amzn2.x86_64
      --> Running transaction check
      (中略)
      Complete!
      Removing intermediate container 2239e8886fb8
       ---> d54ae12273b1
      Step 3/4 : RUN systemctl disable httpd.service
       ---> Running in 6659d8762d96
      Removing intermediate container 6659d8762d96
       ---> 54d02055606b
      Step 4/4 : CMD ["/usr/sbin/apachectl", "-DFOREGROUND"]
       ---> Running in 16659299e688
      Removing intermediate container 16659299e688
       ---> 42491af99338
      Successfully built 42491af99338
      Successfully tagged 639395043347.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-handson-httpd:latest
```

最後の行に `Successfully tagged` と出ていれば成功です。

3.6. コンテナイメージのpush
---------------------

作成したコンテナイメージをECRへプッシュしてECSからコンテナを利用可能にします。

```bash:コマンド:
docker push ${ECR_REGISTRY}/${ECR_REPOSITORY}:latest
```
```text:結果(例):
      The push refers to repository [639395043347.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-handson-httpd]
      42c1b5dc9a31: Pushed
      cee8d35c645b: Pushed
      latest: digest: sha256:d278bf23016fbf500987f332d9b3c3e5188deb5e0394fd3a1b82b17087793750 size: 742
```
最後の行に `latest: digest: ` と出ていれば成功です。

3.7. コンテナインスタンスからログアウトする
---------------------

```bash:コマンド:
logout
```
```text:結果(例):
      Connection to 54.32.10.999 closed.
```
ログアウトして作業端末に戻りました。
（必ずログアウトしてください。ECS用インスタンスにはAWSCLIが存在しないため以後の操作が失敗します）

3.8. コンテナイメージがpushされたか確認します
---------------------

```bash:コマンド:
aws ecr list-images --repository-name ${ECR_REPOSITORY} --output text
```
```text:結果(例):
      IMAGEIDS        sha256:d278bf23016fbf500987f332d9b3c3e5188deb5e0394fd3a1b82b17087793750 latest
```

表示された `IMAGEIDS` のsha256ハッシュ値と、手順3.6. 最下行 `latest: digest: ` のsha256ハッシュ値が一致していることを確認します。



4. ECSクラスタを構築しコンテナ稼働
===========

dockerコンテナを直接起動しても動作しますが、コンテナのメリットは落ちたときに自動的に新しいコンテナが起動されることです。
これをECSクラスタで実現します。
ECSクラスタは実行するコンテナの内容を定義したタスク定義、サービスとしてどのようにタスクを実行するかを設定するサービスからなります。
サービスの集合体をクラスタと呼びます。

4.1. ECRレジストリの環境変数への格納
---------------------

先ほど 3.3. でメモしたコマンド(ECR_REGISTRY=...)を実行します。
これは作業用インスタンスに戻ったため、変数に値が格納されていないためです。

```bash:コマンド(例):
ECR_REGISTRY=639395043347.dkr.ecr.ap-northeast-1.amazonaws.com
```
```text:結果:
      返り値なし
```

4.2. タスク定義の登録
---------------------

タスク定義をJSONファイルで作成します。

```bash:コマンド:
ECS_TASK="ecr-handson-task"
cat << EOF > ${ECS_TASK}.json
[
  {
    "name": "${ECS_TASK}",
    "image": "${ECR_REGISTRY}/${ECR_REPOSITORY}:latest",
    "cpu": 0,
    "memory": 256,
    "portMappings": [
      {
        "containerPort": 80,
        "hostPort": 80,
        "protocol": "tcp"
      }
    ],
    "essential": true
  }
]
EOF
```
```text:結果:
      返り値なし
```

JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。

```bash:コマンド:
jsonlint -q ${ECS_TASK}.json
```
```text:結果:
      返り値なし
```

タスク定義を登録します。

```bash:コマンド:
aws ecs register-task-definition --family ${ECS_TASK} --container-definitions file://${ECS_TASK}.json
```
```text:結果(例):
      {
          "taskDefinition": {
              "status": "ACTIVE",
              "family": "ecr-handson-task",
              "placementConstraints": [],
              "requiresAttributes": [
                  {
                      "name": "com.amazonaws.ecs.capability.ecr-auth"
                  }
              ],
              "compatibilities": [
                  "EC2"
              ],
              "volumes": [],
              "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:639395043347:task-definition/ecr-handson-task:1",
              "containerDefinitions": [
                  {
                      "environment": [],
                      "name": "ecr-handson-task",
                      "mountPoints": [],
                      "image": "639395043347.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-handson-httpd:latest",
                      "cpu": 0,
                      "portMappings": [
                          {
                              "protocol": "tcp",
                              "containerPort": 80,
                              "hostPort": 80
                          }
                      ],
                      "memory": 256,
                      "essential": true,
                      "volumesFrom": []
                  }
              ],
              "revision": 1
          }
      }
```

最後から３行目に revision という項目があります。最初に登録すると 1 です。
タスク定義は登録のみで変更コマンドはありません。変更したい場合は新しいタスク定義を登録するとその内容に上書き更新されます。その際 revision が増加します。
次の手順で行うサービスの登録で実行するタスク定義の revision を指定することができます。

4.3. サービスの登録
---------------------

実行させたタスクをサービスとして登録します。

```bash:コマンド:
ECS_SERVICE="ecr-handson-service"
aws ecs create-service --service-name ${ECS_SERVICE} --task-definition ${ECS_TASK} --desired-count 1
```
```text:結果(例):
      {
          "service": {
              "status": "ACTIVE",
              "serviceRegistries": [],
              "pendingCount": 0,
              "launchType": "EC2",
              "enableECSManagedTags": false,
              "schedulingStrategy": "REPLICA",
              "loadBalancers": [],
              "placementConstraints": [],
              "createdAt": 1609056251.51,
              "desiredCount": 1,
              "serviceName": "ecr-handson-service",
              "clusterArn": "arn:aws:ecs:ap-northeast-1:639395043347:cluster/default",
              "createdBy": "arn:aws:iam::639395043347:user/a12",
              "taskDefinition": "arn:aws:ecs:ap-northeast-1:639395043347:task-definition/ecr-handson-task:1",
              "serviceArn": "arn:aws:ecs:ap-northeast-1:639395043347:service/default/ecr-handson-service",
              "propagateTags": "NONE",
              "deploymentConfiguration": {
                  "maximumPercent": 200,
                  "minimumHealthyPercent": 100
              },
              "deployments": [
                  {
                      "status": "PRIMARY",
                      "pendingCount": 0,
                      "launchType": "EC2",
                      "createdAt": 1609056251.51,
                      "desiredCount": 1,
                      "taskDefinition": "arn:aws:ecs:ap-northeast-1:639395043347:task-definition/ecr-handson-task:1",
                      "updatedAt": 1609056251.51,
                      "id": "ecs-svc/0432504802543229673",
                      "runningCount": 0
                  }
              ],
              "events": [],
              "runningCount": 0,
              "placementStrategy": []
          }
      }
```

最後から4行目の runningCount がまだ 0 でサービス登録直後はタスクがまだ実行されていないことがわかります。

4.4. サービス稼働の確認
---------------------

ECSへサービスを登録してから実際にタスクが実行状態となるまで30秒ぐらいかかります。しばらく待ってからコマンドを実行します。

```bash:コマンド:
aws ecs describe-services --services ${ECS_SERVICE}
```
```text:結果(例):
      {
          "services": [
              {
                  "status": "ACTIVE",
                  "serviceRegistries": [],
                  "pendingCount": 0,
                  "launchType": "EC2",
                  "enableECSManagedTags": false,
                  "schedulingStrategy": "REPLICA",
                  "loadBalancers": [],
                  "placementConstraints": [],
                  "createdAt": 1609056251.51,
                  "desiredCount": 1,
                  "serviceName": "ecr-handson-service",
                  "clusterArn": "arn:aws:ecs:ap-northeast-1:639395043347:cluster/default",
                  "createdBy": "arn:aws:iam::639395043347:user/a12",
                  "taskDefinition": "arn:aws:ecs:ap-northeast-1:639395043347:task-definition/ecr-handson-task:1",
                  "serviceArn": "arn:aws:ecs:ap-northeast-1:639395043347:service/default/ecr-handson-service",
                  "propagateTags": "NONE",
                  "deploymentConfiguration": {
                      "maximumPercent": 200,
                      "minimumHealthyPercent": 100
                  },
                  "deployments": [
                      {
                          "status": "PRIMARY",
                          "pendingCount": 0,
                          "launchType": "EC2",
                          "createdAt": 1609056251.51,
                          "desiredCount": 1,
                          "taskDefinition": "arn:aws:ecs:ap-northeast-1:639395043347:task-definition/ecr-handson-task:1",
                          "updatedAt": 1609056263.6010001,
                          "id": "ecs-svc/0432504802543229673",
                          "runningCount": 1
                      }
                  ],
                  "events": [
                      {
                          "message": "(service ecr-handson-service) has reached a steady state.",
                          "id": "fb70ffa3-7bee-44c1-bdc7-2a116b3b38ee",
                          "createdAt": 1609056263.608
                      },
                      {
                          "message": "(service ecr-handson-service) (deployment ecs-svc/0432504802543229673) deployment completed.",
                          "id": "4aa6f68e-6d93-4de4-b6f7-a324aa48ff24",
                          "createdAt": 1609056263.6070001
                      },
                      {
                          "message": "(service ecr-handson-service) has started 1 tasks: (task 55fa9449abe044e3bcaed5169f2899f3).",
                          "id": "a7e14ed9-fc11-44a7-90db-23f5758b45fd",
                          "createdAt": 1609056254.198
                      }
                  ],
                  "runningCount": 1,
                  "placementStrategy": []
              }
          ],
          "failures": []
      }
```

最後から6行目の `runningCount` の値を確認します。先ほど `0` だったのが `1` になっていますね。
`0` のままの場合はもう少し待ってから再実行してみてください。（遅くても数分なのでそれ以上待っても変わらない場合は失敗しています）

4.5. Webブラウザで動作確認
---------------------

Webブラウザで実際に動作確認しましょう。インスタンスのIPアドレスを確認します。

```bash:コマンド:
EC2_PUBLIC_IP=`aws ec2 describe-instances --instance-id ${EC2_INSTANCE_ID} --query "Reservations[].Instances[].PublicIpAddress" --output text` && echo ${EC2_PUBLIC_IP}
```
```text:結果(例):
      54.32.10.999
```

Webブラウザへ表示されたIPアドレスをコピーペーストしてアクセスしてみます。
```http://54.32.10.999/```
Apache httpdのテストページが表示されれば成功です。
また後でアクセスするのでブラウザの画面はそのままにしておきましょう。


5. コンテナを強制終了させてECSによる自動再稼働を確認する
===========

5.1. コンテナインスタンスへのログイン
---------------------

```bash:コマンド:
ssh -l ec2-user -i ${FILE_SSH_KEY} ${EC2_PUBLIC_IP}
```

```text:結果(例):
      Last login: Sun Dec 27 07:31:10 2020 from example.tw
      
         __|  __|  __|
         _|  (   \__ \   Amazon Linux 2 (ECS Optimized)
       ____|\___|____/
      
      For documentation, visit http://aws.amazon.com/documentation/ecs
      No packages needed for security; 6 packages available
      Run "sudo yum update" to apply all updates.
```

以下、この章はコンテナインスタンス上で作業を行います。

5.2. dockerコンテナのコンテナIDを確認する
---------------------

```bash:コマンド:
docker ps
```
```text:結果(例):
      CONTAINER ID        IMAGE                                                                        COMMAND                  CREATED             STATUS                    PORTS                NAMES
      d4437cfcb85a        639395043347.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-handson-httpd:latest   "/usr/sbin/apachectl皃"   9 minutes ago       Up 9 minutes              0.0.0.0:80->80/tcp   ecs-ecr-handson-task-1-ecr-handson-task-b2e2e0e6a7b1eab96f00
      e1317281fcf1        amazon/amazon-ecs-agent:latest                                               "/agent"                 48 minutes ago      Up 48 minutes (healthy)                        ecs-agent
```

複数行表示されますが `IMAGE` に `ecr-handson-httpd` を含む行を探します。
その行の先頭の文字列がコンテナIDです（この例では `d4437cfcb85a` ）。この値をコピーしておきます。

5.3. dockerコンテナを強制終了させる
---------------------

コピーしておいたコンテナIDを指定して `docker kill` コマンドを実行します。
強制終了によって落ちたことを確認するにはタイミングが重要です。コマンドを実行したらすぐに次の手順へ進みましょう。

```bash:コマンド(例):
docker kill d4437cfcb85a
```
```text:結果(例):
      d4437cfcb85a
```

5.4. Webブラウザで落ちたか確認
---------------------

Webブラウザをリロードするとエラーとなり表示されないことを確認します。
普通に表示される！？
そんなこともあります。ECSのサービス再起動は優秀ですぐに復活するため、表示されたとしても気にしない。
結果がどうあれ次へ進みます。

5.5. コンテナIDの変化を確認する
---------------------

ECSによってサービスが再起動されたことを論理的に確認するために、旧コンテナが消滅し新しいコンテナが起動されたことを確認します。

```bash:コマンド:
docker ps
```
```text:結果(例):
      CONTAINER ID        IMAGE                                                                        COMMAND                  CREATED             STATUS                    PORTS                NAMES
      ed92d50c23c6        639395043347.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-handson-httpd:latest   "/usr/sbin/apachectl皃"   2 minutes ago       Up 2 minutes              0.0.0.0:80->80/tcp   ecs-ecr-handson-task-1-ecr-handson-task-da92a2a7cf9edbf0a801
      e1317281fcf1        amazon/amazon-ecs-agent:latest                                               "/agent"                 59 minutes ago      Up 59 minutes (healthy)                        ecs-agent
```

`ecr-handson-httpd` のコンテナIDを先ほどの 手順5.3. でdocker killした値と見比べます。
新しいコンテナIDになっている事が確認できるはずです。（この例では `d4437cfcb85a` から `ed92d50c23c6` に変化）
先ほどWebブラウザで落ちたか確認できなかったとしても、コンテナIDの変化でECSによって新しいコンテナが自動起動されたことを確認できます。

5.6. コンテナインスタンスからログアウトする
---------------------

```bash:コマンド:
logout
```
```text:結果(例):
      Connection to 54.32.10.999 closed.
```

ログアウトして再び作業端末に戻ります。

5.7. Webブラウザでサービス復旧を確認
---------------------

Webブラウザをリロードして再びWebページが表示されることを確認します。
以上でハンズオンの目的は達成されました。お疲れ様でした。


6. 後片付け
===========

6.1. サービスARNの取得
---------------------

```bash:コマンド:
SERVICE_ARN=`aws ecs list-services --query serviceArns --output text` && echo ${SERVICE_ARN}
```
```text:結果(例):
      arn:aws:ecs:ap-northeast-1:639395043347:service/default/ecr-handson-service
```

6.2. サービスの起動数を0にする
---------------------

サービスを削除するには、まずサービスの起動数(desired-count)を0にしなければなりません。

```bash:コマンド:
aws ecs update-service --service ${ECS_SERVICE} --desired-count 0
```
```text:結果(例):
      {
          "service": {
              "status": "ACTIVE",
              "serviceRegistries": [],
              "pendingCount": 0,
              "launchType": "EC2",
              "enableECSManagedTags": false,
              "schedulingStrategy": "REPLICA",
              "loadBalancers": [],
              "placementConstraints": [],
              "createdAt": 1609056251.51,
              "desiredCount": 0,
              "serviceName": "ecr-handson-service",
              "clusterArn": "arn:aws:ecs:ap-northeast-1:639395043347:cluster/default",
              "createdBy": "arn:aws:iam::639395043347:user/a12",
              "taskDefinition": "arn:aws:ecs:ap-northeast-1:639395043347:task-definition/ecr-handson-task:1",
              "serviceArn": "arn:aws:ecs:ap-northeast-1:639395043347:service/default/ecr-handson-service",
              "propagateTags": "NONE",
              "deploymentConfiguration": {
                  "maximumPercent": 200,
                  "minimumHealthyPercent": 100
              },
      （中略）
                  {
                      "message": "(service ecr-handson-service) (deployment ecs-svc/0432504802543229673) deployment completed.",
                      "id": "4aa6f68e-6d93-4de4-b6f7-a324aa48ff24",
                      "createdAt": 1609056263.6070001
                  },
                  {
                      "message": "(service ecr-handson-service) has started 1 tasks: (task 55fa9449abe044e3bcaed5169f2899f3).",
                      "id": "a7e14ed9-fc11-44a7-90db-23f5758b45fd",
                      "createdAt": 1609056254.198
                  }
              ],
              "runningCount": 1,
              "placementStrategy": []
          }
      }
```

6.3. サービスを削除する
---------------------

```bash:コマンド:
aws ecs delete-service --service ${ECS_SERVICE}
```
```text:結果(例):
      {
          "service": {
              "status": "DRAINING",
              "serviceRegistries": [],
              "pendingCount": 0,
              "launchType": "EC2",
              "enableECSManagedTags": false,
              "schedulingStrategy": "REPLICA",
              "loadBalancers": [],
              "placementConstraints": [],
              "createdAt": 1609056251.51,
              "desiredCount": 0,
              "serviceName": "ecr-handson-service",
              "clusterArn": "arn:aws:ecs:ap-northeast-1:639395043347:cluster/default",
              "createdBy": "arn:aws:iam::639395043347:user/a12",
              "taskDefinition": "arn:aws:ecs:ap-northeast-1:639395043347:task-definition/ecr-handson-task:1",
              "serviceArn": "arn:aws:ecs:ap-northeast-1:639395043347:service/default/ecr-handson-service",
              "propagateTags": "NONE",
              "deploymentConfiguration": {
                  "maximumPercent": 200,
                  "minimumHealthyPercent": 100
              },
      （中略）
                  {
                      "message": "(service ecr-handson-service) (deployment ecs-svc/0432504802543229673) deployment completed.",
                      "id": "4aa6f68e-6d93-4de4-b6f7-a324aa48ff24",
                      "createdAt": 1609056263.6070001
                  },
                  {
                      "message": "(service ecr-handson-service) has started 1 tasks: (task 55fa9449abe044e3bcaed5169f2899f3).",
                      "id": "a7e14ed9-fc11-44a7-90db-23f5758b45fd",
                      "createdAt": 1609056254.198
                  }
              ],
              "runningCount": 0,
              "placementStrategy": []
          }
      }
```

`runningCount` が `0` になりサービスが削除されました。

6.4. タスク定義ARNの取得
---------------------

```bash:コマンド:
TASK_ARN=`aws ecs list-task-definitions --query taskDefinitionArns --output text` && echo ${TASK_ARN}
```
```text:結果(例):
      arn:aws:ecs:ap-northeast-1:639395043347:task-definition/ecr-handson-task:1
```

6.5. タスク定義の登録解除
---------------------

タスク定義には削除が存在しません。登録解除を行います。

```bash:コマンド:
aws ecs deregister-task-definition --task-definition ${TASK_ARN}
```
```text:結果(例):
      {
          "taskDefinition": {
              "status": "INACTIVE",
              "family": "ecr-handson-task",
              "placementConstraints": [],
              "requiresAttributes": [
                  {
                      "name": "com.amazonaws.ecs.capability.ecr-auth"
                  }
              ],
              "compatibilities": [
                  "EC2"
              ],
              "volumes": [],
              "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:639395043347:task-definition/ecr-handson-task:1",
              "containerDefinitions": [
                  {
                      "environment": [],
                      "name": "ecr-handson-task",
                      "mountPoints": [],
                      "image": "639395043347.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-handson-httpd:latest",
                      "cpu": 0,
                      "portMappings": [
                          {
                              "protocol": "tcp",
                              "containerPort": 80,
                              "hostPort": 80
                          }
                      ],
                      "memory": 256,
                      "essential": true,
                      "volumesFrom": []
                  }
              ],
              "revision": 1
          }
      }
```

6.6. コンテナインスタンスARNの取得
---------------------

```bash:コマンド:
EC2_INSTANCE_ARN=`aws ecs list-container-instances --query containerInstanceArns --output text` && echo ${EC2_INSTANCE_ARN}
```
```text:結果(例):
      arn:aws:ecs:ap-northeast-1:639395043347:container-instance/default/06aba53290cd45ddbe78bb4ef5964f7d
```

6.7. コンテナインスタンスの登録解除
---------------------

ECSクラスタを削除するため登録されているコンテナインスタンス(EC2)を登録解除します。

```bash:コマンド:
aws ecs deregister-container-instance --container-instance ${EC2_INSTANCE_ARN} --force
```
```text:結果(例):
      {
          "containerInstance": {
              "status": "INACTIVE",
              "registeredAt": 1609053944.211,
              "registeredResources": [
                  {
                      "integerValue": 1024,
                      "longValue": 0,
                      "type": "INTEGER",
                      "name": "CPU",
                      "doubleValue": 0.0
                  },
      （中略）
                  {
                      "name": "com.amazonaws.ecs.capability.task-iam-role"
                  }
              ],
              "versionInfo": {
                  "agentVersion": "1.48.1",
                  "agentHash": "e9b600d2",
                  "dockerVersion": "DockerVersion: 19.03.13-ce"
              },
              "runningTasksCount": 0,
              "attachments": []
          }
      }
```

6.8. ECSクラスタの削除
---------------------

```bash:コマンド:
aws ecs delete-cluster --cluster default
```
```text:結果(例):
      {
          "cluster": {
              "status": "INACTIVE",
              "statistics": [],
              "tags": [],
              "clusterName": "default",
              "registeredContainerInstancesCount": 0,
              "pendingTasksCount": 0,
              "runningTasksCount": 0,
              "activeServicesCount": 0,
              "clusterArn": "arn:aws:ecs:ap-northeast-1:639395043347:cluster/default"
          }
      }
```

6.9. コンテナインスタンスの削除（ターミネート）
---------------------

```bash:コマンド:
aws ec2 terminate-instances --instance-ids ${EC2_INSTANCE_ID}
```
```text:結果(例):
      {
          "TerminatingInstances": [
              {
                  "InstanceId": "i-0a5bc15f73529ddee",
                  "CurrentState": {
                      "Code": 32,
                      "Name": "shutting-down"
                  },
                  "PreviousState": {
                      "Code": 16,
                      "Name": "running"
                  }
              }
          ]
      }
```

6.10. ECRレポジトリの削除
---------------------

```bash:コマンド:
aws ecr delete-repository --repository-name ${ECR_REPOSITORY} --force
```
```text:結果(例):
      {
          "repository": {
              "registryId": "639395043347",
              "repositoryName": "ecr-handson-httpd",
              "repositoryArn": "arn:aws:ecr:ap-northeast-1:639395043347:repository/ecr-handson-httpd",
              "createdAt": 1609053056.0,
              "repositoryUri": "639395043347.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-handson-httpd"
          }
      }
```

以上で後片付け完了です。（実際には課金対象外のIAMロールやセキュリティグループなどが残っています）
お疲れ様でした。



まとめ
===========

コンテナはもともとCLIで使う物なのでAWS CLIとの相性が良いですね。

気になる点は。
レポジトリのポリシー制御がマネジメントコンソールだと複数項目を任意に設定・削除できるのに対し、CLIでは一括設定・削除しかなく、変更する場合は変更後の内容を自分で作ってJSONで引き渡すしかありません。
これはECRに限らず他のサービスでもそうなので現在のAWSCLIの仕様なのでしょう。

ECRはdocker hub互換のコンテナレジストリですがAWS内部に存在しVPCエンドポイントにも対応しており、速度・セキュリティ共にdocker hubから呼び出すよりも優れていますからECSで利用するコンテナイメージはECRを利用するのが基本となるでしょう。
<STRIKE>ECRにはVPCエンドポイントがありません。
AWSという同一AS内で通信が完結しているから気にしなくて良いとも思えますが会社の方針で使用許可が下りない場合もありそうですね。
今後に期待しましょう。<STRIKE>