Skip to content

Instantly share code, notes, and snippets.

@jlis
Created May 15, 2018 13:16
Show Gist options
  • Star 77 You must be signed in to star a gist
  • Fork 26 You must be signed in to fork a gist
  • Save jlis/4bc528041b9661ae6594c63cd2ef673c to your computer and use it in GitHub Desktop.
Save jlis/4bc528041b9661ae6594c63cd2ef673c to your computer and use it in GitHub Desktop.
AWS ECS and ECR deployment via Docker and Gitlab CI
image: docker:latest
variables:
REPOSITORY_URL: <AWS ACCOUNT ID>.dkr.ecr.eu-central-1.amazonaws.com/<ECS REPOSITORY NAME>
REGION: eu-central-1
TASK_DEFINTION_NAME: <TASK DEFINITION NAME>
CLUSTER_NAME: <CLUSTER NAME>
SERVICE_NAME: <SERVICE NAME>
services:
- docker:dind
before_script:
- apk add --no-cache curl jq python py-pip
- pip install awscli
- $(aws ecr get-login --no-include-email --region "${REGION}")
- IMAGE_TAG="$(echo $CI_COMMIT_SHA | head -c 8)"
stages:
- build
- deploy
build:
stage: build
script:
- echo "Building image..."
- docker build -t $REPOSITORY_URL:latest .
- echo "Tagging image..."
- docker tag $REPOSITORY_URL:latest $REPOSITORY_URL:$IMAGE_TAG
- echo "Pushing image..."
- docker push $REPOSITORY_URL:latest
- docker push $REPOSITORY_URL:$IMAGE_TAG
only:
- master
deploy:
stage: deploy
script:
- echo $REPOSITORY_URL:$IMAGE_TAG
- TASK_DEFINITION=$(aws ecs describe-task-definition --task-definition "$TASK_DEFINTION_NAME" --region "${REGION}")
- NEW_CONTAINER_DEFINTIION=$(echo $TASK_DEFINITION | python $CI_PROJECT_DIR/update_task_definition_image.py $REPOSITORY_URL:$IMAGE_TAG)
- echo "Registering new container definition..."
- aws ecs register-task-definition --region "${REGION}" --family "${TASK_DEFINTION_NAME}" --container-definitions "${NEW_CONTAINER_DEFINTIION}"
- echo "Updating the service..."
- aws ecs update-service --region "${REGION}" --cluster "${CLUSTER_NAME}" --service "${SERVICE_NAME}" --task-definition "${TASK_DEFINTION_NAME}"
only:
- master
import sys, json, argparse
parser = argparse.ArgumentParser('Replaces image in the task definition')
parser.add_argument('image_uri', metavar='I', type=str, nargs='+',
help='The new image URI')
args = parser.parse_args()
definition = json.load(sys.stdin)['taskDefinition']['containerDefinitions']
definition[0]['image'] = args.image_uri[0]
print json.dumps(definition)
@philipgiuliani
Copy link

If you have volumes you can add the following:

- VOLUMES=$(echo $TASK_DEFINITION | jq '.taskDefinition.volumes')
- aws ecs register-task-definition --region "${REGION}" --family "${TASK_DEFINTION_NAME}" --container-definitions "${NEW_CONTAINER_DEFINTIION} --volumes "${VOLUMES}"

@tsuz
Copy link

tsuz commented Aug 14, 2019

- IMAGE_TAG="$(echo $CI_COMMIT_SHA | head -c 8)"

could also be replaced by

- IMAGE_TAG="$CI_COMMIT_SHORT_SHA"

Reference

@davehodg
Copy link

I'm getting:

Pushing image...
$ docker push $REPOSITORY_URL:latest
The push refers to repository [733970770342.dkr.ecr.eu-west-2.amazonaws.com/nettools]
729283870dfa: Preparing
6ead9a587789: Preparing
1e0f48f41d21: Preparing
3bfeb766f97b: Preparing
ea1227feeccb: Preparing
9cae1895156d: Preparing
52dba9daa22c: Preparing
78c1b9419976: Preparing
no basic auth credentials

The internet's not being helpful. I've made a few suggested mods.

@kamihouse
Copy link

kamihouse commented Oct 7, 2019

@davehodg try custom this:

Login: $(aws ecr get-login --no-include-email --region "${AWS_DEFAULT_REGION}")

variables:
  DOCKER_DRIVER: "overlay2"
  DOCKER_TLS_CERTDIR: ""
  DOCKER_AUTH_CONFIG: "{\"credsStore\": \"ecr-login\"}"

@davehodg
Copy link

