CICD_DEMO完整版示例

环境准备

部署gitlab

1
docker run --detach --hostname 10.8.242.28 --publish 443:443 --publish 80:80 --publish 1022:22 --name gitlab --restart always --volume /srv/gitlab/config:/etc/gitlab --volume /srv/gitlab/logs:/var/log/gitlab --volume /srv/gitlab/data:/var/opt/gitlab gitlab/gitlab-ce:12.10.3-ce.0

替换hostname为实际节点外网IP

部署Harbor

Harbor部署与管理
部署前先修改docker
编辑docker

1
2
3
4
vim /etc/docker/daemon.json
{
"insecure-registries" : ["0.0.0.0/0"]
}

重启docker

1
systemctl restart docker

安装docker-compose

1
2
curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

下载harbor

1
https://github.com/goharbor/harbor/releases/download/v1.10.2/harbor-online-installer-v1.10.2.tgz

配置harbo.yaml

1
hostname: 172.31.48.86 //修改为实际节点IP

屏蔽https配置

安装harbor

1
./install.sh --with-clair

1
2
3
4
5
6
7
8
9
10
11
12
13
docker-compose ps
Name Command State Ports
---------------------------------------------------------------------------------------------
clair /docker-entrypoint.sh Up (healthy) 6060/tcp, 6061/tcp
harbor-core /harbor/start.sh Up (healthy)
harbor-db /entrypoint.sh postgres Up (healthy) 5432/tcp
harbor-jobservice /harbor/start.sh Up
harbor-log /bin/sh -c /usr/local/bin/ ... Up (healthy) 127.0.0.1:1514->10514/tcp
harbor-portal nginx -g daemon off; Up (healthy) 80/tcp
nginx nginx -g daemon off; Up (healthy) 0.0.0.0:80->80/tcp
redis docker-entrypoint.sh redis ... Up 6379/tcp
registry /entrypoint.sh /etc/regist ... Up (healthy) 5000/tcp
registryctl /harbor/start.sh Up (healthy)

访问http://node_ip

admin/Harbor12345

Drone配置使用

Rancher应用商店已经提供了Drone的部署安装及配置,通过Rancher应用商店即可将Drone部署并配置对接

在gitlab上创建一个项目名为go-server,编写个应用
server.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main
import (
"fmt"
"log"
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World")
}
func main() {
http.HandleFunc("/", hello)
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}

创建Dockerfile
Dockerfile

1
2
3
4
FROM golang
WORKDIR /go
ADD server /go
CMD ["./server"]

1、配置gitlab外部认证

回调接口填写其中一个节点的http://ip/login

如下

保存好对应的ApplicationID和secret

部署Drone

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
docker run \
--volume=/var/run/docker.sock:/var/run/docker.sock \
--volume=/var/lib/drone:/data \
--env=DRONE_LOGS_DEBUG=true \
--env=DRONE_GIT_ALWAYS_AUTH=false \
--env=DRONE_GITLAB_SERVER=http://114.215.25.58 \
--env=DRONE_GITLAB_CLIENT_ID=d6272993ac02c3bb4069d73bf0ff8dabeaff47c0739ae27d1a23e8b80e33faa5 \
--env=DRONE_GITLAB_CLIENT_SECRET=01f454fe0a55256a974d420b8ca023df6efc80b33d8a917dd16138b152b73253 \
--env=DRONE_RPC_SECRET=12345678\
--env=DRONE_RUNNER_CAPACITY=3 \
--env=DRONE_SERVER_HOST=114.215.130.36\
--env=DRONE_SERVER_PROTO=http \
--env=DRONE_TLS_AUTOCERT=false \
--publish=80:80 \
--publish=443:443 \
--restart=always \
--detach=true \
--name=drone \
drone/drone:1

参数说明:

参数 说明
DRONE_RPC_SECRET runner连接server的凭证
DRONE_RUNNER_CAPACITY server调用runner的并发数
DRONE_SERVER_HOST server的ip
DRONE_SERVER_PROTO server对外提供的协议可选http和https

部署Drone-runner
采用Docker的方式

1
2
3
4
5
6
7
8
9
10
11
docker run -d \
-v /var/run/docker.sock:/var/run/docker.sock \
-e DRONE_RPC_PROTO=http \
-e DRONE_RPC_HOST=172.31.227.147 \
-e DRONE_RPC_SECRET=12345678 \
-e DRONE_RUNNER_CAPACITY=3 \
-e DRONE_RUNNER_NAME=${HOSTNAME} \
-p 3000:3000 \
--restart always \
--name runner \
drone/drone-runner-docker:1

