Skip to main content

GitLab CI Pipeline - Build and Push Image to AWS Elastic Container Registry

589 words·
GitLab GitLab CI CI Pipeline AWS AWS CLI Elastic Container Registry
Table of Contents

AWS Prerequisites
#

Create Elastic Container Registry Repository
#

# Create a repository
aws ecr create-repository --repository-name example-application --region eu-central-1

# Shell output:
{
    "repository": {
        "repositoryArn": "arn:aws:ecr:eu-central-1:012345678912:repository/example-application",
        "registryId": "012345678912",
        "repositoryName": "example-application",
        "repositoryUri": "012345678912.dkr.ecr.eu-central-1.amazonaws.com/example-application",
        "createdAt": "2024-12-14T11:00:07.003000+00:00",
        "imageTagMutability": "MUTABLE",
        "imageScanningConfiguration": {
            "scanOnPush": false
        },
        "encryptionConfiguration": {
            "encryptionType": "AES256"
        }
    }
}

IAM User & Permissions
#

Create IAM User
#

# Create a new IAM user with the name "ecr-user"
aws iam create-user --user-name ecr-user

# Shell output:
{
    "User": {
        "Path": "/",
        "UserName": "ecr-user",
        "UserId": "AIDARCHUALIN47DNKQC4L",
        "Arn": "arn:aws:iam::012345678912:user/ecr-user",
        "CreateDate": "2024-12-14T09:55:52+00:00"
    }
}

Create Access Keys for the User
#

# Create Access Keys for the user
aws iam create-access-key --user-name ecr-user

# Shell output:
{
    "AccessKey": {
        "UserName": "ecr-user",
        "AccessKeyId": "AKIARCHUALIN2XLCCOHZ",
        "Status": "Active",
        "SecretAccessKey": "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",
        "CreateDate": "2024-12-14T09:57:16+00:00"
    }
}

Copy the Access Key and Secret Access Key:

  • “AccessKeyId”: AKIARCHUALIN2XLCCOHZ

  • “SecretAccessKey”: xnndW1/tbkUmJDfjB/TERhDrN4UdexHcqZ5RRu3x


Attach Policy to User
#

ECR Policies:

  • AmazonEC2ContainerRegistryReadOnly Pull permission

  • AmazonEC2ContainerRegistryFullAccess Push and Pull permission

# Attach the "AmazonEC2ContainerRegistryFullAccess" policy to the user:
aws iam attach-user-policy --user-name ecr-user --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess

Verify the User Policy
#

# Confirm the policy is attached to the user
aws iam list-attached-user-policies --user-name ecr-user

# Shell output:
{
    "AttachedPolicies": [
        {
            "PolicyName": "AmazonEC2ContainerRegistryFullAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess"
        }
    ]
}



GitLab Repository
#

CI/CD Variables
#

  • Go to: (Project) “Settings” > “CI/CD”

  • Expand the “Variables” section

  • Click “Add variable”

  • Select type “Variable (default)”

  • Unflag the “Protect variable” option


Add the following variables:

  • Key: AWS_ACCOUNT_ID Value: 012345678912

  • Key: AWS_REGION Value: eu-central-1

  • Key: AWS_ACCESS_KEY_ID Value: AKIARCHUALIN2XLCCOHZ

  • key: AWS_SECRET_ACCESS_KEY Value: xnndW1/tbkUmJDfjB/TERhDrN4UdexHcqZ5RRu3x


CI Pipeline Manifest
#

  • .gitlab-ci.yml
variables:
  # Define ECR repository name
  IMAGE_NAME: example-application
  TAG_LATEST: $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest
  TAG_COMMIT: $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA


stages:
  - build


# Build image and push it to AWS ECR repository
build_image:
  image: docker:stable
  stage: build
  services:
    - name: docker:23.0.6-dind
      command: ["--tls=false"]
  variables:
    DOCKER_TLS_CERTDIR: ""
  before_script:
    # Install AWS CLI
    - apk add --no-cache python3 py3-pip
    - pip3 install --no-cache-dir awscli
  script:
    # Login the AWS ECR
    - aws ecr get-login-password --region $AWS_REGION |
      docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
    # Try pulling the latest image, keep going if it does not exist
    - docker pull $TAG_LATEST || true
    # Build image
    - docker build --cache-from $TAG_LATEST -t $TAG_COMMIT -t $TAG_LATEST -f Dockerfile .
    # Push image to AWS ECR repository
    - docker push $TAG_COMMIT
    - docker push $TAG_LATEST
  rules: 
    # Rule: Run this job only for the main branch and if the specified Dockerfile exists
    - if: $CI_COMMIT_BRANCH == "main"
      exists:
        - Dockerfile

Example Application
#

Dockerfile
#

  • Dockerfile
# Use the official Caddy image as the base
FROM caddy:alpine

# Create a non-root user "caddy"
RUN addgroup -S caddy && adduser -S -G caddy caddy

# Adjust permissions
RUN mkdir -p /usr/share/caddy && \
    chown -R caddy:caddy /usr/share/caddy /config /data

# Copy website files into the container
ADD index.html /usr/share/caddy/

# Switch to the non-root user
USER caddy

# Expose the default Caddy port
EXPOSE 80

HTML File
#

  • index.html
<!DOCTYPE html>
<html>

<head>
    <title>jklug.work</title>
</head>

<body>
    <h1>Some HTML</h1>
    <p>Example Application<br></p>

</body>

</html>



Cleanup
#

Delete Elastic Container Registry
#

# Delete the ECR repository
aws ecr delete-repository --repository-name example-application --region eu-central-1 --force

Delete IAM User
#

# Detach the policies attached to the user
aws iam detach-user-policy --user-name ecr-user --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess
# List the access keys for the user
aws iam list-access-keys --user-name ecr-user

# Shell output:
{
    "AccessKeyMetadata": [
        {
            "UserName": "ecr-user",
            "AccessKeyId": "AKIARCHUALIN2XLCCOHZ",
            "Status": "Active",
            "CreateDate": "2024-12-14T09:57:16+00:00"
        }
    ]
}

# Delete the access keys
aws iam delete-access-key --user-name ecr-user --access-key-id AKIARCHUALIN2XLCCOHZ
# Delete the "ecr-user" IAM user
aws iam delete-user --user-name ecr-user