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.
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.
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.
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.
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.