CICD on OpenShift
Wrocław 14-01-2020 @ <WrocławJUG>
Andrzej Goławski
Application Delivery Cycle
Dev
Code
GIT
Build
Artifact
Deploy Test
Deploy Green
Testing
Internal Testing
Switch Traffic to Green
Monitoring
Deploy Blue
Switch Traffic to Blue
OpenShift
The Origin Community Distribution of Kubernetes
OpenShift
Concepts:
- Source to Image (s2i)
OpenShift
Concepts:
- Source to Image (s2i)
- POD
POD
OpenShift
Concepts:
- Source to Image (s2i)
- POD
- Resource/Object
Resource
apiVersion: v1
kind: Pod
metadata:
name: simple-app
spec:
containers:
- name: simple-app-container
image: andipansa/courses-service
oc create -f simple-pod.yaml
OpenShift
Concepts:
- Source to Image (s2i)
- POD
- Resource/Object
- Image Stream
Image Stream
kind: Deployment
....
containers:
- image: mailcatcher
---
kind: Pod
....
containers:
- image: mailcatcher
kubectl run mailcatcher --image=mailcatcher
oc new-app mailcatcher
mailcatcher
DOCKERHUB
is
kind: ImageStream
....
tags:
from:
kind: DockerImage
name: mailcatcher
name: latest
---
kind: DeploymentConfig
....
from:
kind: ImageStreamTag
name: mailcatcher:latest
---
kind: Pod
....
containers:
- image: mailcatcher@sha256:4....
OpenShift
Concepts:
- Source to Image (s2i)
- POD
- Resource/Object
- Image Stream
- Project
Application Delivery Cycle
GIT
Code
Build
Deploy
APP
APP
APP
inner registry
push
pull
pull
clone
app
app-test
app-prod
Deploy
Deploy
Build Process on OpenShift
openshift
app
inner registry
s2i-java11-maven
bc
s2i
application
is
is
GIT
push
push
clone
oc new-build --name=app s2i-java11-maven~http://URL-TO-REPO
oc new-project app
Build Process on OpenShift
#!/bin/bash
#Create app project
oc new-project app
#Create Build Configuration
oc new-build --name=app s2i-java11-maven~$APP_GIT_URL -n app
Build Application script: build-app.sh
Deploy Process on OpenShift
app
app-test
inner registry
bc
is
app:latest
dc
rc
pod
pull
app:test
oc new-app --name=app-test --image-stream=app/app:test --allow-missing-imagestream-tags
oc tag app:latest app:test -n app
oc new-project app-test
oc policy add-role-to-user system:image-puller system:serviceaccount:app-test:default -n app
Deploy Process on OpenShift
#!/bin/bash
#Create app-test project
oc new-project app-test
#Add permissions to pull image from app project
oc adm policy add-role-to-user system:image-puller \
system:serviceaccount:app-test:default -n app
#Create DeploymentConfiguration
oc new-app --name=app-test --image-stream=app/app:test --allow-missing-imagestream-tags
#Remove unnecessary Image Stream
oc delete is app-test -n app-test
#Add readiness probe
oc set probe dc/app-test --readiness --get-url=http://:8080/actuator/health -n app-test
Deploy Application on Test script: create-app-test.sh
Network on OpenShift
app-test
pod
svc
route
http request
oc expose dc/app-test
oc expose scv/app-test
Deploy Production
Deploy Production
#!/bin/bash
#Create project prod
oc new-project app-prod
#Add permissions to pull image from app project
oc policy add-role-to-user system:image-puller \
system:serviceaccount:app-prod:default -n app
#Create blue and green deployments
oc new-app --name=app-blue --image-stream=app/app:prod-blue \
--allow-missing-imagestream-tags -n app-prod
oc new-app --name=app-green --image-stream=app/app:prod-green \
--allow-missing-imagestream-tags -n app-prod
#Remove unnecessary Image Streams
oc delete is --all -n app-prod
#Expose prod applications
oc expose deploymentconfig.apps.openshift.io/app-blue --port=8080 -n app-prod
oc expose deploymentconfig.apps.openshift.io/app-green --port=8080 -n app-prod
oc expose service app-blue --hostname=blue.apps-crc.testing -n app-prod
oc expose service app-green --hostname=green.apps-crc.testing -n app-prod
#Add readiness probe
oc set probe dc/app-blue --readiness --get-url=http://:8080/actuator/health -n app-prod
oc set probe dc/app-green --readiness --get-url=http://:8080/actuator/health -n app-prod
Deploy Production script: create-app-prod.sh
Split traffic
app-test
pod
svc
route
http request
pod
svc
70%
30%
oc expose svc/app-green
oc set route-backends app-green app-green=70 app-blue=30
Expose Production
#!/bin/bash
#Create production Route
oc expose service app-blue --name prod \
--hostname=prod.apps-crc.testing -n app-prod
oc set route-backends prod app-green=0 app-blue=100 -n app-prod
Expose production script: expose-prod.sh
"CICD" on OpenShift without Jenkis
oc start-build app -n app
build project
oc tag app:latest app:test -n app
deploy test
oc tag app:test app:prod-green -n app
deploy green
oc tag app:test app:prod-blue -n app
switch traffic to green
oc set route-backends prod \
app-green=100 app-blue=0 -n app-prod
deploy blue
oc set route-backends prod \
app-green=0 app-blue=100 -n app-prod
switch traffic to blue
Jenkins on OpenShift
Install Jenkins on OpenShift
Install using CLI
oc new-app jenkins-persistent
oc get templates -n openshift | grep jenkins
jenkins-ephemeral Jenkins service, without persistent storage....
jenkins-persistent Jenkins service, with persistent storage....
Available Templates
Install using Web Console
Jenkins on OpenShift
cicd
J
bc
kind: BuildConfig
apiVersion: v1
metadata:
name: app-pipeline
namespace: cicd
labels:
name: app-pipeline
spec:
strategy:
type: JenkinsPipeline
jenkinsPipelineStrategy:
jenkinsfile: |-
echo 'hello world!'
oc start-build app-pipeline -n cicd
oc apply -f app-pipeline.yaml -n cicd
Jenkins on OpenShift
cicd
J
bc
stage('build') {
echo 'build'
}
stage('deploy') {
echo 'deploy'
}
jenkinsfile
oc start-build app-pipeline -n cicd
Jenkins on OpenShift
cicd
J
bc
node {
stage('build') {
echo 'build'
}
stage('deploy') {
echo 'deploy'
}
}
jenkinsfile
oc start-build app-pipeline -n cicd
node('slave') {
stage('build') {
echo 'build'
}
stage('deploy') {
echo 'deploy'
}
}
slave
execute build
execute deploy
Jenkins on OpenShift
cicd
J
bc
node {
stage('build') {
sh 'oc start-build app -n app'
}
stage('deploy test') {
sh 'oc tag app:latest app:test -n app'
sh 'oc rollout latest app-test -n app-test'
}
}
jenkinsfile
oc start-build app-pipeline -n cicd
app
app-test
build
deploy
dc
bc
OpenShift Jenkins Pipeline (DSL) Plugin
OpenShift Jenkins Pipeline (DSL) Plugin
Build Configuration Definition
kind: BuildConfig
apiVersion: v1
metadata:
name: app-pipeline
namespace: cicd
labels:
name: app-pipeline
spec:
strategy:
type: JenkinsPipeline
jenkinsPipelineStrategy:
jenkinsfile: |-
node {
stage("build") {
.....
}
}
OpenShift Jenkins Pipeline (DSL) Plugin
Build
node {
stage("build") {
openshift.withCluster() {
openshift.withProject("app") {
def buildConfig = openshift.selector("bc", "app")
def build = buildConfig.startBuild("--incremental")
build.untilEach() {
return it.object().status.phase == "Complete"
}
}
}
}
}
OpenShift 3.x
node {
stage("build") {
openshiftBuild bldCfg: 'app', namespace: 'app'
}
}
OpenShift Jenkins Pipeline (DSL) Plugin
Deploy Test
node {
stage("build") {.....}
stage("deploy to test") {
openshift.withCluster() {
openshift.withProject("app-test") {
openshift.tag( "app/app:latest", "app/app:test")
def deployConfig = openshift.selector("dc", "app-test")
def replicasNumber = deployConfig.object().spec.replicas
deployConfig.rollout().latest()
def latestDeploymentVersion =
deployConfig.object().status.latestVersion
def replicationController =
openshift.selector('rc', "app-test-${latestDeploymentVersion}")
replicationController.untilEach() {
def replicationControllerObject = it.object()
return (replicasNumber.equals(
replicationControllerObject.status.readyReplicas))
}
}
}
}
}
OpenShift Jenkins Pipeline (DSL) Plugin
node {
stage("build") {.....}
stage("deploy to test") {
openshiftDeploy depCfg: 'app-test', namespace: 'app-test'
}
}
OpenShift 3.x
OpenShift Jenkins Pipeline (DSL) Plugin
Extract Deploy to method
node {
stage("build") {.....}
stage("deploy to test") {
openshift.withCluster() {
openshift.withProject("app-test") {
openshift.tag( "app/app:latest", "app/app:test")
deploy("app-test")
}
}
}
}
def deploy(String deploymentConfigurationName) {
def deployConfig = openshift.selector("dc", deploymentConfigurationName)
def replicasNumber = deployConfig.object().spec.replicas
deployConfig.rollout().latest()
def latestDeploymentVersion = deployConfig.object().status.latestVersion
def replicationController =
openshift.selector('rc', "${deploymentConfigurationName}-${latestDeploymentVersion}")
replicationController.untilEach() {
def replicationControllerObject = it.object()
return (replicasNumber.equals(
replicationControllerObject.status.readyReplicas))
}
}
OpenShift Jenkins Pipeline (DSL) Plugin
Testing
node {
stage("build") {.....}
stage("deploy to test") {.....}
stage("manual testing") {
input message: 'Should deploy to production as green?', ok: 'Yes'
}
}
def deploy(String deploymentConfigurationName) {.....}
OpenShift Jenkins Pipeline (DSL) Plugin
Deploy Green
node {
stage("build") {.....}
stage("deploy to test") {.....}
stage("manual testing") {.....}
stage("deploy green on production") {
openshift.withCluster() {
openshift.withProject("app-prod") {
openshift.tag("app/app:latest", "app/app:prod-green")
openshift.selector("dc", "app-green").scale("--replicas=1")
deploy("app-green")
}
}
}
}
def deploy(String deploymentConfigurationName) {.....}
OpenShift Jenkins Pipeline (DSL) Plugin
Internal testing Green
node {
stage("build") {.....}
stage("deploy to test") {.....}
stage("manual testing") {.....}
stage("deploy green on production") {.....}
stage("internal testing green") {
input message: 'Should swich traffic to green?', ok: 'Yes'
}
}
def deploy(String deploymentConfigurationName) {.....}
OpenShift Jenkins Pipeline (DSL) Plugin
Switch traffic to Green
node {
stage("build") {.....}
stage("deploy to test") {.....}
stage("manual testing") {.....}
stage("deploy on production as green") {.....}
stage("internal testing green") {.....}
stage("swich traffic to green") {
openshift.withCluster() {
openshift.withProject('app-prod') {
def routeObject = openshift.selector("route", "prod").object()
routeObject.spec.alternateBackends[0].weight=100
routeObject.spec.to.weight=0
openshift.apply(routeObject)
}
}
}
}
def deploy(String deploymentConfigurationName) {.....}
or
stage("swich traffic to green") {
sh 'oc set route-backends prod app-green=100 app-blue=0 -n app-prod'
}
OpenShift Jenkins Pipeline (DSL) Plugin
Extract switch traffic to method
node {
stage("build") {.....}
stage("deploy to test") {.....}
stage("manual testing") {.....}
stage("deploy on production as green") {.....}
stage("internal testing green") {.....}
stage("swich traffic to green") {
openshift.withCluster() {
openshift.withProject('app-prod') {
switchTraffic('prod', 100, 0)
}
}
}
def deploy(String deploymentConfigurationName) {.....}
def switchTraffic(String routeName, int greenWeight, int blueWeight) {
def routeObject = openshift.selector("route", routeName).object()
routeObject.spec.alternateBackends[0].weight=blueWeight
routeObject.spec.to.weight=greenWeight
openshift.apply(routeObject)
}
OpenShift Jenkins Pipeline (DSL) Plugin
Testing Green on production
node {
stage("build") {.....}
stage("deploy to test") {.....}
stage("manual testing") {.....}
stage("deploy green on production") {.....}
stage("internal testing green") {.....}
stage("swich traffic to green") {.....}
stage("testing green on production") {
input message: 'Should deploy to production as blue?', ok: 'Yes'
}
}
def deploy(String deploymentConfigurationName) {.....}
def switchTraffic(String routeName, int greenWeight, int blueWeight) {....}
OpenShift Jenkins Pipeline (DSL) Plugin
Deploy Blue
node {
stage("build") {.....}
stage("deploy to test") {.....}
stage("manual testing") {.....}
stage("deploy green on production") {.....}
stage("internal testing green") {.....}
stage("swich traffic to green") {.....}
stage("testing green on production") {.....}
stage("deploy blue on production") {
openshift.withCluster() {
openshift.withProject('app-prod') {
openshift.tag('app/app:latest', 'app/app:prod-blue')
deploy("app-blue")
}
}
}
}
def deploy(String deploymentConfigurationName) {.....}
def switchTraffic(String routeName, int greenWeight, int blueWeight) {....}
OpenShift Jenkins Pipeline (DSL) Plugin
Switch traffic to Blue
node {
stage("build") {.....}
stage("deploy to test") {.....}
stage("manual testing") {.....}
stage("deploy green on production") {.....}
stage("internal testing green") {.....}
stage("swich traffic to green") {.....}
stage("testing green on production") {.....}
stage("deploy blue on production") {.....}
stage("swich traffic to blue") {
openshift.withCluster() {
openshift.withProject('app-prod') {
switchTraffic('prod', 0, 100)
}
}
}
}
def deploy(String deploymentConfigurationName) {.....}
def switchTraffic(String routeName, int greenWeight, int blueWeight) {....}
OpenShift Jenkins Pipeline (DSL) Plugin
Turn off Geen
node {
stage("build") {.....}
stage("deploy to test") {.....}
stage("manual testing") {.....}
stage("deploy green on production") {.....}
stage("internal testing green") {.....}
stage("swich traffic to green") {.....}
stage("testing green on production") {.....}
stage("deploy blue on production") {.....}
stage("swich traffic to blue") {.....}
stage("turn off green") {
openshift.selector("dc", 'app-green').scale('--replicas=0')
}
}
def deploy(String deploymentConfigurationName) {.....}
def switchTraffic(String routeName, int greenWeight, int blueWeight) {....}
OpenShift Jenkins Pipeline (DSL) Plugin
Let's run IT
#!/bin/bash
#Add permissions for jenkins
oc policy add-role-to-user edit system:serviceaccount:cicd:jenkins -n app
oc policy add-role-to-user edit system:serviceaccount:cicd:jenkins -n app-test
oc policy add-role-to-user edit system:serviceaccount:cicd:jenkins -n app-prod
Setup permissions for Jenkins
OpenShift Jenkins Pipeline (DSL) Plugin
Turn off the triggers
#!/bin/bash
#Turn off the triggers for dc in project app-test
oc set triggers dc/app-test --from-config --remove -n app-test
oc patch dc/app-test \
-p '[{"op": "replace", "path": "/spec/triggers/0/imageChangeParams/automatic", "value":false}]' \
--type=json -n app-test
#Turn off the triggers for dc in project app-prod
oc set triggers dc/app-green --from-config --remove -n app-prod
oc set triggers dc/app-blue --from-config --remove -n app-prod
oc patch dc/app-green -p ....
oc patch dc/app-blue -p ....
oc apply -f app-pipeline.yaml
Apply pipeline to OpenShift
OpenShift Jenkins Pipeline (DSL) Plugin
Zapraszam na
Warszawa 02.06.2020
CICI on OpenShift JUG Wrocław
By andipansa
CICI on OpenShift JUG Wrocław
- 193