Saturday, June 23, 2018

Containerised CI Deployment 


Today's article is about is a discussion about how to achive an end-to-end CI &CD pipeline solution for Test Automation. I am writing this blog today since I have not seen many articles written on how to integrate your UI Automation tests with modern technologies such as "Docker" , "Kubernetes" & CI Sever such as "Jenkins". For this article I am going to present you a complete solution using Docker, K8S & Jenkins built on top of CentOS.

So lets first get some idea on how Docker works on Linux (In our case CentOS) environment. 

Docker Engine Components Flow

As you can see there is a CentOS machine which runs Docker Server (which is the host machine) and also Docker client & the communication between these two are happening via REST API. 
So the Docker server deficits as the Docker daemons which is responsive for create , manage Docker objects. 

Docker clients use CLI to communicate with one or many Docker daemons. 

You can build your Dokcer file to create Docker images and run Docker images to transform them into Docker containers. 

                                     buid                                    run
               Dockerfile --------------> Docker image -------> Docker containers


Docker Architecture Diagram


Following is an example Dockerfile written for creating and running of Docker image which is capable of executing UI tests which uses "Cucumber" & "Selenium" tests based on "Java" & "Maven". 

Additionally if you are running on an old version of  forefox as with a different profile you canl run Mozilla Firefox for this test scenario & you how to run UI Firefox tests using Firefox profile manager (i.e creating a Firefox profile called eg: "P2"). You can see the profile information in CentOS by navigating to ~/.mozilla/firefox/profile.ini file.

Inside your docker container you can run Firefox either in Headless mode or with real Firefox browser loading. If you need to run with real Firefox browser use VNCServer. For this article I will use running Firefox in headless mode.

First I will describe what the actual docker file does here.

The architecture I am going to describe would consists of several layers of docker image.

FROM centos:centos7   will pull the centos7 docker image (image1) from the docker hub to your local machine. Next our Docker file will also create a seperate Docker image (image2)  called ":latest" which built on top of centos7 image. Next we will use another Dockerfile which uses "" as the base image for its implementation of the newest Docker image (image3).

                Dockerfile1                       ----------->                      Dockerfile2   
(  uses Dockerimage1 - centos7 ---> Dockerimage2 ) ---> (   Dockerimage3 ---> )

We will trigger a build using Jenkins server to create the Dockerimage3 and to start the respective Docker container by running Dockerimage3.

Running the Docker container from Docker image3 can be achived using two ways.

1. Allowing Jenkins to use the Agent machines workspace to clone github repo and start executing your tests. (For this approach you only need to configure a Jenkins Agent with your master Jenkins machine.)

2. Allowing Jenkins to create a separate workspace for each Docker container you are going to spawn using Dockerimage3. (For this approach you need to configure Jenkins Agent with master Jenkins machine and a separate Docker NodeLabel parameter plugin.)

[1] https://wiki.jenkins.io/display/JENKINS/NodeLabel+Parameter+Plugin

The drawback of the first approach is all Docker containers will have to use the same workspace of the Agent machine which will drive you into many problems. So we recommend to use the second approach as it will help us to spawn several; Docker containers which uses its own workspace to execute tests, this way we can spawn many Docker containers using the same Dockerimage3 and inside each Docker container we can run distinct test cases.

Now then back to technical stuff :) ....

So lets list down two Dockerfiles.

Dockerfile 1



Dockerfile 2


2 nd Approach
===========

I am sure that you would have experience when ever we run maven tests with docker , every time , every container will start downloading maven dependencies to its own m2 repo. Well that's not nice if the dependency list is very long since the container will consume a considerable time on downloading dependencies. One solution to prevent this is a mapping between m2 repo of the Agent machine to Docker container, so that every time a Docker container spin up instead of downloading maven dependencies to container from the beginning we can tell the container to use Agent machine's m2 repo for running tests which will all Docker containers use parallelly. But then how to do that? Below is a sample Dockerfile to be used for that purpose.

