CI/CD Workflow

Using

Jenkins & Docker

Teerapat Khunpech

ball@engineerball.com

Teerapat Khunpech

@engineerball

https://github.com/engineerball

https://engineerball.com

ball@engineerball.com

Outline
Basic CI/CD workflow
Basic Docker
Docker setup & config
Jenkins setup & config
CI/CD Workflow

Basic CI/CD Workflow

Continuous Integration

http://www.mindtheproduct.com/2016/02/what-the-hell-are-ci-cd-and-devops-a-cheatsheet-for-the-rest-of-us/

Continuous Delivery

http://www.mindtheproduct.com/2016/02/what-the-hell-are-ci-cd-and-devops-a-cheatsheet-for-the-rest-of-us/

Jenkins

  • CI/CD tools
  • Easy installation
  • Rich plugins
  • Distributed build

CI/CD Workflow

Dev

Commit

Build

Test

Stage

Deploy

Production

What is it?

Build, Ship and Run

Any App, Anywhere

Docker

VM v Docker

Docker Basics

Docker Image

The basis of a Docker container

Docker Container

The standard unit in which the application service resides

Docker Engine
Creates, ships and runs Docker containers deployable on physical or virtual host locally, in a datacenter or cloud service provider

Docker Registry
On-premises registry for image storing and collaboration

Docker Solutions

Docker CLI

docker build # Build an image from Dockerfile
docker images # List all images on a Docker host
docker run # Run an image
docker ps # List all running and stopping instances
docker stop # Stop running instances
docker rm # Remove an instance
docker rmi # Remove an image

Docker Architecture

Jenkins & Docker

What is it?

It turns a pool of Docker host into a single

Native clustering for Docker

Docker Swarm

Docker Architecture

Swarm Manager

Discovery Service

Swarm Node

Docker Engine

Swarm Node

Docker Engine

Swarm Node

Docker Engine

Docker Client

What is it?

Service discovery, Key/Value Storage

Consul

What is it?

Tool for running multiple-container Docker application

Great with CI Workflows

Docker Compose

---
version: "2"
services:
  redis:
    image: redis
    expose: ["6379"]
  db:
    image: postgres:9.4
  voting-app:
    image: engineerball/voting-app
    ports:
      - "5000:80"
  result-app:
    image: engineerball/result-app
    ports:
      - "5001:80"
  worker:
    image: engineerball/worker

$ docker-compose up

Workshop

I      : Docker Swarm Setup

II     : Jenkins Setup  

III    : Repositories & Webhooks Setup

IV    : Docker Registry

V     : The Build Jobs

VI    : The Deploy Job

VII   : Deploying App

CI/CD Workflow

LAB Architecture

node-1

node-2

node-3

Docker engine

Docker engine

Docker engine

Swarm Manager

Consul

Jenkins master

Swarm Node

Swarm Node

Jenkins slave

node-4

Docker engine

Swarm Node

(Git Server)

192.168.56.101
192.168.56.102
192.168.56.103
192.168.56.104

Vagrant

# Clone vagrant file
$ git clone https://gist.github.com/6b83a6d3ceecf6aa919efa40aa194f7f.git devops

# Enter directory
$ cd devops

# Start VM
$ vagrant up

# Acccess VM
$ vagrant ssh node-1
$ vagrant ssh node-2
$ vagrant ssh node-3
$ vagrant ssh node-4

# Stop VM
$ vagrant halt

# Delete VM
$ vagrant destroy

Task I

Docker Swarm Set up

Install Docker

all node

$ sudo apt-get install -y git
$ git clone https://gist.github.com/257fbfac796be900bf5171b307a062c1.git devops-cicd && cd devops-cicd
$ chmod +x install-docker.sh
$ sudo sh install-docker.sh

Edit Docker conf

 all node

DOCKER_OPTS="-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock \
--cluster-store=consul://192.168.56.101:8500/network \
--cluster-advertise=eth1:2375"

Edit file : /etc/default/docker

Restart service :

$ sudo /etc/init.d/docker restart

Start Consul 

@node-1 

$ sudo docker run -d -p 8500:8500 \
   --name=consul progrium/consul \
   -server -bootstrap

Create Swarm 

@node-1

$ sudo docker run -d -p 4000:4000 swarm manage -H :4000 \
  --replication --advertise 192.168.56.101:4000 \
  consul://192.168.56.101:8500

Join Swarm node 

@node-1 

$ sudo docker run -d swarm join \
  --advertise=192.168.56.102:2375 \
  consul://192.168.56.101:8500

 

$ sudo docker run -d swarm join \
  --advertise=192.168.56.103:2375 \
  consul://192.168.56.101:8500

 

$ sudo docker run -d swarm join \
  --advertise=192.168.56.104:2375 \
  consul://192.168.56.101:8500

 

 

 

 Confirm the cluster 

 @node-1

