CI/CD — Docker-Compose, Elastic Beanstalk, and Github Actions

Continuous Integration and Continuous Deployments creates and environment of less bug by running test against all commits to a codebase.

Kirtfieldk
6 min readJan 31, 2021
AWS

The aim of this post is to set up a CI/CD pipeline with Github Actions so that every commit made to the master branch will trigger a set of actions to run against our code and deploy to Elastic Beanstalk if all actions pass. the code base will be below. First, note that the Docker-compose has been full explained in my previous post. In addition, the concepts of Elastic Beanstalk and the eb cli has been explained in previous post.

Setting up Github Actions

Github actions allow us to perform actions and commands every time a specific even fires against our codebase in a virtual environment. Github actions is configured in <file_name>.yml in the /.github/workflows/ directory.

Navigate to a personal Github repository and click Action. Github offers a few templates on how to start a workflow, this is helpful if we need language specific commands(npm) to run events like test or builds. However, this case only needs to interact with Docker and AWS.

To build a custom, flow click set up a workflow yourself and use the search bar to search up helpful actions. Actions in Marketplace are open source and greatly reduce complexity. All workflows will have the structure of name, event, and jobs.

#NAME
name: Push images to Dockerhub and deploy on ELastic Beanstalk
#EVENT
on:
push:
branches:
- master
#JOBS
jobs:
build_docker_images:
name: build docker images
runs-on: [ubuntu-latest]
steps:
- name: checkout
uses: actions/checkout@v2
...

The event section is triggered on the term on and can be configured to run on a magnitude of different Github events. Specifically, this workflow will run every time there is a push on the master branch. Then the jobs section is signalled with the jobs term where every job has a name like build_docker_images. Each job has an OS it runs on. In addition, each job has a set of steps with a name property. We use the term uses to pull from the help of open source actions like cloning the repo into the step or logging into docker.

In order for Elastic Beanstalk to successfully deploy the content of the git commit, the first step is to configure out Github Action to build and push the new images to DockerHub. This means we will need to log into Dockerhub in Github’s virtual environment. We can leverage another community action, docker/login-action@v1.8.0, to log into our Docker account. The {{secrets.DOCKERHUB_USERNAME}} pulls from the secrets encrypted in out Github profile — basically env variables.

- name: Docker Login
uses: docker/login-action@v1.8.0
with:
username: ${{secrets.DOCKERHUB_USERNAME}}
password: ${{secrets.DOCKERHUB_TOKEN}}
logout: true

Now that we have access to our Dockerhub account, all that remains is building and pushing our images to their repositories. When pushing images to Dockerhub, the commands below will build, tag, and push the image built from the Dockerfile in the directory.

docker build -t keithkfield/blog-server . 
docker tag keithkfield/blog-server keithkfield/blog-server:latest
docker push keithkfield/blog-server:latest

These three commands should be the only three needed to push images built from our source code into Dockerhub.

- name: Build Server image
run: docker build -t keithkfield/blog-server -f
./Server/Dockerfile ./Server
- name: Tag our Image
run: docker tag keithkfield/blog-server
keithkfield/blog-server:latest
- name: Push to dockerhub
run: docker push keithkfield/blog-server

Now just repeat these three steps for the client and nginx container.

Using AWS

After getting our images to be continually deployed, we will need access to our Amazon Web Service account. Again, we can use another open source action — einaregilsson/beanstalk-seploy@v14.

- name: Deploy to EB
uses: einaregilsson/beanstalk-deploy@v14
with:
aws_access_key: ${{ secrets.AWS_ACCESS_KEY }}
aws_secret_key: ${{ secrets.AWS_ACCESS_SECRET_KEY }}
application_name: medium-compose-series
environment_name: Mediumblogseries-dev
version_label: "app-cbe0-210131_121135"
region: us-east-1

We pull the aws_access_key and aws_secret_key from our encrypted secrets. The application_name, enviorment_name, and version_label will be gathered from the process of creating an Elastic Beanstalk application.

Creating Elastic Beanstalk App

For this process to even work we will need to create the application and environment name beforehand. To start, use the command

eb init -r <region> <application_name>

to begin the process of creating an application.

Process of creating app

The only configuration that needs to stray from default is the platform question. Instead of the default 1, this application is Multi-container Docker, so select 2.

Select a platform branch.
1) Docker running on 64bit Amazon Linux 2
2) Multi-container Docker running on 64bit Amazon Linux
3) Docker running on 64bit Amazon Linux

After this init command, there will be a .elasticbeanstalk directory with a config.yml file. In aws, the file Dockerrun.aws.json directs how multiple container within an application should work.

Dockerrun.aws.json

The Dockerrun.aws.json resembles a Docker-compose file. Its format is .json and list all its containers in the containerDefinitions key. The main points is that only the keithkfield/blog-nginx image has port mappings and it links the client and server images.

Now that the Dockerrun configuration file is built, we can now use the command

eb create <enviorment-name> 

to create an environment variable. Running this command, looks to the Dockerrun.aws.json for instructions. First, it notes that AWSEBDockerrunVersion is set to 3, this means that this is a multi-container application. Elastic beanstalk pulls all three images from dockerhub and networks them together by the configuration of the nginx load balancer. This process takes a while but establishes the base application we can build from.

Put Together

Now that we have defined the application and environment name, we can put them into the last step of our workflow. All that remains is getting the version label. To get this run the command

eb appversion

to get a list of valid versions to use.

My final command in my workflow is below.

Activating the Flow

To simply activate this flow, push these changes to the master branch. This triggers the action with the name of the commit in the actions tab. After a few minutes we can check the Elastic Beanstalk console to get the URL generated and visit the site.

Initial Push

Successfully, our application is healthy! Now making any change to the UI and pushing it will result in a deployment of the same application.

Success!!

Note that this is a long process and usually takes hours to get it correct.

It took 30 flows to get my CI/CD pipeline to work perfectly.

--

--