CI/CD概述

持续集成(Continuous Integration,CI):代码合并、构建、部署、测试都在一起,不断地执行这个过程,并对结果反馈。

持续部署(Continuous Deployment,CD):部署到测试环境、预生产环境、生产环境。

持续交付(Continuous Delivery,CD):将最终产品发布到生产环境,给用户使用。

CI工作流程设计

开发者提交代码到gitlab仓库,gitlab随后触发jenkins代码编译,构建镜像以及推送镜像到harbor仓库,紧接着Jenkins部署到docker主机(从harbor仓库拉取镜像到本地部署启动)。

gitlab主机 jenkins主机 harbor主机
192.168.0.11/24 192.168.0.13/24 192.168.0.12/24
                                                       项目环境表

1、部署Gitlab

1.1 部署Gitlab

mkdir gitlab
cd gitlab
docker run -d \
--name gitlab \
-p 8443:443 \
-p 9999:80 \
-p 9998:22 \
-v $PWD/config:/etc/gitlab \
-v $PWD/logs:/var/log/gitlab \
-v $PWD/data:/var/opt/gitlab \
-v /etc/localtime:/etc/localtime \
--restart=always \
lizhenliang/gitlab-ce-zh:latest

访问地址:http://IP:9999

初次会先设置管理员密码 ,然后登陆,默认管理员用户名root,密码就是刚设置的。

1.2 创建项目,提交测试代码

进入后先创建项目,提交代码,以便后面测试。

unzip tomcat-java-demo-master.zip
cd tomcat-java-demo-master
git init
git remote add origin http://192.168.0.11:9999/root/java-demo.git
git add .
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
git commit -m 'all'
git push origin master

2、部署Harbor镜像仓库

2.1 安装docker与docker-compose

2.2 解压离线包部署

# tar zxvf harbor-offline-installer-v2.0.0.tgz
# cd harbor
# cp harbor.yml.tmpl harbor.yml
# vi harbor.yml
hostname: reg.ctnrs.com
https: # 先注释https相关配置
harbor_admin_password: Harbor12345
# ./prepare
# ./install.sh

2.3 在Jenkins主机配置Docker可信任,如果是HTTPS需要拷贝证书

由于habor未配置https,还需要在docker配置可信任。

# cat /etc/docker/daemon.json 
{"registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"],
"insecure-registries": ["192.168.0.12"]
}
# systemctl restart docker

2.4 构建tomcat镜像并上传到harbor仓库上(供jenkinsfile脚本调用)

#准备的安装包(apache-tomcat-8.5.43.tar.gz)和Dockerfile

FROM centos:7
MAINTAINER www.ctnrs.com

ENV VERSION=8.5.43

