ecs-cmd¶
get ami list¶
amazon linux 2¶
-
get full list
-
get ecs recommended optimized ami
-
get last 2 AMI ID
export AWS_DEFAULT_REGION=us-east-1 AMI_IDS=($( aws ssm get-parameters-by-path \ --path /aws/service/ecs/optimized-ami/amazon-linux-2 \ --query 'sort_by(Parameters,&LastModifiedDate)[?contains(ARN, `arn:aws:ssm:'"${AWS_DEFAULT_REGION}"'::parameter/aws/service/ecs/optimized-ami/amazon-linux-2/ami-`)==`true`]' \ |jq -r '.[].Value' |jq -r '.image_id' \ |tail -n 2 )) OLD_AMI_ID=${AMI_IDS[0]} NEW_AMI_ID=${AMI_IDS[1]} echo ${AMI_IDS[@]}
amazon linux 2023¶
AMI_IDS=($(
aws ssm get-parameters-by-path \
--path /aws/service/ecs/optimized-ami/amazon-linux-2023 \
--query 'sort_by(Parameters,&LastModifiedDate)[?contains(ARN, `arn:aws:ssm:'"${AWS_DEFAULT_REGION}"'::parameter/aws/service/ecs/optimized-ami/amazon-linux-2023/ami-`)==`true`]' \
|jq -r '.[].Value' |jq -r '.image_id' \
|tail -n 2
))
windows 2019¶
export AWS_DEFAULT_REGION=us-east-1
export AMI_WIN2019_NAME="Windows_Server-2019-English-Full-ECS_Optimized"
AMI_IDS=($(
aws ec2 describe-images \
--filter "Name=name,Values=${AMI_WIN2019_NAME}*" \
--query 'Images[*].[ImageId,CreationDate,Name]' --output text |grep ECS_Optimized |sort -k2 |tail -n 2 |awk '{print $1}'
))
OLD_AMI_ID=${AMI_IDS[0]}
NEW_AMI_ID=${AMI_IDS[1]}
echo ${AMI_IDS[@]}
create-ecs-cluster-¶
create launch template v1¶
ECS_CLUSTER=myecs1
VPC_ID=$(aws ec2 describe-vpcs --filter Name=is-default,Values=true --query 'Vpcs[0].VpcId' --output text)
export AWS_PAGER=""
export AWS_DEFAULT_REGION=us-east-1
-
get default security group (func-create-sg.sh)
-
execute function to create launch template (auto-scaling-cmd)
-
execute function to create ec2 admin role (git/git-mkdocs/CLI/awscli/iam-cmd)
add user data to launch template and make v2 as default¶
linux node¶
TMP=$(mktemp --suffix .UserData)
envsubst >${TMP} <<-EOF
#!/bin/bash
echo ECS_CLUSTER=${ECS_CLUSTER} >> /etc/ecs/ecs.config
sudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP
sudo service iptables save
echo ECS_AWSVPC_BLOCK_IMDS=true >> /etc/ecs/ecs.config
EOF
B64STRING=$(cat ${TMP}|base64 -w 0)
aws ec2 create-launch-template-version --launch-template-id ${LAUNCH_TEMPLATE_ID} \
--version-description ${LAUNCH_TEMPLATE_ID}-$(TZ=CST-8 date +%H%M) \
--source-version 1 \
--launch-template-data '{"UserData":"'"${B64STRING}"'","IamInstanceProfile":{"Arn":"'"${INSTANCE_PROFILE_ARN}"'"}}' |tee ${TMP}.out
aws ec2 modify-launch-template --launch-template-id ${LAUNCH_TEMPLATE_ID} --default-version "2"
if you use gMSA on linux, add following lines to beginning of user data
#!/bin/bash
echo "ECS_GMSA_SUPPORTED=true" >> /etc/ecs/ecs.config
echo "sleeping for 80 secs to avoid RPM lock error..."
sleep 80s
dnf install dotnet realmd oddjob oddjob-mkhomedir sssd adcli krb5-workstation samba-common-tools credentials-fetcher -y
systemctl enable credentials-fetcher
systemctl start credentials-fetcher
windows node¶
TMP=$(mktemp --suffix .UserData)
envsubst >${TMP} <<-EOF
Content-Type: multipart/mixed; boundary="67fa5624dc94e98fee4093fbc2d9d1de2825990f57aa9b139aed1acf3b3a"
MIME-Version: 1.0
--67fa5624dc94e98fee4093fbc2d9d1de2825990f57aa9b139aed1acf3b3a
Content-Type: text/text/plain; charset="utf-8"
Mime-Version: 1.0
<powershell>
Import-Module ECSTools;
[Environment]::SetEnvironmentVariable("ECS_CLUSTER", "${ECS_CLUSTER}", "Machine");
[Environment]::SetEnvironmentVariable("ECS_GMSA_SUPPORTED", "true", "Machine")
[Environment]::SetEnvironmentVariable("ECS_ENABLE_AWSLOGS_EXECUTIONROLE_OVERRIDE", "true", "Machine");
[Environment]::SetEnvironmentVariable("ECS_ENABLE_TASK_ENI", "true", "Machine");
[Environment]::SetEnvironmentVariable("ECS_AVAILABLE_LOGGING_DRIVERS", '["json-file","awslogs"]', "Machine");
[Environment]::SetEnvironmentVariable("ECS_ENABLE_TASK_IAM_ROLE", "true", "Machine");
Install-PackageProvider -Name NuGet -Force;
Initialize-ECSAgent -Cluster '${ECS_CLUSTER}' -EnableTaskIAMRole -AwsvpcBlockIMDS
</powershell>
--67fa5624dc94e98fee4093fbc2d9d1de2825990f57aa9b139aed1acf3b3a
Content-Type: text/text/x-shellscript; charset="utf-8"
Mime-Version: 1.0
#!/bin/bash
echo ECS_CLUSTER=${ECS_CLUSTER} >> /etc/ecs/ecs.config
echo ECS_GMSA_SUPPORTED=true >> /etc/ecs/ecs.config
--67fa5624dc94e98fee4093fbc2d9d1de2825990f57aa9b139aed1acf3b3a--
EOF
B64STRING=$(cat ${TMP}|base64 -w 0)
aws ec2 create-launch-template-version \
--launch-template-id ${LAUNCH_TEMPLATE_ID} \
--version-description ${LAUNCH_TEMPLATE_ID}-$(TZ=CST-8 date +%H%M) \
--source-version 1 \
--launch-template-data '{"UserData":"'"${B64STRING}"'","IamInstanceProfile":{"Arn":"'"${INSTANCE_PROFILE_ARN}"'"}}' |tee ${TMP}.out
aws ec2 modify-launch-template --launch-template-id ${LAUNCH_TEMPLATE_ID} --default-version "2"
create auto scaling group¶
- execute function to create auto scaling group (auto-scaling-cmd)
- ASG’s desire number is zero
create cluster¶
- create ecs cluster (before create asg if desire number is greater than 0)
create capacity provider¶
- capacity provider (if desire number in asg is greater than 0, than no CP needed)
ECS_CAP_PROVIDER=mycp1 aws ecs create-capacity-provider \ --name "${ECS_CAP_PROVIDER}" \ --auto-scaling-group-provider "autoScalingGroupArn=${ASG_ARN},managedScaling={status=ENABLED,targetCapacity=100},managedTerminationProtection=ENABLED" aws ecs put-cluster-capacity-providers \ --cluster ${ECS_CLUSTER} \ --capacity-providers ${ECS_CAP_PROVIDER} \ --default-capacity-provider-strategy capacityProvider=${ECS_CAP_PROVIDER},weight=1
change asg desire number to non-zero¶
- if you do not create CP, change ASG desire number to 1, EC2 will be spun up and add to ECS cluster
get container instances¶
CONTAINER_INST_ARN=($(aws ecs list-container-instances --cluster ${ECS_CLUSTER} \
--query 'containerInstanceArns[]' --output text |xargs ))
CONTAINER_INST=($(
for i in ${CONTAINER_INST_ARN[@]} ; do
echo ${i##*/}
done
))
echo ${CONTAINER_INST[@]}
aws ecs describe-container-instances --cluster ${ECS_CLUSTER} \
--container-instances ${CONTAINER_INST[@]} |tee /tmp/$$-instance
cat /tmp/$$-instance |jq -r '.containerInstances[] | [.ec2InstanceId, .versionInfo.agentVersion]|@tsv'
sample for ami upgrade / replace¶
register task definition¶
TASK_NAME=task2-$(TZ=EAT-8 date +%Y%m%d-%H%M)
envsubst >task-definition.json <<-EOF
{
"containerDefinitions": [
{
"name": "nginx",
"image": "nginx",
"portMappings": [
{
"containerPort": 80,
"hostPort": 80,
"protocol": "tcp",
"name": "nginx-80-tcp",
"appProtocol": "http"
}
],
"essential": true
}
],
"family": "${TASK_NAME}",
"networkMode": "awsvpc",
"requiresCompatibilities": [
"EC2"
],
"cpu": "1024",
"memory": "3072"
}
EOF
aws ecs register-task-definition \
--cli-input-json file://./task-definition.json
create service¶
- create alb and target group first (elb-cmd)
- create service
echo ${ECS_CLUSTER} echo ${TG80_ARN} echo ${TASK_NAME} echo ${VPC_ID} SERVICE_NAME=${TASK_NAME}-svc DEFAULT_SG_ID=$(aws ec2 describe-security-groups \ --filter Name=vpc-id,Values=${VPC_ID} \ --query "SecurityGroups[?GroupName == 'default'].GroupId" \ --output text) ALL_SUBNET_ID=$(aws ec2 describe-subnets \ --filter "Name=vpc-id,Values=${VPC_ID}" \ --query 'Subnets[?MapPublicIpOnLaunch==`true`].SubnetId') envsubst >svc-definition.json <<-EOF { "serviceName": "${SERVICE_NAME}", "taskDefinition": "${TASK_NAME}", "loadBalancers": [ { "targetGroupArn": "${TG80_ARN}", "containerName": "nginx", "containerPort": 80 } ], "networkConfiguration": { "awsvpcConfiguration": { "subnets": ${ALL_SUBNET_ID}, "securityGroups": [ "${DEFAULT_SG_ID}" ], "assignPublicIp": "DISABLED" } }, "desiredCount": 3 } EOF aws ecs create-service \ --cluster ${ECS_CLUSTER} \ --service-name ${SERVICE_NAME} \ --cli-input-json file://svc-definition.json
update-launch-template-¶
- update launch template based on default version
echo ${NEW_AMI_ID} LT_DEF_VER=$(aws ec2 describe-launch-templates --launch-template-ids ${LAUNCH_TEMPLATE_ID} --query 'LaunchTemplates[0].DefaultVersionNumber' --output text) aws ec2 create-launch-template-version --launch-template-id ${LAUNCH_TEMPLATE_ID} \ --version-description ${LAUNCH_TEMPLATE_ID}-$(TZ=CST-8 date +%H%M) \ --source-version ${LT_DEF_VER} \ --launch-template-data '{"ImageId":"'"${NEW_AMI_ID}"'"}' |tee /tmp/$$-new-lt LT_VER=$(cat /tmp/$$-new-lt |jq -r '.LaunchTemplateVersion.VersionNumber')
update-asg-¶
- update asg using new template version
get task definition¶
ECS_SVC_NAME=${SERVICE_NAME}
ECS_TASK_DEF_ARN=$(aws ecs describe-services --cluster ${ECS_CLUSTER} \
--services ${ECS_SVC_NAME} \
--query "services[].deployments[].["taskDefinition"]" --output text)
modify-task-definition-¶
- update task definition
# ami-0b7778d86d8789cff / ami-0f896121197c465b6 echo ${NEW_AMI_ID} aws ecs describe-task-definition --task-definition ${ECS_TASK_DEF_ARN} --query taskDefinition | \ jq '. + {placementConstraints: [{"expression": "attribute:ecs.ami-id == '"${NEW_AMI_ID}"'", "type": "memberOf"}]}' | \ jq 'del(.status)'| jq 'del(.revision)' | jq 'del(.requiresAttributes)' | \ jq '. + {containerDefinitions:[.containerDefinitions[] + {"memory":256, "memoryReservation": 128}]}'| \ jq 'del(.compatibilities)' | jq 'del(.taskDefinitionArn)' | jq 'del(.registeredAt)' | jq 'del(.registeredBy)' > new-task-def.json
- register new one
update service¶
aws ecs update-service --cluster ${ECS_CLUSTER} --service ${SERVICE_NAME} --task-definition ${TASK_DEF_ARN##*/}
func ecsexec to test¶
ECS_CLUSTER=CapacityProviderDemo
function ecsexec {
aws ecs execute-command \
--cluster ${ECS_CLUSTER} --task $1 \
--command /bin/sh --interactive \
--command "curl localhost:5000" \
--region us-east-2
}
sample for domain-less windows¶
envsubst >${TMP}.td.3 <<-'EOF'
{
"family": "windows-gmsa-domainless-task",
"containerDefinitions": [
{
"name": "windows_sample_app",
"image": "111111111111.dkr.ecr.us-east-1.amazonaws.com/amazon-ecs-gmsa-linux/web-site",
"cpu": 1024,
"memory": 1024,
"essential": true,
"credentialSpecs": [
"credentialspecdomainless:arn:aws:ssm:us-east-1:111111111111:parameter/amazon-ecs-gmsa-linux/credspec"
],
"entryPoint": [
"powershell",
"-Command"
],
"command": [
"New-Item -Path C:\\inetpub\\wwwroot\\index.html -ItemType file -Value '<html> <head> <title>Amazon ECS Sample App</title> <style>body {margin-top: 40px; background-color: #333;} </style> </head><body> <div style=color:white;text-align:center> <h1>Amazon ECS Sample App</h1> <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p>' -Force ; C:\\ServiceMonitor.exe w3svc"
],
"portMappings": [
{
"protocol": "tcp",
"containerPort": 80,
"hostPort": 8080
}
]
}
],
"taskRoleArn": "arn:aws:iam::111111111111:role/ecs-exec-demo-task-role",
"executionRoleArn": "arn:aws:iam::111111111111:role/ecs-exec-demo-task-execution-role"
}
EOF
aws ecs register-task-definition \
--cli-input-json file://${TMP}.td.3
others¶
get-windows-ecs-ami-list-¶
aws ssm get-parameters-by-path \
--path /aws/service/ami-windows-latest \
--query 'Parameters[].Name' |grep 2019 |grep ECS
aws ssm get-parameter \
--name "/aws/service/ami-windows-latest/Windows_Server-2019-English-Full-ECS_Optimized"
sample¶
- create asg & launch template first
- auto-scaling-cmd
ECS_CLUSTER_NAME=MyCluster aws ecs create-cluster \ --cluster-name ${ECS_CLUSTER_NAME} ECS_CAP_PROVIDER=MyCapacityProvider aws ecs create-capacity-provider \ --name "${ECS_CAP_PROVIDER}" \ --auto-scaling-group-provider "autoScalingGroupArn=${ASG_ARN},managedScaling={status=ENABLED,targetCapacity=10}" aws ecs put-cluster-capacity-providers \ --cluster ${ECS_CLUSTER_NAME} \ --capacity-providers ${ECS_CAP_PROVIDER} \ --default-capacity-provider-strategy capacityProvider=${ECS_CAP_PROVIDER},weight=1
- auto-scaling-cmd