davehodg commented Oct 8, 2019

- docker build -t xxx .
- docker tag xxx:latest 733970770999.dkr.ecr.eu-west-2.amazonaws.com/xxx:latest
- echo "Pushing image..."
- docker push 733970770999.dkr.ecr.eu-west-2.amazonaws.com/nettools:latest
- $(aws ecr get-login --no-include-email --region eu-west-2| sed 's|https://||')
- aws ecs update-service --service xx-container-service --task-definition task-definition:20 --cluster xxx-cluster

Works fine!

@hikmahgumelar
Copy link

Nice......

@kutzhanov
Copy link

You can also add the following commands to check if the previous task was successfully stopped and the new one is started:

- TASK_ARN=$(aws ecs list-tasks --region "${REGION}" --cluster "${CLUSTER_NAME}" --service-name "${SERVICE_NAME}")
- TASK_ID=$(echo ${TASK_ARN:72:36})
- echo "Waiting until the previous task ${TASK_ID} is stopped..."
- aws ecs wait tasks-stopped --region "${REGION}" --cluster "${CLUSTER_NAME}" --tasks "${TASK_ID}"
- echo "The previous task ${TASK_ID} has stopped."
- TASK_ARN=$(aws ecs list-tasks --region "${REGION}" --cluster "${CLUSTER_NAME}" --service-name "${SERVICE_NAME}")
- TASK_ID=$(echo ${TASK_ARN:72:36})
- echo "New task ${TASK_ID} is running."
- aws ecs describe-tasks --region "${REGION}" --cluster "${CLUSTER_NAME}" --tasks "${TASK_ID}"

@ccetina
Copy link

ccetina commented Jun 27, 2020

(y)

@guerzon
Copy link

guerzon commented Aug 9, 2020

Hey, newbie question:

/bin/bash: line 102: apk: command not found

..isn't it support to be in the docker image?

@jlis
Copy link
Author

jlis commented Aug 11, 2020

Hey, newbie question:

/bin/bash: line 102: apk: command not found

..isn't it support to be in the docker image?

@guerzon APK is a package manager used by Alpine Linux (for example). It depends what image you're using. For Ubuntu, you might use APT.

@guerzon
Copy link

guerzon commented Aug 12, 2020

Hey, newbie question:
/bin/bash: line 102: apk: command not found
..isn't it support to be in the docker image?

@guerzon APK is a package manager used by Alpine Linux (for example). It depends what image you're using. For Ubuntu, you might use APT.

Yeah I'm aware of that, which was why I was kicking myself when I found out I was using another image in the step where this error showed up. Thanks, all good now.

@pavel-rezabek
Copy link

you can also use --query parameter instead of jq to get specific output:
https://docs.aws.amazon.com/cli/latest/userguide/cli-usage-output.html#cli-usage-output-filter

@fgiudici95
Copy link

fgiudici95 commented Sep 4, 2020

Hey, newbie question:
/bin/bash: line 102: apk: command not found
..isn't it support to be in the docker image?

@guerzon APK is a package manager used by Alpine Linux (for example). It depends what image you're using. For Ubuntu, you might use APT.

Yeah I'm aware of that, which was why I was kicking myself when I found out I was using another image in the step where this error showed up. Thanks, all good now.

Hi @guerzon and @jlis
Can you share your gitlab-runner configuration?
I have the same problem as you.
Thanks

@guerzon
Copy link

guerzon commented Sep 4, 2020

Hey, newbie question:
/bin/bash: line 102: apk: command not found
..isn't it support to be in the docker image?

@guerzon APK is a package manager used by Alpine Linux (for example). It depends what image you're using. For Ubuntu, you might use APT.

Yeah I'm aware of that, which was why I was kicking myself when I found out I was using another image in the step where this error showed up. Thanks, all good now.

Hi @guerzon and @jlis
Can you share your gitlab-runner configuration?
I have the same problem as you.
Thanks

Hi, here is my working configuration: https://gist.github.com/guerzon/3844df5399b28493136c940e0b5fbecd

Just to be clear, my issue was that the stage where I get this error was using another image, and it has nothing to do with my gitlab runner config. But hopefully the config works for you.

@tusharpatil985
Copy link

Hello,

I am running the same in GitlabCI but getting the authentication error in below command:

aws ecs register-task-definition --region "${AWS_DEFAULT_REGION}" --family "${TASK_DEFINITION_NAME}" --container-definitions "${NEW_CONTAINER_DEFINTIION}"

Error :"An error occurred (ClientException) when calling the RegisterTaskDefinition operation: Private repository authentication requires that the task definition specify a value for executionRoleArn."