RUN yum install java-1.8.0-openjdk wget curl unzip iproute net-tools -y && \
yum clean all && \
rm -rf /var/cache/yum/*

ADD apache-tomcat-${VERSION}.tar.gz /usr/local/
RUN mv /usr/local/apache-tomcat-${VERSION} /usr/local/tomcat && \
sed -i '1a JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom"' /usr/local/tomcat/bin/catalina.sh && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

ENV PATH $PATH:/usr/local/tomcat/bin

WORKDIR /usr/local/tomcat

EXPOSE 8080
CMD ["catalina.sh", "run"]

构建并上传到harbor

docker build -t 192.168.0.12/library/tomcat:v1 .
docker login 192.168.0.12
docker /images
docker push 192.168.0.12/library/tomcat:v1

3、部署Jenkins

3.1 准备JDK和Maven环境

将二进制包上传到服务器并解压到工作目录,用于让Jenkins容器挂载使用。

# tar zxvf jdk-8u45-linux-x64.tar.gz
# mv jdk1.8.0_45 /usr/local/jdk
# tar zxf apache-maven-3.5.0-bin.tar.gz
# mv apache-maven-3.5.0 /usr/local/maven

修改Maven源:

vi /usr/local/maven/conf/setting.xml

<mirrors>

<mirror>
<id>central</id>
<mirrorOf>central</mirrorOf>
<name>aliyun maven</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>

</mirrors>

部署jenkins

docker run -d --name jenkins -p 80:8080 -p 50000:50000 -u root  \
-v /opt/jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/bin/docker:/usr/bin/docker \
-v /usr/local/maven:/usr/local/maven \
-v /usr/local/jdk:/usr/local/jdk \
-v /etc/localtime:/etc/localtime \
--restart=always \
--name jenkins jenkins/jenkins

访问地址:http://IP

3.2 安装插件

管理Jenkins->系统配置–>管理插件**–>搜索git/pipeline,选中点击安装。

默认从国外网络下载插件,会比较慢,建议修改国内源:

cd /opt/jenkins_home/updates
sed -i 's/http:\/\/updates.jenkins-ci.org\/download/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/g' default.json && \
sed -i 's/http:\/\/www.google.com/https:\/\/www.baidu.com/g' default.json

docker restart jenkins

3.3 Jenkins Pipeline介绍

Jenkins Pipeline是一套插件,支持在Jenkins中实现集成和持续交付管道;

Pipeline通过特定语法对简单到复杂的传输管道进行建模;

声明式:遵循与Groovy相同语法。pipeline { }

脚本式:支持Groovy大部分功能,也是非常表达和灵活的工具。node { }

Jenkins Pipeline的定 义被写入一个文本文件,称为Jenkinsfile。

注意: 发布之前可以在jenkins主机上测试一下是否能登录harbor仓库

如果不能登录报错为连接443拒绝,那么请使用以下方式解决:

修改Docker启动文件添加“–insecure-registry 192.168.0.12”

vim /usr/lib/systemd/system/docker.service 
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry 192.168.0.12

systemctl restart docker

4、发布测试

4.1添加连接git仓库凭据和harbor仓库凭据

管理Jenkins->安全–>管理凭据->Jnekins->添加凭据->Username with password

  • Username:用户名

  • Password:密码

  • ID:留空

  • Description:描述

分别添加连接git和harbor凭据,并修改脚本为实际凭据ID。

4.2 创建项目并配置

New Item -> Pipeline -> This project is parameterized -> String Parameter

  • Name:Branch # 变量名,下面脚本中调用

  • Default Value:master # 默认分支

  • Description:发布的代码分支 # 描述

4.3 Pipeline脚本(在Jenkins本地机器上部署并启动容器)

一. 添加注释方便查看使用说明(添加注释到jenkins执行会报错,请使用第二个构建)

#!/usr/bin/env groovy

def registry = "192.168.0.12" #harbor仓库地址
def project = "dev" #harbor仓库项目名
def app_name = "java-demo" #镜像的应用名
def image_name = "${registry}/${project}/${app_name}:${Branch}-${BUILD_NUMBER}" #Branch代表引用的分支名
#BUILD_NUMBER代表构建编号
def git_address = "http://192.168.0.11:9999/root/java-demo.git" #git仓库连接地址
def docker_registry_auth = "30fae7e1-22c4-4083-848f-a5e90eff9e1f" #harbor仓库认证(用户名和密码)
def git_auth = "05750892-5303-49ab-a6d0-33a78ef6c839" #git仓库认证(用户名和密码)

pipeline {
agent any
stages {
stage('拉取代码'){
steps {
checkout([$class: 'GitSCM', branches: [[name: '${Branch}']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
}
}

stage('代码编译'){
steps {
sh """
pwd
ls
JAVA_HOME=/usr/local/jdk
PATH=$JAVA_HOME/bin:/usr/local/maven/bin:$PATH
mvn clean package -Dmaven.test.skip=true
"""
}
}

stage('构建镜像'){
steps {
withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
sh """
echo '
FROM ${registry}/library/tomcat:v1
LABEL maitainer liuzhe
RUN rm -rf /usr/local/tomcat/webapps/*
ADD target/*.war /usr/local/tomcat/webapps/ROOT.war
' > Dockerfile
docker build -t ${image_name} .
docker login -u ${username} -p '${password}' ${registry}
docker push ${image_name}
"""
}
}
}

stage('部署到Docker'){
steps {
sh """
docker rm -f tomcat-java-demo |true
docker container run -d --name tomcat-java-demo -p 88:8080 ${image_name}
"""
}
}
}
}

二.请使用这个pipeline脚本构建

#!/usr/bin/env groovy

def registry = "192.168.0.12"
def project = "dev"
def app_name = "java-demo"
def image_name = "${registry}/${project}/${app_name}:${Branch}-${BUILD_NUMBER}"
def git_address = "http://192.168.0.11:9999/root/java-demo.git"
def docker_registry_auth = "30fae7e1-22c4-4083-848f-a5e90eff9e1f"
def git_auth = "05750892-5303-49ab-a6d0-33a78ef6c839"

pipeline {
agent any
stages {
stage('拉取代码'){
steps {
checkout([$class: 'GitSCM', branches: [[name: '${Branch}']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
}
}

stage('代码编译'){
steps {
sh """
pwd
ls
JAVA_HOME=/usr/local/jdk
PATH=$JAVA_HOME/bin:/usr/local/maven/bin:$PATH
mvn clean package -Dmaven.test.skip=true
"""
}
}

stage('构建镜像'){
steps {
withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
sh """
echo '
FROM ${registry}/library/tomcat:v1
LABEL maitainer liuzhe
RUN rm -rf /usr/local/tomcat/webapps/*
ADD target/*.war /usr/local/tomcat/webapps/ROOT.war
' > Dockerfile
docker build -t ${image_name} .
docker login -u ${username} -p '${password}' ${registry}
docker push ${image_name}
"""
}
}
}

stage('部署到Docker'){
steps {
sh """
docker rm -f tomcat-java-demo |true
docker container run -d --name tomcat-java-demo -p 88:8080 ${image_name}
"""
}
}
}
}

上述脚本中,docker_registry_auth 和git_auth变量的值为Jenkins凭据ID,添加凭据后修改。

4.4 Pipeline脚本( 在其他的Docker主机上部署并启动容器)

安装jenkins插件

插件名称: SSH Pipeline Steps

使用说明: https://github.com/jenkinsci/ssh-steps-plugin#pipeline-steps

#!/usr/bin/env groovy

def registry = "192.168.0.12"
def project = "dev"
def app_name = "java-demo"
def image_name = "${registry}/${project}/${app_name}:${Branch}-${BUILD_NUMBER}"
def git_address = "http://192.168.0.11:9999/root/java-demo.git"
def docker_registry_auth = "30fae7e1-22c4-4083-848f-a5e90eff9e1f"
def git_auth = "05750892-5303-49ab-a6d0-33a78ef6c839"
def remote = [:]
remote.name = "test"
remote.host = "192.168.0.12"
remote.user = 'root'
remote.password = '123.com'
remote.allowAnyHosts = true

pipeline {
agent any
stages {
stage('拉取代码'){
steps {
checkout([$class: 'GitSCM', branches: [[name: '${Branch}']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
}
}

stage('代码编译'){
steps {
sh """
pwd
ls
JAVA_HOME=/usr/local/jdk
PATH=$JAVA_HOME/bin:/usr/local/maven/bin:$PATH
mvn clean package -Dmaven.test.skip=true
"""
}
}

stage('构建镜像'){
steps {
withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
sh """
echo '
FROM ${registry}/library/tomcat:v1
LABEL maitainer liuzhe
RUN rm -rf /usr/local/tomcat/webapps/*
ADD target/*.war /usr/local/tomcat/webapps/ROOT.war
' > Dockerfile
docker build -t ${image_name} .
docker login -u ${username} -p '${password}' ${registry}
docker push ${image_name}
"""
}
}
}

stage('部署到Docker'){
steps {
sshCommand remote: remote, command: """
ip a
docker rm -f tomcat-java-demo |true
docker pull ${image_name}
docker container run -d --name tomcat-java-demo -p 88:8080 ${image_name}
"""
}
}
}
}

验证:

1.访问harbor仓库查看是否有上传的镜像 http://192.168.0.12/

2.docker主机上运行的项目镜像容器

3.访问部署java-demo示例 http://192.168.0.13:88/

CI/CD收益

高效的CI/CD环境可以获得:

1.及时发现问题

2.大幅度减少故障率

3.加快迭代速度

4.减少时间成本