It’s time to complete our CI/CD environment. So far we created our build repository with source code and build the project. Now it’s time to automatically deploy our software into the server. There are many products available on the market for Continous Delivery (for example Ansible, Puppet, Chef etc.). I’d like to show you another one, CodeDeploy from AWS services suite. At first glance, you can think that it’s too much complicated to simply deploy our RPM using CodeDeploy. However, I’d like to demonstrate for you the overall idea and logic of this tool as well as some of the advantages, like good integration with CodePipeline. This last one will connect all of our build components into one project to automate our CI/CD environment. I hope, I interested you in this topic. So let’s get started!

cd

Let’s discuss the steps required to integrate all of our components into a CI/CD ready environment. The above diagram shows step by step the workflow of our CI/CD.

  1. The Developer writes a code and commits it to CodeCommit GIT repository
  2. CodePipeline detects a change in repository and starts the CI/CD process
  3. CodePipeline triggers CodeBuild to start building our application
  4. CodeBuild copies the code from CodeCommit and builds an application
  5. When the application is compiled, artifacts are copied to S3 bucket
  6. Upon completion of the build process, CodeBuild informs CodePipeline about success.
  7. CodePipeline triggers CodeDeploy to start its job
  8. CodeDeploy contact the agents and sends the instructions
  9. Agents copy installation bundle from S3 and start the installation process
  10. Upon completion, agents inform CodeDeploy about the deployment status
  11. CodeDeploy inform CodePipeline about the completion status

Above steps are some of the high-level points to accomplish our task.

Deployment configuration must start from the source code change. CodeBuild needs all files in the .zip file (or .tar/.tar.gz), with AppSpec file inside. This file needs to be in the root folder of zipped directory together with other scripts and installation files. Let’s create a folder inside our project and appspec.yml file inside.

$ cd rpmbuild
$ mkdir codedeploy
$ touch codedeploy/appspec.yml

Below is a very simple example of our AppSpec file. This procedure is very flexible, you can add many more steps to your deployment profile. For the purpose of our example, we need a.rpm file and two scripts.

version: 0.0
os: linux
files:
  - source: /
    destination: /tmp/
hooks:
  BeforeInstall:
    - location: cleanup.sh
      timeout: 300
      runas: root
  AfterInstall:
    - location: install.sh
      timeout: 300
      runas: root
    - location: cleanup.sh
      timeout: 300
      runas: root

The first script will delete all rpm’s and yml’s files in /tmp directory. It’s necessary as CodeDeploy cannot overwrite existing files.

$ vi codedeploy/cleanup.sh
#!/bin/bash
rm -rf /tmp/*.rpm /tmp/*.yml

The second one will install our rpm which we are going to copy into the /tmp folder.

$ vi codedeploy/install.sh
#!/bin/bash
rpm -ivh /tmp/*.rpm --force

Next, we need to adjust our buildspec.yml file, to produce artifacts generated in codedeploy folder

version: 0.2

phases:
  install:
    commands:
       - echo install rpm packages
       - apt-get update
       - apt-get install rpm tree -y
  pre_build:
    commands:
       - echo check rpmbuild version
       - rpmbuild --version
  build:
    commands:
       - echo start building process
       - rpmbuild -ba -v SPECS/motd.spec
       - mv RPMS/noarch/*.rpm codedeploy/
  post_build:
    commands:
       - echo checking content
       - tree codedeploy
artifacts:
  files:
     - codedeploy/*
  discard-paths: yes

Before you commit the project, your folder structure should be similar to the below example:

--rpmbuild
   |
   - BUILD
   - codedeploy
       |
       - appspec.yml
       - cleanup.sh
       - install.sh
   - RPMS
       |
       - noarch
           |
           - somefile.txt
   - SOURCES
       |
       - motd-1.0.tar.gz
   - SPECS
       |
       - motd.spec
   - SRPMS
       |
       - somefiles
   - buildspec.yml

Let’s commit it and push to the GIT repository

$ git add .
$ git commit -m "CodeDeploy package ready"
$ git push origin master

Go to CodeBuild service in AWS console. Edit your build project and change artifacts settings by providing static name motd.zip, define the path and change artifacts packaging to the Zip. Save the project and run a build to make sure that all works.

cd2

Time to create our deployment environment. We need a Linux EC2 instance which supports rpm packages installation. Amazon Linux is ideal for our test purposes. But before we start, we need to create extra IAM policy for our EC2. It must allow access to S3 buckets. Go to IAM -> Roles -> Create role -> EC2 -> AWS Services -> EC2 -> Create Policy -> JSON editor. Then paste the below policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:Get*",
                "s3:List*"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}

Name the new policy as EC2AccessToS3ReadOnly and save. Next, attach this policy to our newly created role and named it as EC2CodeDeployS3Access. Go to EC2 services and Launch instance. Select Amazon Linux, t2.micro instance (it’s eligible for the free tier). Put this instance in the default VPC, subnet. Assign public IP. Make sure you attached IAM role created before. Select default storage configuration. Add the following tag AppName:Motd. This is very important because CodeDeploy will select instances, based on their tags. Next, configure the security group which allows on access on port TCP22 (SSH) to this instance. Confirm all the changes and boot EC2.

When EC2 is ready, log in to it using your private keys and ec2-user. Then run the following commands to install the CodeDeploy agent

$ sudo yum install ruby wget -y
$ cd /home/ec2-user/
$ wget https://aws-codedeploy-eu-central-1.s3.amazonaws.com/latest/install
$ sudo chmod +x ./install
$ sudo ./install auto
$ sudo service codedeploy-agent status 

Now our EC2 instance is ready to get an application deployed from CodeDeploy.

It’s all for this post. In the next one, I’ll show you how to configure CodePipeline to automate our deployments.