Skip to main content

Unit 4: Deploying a dockerized application to AWS

About 5 minDevOpsGitlabDockerAWSYoutubeblogfreecodecamp.orgdevopsgitlabdockerawsscm@vdespayamlcicdcicdyoutubecrashcourse

Unit 4: Deploying a dockerized application to AWS 관련

DevOps with GitLab CI Course

GitLab CI/CD can automatically build, test, deploy, and monitor your applications. We just published a full course on the freeCodeCamp.org YouTube channel that will teach you how to use GitLab CI. Valentin Despa developed this course. Valentin is an ...

DevOps with GitLab CI Course
GitLab CI/CD can automatically build, test, deploy, and monitor your applications. We just published a full course on the freeCodeCamp.org YouTube channel that will teach you how to use GitLab CI. Valentin Despa developed this course. Valentin is an ...

Unit overview

  • modern applications tend to be more complex, and most of them use Docker
  • we will build & deploy an application that runs in a Docker container

Introduction to AWS Elastic Beanstalk

AWS Elastic Beanstalk (AWS EB) is a service that allows us to deploy an application in the AWS cloud without having to worry about managing the virtual server(s) that runs it


Creating a new AWS Elastic Beanstalk application

  • when creating an EB app, choose the Docker platform and deploy the sample app
  • AWS EB will use two AWS services to run the application:
    • EC2 (virtual servers)
    • S3 (storage)
  • to deploy a new version, go to the environment in EB and upload the file in templates called Dockerrun.aws.public.json
Dockerrun.aws.public.json
{
  "AWSEBDockerrunVersion": "1",
  "Image": {
    "Name": "nginx"
  },
  "Ports": [
    {
      "ContainerPort": 80
    }
  ]
}

Creating the Dockerfile

  • create a new file called Dockerfile in the root of the project
  • add the following contents to it:
FROM nginx:1.20-alpine
COPY build /usr/share/nginx/html

Building the Docker image

  • to build the Docker image, we will use the command docker build
  • to build Docker images from a GitLab CI pipeline, we need to start the Docker Daemon as a service

Pipeline after this lecture

.gitlab-ci.yml
stages:
  - build
  - package

variables:
  APP_VERSION: $CI_PIPELINE_IID

build website:
  image: node:16-alpine
  stage: build
  script:
    - yarn install
    - yarn lint
    - yarn test
    - yarn build
    - echo $APP_VERSION > build/version.html
  artifacts:
    paths:
      - build

build docker image:
  stage: package
  image: docker:20.10.12
  services:
    - docker:20.10.12-dind
  script:
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin
    - docker build -t $CI_REGISTRY_IMAGE -t $CI_REGISTRY_IMAGE:$APP_VERSION .
    - docker image ls
    - docker push --all-tags $CI_REGISTRY_IMAGE


Docker container registry

  • to preserve a Docker image, we need to push it to a registry
  • Dockerhub is a public registry with Docker images
  • GitLab offers a private Docker registry which is ideal for private projects

Pipeline after this lecture

.gitlab-ci.yml
stages:
  - build
  - package

variables:
  APP_VERSION: $CI_PIPELINE_IID

build website:
  image: node:16-alpine
  stage: build
  script:
    - yarn install
    - yarn lint
    - yarn test
    - yarn build
    - echo $APP_VERSION > build/version.html
  artifacts:
    paths:
      - build

build docker image:
  stage: package
  image: docker:20.10.12
  services:
    - docker:20.10.12-dind
  script:
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin
    - docker build -t $CI_REGISTRY_IMAGE -t $CI_REGISTRY_IMAGE:$APP_VERSION .
    - docker image ls
    - docker push --all-tags $CI_REGISTRY_IMAGE


Testing the container

  • we want to test the container to see if the application running on it (web server) is working properly
  • to start the container, we will use the services: keyword

Pipeline after this lecture

.gitlab-ci.yml
stages:
  - build
  - package
  - test

variables:
  APP_VERSION: $CI_PIPELINE_IID

