Kubernetes学习笔记(4):打造自动化流水线(CI/CD)

Jenkins介绍

Jenkins是一款开源 CI&CD 软件,用于自动化各种任务,包括构建、测试和部署软件。Jenkins 支持各种运行方式,可通过系统包、Docker 或者通过一个独立的 Java 程序。

传统部署方式存在的问题

使用Jenkins + Docker做CI/CD的好处

Jenkins安装

下载Jenkins安装介质

https://jenkins.io/zh/download/

简单起见,这里直接选择docker版本进行安装: https://hub.docker.com/r/jenkins/jenkins

1
docker pull jenkins/jenkins

更多有关Jenkins docker版本的说明可以参阅这里

启动Jenkins容器

1
docker run -p 8080:8080 -p 50000:50000 jenkins/jenkins:latest

第一次启动时会提示记录一下安装密码。

安装配置Jenkins实例

http://localhost:8080

输入刚才的安装密码输入,点击“继续”按钮。

选择“安装推荐的插件”即可,继续。。。

安装过程都是全自动的,可能会比较慢,冲杯咖啡稍等片刻。。。

第一次登陆,需要创建管理员账号,并配置一下Jenkins的地址。之后就可以使用Jenkins了。

创建你的第一个Job任务

新建一个Job,输入名称,任务类型选择流水线

配置Job任务

可以通过选择sample来参考一下如何编写job脚本。

编写pipeline script并保存。

1
2
3
4
5
6
7
node {

stage('Preparation') {
git "https://gitee.com/pa/mooc-k8s-demo-docker.git"
}

}

运行Job任务

点击屏幕左侧的”立即构建”菜单,执行任务构建。

点击左侧下方”Build History”里的小圆点,可以查看具体的构建日志,以便排查问题。

好了,现在可以开始编写正式的流水线代码了。

CI/CD 持续集成/持续部署过程

总体思路

编写相关脚本,通过Jenkis的job任务调用这些脚本,进而达到自动化集成/部署的效果。

需要编写的脚本如下:

  • Jenkis流水线脚本
  • Dockerfile
  • 主要执行shell脚本
  • 模板文件(可选)
  • k8s部署脚本

编写Jenkis流水线脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
node {
env.BUILD_DIR = "/root/build-workspace/"
env.MODULE = "web-demo"
env.HOST = "k8s-web.mooc.com"
stage('Preparation') {
git "https://gitee.com/pa/mooc-k8s-demo-docker.git"
}

stage('Maven Build') {
sh "mvn -pl ${MODULE} -am clean package"
}

stage('Build Image') {
sh "/root/script/build-image-web.sh"
}

stage('Deploy') {
sh "/root/script/deploy.sh"
}

}

编写Dockerfile

1
2
3
4
5
6
7
FROM hub.mooc.com/kubernetes/tomcat:8.0.51-alpine

COPY ROOT /usr/local/tomcat/webapps/ROOT

COPY dockerfiles/start.sh /usr/local/tomcat/bin/start.sh

ENTRYPOINT ["sh" , "/usr/local/tomcat/bin/start.sh"]

主要执行shell脚本

build-image-web.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/bin/bash

if [ "$[BUILD_DIR]" == "" ];then
echo "env 'BUILD_DIR' is not set"
exit 1
fi

# BUILD_DIR 和 JOB_NAME 是Jenkins自带的环境变量
DOCKER_DIR=${BUILD_DIR}/${JOB_NAME}

if [ ! -d "$[DOCKER_DIR]" ];then
mkdir -p ${DOCKER_DIR}
fi

echo "docker workspace ${DOCKER_DIR}"

JENKINS_DIR=${WORKSPACE}/${MODULE}

echo "jenkins workspace: ${JENKINS_DIR}"