检查日志查看是否部署成功

在gitlab go-server项目创建名为.drone文件填入以下内容

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
kind: pipeline
type: docker
name: build
steps:
- name: build-code
image: golang:alpine
pull: if-not-exists # always never
commands:
- pwd
- ls
- go build server.go
- ls
- name: build-image
image: plugins/docker
settings:
repo: 172.31.227.151/go-server/go-server
registry: 172.31.227.151
use_cache: true
username:
from_secret: registry_username
password:
from_secret: registry_password
tags: ${DRONE_BUILD_NUMBER}
insecure: true
mirror: https://yefnfc9c.mirror.aliyuncs.com/
trigger:
branch:
- master
event:
- push

连接镜像仓库的凭证环境变量可在Drone对应的项目中定义好

参数说明:

参数 说明
DRONE_RPC_PROTO runner连接server协议
DRONE_RPC_HOST server的地址
DRONE_RPC_SECRET 连接server的凭证

commit代码后会自动运行CI

Jenkins配置使用

应用概述

整体CICD流程

  • push code to trigger Gitlab webhook
  • compiling the code and build image
  • test
  • upload image to harbor
  • Generate a new YML file
  • deploy/upgrade

先决条件

1、clone front-end项目

1
git clone https://gitee.com/wanshaoyuan/front-end.git

2、并上传到内部gitlab上

3、在harbor中创建front-ent项目

4、在jenkins中安装好gitlab、docker、Kubernetes插件

5、部署sock-shap

1
git clone https://gitee.com/wanshaoyuan/microservices-demo.git
1
2
kubectl create namespace sock-shop
kubectl apply -f microservices-demo/deploy/kubernetes/manifests/.

访问

1
http://node_ip:30001

Jenkins安装

1
docker run -itd --name jenkins -u root -p 50000:50000 -p 8080:8080 -v /var/jenkins_home:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock jenkins/jenkins:lts

节点安装git

1
yum install git

获取jenkins密码

1
docker logs jenkins |grep password

安装推荐的Jenkins插件

Jenkins对接Gitlab

安装gitlab插件

Gitlab中申请AccessToken

将申请成功的token保存好

配置Jenkins对接gitlab


测试连接

测试

在gitlab中创建项目
上传个test.txt文件通过Jenkins读取

配置连接gitlab私有项目的密钥可以用ssh密钥也可以使用账号密码

构建

去cat这个文件输出内容

执行立即构建

输出结果

Jenkins对接Kubernetes

安装Kubernetes插件

Jenkins-CI对接Kubernetes

配置
系统管理—>系统设置—>新增一个云

配置Jenkins URL,这里我们没有配置api-server地址和证书key,连接kubernetes,所以默认会去读取放在JENKINS_HOME的.kube/目录的kubeconfig文件,用于连接集群。我这里是通过安装包的方式安装的Jenkins HOME在/var/lib/jenkins/目录,如果是通过容器方式启动,将kubeconfig文件直接放~/.kube/目录。

从RancherUI上复制配置文件

保存到Jenkins主机的config文件中
复制粘贴到Jenkins容器内的~/.kube/config文件中

1
2
docker exec -it jenkins mkdir /root/.kube/
docker cp config jenkins:/root/.kube/config

在编译集群对应项目下创建secret

在jenkins系统设置中创建一个Kubernetes云

演示效果