$ sudo docker -H :4000 info
Server Version: swarm/1.2.5
Role: primary
Nodes: 3
 node-2: 192.168.56.102:2375
 node-3: 192.168.56.103:2375
 node-4: 192.168.56.104:2375

 Confirm the cluster 

 @node-1

$ sudo docker -H :4000 version

Client:
Version:      1.12.1
API version:  1.24
Go version:   go1.6.3
Git commit:   23cf638
Built:        Thu Aug 18 05:22:43 2016
OS/Arch:      linux/amd64

Server:
Version:      swarm/1.2.5 <<<<<<<<<<<<<<<<<<<<<<<
API version:  1.22
Go version:   go1.5.4
Git commit:   27968ed
Built:        Thu Aug 18 23:10:29 UTC 2016
 OS/Arch:      linux/amd64

 Install Docker Compose

 @node-2

$ sudo curl -L https://github.com/docker/compose/releases/download/1.8.0/docker-compose-`uname -s`-`uname -m` | sudo tee /usr/local/bin/docker-compose > /dev/null
$ sudo chmod +x /usr/local/bin/docker-compose

Jenkins Setup

Task II

Start Jenkins Master

@node1 

$ sudo docker  run -d -p 8080:8080 \
  --name jenkins-master jenkins

 

Jenkins credential

@node1

$ ssh-keygen -t rsa

 