How to pass the credentials for private repo.

@Kif11
Copy link

Kif11 commented Jun 8, 2021

If you don't want to have external definition file you can also do this.

aws ecs register-task-definition --region ${region} --family ${task_defintion_name} --container-definitions " \
[
  {
    \"name\": \"hippo-ui\",
    \"image\": \"${image}\",
    \"cpu\": 10,
    \"memory\": 256,
    \"essential\": true,
    \"portMappings\": [
      {
        \"containerPort\": 4200
      }
    ],
    \"logConfiguration\": {
      \"logDriver\": \"awslogs\",
      \"options\": { 
        \"awslogs-group\" : \"/ecs/hippo-ui-container\",
        \"awslogs-region\": \"us-east-2\"
      }
    }
  }
]"

@Tushar-Sawdays
Copy link

You can also add the following commands to check if the previous task was successfully stopped and the new one is started:

- TASK_ARN=$(aws ecs list-tasks --region "${REGION}" --cluster "${CLUSTER_NAME}" --service-name "${SERVICE_NAME}")
- TASK_ID=$(echo ${TASK_ARN:72:36})
- echo "Waiting until the previous task ${TASK_ID} is stopped..."
- aws ecs wait tasks-stopped --region "${REGION}" --cluster "${CLUSTER_NAME}" --tasks "${TASK_ID}"
- echo "The previous task ${TASK_ID} has stopped."
- TASK_ARN=$(aws ecs list-tasks --region "${REGION}" --cluster "${CLUSTER_NAME}" --service-name "${SERVICE_NAME}")
- TASK_ID=$(echo ${TASK_ARN:72:36})
- echo "New task ${TASK_ID} is running."
- aws ecs describe-tasks --region "${REGION}" --cluster "${CLUSTER_NAME}" --tasks "${TASK_ID}"

You can also add the following commands to check if the previous task was successfully stopped and the new one is started:

- TASK_ARN=$(aws ecs list-tasks --region "${REGION}" --cluster "${CLUSTER_NAME}" --service-name "${SERVICE_NAME}")
- TASK_ID=$(echo ${TASK_ARN:72:36})
- echo "Waiting until the previous task ${TASK_ID} is stopped..."
- aws ecs wait tasks-stopped --region "${REGION}" --cluster "${CLUSTER_NAME}" --tasks "${TASK_ID}"
- echo "The previous task ${TASK_ID} has stopped."
- TASK_ARN=$(aws ecs list-tasks --region "${REGION}" --cluster "${CLUSTER_NAME}" --service-name "${SERVICE_NAME}")
- TASK_ID=$(echo ${TASK_ARN:72:36})
- echo "New task ${TASK_ID} is running."
- aws ecs describe-tasks --region "${REGION}" --cluster "${CLUSTER_NAME}" --tasks "${TASK_ID}"

If there are more than ONE task then it should be TASK_ID=$(echo ${TASK_ARN:82:32})

@robsonalves
Copy link

Anyone here has a solution to pass a container environment used on applications?

Like injecting this Envs in the task definition.

@Dascienz
Copy link

Dascienz commented Oct 29, 2021

Note, in Gitlab you can now use $CI_COMMIT_SHORT_SHA instead of "$(echo $CI_COMMIT_SHA | head -c 8)". See: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html#:~:text=is%20built%20for.-,CI_COMMIT_SHORT_SHA,-11.7

@Tushar-Sawdays
Copy link

Anyone here has a solution to pass a container environment used on applications?

Like injecting this Envs in the task definition.

I have used sed command in my pipeline to inject env vars in task definition file.

@Peterragheb
Copy link

    - TASK_DEFINITION=$(aws ecs describe-task-definition --task-definition "$CI_AWS_ECS_TASK_DEFINITION" --region "${AWS_DEFAULT_REGION}")
    - NEW_CONTAINER_DEFINTIION=$(echo $TASK_DEFINITION | jq --arg IMAGE "$DOCKER_REGISTRY/$APP_NAME:latest" '.taskDefinition.containerDefinitions[0].image = $IMAGE | .taskDefinition.containerDefinitions[0]')
    - echo "Registering new container definition..."
    - aws ecs register-task-definition --region "${AWS_DEFAULT_REGION}" --family "${CI_AWS_ECS_TASK_DEFINITION}" --container-definitions "${NEW_CONTAINER_DEFINTIION}"
    - echo "Updating the service..."
    - aws ecs update-service --region "${AWS_DEFAULT_REGION}" --cluster "${CI_AWS_ECS_CLUSTER}" --service "${CI_AWS_ECS_SERVICE}"  --task-definition "${CI_AWS_ECS_TASK_DEFINITION}"