build website:
  image: node:16-alpine
  stage: build
  script:
    - yarn install
    - yarn lint
    - yarn test
    - yarn build
    - echo $APP_VERSION > build/version.html
  artifacts:
    paths:
      - build

build docker image:
  stage: package
  image: docker:20.10.12
  services:
    - docker:20.10.12-dind
  script:
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin
    - docker build -t $CI_REGISTRY_IMAGE -t $CI_REGISTRY_IMAGE:$APP_VERSION .
    - docker image ls
    - docker push --all-tags $CI_REGISTRY_IMAGE

test docker image:
  stage: test
  image: curlimages/curl
  services:
    - name: $CI_REGISTRY_IMAGE:$APP_VERSION
      alias: website
  script:
    - curl http://website/version.html | grep $APP_VERSION

Private registry authentication

  • AWS EB requires authentication credentials to pull our Docker image
  • GitLab allows us to create a Deploy Token that AWS can use to log to the registry
  • to generate a Deploy Token, from your project, go to Settings > Repository > Deploy tokens.
  • create a new Deploy Token with the following scopes:
    • read_repository
    • read_registry

Pipeline after this lecture

.gitlab-ci.yml
stages:
  - build
  - package
  - test
  - deploy

variables:
  APP_VERSION: $CI_PIPELINE_IID

.build website:
  image: node:16-alpine
  stage: build
  script:
    - yarn install
    - yarn lint
    - yarn test
    - yarn build
    - echo $APP_VERSION > build/version.html
  artifacts:
    paths:
      - build

.build docker image:
  stage: package
  image: docker:20.10.12
  services:
    - docker:20.10.12-dind
  script:
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin
    - docker build -t $CI_REGISTRY_IMAGE -t $CI_REGISTRY_IMAGE:$APP_VERSION .
    - docker image ls
    - docker push --all-tags $CI_REGISTRY_IMAGE

.test docker image:
  stage: test
  image: curlimages/curl
  services:
    - name: $CI_REGISTRY_IMAGE:$APP_VERSION
      alias: website
  script:
    - curl http://website/version.html | grep $APP_VERSION

deploy to production:
  image:
    name: amazon/aws-cli:2.4.11
    entrypoint: [""]
  stage: deploy
  variables:
    APP_NAME: My website
    APP_ENV_NAME: Mywebsite-env
  environment: production
  script:
    - aws --version
    - yum install -y gettext
    - export DEPLOY_TOKEN=$(echo $GITLAB_DEPLOY_TOKEN | tr -d "\n" | base64)
    - envsubst < templates/Dockerrun.aws.json > Dockerrun.aws.json
    - envsubst < templates/auth.json > auth.json
    - cat Dockerrun.aws.json
    - cat auth.json
    - aws s3 cp Dockerrun.aws.json s3://$AWS_S3_BUCKET/Dockerrun.aws.json
    - aws s3 cp auth.json s3://$AWS_S3_BUCKET/auth.json


Deploying to AWS Elastic Beanstalk

  • a new deployment to AWS EB happens in two steps
  • step 1: we create a new application version with aws elasticbeanstalk create-application-version
  • step 2: we update the environment with the new application version with aws elasticbeanstalk update-environment

Pipeline after this lecture

.gitlab-ci.yml
stages:
  - build
  - package
  - test
  - deploy

variables:
  APP_VERSION: $CI_PIPELINE_IID

build website:
  image: node:16-alpine
  stage: build
  script:
    - yarn install
    - yarn lint
    - yarn test
    - yarn build
    - echo $APP_VERSION > build/version.html
  artifacts:
    paths:
      - build

build docker image:
  stage: package
  image: docker:20.10.12
  services:
    - docker:20.10.12-dind
  script:
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin
    - docker build -t $CI_REGISTRY_IMAGE -t $CI_REGISTRY_IMAGE:$APP_VERSION .
    - docker image ls
    - docker push --all-tags $CI_REGISTRY_IMAGE