​Generating public/private rsa key pair.
Enter file in which to save the key (/home/vagrant/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/vagrant/.ssh/id_rsa.
Your public key has been saved in /home/vagrant/.ssh/id_rsa.pub.
The key fingerprint is:
bb:93:37:94:b7:80:41:bd:33:54:5f:7d:34:f6:e2:d7 vagrant@node-1
The key's randomart image is:
+--[ RSA 2048]----+
|         . ..  ++|
|        . o  ...=|
|       . . .  o o|
|        . +  . ..|
|        So +  . E|
|        ..+ .  . |
|        .o o .   |
|        o.o .    |
|        .o .     |
+-----------------+

Jenkins credential

@node1

$ ssh-copy-id vagrant@192.168.56.102

vagrant@192.168.56.102's password:

<type vagrant>

Jenkins Slave setup

@node2

$ sudo apt-get install -y default-jre

Jenkins 2.0 setup

@node1

Open browser and go to
http://192.168.56.101:8080

Jenkins 2.0

@node-1

setup

@node1

$ sudo docker logs jenkins-master

 

*************************************************************
Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:

9fc503eb2bf64126bb4705ae100f7e73

This may also be found at: /var/jenkins_home/secrets/initialAdminPassword

*************************************************************
--> setting agent port for jnlp
--> setting agent port for jnlp... done

Jenkins 2.0 setup

@node1

Jenkins 2.0 setup

@node1

Jenkins 2.0 install plugins

 

@node1

Install plugins

Go to menu

  Manage Jenkins        Manage Plugins

  Available

  • Cloudbees Docker build
    and Publish plugin
  • Github plugin
  • Gogs plugin

Jenkins 2.0

Add slave node

 

@node1

Set Node name

Go to menu

  Manage Jenkins        Manage Nodes        New Node

"slave"

Jenkins 2.0

Add slave node

 

@node1

Jenkins 2.0

Add slave node

 

@node1

$ cat ~/.ssh/id_rsa

@node-1

Jenkins 2.0

Add slave node

 

@node1

Repositories

&

Webhooks

Task III

CI/CD with Jenkins

CI/CD with Jenkins

Gogs

Repositories

&

Webhooks

Gogs setup

@node4

 

# Start MySQL container
$ sudo docker run -d --name=mysql -p 3306:3306 -e MYSQL_PASS="password" -e ON_CREATE_DB="gogs_db" tutum/mysql

# Create local directory for volume.
$ sudo mkdir -p /var/gogs

# Use `docker run` for the first time.
$ sudo docker run -d --name=gogs -p 10022:22 \
  -p 10080:3000 -v /var/gogs:/data gogs/gogs

Gogs setup

@node4

http://192.168.56.104:10080

Gogs setup

@node4

http://192.168.56.104:10080

Create Repo

Create Repo

Create Repo

Commit

CODE

$ git clone https://github.com/engineerball/example-voting-app.git
$ cd example-voting-app
$ git remote set-url origin http://192.168.56.104:10080/devops/example-voting-app.git
$ git add .
$ git commit -m "First commit"
$ git push origin master

Create Repo

Commit

CODE

Docker

Registry

https://hub.docker.com

Task IV

The Build Jobs

Task V

The Voting App

▷ A Python webapp which lets you vote between two options
▷ A Redis queue which collects new votes
▷ A Java worker which consumes votes and stores them in…
▷ A Postgres database backed by a Docker volume
A Node.js webapp which shows the results of the voting in real time

Voting App

# Using official python runtime base image
FROM python:2.7

# Set the application directory
WORKDIR /app

# Install our requirements.txt
ADD requirements.txt /app/requirements.txt
RUN pip install -r requirements.txt

# Copy our code from the current folder to /app inside the container
ADD . /app

# Make port 5000 available for links and/or publish
EXPOSE 80 

# Define our command to be run when launching the container
CMD ["python", "app.py"]

Result App

FROM node:0.10

RUN mkdir /app
WORKDIR /app

ADD package.json /app/package.json
RUN npm install && npm ls
RUN mv /app/node_modules /node_modules

ADD . /app

ENV PORT 80
EXPOSE 80

CMD ["node", "server.js"]

Worker

FROM java:7

RUN apt-get update -qq && apt-get install -y maven && apt-get clean

WORKDIR /code

ADD pom.xml /code/pom.xml
RUN ["mvn", "dependency:resolve"]
RUN ["mvn", "verify"]

# Adding source, compile and package into a fat jar
ADD src /code/src
RUN ["mvn", "package"]

CMD ["/usr/lib/jvm/java-7-openjdk-amd64/bin/java", "-jar", "target/worker-jar-with-dependencies.jar"]

Worker

FROM java:7

RUN apt-get update -qq && apt-get install -y maven && apt-get clean

WORKDIR /code

ADD pom.xml /code/pom.xml
RUN ["mvn", "dependency:resolve"]
RUN ["mvn", "verify"]

# Adding source, compile and package into a fat jar
ADD src /code/src
RUN ["mvn", "package"]

CMD ["/usr/lib/jvm/java-7-openjdk-amd64/bin/java", "-jar", "target/worker-jar-with-dependencies.jar"]

docker-compose.yml

version: "2"

services:
  redis:
    image: redis
    expose: ["6379"]

  db:
    image: postgres:9.4

  voting-app:
    image: $DTR/voting-app:$GIT_COMMIT
    ports:
      - "5000:80"

  result-app:
    image: $DTR/result-app:$GIT_COMMIT
    ports:
      - "5001:80"

  worker:
    image: $DTR/worker:$GIT_COMMIT

Create jobs (build-voting-app)

Select target to run

Add SCM

http://192.168.56.104:10080/devops/example-voting-app.git

Build Triggers

App Build

Docker hub credential

App Build (Advanced)

Create jobs

  • New Item
  • Copy existing Item

Create jobs

  • Edit build

Create jobs

  • Edit build

Create jobs

  • Repeat step again
  • Change
    "build-result-app" to
    "build-worker"

Enable build triggers

  1. Go to Gogs http://192.168.56.104:10080
  2. Go to repository and Settings (http://192.168.56.104:10080/devops/example-voting-app/settings)
  3. At Webhooks click "Add Webhook"
  4. Select "Gogs"
  5. Fill information 
    • Payload => "http://192.168.56.101:8080/gogs-webhook/?job=build-voting-app"
  6. Add another Webhooks and fill information
    • Payload => "http://192.168.56.101:8080/gogs-webhook/?job=build-result-app"
    • Payload => "http://192.168.56.101:8080/gogs-webhook/?job=build-worker"

Enable build triggers

Enable build triggers

The Deploy Job

Task VI

The Deploy Job

Restrict run target

Config SCM

Config Build Triggers

Config Build

export DOCKER_HOST=192.168.56.101:4000 # Docker swarm manager
export DTR=engineerball # DockerHub ID
docker-compose -f vote-apps/docker-compose.yml stop voting-app result-app worker
docker-compose -f vote-apps/docker-compose.yml rm -f 
docker-compose -f vote-apps/docker-compose.yml pull voting-app result-app worker
docker-compose -f vote-apps/docker-compose.yml up -d 

vote-apps-deploy

Deploying App

Task VII

Clone git
Make changes
Push git

Running App 

@node-1 

$ docker -H :4000 ps -a
CONTAINER ID  IMAGE          		      COMMAND      		CREATED       	STATUS         PORTS         		    NAMES
edd35c4ad17d  engineerball/result-app:99883f  "node server.js"   	18 minutes ago  Up 18 minutes  192.168.56.103:5001->80/tcp  node-3/voteapps_result-app_1
f4a6383f76ca  engineerball/voting-app:99883f  "python app.py"    	18 minutes ago  Up 18 minutes  192.168.56.104:5000->80/tcp  node-4/voteapps_voting-app_1
9a885721e2cf  engineerball/worker:99883f      "/usr/lib/jvm/java-7-"   	18 minutes ago  Up 18 minutes 							 	node-3/voteapps_worker_1
fee6b1763e8c  redis                           "docker-entrypoint.sh"   	57 minutes ago  Up 57 minutes  6379/tcp        			 	node-3/voteapps_redis_1
94388eb94323  postgres:9.4         	      "/docker-entrypoint.s"   	57 minutes ago  Up 57 minutes  5432/tcp         			node-3/voteapps_db_1

Running App

@node-1

result-app  192.168.56.103:5001->80/tcp
voting-app  192.168.56.104:5000->80/tcp

Voting App

Running App 

@node-1 

Result App

CICD Workflow

By Teerapat Khunpech