this is the script i'm using however the service deployment does not start the desired task count is 1 however it does not create any
Screen Shot 2021-11-16 at 4 27 04 PM

@rmpt
Copy link

rmpt commented Nov 17, 2021

or you can use sed to set the new image tag, in my case, the CI_PIPELINE_IID and then use jq to extract the container definition:

NEW_CONTAINER_DEFINITION=$(echo $TASK_DEFINITION | sed -e "s/ecr-image:.*/ecr-image:$CI_PIPELINE_IID\",/g")
NEW_CONTAINER_DEFINITION=$(echo $NEW_CONTAINER_DEFINITION | jq '.taskDefinition.containerDefinitions[0]')

@Peterragheb
Copy link

Actually there was nothing wrong with the script, it was failing to bind a port to the new container because I was using a static port binding.
Later I switched to using gitlab's ecs deployment script

@elfadl
Copy link

elfadl commented Dec 29, 2022

    - TASK_DEFINITION=$(aws ecs describe-task-definition --task-definition "$CI_AWS_ECS_TASK_DEFINITION" --region "${AWS_DEFAULT_REGION}")
    - NEW_CONTAINER_DEFINTIION=$(echo $TASK_DEFINITION | jq --arg IMAGE "$DOCKER_REGISTRY/$APP_NAME:latest" '.taskDefinition.containerDefinitions[0].image = $IMAGE | .taskDefinition.containerDefinitions[0]')
    - echo "Registering new container definition..."
    - aws ecs register-task-definition --region "${AWS_DEFAULT_REGION}" --family "${CI_AWS_ECS_TASK_DEFINITION}" --container-definitions "${NEW_CONTAINER_DEFINTIION}"
    - echo "Updating the service..."
    - aws ecs update-service --region "${AWS_DEFAULT_REGION}" --cluster "${CI_AWS_ECS_CLUSTER}" --service "${CI_AWS_ECS_SERVICE}"  --task-definition "${CI_AWS_ECS_TASK_DEFINITION}"

this is the script i'm using however the service deployment does not start the desired task count is 1 however it does not create any
Screen Shot 2021-11-16 at 4 27 04 PM

you can set minimum healthy percent to 0 and maximum percent to 100 if you want only one container running on your ecs.

@vijayrvk
Copy link

vijayrvk commented Feb 1, 2023

Screenshot 2023-02-01 at 4 12 50 PM

Getting an error while using was ecr login. Any idea? I'm passing secret key and access ID from CI/CD in variables

@nholuongut
Copy link

Hi Guys,

I have done with my gitlab pipeline one branh (master) and now i have fail with two branh both master, develop branh.

Please help me to wrire into .gitlab-ci.yml one
build:
stage: build
deploy:
stage: deploy

all develop, master branh?

Thank for help and hope see rely soon.

@SergiyVasylenko
Copy link

SergiyVasylenko commented Jul 6, 2023

Hi there!
Just finished with ECS FARGATE deployment step at the GitLab CI. Want to share the result.
Hopefully, this would be helpful and save time for someone ;)

- TASK_DEFINITION_CURRENT=$(aws ecs describe-services --services "${ECS_SERVICE_NAME}" --cluster "${ECS_CLUSTER_NAME}" | jq -r .services[].taskDefinition | cut -d "/" -f 2)
- TASK_DEFINTION_NAME=$(echo $TASK_DEFINITION_CURRENT | cut -d ":" -f 1 )
- TASK_DEFINITION=$(aws ecs describe-task-definition --task-definition "$TASK_DEFINTION_NAME")
- NEW_CONTAINER_DEFINTIION=$(echo $TASK_DEFINITION | jq --arg IMAGE "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}:${DEPLOY_CONTAINER_TAG}" '.taskDefinition.containerDefinitions[0].image = $IMAGE | .taskDefinition.containerDefinitions[0]')
- EXECUTION_ROLE_ARN=$(echo $TASK_DEFINITION | jq -r '.taskDefinition.executionRoleArn')
- TASK_ROLE_ARN=$(echo $TASK_DEFINITION | jq -r '.taskDefinition.taskRoleArn')
- CONTAINER_MEMORY=$(echo $TASK_DEFINITION | jq -r '.taskDefinition.memory')
- CONTAINER_CPU=$(echo $TASK_DEFINITION | jq -r '.taskDefinition.cpu')
- REQUIRE_COMPATABILITIES=$(echo $TASK_DEFINITION | jq -r '.taskDefinition.requiresCompatibilities[]')
- NETWORK_MODE=$(echo $TASK_DEFINITION | jq -r '.taskDefinition.networkMode')
- echo "Registering new container definition..."
- aws ecs register-task-definition --family "${TASK_DEFINTION_NAME}" --container-definitions "${NEW_CONTAINER_DEFINTIION}" --execution-role-arn "${EXECUTION_ROLE_ARN}" --memory "${CONTAINER_MEMORY}" --requires-compatibilities "${REQUIRE_COMPATABILITIES}" --network-mode "${NETWORK_MODE}" --cpu "${CONTAINER_CPU}"  --task-role-arn "${TASK_ROLE_ARN}"
- echo "Updating the service..."
- aws ecs update-service --cluster "${ECS_CLUSTER_NAME}" --service "${ECS_SERVICE_NAME}"  --task-definition "${TASK_DEFINTION_NAME}"