FROM centos:centos7
MAINTAINER Dimuthu De Lanerolle

# Proxy Settings And Configurations
#==================================
# Your Proxy Settings Goes Here

#Install git
#===========

RUN echo yes | yum install -y git

#Install Firefox Latest Version
#======================================

RUN yum install firefox -y

#For Installing Firefox Older Version
#====================================

#RUN mkdir -p /opt/firefox/50.1.0
#RUN curl -SL https://ftp.mozilla.org/pub/firefox/releases/50.1.0/linux-x86_64/en-US/firefox-50.1.0.tar.bz2
#RUN tar -xjC /opt/firefox/50.1.0
#COPY firefox.sh /usr/bin/firefox
#RUN chmod +x /usr/bin/firefox


#Install From Firefox Distribution Directly
#===========================================

#COPY firefox-50.1.0.tar.gz /tmp/
#workdir /tmp/
#RUN tar -xzf firefox-50.1.0.tar.gz
#RUN cp -a firefox /opt
#RUN cp -a /opt/firefox /usr/bin
#RUN mv /usr/bin/firefox /usr/bin/firefox-old
#RUN cp -a /opt/firefox /usr/bin
#RUN rm -rf /usr/bin/firefox
#RUN ln -s /opt/firefox/firefox /usr/bin/firefox

#Install openjdk
#===============

RUN yum install -y \
       java-1.8.0-openjdk \
       java-1.8.0-openjdk-devel

ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk
ENV PATH $JAVA_HOME/bin:$PATH

RUN echo 'export PATH=/usr/lib/jvm/java-1.8.0-openjdk/bin:$PATH' >>~/.bash_profile
RUN echo ~/.bash_profile

#Install Maven
#=============

ARG MAVEN_VERSION=3.5.0
ARG USER_HOME_DIR="/root"
ARG SHA1=878b8b93a8f9685aefba5c21a17b46eb141b1122
ARG BASE_URL=http://archive.apache.org/dist/maven/maven-3/$MAVEN_VERSION/binaries/apache-maven-$MAVEN_VERSION-bin.tar.gz

RUN mkdir -p /usr/share/maven /usr/share/maven/ref \
  && curl -fsSL -o /tmp/apache-maven.tar.gz ${BASE_URL} \
  && echo "${SHA1}  /tmp/apache-maven.tar.gz" | sha1sum -c - \
  && tar -xzf /tmp/apache-maven.tar.gz -C /usr/share/maven --strip-components=1 \
  && rm -f /tmp/apache-maven.tar.gz \
  && ln -s /usr/share/maven/bin/mvn /usr/bin/mvn

ENV MAVEN_HOME /usr/share/maven
ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2"

#Additional Configurations
#=========================

#Transfer m2 settings.xml file
#===========================

COPY /config/settings.xml /usr/share/maven/conf/

#Transfer certificates and Import certificates
#==============================================

COPY /cert/xx.der /tmp/
workdir /tmp/
RUN echo yes | keytool -importcert -keystore /etc/alternatives/jre/lib/security/cacerts -file /tmp/xx.der -storepass yyy
RUN rm -rf /tmp/


Important Docker Commands
------------------------------------

Docker :

Docker build from the DockerFile : docker build -t friendlyhello .
Docker image list : docker image list
Dockcer container list : docker ps -a
Docker container start : docker container start bd9038b3d022
Docker container start with even logs : docker events&    (Get the Event ID & run the docker image by run bd9038b3d022)
Docker container stop : docker stop bd9038b3d022
Docker container kill : docker kill bd9038b3d022

Docker remove image : docker rmi
Docker remove all images at once : docker rmi $(docker images -q)
Docker remove container : docker rm
Docker remove all containers at once : docker rm $(docker ps -a -q)
Docker remove all* : docker system prune –a

*WARNING! This will remove:
        - all stopped containers
        - all networks not used by at least one container
        - all images without at least one container associated to them
        - all build cache