test docker image:
  stage: test
  image: curlimages/curl
  services:
    - name: $CI_REGISTRY_IMAGE:$APP_VERSION
      alias: website
  script:
    - curl http://website/version.html | grep $APP_VERSION

deploy to production:
  image:
    name: amazon/aws-cli:2.4.11
    entrypoint: [""]
  stage: deploy
  variables:
    APP_NAME: My website
    APP_ENV_NAME: Mywebsite-env
  environment: production
  script:
    - aws --version
    - yum install -y gettext
    - export DEPLOY_TOKEN=$(echo $GITLAB_DEPLOY_TOKEN | tr -d "\n" | base64)
    - envsubst < templates/Dockerrun.aws.json > Dockerrun.aws.json
    - envsubst < templates/auth.json > auth.json
    - cat Dockerrun.aws.json
    - cat auth.json
    - aws s3 cp Dockerrun.aws.json s3://$AWS_S3_BUCKET/Dockerrun.aws.json
    - aws s3 cp auth.json s3://$AWS_S3_BUCKET/auth.json
    - aws elasticbeanstalk create-application-version --application-name "$APP_NAME" --version-label $APP_VERSION --source-bundle S3Bucket=$AWS_S3_BUCKET,S3Key=Dockerrun.aws.json
    - aws elasticbeanstalk update-environment --application-name "$APP_NAME" --version-label $APP_VERSION --environment-name $APP_ENV_NAME


Post-deployment testing

  • updating an EB environment does not happen instantly
  • we will use aws elasticbeanstalk wait to know when the environment has been updated

Pipeline after this lecture

.gitlab-ci.yml
stages:
  - build
  - package
  - test
  - deploy

variables:
  APP_VERSION: $CI_PIPELINE_IID

build website:
  image: node:16-alpine
  stage: build
  script:
    - yarn install
    - yarn lint
    - yarn test
    - yarn build
    - echo $APP_VERSION > build/version.html
  artifacts:
    paths:
      - build

build docker image:
  stage: package
  image: docker:20.10.12
  services:
    - docker:20.10.12-dind
  script:
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin
    - docker build -t $CI_REGISTRY_IMAGE -t $CI_REGISTRY_IMAGE:$APP_VERSION .
    - docker image ls
    - docker push --all-tags $CI_REGISTRY_IMAGE

test docker image:
  stage: test
  image: curlimages/curl
  services:
    - name: $CI_REGISTRY_IMAGE:$APP_VERSION
      alias: website
  script:
    - curl http://website/version.html | grep $APP_VERSION

deploy to production:
  image:
    name: amazon/aws-cli:2.4.11
    entrypoint: [""]
  stage: deploy
  variables:
    APP_NAME: My website
    APP_ENV_NAME: Mywebsite-env
  environment: production
  script:
    - aws --version
    - yum install -y gettext
    - export DEPLOY_TOKEN=$(echo $GITLAB_DEPLOY_TOKEN | tr -d "\n" | base64)
    - envsubst < templates/Dockerrun.aws.json > Dockerrun.aws.json
    - envsubst < templates/auth.json > auth.json
    - cat Dockerrun.aws.json
    - cat auth.json
    - aws s3 cp Dockerrun.aws.json s3://$AWS_S3_BUCKET/Dockerrun.aws.json
    - aws s3 cp auth.json s3://$AWS_S3_BUCKET/auth.json
    - aws elasticbeanstalk create-application-version --application-name "$APP_NAME" --version-label $APP_VERSION --source-bundle S3Bucket=$AWS_S3_BUCKET,S3Key=Dockerrun.aws.json
    - aws elasticbeanstalk update-environment --application-name "$APP_NAME" --version-label $APP_VERSION --environment-name $APP_ENV_NAME
    - aws elasticbeanstalk wait environment-updated --application-name "$APP_NAME" --version-label $APP_VERSION --environment-name $APP_ENV_NAME
    - curl $CI_ENVIRONMENT_URL/version.html | grep $APP_VERSION

이찬희 (MarkiiimarK)
Never Stop Learning.