修改前,先展示前端。(https://github.com/microservices-demo/front-end)这里我们演示修改前端logo,然后git push到代码仓库,触发Jenkins进行cicd操作,修改front-end/public/navbar.html的<img src=”img/logo.png”为新的图片地址,然后将代码提交触发cicd重新打开前端看见logo变了。

创建cicd Pipeline

生成连接gitlab的凭证Grove语句

添加Harbor凭证

创建流水线任务

添加以下Pipeline任务

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
pipeline {
environment {
registry = "172.31.227.151"
project_name = "/front-end"
app_name = "/front-end"
registryCredential = 'harbor'
}
agent {
kubernetes {
defaultContainer 'wanshaoyuan/jnlp-slave:3.27-1-alpine'
yaml """
apiVersion: v1
kind: Pod
metadata:
labels:
some-label: some-label-value
spec:
containers:
- name: jnlp
image: 'wanshaoyuan/jnlp-slave:3.27-1-alpine'
args: ['\$(JENKINS_SECRET)', '\$(JENKINS_NAME)']
- name : docker
image: wanshaoyuan/docker:19.03.2
tty: true
volumeMounts:
- name: repo-docker-sock
mountPath: /var/run/docker.sock
- name: kubectl
image: wanshaoyuan/jenkins-tools:v1.0
tty: true
volumeMounts:
- mountPath: "/root/.kube"
name: "volume-0"
readOnly: false
volumes:
- name: "volume-0"
secret:
secretName: "kubeconfig"
- name: repo-docker-sock
hostPath:
path: /var/run/docker.sock
"""
}
}
stages {
stage('clone code') {
steps {
git credentialsId: '9371d980-6bc2-47aa-a47f-04cf78871e7a', url: 'http://114.215.25.58/root/front-end.git'
}
}
stage('image build ') {
steps {
container('docker') {
script {
docker.withRegistry( 'http://172.31.227.151', registryCredential ) {
def dockerImage=docker.build registry + project_name + app_name +":$BUILD_NUMBER"
dockerImage.push()
}
}
}
}
}
stage('deploy app ') {
steps {
container('kubectl') {
sh 'sed -i "s/image: .*front-end:.*/image: 172.31.227.151\\/front-end\\/front-end:$BUILD_ID/g" front-end-dep.yaml'
sh 'kubectl apply -f front-end-dep.yaml'
}
}
}
}
}

  • 定义环境变量:镜像仓库地址、项目名称、应用名称、连接镜像仓库的认证信息。
  • 定义jnlp-agent POD所启动的container,包括jnlp容器,用于根jenkins进行通信,maven容器用于代码编译、docker 容器,用于容器镜像编译,kubectl容器用于连接对应的kubernetes集群用于应用创建和更新。
  • 定义四个阶段,每个阶段定义对应的步骤,调用对应的容器实现功能(clone代码阶段、代码编译阶段、容器镜像构建阶段、应用部署阶段)
  • 每个阶段调用对应的容器实现,如代码编译阶段调用编译容器进行、容器镜像构建阶段调用docker镜像映射宿主机socket进行。

配置gitlab自动触发Jenkins CICD

编辑jenkins项目

配置使用Build when a change is pushed to GitLab. GitLab CI Service URL: http://191.8.2.112:12000/project/test-go-dev 当代码有更新的时候触发,通过GitLab CI

打开gitlab,配置webhook回掉地址,填入jenkins对应项目地址
gitlab对应的项目设置页

默认触发器是push事件时,触发webhook,需要注意的是因为我们这里的Jenkins配置了用户名和密码所以
url需要在原来基础上添加用户名和密码其他不变
格式为
http://username:password@192.168.1.4:1080/project/dashboard-build

默认测试webhook调用

默认一个代码commit的请求
下载logo放置

1
wget https://rancher.com/img/brand-guidelines/assets/logos/png/cow/rancher-logo-cow-blue.png

修改前端logo,然后git push到代码仓库,触发Jenkins进行cicd操作,修改front-end/public/navbar.html的<img src=”img/logo.png”为新的图片地址,然后将代码提交触发cicd重新打开前端看见logo变了。

Rancher-Pipeline配置使用

概述

Rancher2.x Pipeline支持在发布阶段直接将应用发布到应用商店,部署阶段直接从应用商店部署应用,以下为操作步骤

先决条件

  • 提前创建好存放统一chart的目录目录结构如下:
    ./charts/app_name/version

将app_name和Version替换为实际应用名和版本号

CICD到应用商店流程

1
2
3
4
5
6
graph LR
A[源码库]-- "Jenkins clone源码完成其他CI环节" --> B[clone catalog 模板并通过Jenkins修改模板]
B -- "git push" --> C[push到统一存放catalog目录]
C-- "Rancher-server自动同步更新" --> c[UI显示新版本可更新]

应用chart制作

继续使用go-server项目的例子

创建应用源码目录创建专属chart目录

目录结构如下

创建目录

1
mkdir /go-server/go-server-chart

在目录go-server-chartt内创建以下文件和文件夹

1
2
3
4
5
6
7
8
9
10
go-server-chart
│   ├── 0
│   │   ├── Chart.yaml
│   │   ├── questions.yml
│   │   ├── templates
│   │   │   ├── _helpers.tpl
│   │   │   ├── go-server-dep.yaml
│   │   │   └── go-server-svc.yaml
│   │   └── values.yaml
│   └── README.md

0表示版本文件夹,如果要发布新版本只需要创建个1或者另外文件夹,然后修改Chart.yaml文件里面的版本号。

README.md 内定义对应用的说明
Chart.yaml

1
2
3
4
5
apiVersion: v1
description: go-server
name: go-server
version: ${CICD_EXECUTION_SEQUENCE}
icon: http://ohx02qrb8.bkt.clouddn.com/micheling.png

详细环境变量参考

1
https://rancher.com/docs/rancher/v2.x/en/k8s-in-rancher/pipelines/

定义软件的描述、名字、版本、图标地址

questions.yml

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
questions:
- variable: replicaCount
default: "1"
description: "Replica count"
type: string
required: true
label: Replicas
- variable: imageName
default: "172.31.227.151/go-server/go-server"
description: "image name"
type: string
required: true
label: image_name
- variable: imageTage
default: "latest"
description: "image version"
type: string
required: true
label: image_version
- variable: nodeport
default: "30000"
description: "NodePort port number(to set explicitly, choose port between 30000-32767)"
type: string
required: true
label: nginx Service NodePort number

定义变量,主要与ui上对应
variable: 对应template里面的.Values.xxx值
default:当用户没有设置自定义值时的默认值
description: ui上用户自定义值的描述
type: 自定义值的数据类型
required: 是否必填
label:标签

values.yaml: values.yaml 则提供了这些配置参数的默认值。

templates :放置实际要执行的应用的yaml文件

go-server-dep.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
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
cattle.io/creator: norman
workload.user.cattle.io/workloadselector: deployment-default-test
name: test
namespace: default
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
workload.user.cattle.io/workloadselector: deployment-default-test
template:
metadata:
labels:
workload.user.cattle.io/workloadselector: deployment-default-test
spec:
containers:
- image: {{ .Values.imageName }}:{{ .Values.imageTage }}
imagePullPolicy: Always
name: test
ports:
- containerPort: 8080
name: 8080tcp01
protocol: TCP
stdin: true
tty: true

go-server-svc.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Service
metadata:
labels:
cattle.io/creator: norman
name: test-nodeport
namespace: default
spec:
ports:
- name: 8080tcp01
nodePort: {{ .Values.nodeport }}
port: 8080
protocol: TCP
targetPort: 8080
selector:
workload.user.cattle.io/workloadselector: deployment-default-test
type: NodePort

_helpers.tpl 定义一些系统通用的继承变量一般不需要,直接用默认就好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}