@bilalmeccai
Copy link

to avoid python you can use jq

- NEW_CONTAINER_DEFINTIION=$(echo $TASK_DEFINITION | jq --arg IMAGE "$REPOSITORY_URL:$IMAGE_TAG" '.taskDefinition.containerDefinitions[0].image = $IMAGE | .taskDefinition.containerDefinitions[0]')

Hi,
Is there anyway we can pass two container images with this? I have been trying to work around this and could not find any solutions.

@shamimhan
Copy link

Hi Team ,

ia m getting below vunrability how to fix any one pls help

Improper access control in the Intel(R) Ethernet Controller RDMA driver for linux before version 1.9.30 may allow an unauthenticated user to potentially enable escalation of privilege via network access.

@shamimhan
Copy link

Hi Team ,

i am using for deployment below script

scripts:

  • $(aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ECR_REGISTRY | tr -d '\r')
    • TASK_DEFINITION_CURRENT=$(aws ecs describe-services --services "${SERVICE_NAME}" --cluster "${CLUSTER_NAME}" | jq -r .services[].taskDefinition | cut -d "/" -f 2)
    • TASK_DEFINTION_NAME=$(echo $TASK_DEFINITION_CURRENT | cut -d ":" -f 1 )
    • TASK_DEFINITION=$(aws ecs describe-task-definition --task-definition "$TASK_DEFINTION_NAME")
    • NEW_CONTAINER_DEFINTIION=$(echo $TASK_DEFINITION | jq --arg IMAGE "${AWS_ECR_REGISTRY}/${APP_NAME}/${CI_COMMIT_REF_SLUG}:${SEMVER}" '.taskDefinition.containerDefinitions[0].image = $IMAGE | .taskDefinition.containerDefinitions[0]')
    • EXECUTION_ROLE_ARN=$(echo $TASK_DEFINITION | jq -r '.taskDefinition.executionRoleArn')
    • TASK_ROLE_ARN=$(echo $TASK_DEFINITION | jq -r '.taskDefinition.taskRoleArn')
    • CONTAINER_MEMORY=$(echo $TASK_DEFINITION | jq -r '.taskDefinition.memory')
    • CONTAINER_CPU=$(echo $TASK_DEFINITION | jq -r '.taskDefinition.cpu')
    • REQUIRE_COMPATABILITIES=$(echo $TASK_DEFINITION | jq -r '.taskDefinition.requiresCompatibilities[]')
    • NETWORK_MODE=$(echo $TASK_DEFINITION | jq -r '.taskDefinition.networkMode')
    • echo "Registering new container definition..."
    • aws ecs register-task-definition --family "${TASK_DEFINTION_NAME}" --container-definitions "${NEW_CONTAINER_DEFINTIION}" --execution-role-arn "${EXECUTION_ROLE_ARN}" --memory "${CONTAINER_MEMORY}" --requires-compatibilities "${REQUIRE_COMPATABILITIES}" --network-mode "${NETWORK_MODE}" --cpu "${CONTAINER_CPU}" --task-role-arn "${TASK_ROLE_ARN}"
    • echo "Updating the service..."
    • aws ecs update-service --cluster "${CLUSTER_NAME}" --service "${SERVICE_NAME}" --task-definition "${TASK_DEFINTION_NAME}"

but i am getting below error

“message”: “(service crm-api-secured) failed to launch a task with (error ECS was unable to assume the role ‘arn:aws:iam::XXXXXXXXX:role/null’ that was provided for this task. Please verify that the role being passed has the proper trust relationship and permissions and that your IAM user has permissions to pass this role.).”
any one pls help for the same.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment