(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
aws --version
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.1. リージョンの決定
東京リージョンで利用可能となりましたので利用リージョンを東京リージョンに設定します。
(カレントユーザが利用するカレントリージョンも切り変わります。)
ECRは現在のところ us-east-1(バージニア)、us-west-2(オレゴン)リージョンにしかありません。(2016年3月オレゴンが追加されたので追記)
今回はus-east-1リージョン使うように設定します。
export AWS_DEFAULT_REGION='ap-northeast-1'
0.2. 変数の確認
プロファイルとリージョンが想定のものになっていることを確認します。
aws configure list
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
- レポジトリの構築
===========
ecr-handson-httpd というレポジトリを作成してみます。
1.1. レポジトリの作成
ECR_REPOSITORY=ecr-handson-httpd
aws ecr create-repository --repository-name ${ECR_REPOSITORY}
{
"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. レポジトリの確認
aws ecr describe-repositories
{
"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ファイルで作成します。
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
返り値なし
JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。
jsonlint -q ${ECR_POLICY_NAME}.json
返り値なし
レポジトリへポリシーを設定します。
aws ecr set-repository-policy --repository-name ${ECR_REPOSITORY} --policy-text file://${ECR_POLICY_NAME}.json
{
"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 で現在の設定状況を確認できます。
aws ecr get-repository-policy --repository-name ${ECR_REPOSITORY}
{
"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.1. IAMロールの作成
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" } ] }'
{
"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ロールへ付与します。
POLICY_ARN="arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
aws iam attach-role-policy --role-name ${ROLE_NAME} --policy-arn ${POLICY_ARN}
返り値なし
2.3. インスタンスプロファイルの作成
INSTANCE_PROFILE_NAME="ecr-handson-profile"
aws iam create-instance-profile --instance-profile-name ${INSTANCE_PROFILE_NAME}
{
"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ロールを追加
aws iam add-role-to-instance-profile --instance-profile-name ${INSTANCE_PROFILE_NAME} --role-name ${ROLE_NAME}
返り値なし
2.5. 鍵ペアの作成
鍵ペア virginia を作成します。
秘密鍵ファイルは ~/.ssh/virginia.pem に保存されます。
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}
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAg4AFnT934UdMmuSADElYSWzOQ/XpXUc0570zKwtDB3RXS9Fi8YP8J5jv6QRl
(中略)
MfRletfQDp2Hbth3SnNu2MaFQs8C+ODyx7XTx/13LW247AGVsSfq63jovntOL7PRuFA=
-----END RSA PRIVATE KEY-----
2.6. セキュリティグループの作成
セキュリティグループ ecr-handson-sg を作成します。
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}
{
"GroupId": "sg-0aefee00ff317678c"
}
sg-0aefee00ff317678c
外部からのHTTP接続をセキュリティグループに追加。
aws ec2 authorize-security-group-ingress --group-id ${SG_ID} --protocol 'tcp' --port 80 --cidr 0.0.0.0/0
(返り値なし)
作業中端末のIPアドレスをSSHログインを追加します。
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
54.32.10.999
2.7. コンテナインスタンスの起動
AMI IDの取得
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}
ami-0b60185623255ce57
インスタンスの起動
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}
{
"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の取得
EC2_INSTANCE_ID=`aws ec2 describe-instances --filters Name=instance-state-name,Values=pending --query 'Reservations[].Instances[].InstanceId' --output text` && echo ${EC2_INSTANCE_ID}
i-0a769c87e822e522a
結果が例のように1つだけあることを確認します。
何らかの原因で何も表示されなかったり複数表示された場合はうまくいきません。
この場合は2.7.での出力結果26行目あたりに
"InstanceId": "i-0a5bc15f73529ddee",
と InstanceId が表示された項目があるので、この値を手動で
EC2_INSTANCE_ID="i-0a5bc15f73529ddee"
のように手動でコマンドを打ち環境変数へ格納してください。
2.9. パブリックIPアドレスの取得
EC2_PUBLIC_IP=`aws ec2 describe-instances --instance-id ${EC2_INSTANCE_ID} --query "Reservations[].Instances[].PublicIpAddress" --output text` && echo ${EC2_PUBLIC_IP}
54.32.10.999
- コンテナの構築・ECRレポジトリへのpush
===========
3.1. ECRログイン情報の取得
ECRへの接続にはログインが必要です。
Docker互換のレポジトリですが本家のように簡単にid/pwでは接続出来ないため専用のコマンドが用意されています。
aws ecr get-login --no-include-email
docker login -u AWS -p eyJwYXlsb2FkIjoib2cxK3k4bklYN3hRQWcrQU1sczVycVJQSDZ3dWJyZUNnb01FZzdjMU1YaGRsbUVrWUl1TU5UUmhJallCZmxTQndPYU5zOWYzVXV6SytOZ3Vqd21iV2RXV01RZ1JEeUlITDFXdVNFZTdGSkRqcCthT0h1UkNEOGZOc0ZKNjJRWTFiMHZGUGJadlZQbStLWGNNdjk1SHdJK3ZRQW5xWUFQSUFVaG4vZFRObjkzSEhLamxKYzdDdDBBZDFwZ2ZZQytUbStYQTkybVgwYWJBUEJpcXpFcGFQUCtrTkQ3ZE5hdVZlZXdTMFdLUlBHSHNMcFQwZzk2NVNlclJqb1YxVWVtb043UkV0M0V3YmxZZFVzUlkwM0tLd0h6Z2VHRjYwSklFMC9aaHNqVG1lbmxuUU5xSzYvL282azZXK2ZxQW5UMU5FWUUzOHZJWEp3TlBsTHhpV2RST01PVkhlQkdxN0E1ZFNIRExMWVcyeXdpdkdKbGtOT2dWeU1hUjN0eGU1d0dYUUV1ZThuRmhhK0Vnd2NEK095MWFmMElKME9yREUydEFqakRFVURFbGFMR09HNSt4bFE4WkpTWGJ4RTNiUFFmM1FUeVZnRnJISjdlSlFVWEwvK3lxdS9Va3QwbVhrYzRiWWNyR3FCcEV6K3pWbWdsYnZqSzhZY1ZXNm5BV2oyZDdQWWNURlVaWHZuWUN1Y0dCL0txVmF3dmFiMUdRWDBsUmVLMWdPdUJKNUpyRFc4dFdWUTFZOVhzU05iTzl3R25nQndkd0hYcXhMQW1WVGRuSXNPaDRpR2VCTUgvOFZVUGl2NEZJc3NqVS9YVk5GN3A5bmZKc1ZZS2NQRUgveG10TXlvZFdmOGtBdk1pbUVhMWFiS1liMGhvcmFOQTU1b1BoQk5hZ0F0SUgvZ1VBanppTVh0bzBQUzZYZzB1UnlUbUNrVlRLTDVGeWZjWFZZMWNwU01XOHpvZkFUUVU0L3hRekpud1FGRzd3Rk1QTUVoZmc0RTlkcm1TQllwQVNVZWVGRmoxd21zTEhkNzI0Y2V3ZTR4VlRKV2xTK0FSUDdZcjVyeTVrS2cxSEw0UjdxcVVCRWgyNm5DUk96cEdEVzJhbXc2SG44VjREcmZ2dHl4TUVxbXNTOHZpTUM3c0ZHR0pwblNwNTFER1pkV2NMeUhXZGl1QVpZMzlLMUg1QmsxWnNWVkwrR0hmRzI5NzVhanNSU2FtYUNPZHBjZWZUUFJuRXJHNVVwSEhXbG9SMUE5U3JleGJTN3R3ejBMWGFCZ1NLam9wYkxPUW5xS0xDWlpWUTJ4RU01RDhyUmZxNDl6NDROOWswYkhMd0p4dm9LNno4ZGExWEM5VUNSNzZsMm5SQjlTYzZLNU8zaWVaN05xbkJwbWg1OVN4OTRxekllWmdSQWw2ZHJhU1NXdlhJa3RsUHVHMXRKK3owK3J3UHdQcG01U2dxMHVEZ1l3UTQwMTlQblZTaW8zVnRFUVRoOVdXM0dRdTRJMmRnelZ6WjcvZ0NZc3Y5b1Z3SEw3RzNseVdrM1VRRENSWVB3dVlBdUVRTkthOU5iYng4NnQweXRnZHkzclk9IiwiZGF0YWtleSI6IkFRRUJBSGdBTWZLRGxJb3BDNnpzMGJNZFJyWVNIYS9DMzlrQ3JjUDhrVnByRTlmK2tRQUFBSDR3ZkFZSktvWklodmNOQVFjR29HOHdiUUlCQURCb0Jna3Foa2lHOXcwQkJ3RXdIZ1lKWUlaSUFXVURCQUV1TUJFRURPMVlpb2FxbkdVZnNHamw3Z0lCRUlBN0t6OStYcWZhTTdWRWVlOTB2SFdHcVc1cTB3K0pMNysxbm9tOERUWUNKcTBuTmNScEVoYUthZjhQdzZFclNwTVlxUndOQ0RIRVI4YTVuTkU9IiwidmVyc2lvbiI6IjIiLCJ0eXBlIjoiREFUQV9LRVkiLCJleHBpcmF0aW9uIjoxNjA5MDk3NDEyfQ== https://639395043347.dkr.ecr.ap-northeast-1.amazonaws.com
非常に長いですがこれで1行です。
この後で使うのでメモ帳などにコピーしておいてください。
3.2. コンテナインスタンスへのログイン
ssh -l ec2-user -i ${FILE_SSH_KEY} ${EC2_PUBLIC_IP}
(何か聞かれたら yes と答える)
__| __| __|
_| ( \__ \ 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. で取得したログイン情報(非常に長い1行)をペーストして実行します。
docker login -u AWS -p eyJwYXlsb2FkIjoib2cxK3k4bklYN3hRQWcrQU1sczVycVJQSDZ3dWJyZUNnb01FZzdjMU1YaGRsbUVrWUl1TU5UUmhJallCZmxTQndPYU5zOWYzVXV6SytOZ3Vqd21iV2RXV01RZ1JEeUlITDFXdVNFZTdGSkRqcCthT0h1UkNEOGZOc0ZKNjJRWTFiMHZGUGJadlZQbStLWGNNdjk1SHdJK3ZRQW5xWUFQSUFVaG4vZFRObjkzSEhLamxKYzdDdDBBZDFwZ2ZZQytUbStYQTkybVgwYWJBUEJpcXpFcGFQUCtrTkQ3ZE5hdVZlZXdTMFdLUlBHSHNMcFQwZzk2NVNlclJqb1YxVWVtb043UkV0M0V3YmxZZFVzUlkwM0tLd0h6Z2VHRjYwSklFMC9aaHNqVG1lbmxuUU5xSzYvL282azZXK2ZxQW5UMU5FWUUzOHZJWEp3TlBsTHhpV2RST01PVkhlQkdxN0E1ZFNIRExMWVcyeXdpdkdKbGtOT2dWeU1hUjN0eGU1d0dYUUV1ZThuRmhhK0Vnd2NEK095MWFmMElKME9yREUydEFqakRFVURFbGFMR09HNSt4bFE4WkpTWGJ4RTNiUFFmM1FUeVZnRnJISjdlSlFVWEwvK3lxdS9Va3QwbVhrYzRiWWNyR3FCcEV6K3pWbWdsYnZqSzhZY1ZXNm5BV2oyZDdQWWNURlVaWHZuWUN1Y0dCL0txVmF3dmFiMUdRWDBsUmVLMWdPdUJKNUpyRFc4dFdWUTFZOVhzU05iTzl3R25nQndkd0hYcXhMQW1WVGRuSXNPaDRpR2VCTUgvOFZVUGl2NEZJc3NqVS9YVk5GN3A5bmZKc1ZZS2NQRUgveG10TXlvZFdmOGtBdk1pbUVhMWFiS1liMGhvcmFOQTU1b1BoQk5hZ0F0SUgvZ1VBanppTVh0bzBQUzZYZzB1UnlUbUNrVlRLTDVGeWZjWFZZMWNwU01XOHpvZkFUUVU0L3hRekpud1FGRzd3Rk1QTUVoZmc0RTlkcm1TQllwQVNVZWVGRmoxd21zTEhkNzI0Y2V3ZTR4VlRKV2xTK0FSUDdZcjVyeTVrS2cxSEw0UjdxcVVCRWgyNm5DUk96cEdEVzJhbXc2SG44VjREcmZ2dHl4TUVxbXNTOHZpTUM3c0ZHR0pwblNwNTFER1pkV2NMeUhXZGl1QVpZMzlLMUg1QmsxWnNWVkwrR0hmRzI5NzVhanNSU2FtYUNPZHBjZWZUUFJuRXJHNVVwSEhXbG9SMUE5U3JleGJTN3R3ejBMWGFCZ1NLam9wYkxPUW5xS0xDWlpWUTJ4RU01RDhyUmZxNDl6NDROOWswYkhMd0p4dm9LNno4ZGExWEM5VUNSNzZsMm5SQjlTYzZLNU8zaWVaN05xbkJwbWg1OVN4OTRxekllWmdSQWw2ZHJhU1NXdlhJa3RsUHVHMXRKK3owK3J3UHdQcG01U2dxMHVEZ1l3UTQwMTlQblZTaW8zVnRFUVRoOVdXM0dRdTRJMmRnelZ6WjcvZ0NZc3Y5b1Z3SEw3RzNseVdrM1VRRENSWVB3dVlBdUVRTkthOU5iYng4NnQweXRnZHkzclk9IiwiZGF0YWtleSI6IkFRRUJBSGdBTWZLRGxJb3BDNnpzMGJNZFJyWVNIYS9DMzlrQ3JjUDhrVnByRTlmK2tRQUFBSDR3ZkFZSktvWklodmNOQVFjR29HOHdiUUlCQURCb0Jna3Foa2lHOXcwQkJ3RXdIZ1lKWUlaSUFXVURCQUV1TUJFRURPMVlpb2FxbkdVZnNHamw3Z0lCRUlBN0t6OStYcWZhTTdWRWVlOTB2SFdHcVc1cTB3K0pMNysxbm9tOERUWUNKcTBuTmNScEVoYUthZjhQdzZFclNwTVlxUndOQ0RIRVI4YTVuTkU9IiwidmVyc2lvbiI6IjIiLCJ0eXBlIjoiREFUQV9LRVkiLCJleHBpcmF0aW9uIjoxNjA5MDk3NDEyfQ== https://639395043347.dkr.ecr.ap-northeast-1.amazonaws.com
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レジストリです。(レポジトリが置かれている場所)
ECR_REGISTRY=639395043347.dkr.ecr.ap-northeast-1.amazonaws.com
返り値なし
このコマンド(ECR_REGISTRY=639395043347.dkr.ecr.ap-northeast-1.amazonaws.com
)は後でもう一度実行するのでメモ帳などにコピーしておいてください。
3.4. Dockerfile ファイルの作成
ハンズオン用にWebサーバのコンテナを作成します。
今回はCentOS6をベースにapache httpdのコンテナを作成します。
まずは Dockerfile を作成します。
注意:コマンド1行目のECR_REPOSITORYは 1.1. と必ず同一内容にしてください
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
返り値なし
3.5. コンテナイメージのビルド
Dockerファイルからコンテナイメージをビルドします。
(この処理は大量の画面出力がされ、少々時間がかかります。)
docker build -t ${ECR_REGISTRY}/${ECR_REPOSITORY} ${ECR_REPOSITORY}
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からコンテナを利用可能にします。
docker push ${ECR_REGISTRY}/${ECR_REPOSITORY}:latest
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. コンテナインスタンスからログアウトする
logout
Connection to 54.32.10.999 closed.
ログアウトして作業端末に戻りました。
(必ずログアウトしてください。ECS用インスタンスにはAWSCLIが存在しないため以後の操作が失敗します)
3.8. コンテナイメージがpushされたか確認します
aws ecr list-images --repository-name ${ECR_REPOSITORY} --output text
IMAGEIDS sha256:d278bf23016fbf500987f332d9b3c3e5188deb5e0394fd3a1b82b17087793750 latest
表示された IMAGEIDS
のsha256ハッシュ値と、手順3.6. 最下行 latest: digest:
のsha256ハッシュ値が一致していることを確認します。
- ECSクラスタを構築しコンテナ稼働
===========
dockerコンテナを直接起動しても動作しますが、コンテナのメリットは落ちたときに自動的に新しいコンテナが起動されることです。
これをECSクラスタで実現します。
ECSクラスタは実行するコンテナの内容を定義したタスク定義、サービスとしてどのようにタスクを実行するかを設定するサービスからなります。
サービスの集合体をクラスタと呼びます。
4.1. ECRレジストリの環境変数への格納
先ほど 3.3. でメモしたコマンド(ECR_REGISTRY=...)を実行します。
これは作業用インスタンスに戻ったため、変数に値が格納されていないためです。
ECR_REGISTRY=639395043347.dkr.ecr.ap-northeast-1.amazonaws.com
返り値なし
4.2. タスク定義の登録
タスク定義をJSONファイルで作成します。
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
返り値なし
JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。
jsonlint -q ${ECS_TASK}.json
返り値なし
タスク定義を登録します。
aws ecs register-task-definition --family ${ECS_TASK} --container-definitions file://${ECS_TASK}.json
{
"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
}
}
最後から3行目に revision という項目があります。最初に登録すると 1 です。
タスク定義は登録のみで変更コマンドはありません。変更したい場合は新しいタスク定義を登録するとその内容に上書き更新されます。その際 revision が増加します。
次の手順で行うサービスの登録で実行するタスク定義の revision を指定することができます。
4.3. サービスの登録
実行させたタスクをサービスとして登録します。
ECS_SERVICE="ecr-handson-service"
aws ecs create-service --service-name ${ECS_SERVICE} --task-definition ${ECS_TASK} --desired-count 1
{
"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秒ぐらいかかります。しばらく待ってからコマンドを実行します。
aws ecs describe-services --services ${ECS_SERVICE}
{
"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アドレスを確認します。
EC2_PUBLIC_IP=`aws ec2 describe-instances --instance-id ${EC2_INSTANCE_ID} --query "Reservations[].Instances[].PublicIpAddress" --output text` && echo ${EC2_PUBLIC_IP}
54.32.10.999
Webブラウザへ表示されたIPアドレスをコピーペーストしてアクセスしてみます。
http://54.32.10.999/
Apache httpdのテストページが表示されれば成功です。
また後でアクセスするのでブラウザの画面はそのままにしておきましょう。
- コンテナを強制終了させてECSによる自動再稼働を確認する
===========
5.1. コンテナインスタンスへのログイン
ssh -l ec2-user -i ${FILE_SSH_KEY} ${EC2_PUBLIC_IP}
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を確認する
docker ps
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
コマンドを実行します。
強制終了によって落ちたことを確認するにはタイミングが重要です。コマンドを実行したらすぐに次の手順へ進みましょう。
docker kill d4437cfcb85a
d4437cfcb85a
5.4. Webブラウザで落ちたか確認
Webブラウザをリロードするとエラーとなり表示されないことを確認します。
普通に表示される!?
そんなこともあります。ECSのサービス再起動は優秀ですぐに復活するため、表示されたとしても気にしない。
結果がどうあれ次へ進みます。
5.5. コンテナIDの変化を確認する
ECSによってサービスが再起動されたことを論理的に確認するために、旧コンテナが消滅し新しいコンテナが起動されたことを確認します。
docker ps
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. コンテナインスタンスからログアウトする
logout
Connection to 54.32.10.999 closed.
ログアウトして再び作業端末に戻ります。
5.7. Webブラウザでサービス復旧を確認
Webブラウザをリロードして再びWebページが表示されることを確認します。
以上でハンズオンの目的は達成されました。お疲れ様でした。
- 後片付け
===========
6.1. サービスARNの取得
SERVICE_ARN=`aws ecs list-services --query serviceArns --output text` && echo ${SERVICE_ARN}
arn:aws:ecs:ap-northeast-1:639395043347:service/default/ecr-handson-service
6.2. サービスの起動数を0にする
サービスを削除するには、まずサービスの起動数(desired-count)を0にしなければなりません。
aws ecs update-service --service ${ECS_SERVICE} --desired-count 0
{
"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. サービスを削除する
aws ecs delete-service --service ${ECS_SERVICE}
{
"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の取得
TASK_ARN=`aws ecs list-task-definitions --query taskDefinitionArns --output text` && echo ${TASK_ARN}
arn:aws:ecs:ap-northeast-1:639395043347:task-definition/ecr-handson-task:1
6.5. タスク定義の登録解除
タスク定義には削除が存在しません。登録解除を行います。
aws ecs deregister-task-definition --task-definition ${TASK_ARN}
{
"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の取得
EC2_INSTANCE_ARN=`aws ecs list-container-instances --query containerInstanceArns --output text` && echo ${EC2_INSTANCE_ARN}
arn:aws:ecs:ap-northeast-1:639395043347:container-instance/default/06aba53290cd45ddbe78bb4ef5964f7d
6.7. コンテナインスタンスの登録解除
ECSクラスタを削除するため登録されているコンテナインスタンス(EC2)を登録解除します。
aws ecs deregister-container-instance --container-instance ${EC2_INSTANCE_ARN} --force
{
"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クラスタの削除
aws ecs delete-cluster --cluster default
{
"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. コンテナインスタンスの削除(ターミネート)
aws ec2 terminate-instances --instance-ids ${EC2_INSTANCE_ID}
{
"TerminatingInstances": [
{
"InstanceId": "i-0a5bc15f73529ddee",
"CurrentState": {
"Code": 32,
"Name": "shutting-down"
},
"PreviousState": {
"Code": 16,
"Name": "running"
}
}
]
}
6.10. ECRレポジトリの削除
aws ecr delete-repository --repository-name ${ECR_REPOSITORY} --force
{
"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を利用するのが基本となるでしょう。
ECRにはVPCエンドポイントがありません。
AWSという同一AS内で通信が完結しているから気にしなくて良いとも思えますが会社の方針で使用許可が下りない場合もありそうですね。
今後に期待しましょう。