同时创建实际的catalog目录

1
mkdir test-chart

继续按上面方式创建一个go-server的chart应用,需要修改Chart.yaml文件这里就不能在用变量了,因为使用添加私有应用商店时是读取不出变量的,在源码库内的chart.yaml用变量是因为需要动态更新。

Chart.yaml

1
2
3
4
5
apiVersion: v1
description: go-server
name: go-server
version: v1.0
icon: http://ohx02qrb8.bkt.clouddn.com/micheling.png

然后push到代码仓库的chart项目

Pipeline配置

创建secret用于push时的代码仓库的帐号密码

如果push是ssh方式这里的secret就放置公钥

USERNAME和PASSWORD两个key

创建镜像仓库凭证用于上传镜像

选择代码库创建Pipeline

创建代码编译步骤

创建编译上传镜像步骤

创建发布应用模板步骤

类型选择Publish Catalog Template

  • Chart Folder:填写源码库内的chart的相对路径
  • Catalog Template Version:这里填写Rancher Pipeline环境变量根在代码库内chart目录Chart.yaml Version一致
  • Catalog Template Name:为你的catalog应用的名称,如果你是生成一个新的catalog。统一存放catalog目录没有这个应用则可以根据实际需求取名字,如果是在原有基础上更新则有已有的catalog,则用原目录名

运行Pipeline

catalog自动更新

对应代码目录下 .rancher-pipeline.yml文件

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
stages:
- name: cod_build
steps:
- runScriptConfig:
image: golang:latest
shellScript: go build server.go
- name: image_build
steps:
- publishImageConfig:
dockerfilePath: ./Dockerfile
buildContext: .
tag: go-server/go-server:${CICD_GIT_COMMIT}
pushRemote: true
registry: 172.31.227.151
env:
PLUGIN_INSECURE: "true"
PLUGIN_MIRROR: https://yefnfc9c.mirror.aliyuncs.com
- name: update_catalog
steps:
- publishCatalogConfig:
path: ./go-server-chart/0
catalogTemplate: go-server
version: ${CICD_EXECUTION_SEQUENCE}
gitUrl: http://114.215.25.58/root/chart.git
gitBranch: master
gitAuthor: root
gitEmail: shaoyua@rancher.com
envFrom:
- sourceName: gitlab
sourceKey: USERNAME
targetKey: USERNAME
- sourceName: gitlab
sourceKey: PASSWORD
targetKey: PASSWORD
timeout: 60