if [ ! -f ${JENKINS_DIR}/target/*.war ];then
echo "target war file not found ${JENKINS_DIR}/target/*.war"
exit 1
fi

cd ${DOCKER_DIR}
rm -rf *
unzip -oq ${JENKINS_DIR}/target/*.war -d ./ROOT

mv ${JENKINS_DIR}/Dockerfile .

if [ -d ${JENKINS_DIR}/dockerfiles ];then
mv ${JENKINS_DIR}/dockerfiles .
fi

VERSION=$(date +%Y%m%d%H%M%S)
IMAGE_NAME=hub.mooc.com/kubernetes/${JOB_NAME}:${VERSION}

# 将当前构建的docker image & tag写到一个文件里,以便后续deploy.sh脚本获取
echo "${IMAGE_NAME}" > ${WORKSPACE}/IMAGE

echo "building image: ${IMAGE_NAME}"
docker build -t ${IMAGE_NAME}

docker push ${IMAGE_NAME}

build-image-web.sh添加执行权限。

1
chmod +x build-image-web.sh

在Jenkins所在机器上登录下镜像仓库,确保其可以推送docker镜像

1
docker login hub.mooc.com

编写模板文件(可选)

编写template模板文件。直接拿一个k8s的yaml文件进行修改即可。替换其中的一些变量名字,如:name, image, host等。

在vim中替换的命令为%s/要替换的目标字符串/结果字符串/g
即:%s/web-demo//g

web.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#deploy
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{name}}
spec:
selector:
matchLabels:
app: {{name}}
replicas: 1
template:
metadata:
labels:
app: {{name}}
spec:
containers:
- name: {{name}}
image: {{image}}
ports:
- containerPort: 8080
---
#service
apiVersion: v1
kind: Service
metadata:
name: {{name}}
spec:
ports:
- port: 80
protocol: TCP
targetPort: 8080
selector:
app: {{name}}
type: ClusterIP

---
#ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{name}}
spec:
rules:
- host: {{host}}
http:
paths:
- path: /
backend:
serviceName: {{name}}
servicePort: 80

编写k8s部署脚本

deploy.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#!/bin/bash

name=${JOB_NAME}
image=$(cat ${WORKSPACE}/IMAGE)
host=${HOST}

echo "---> deploying ... name: ${name}, image: ${image}, host: ${host}"

# $(dirname "${BASH_SOURCE[0]} 获取脚本当前运行的目录
cp $(dirname "${BASH_SOURCE[0]}")/template/web.yaml
echo "---> copy success"

# 用sed命令进行查找替换
sed -i "s,{{name}},${name},g" web.yaml
sed -i "s,{{image}},${image},g" web.yaml
sed -i "s,{{host}},${host},g" web.yaml

# 部署到k8s
echo "---> ready to apply"
kubectl apply -f web.yaml
echo "---> apply is finished"

# 健康检查
count=60
success=0

# 设置数组分隔符
IFS=","

# 为了避免k8s部署还没开始就进行健康检查,sleep 5秒再进行
# 更靠谱的方式其实应该判断k8s服务信息里的".metadata.annotations.deployment.kubernetes.io/revision"来判断,这个值每次部署后会自动加1
sleep 5

while [ ${count} -gt 0 ]
do
# 从k8s获取服务信息
replicas=$(kubectl get deploy ${name} -o go-template='{{.status.replicas}},{{.status.updatedReplicas}},{{.status.readyRelicas}},{{.status.availableRelicas}}')
echo replicas

# 转换成数组
arr=(${replicas})
if [ "${arr[0]}" == "${arr[1]}" -a "${arr[1]}" == "${arr[2]}" -a "${arr[2]}" == "${arr[3]}" ]; then
echo "health check success!"
success=1
break
fi

((count--))
sleep 2
done

if [ ${success} -ne 1 ]; then
echo "health check failed!"
exit 1
fi

# 打印log以便调试
cat web.yaml

设置执行权限

1
chmod +x deploy.sh

王方钢 / Kenny Wang wechat
0%