12DevOps

0 阅读31分钟

DevOps

DevOps介绍

软件开发最开始是由两个团队组成:

  • 开发计划由开发团队从头开始设计和整体系统的构建,需要系统不停的迭代更新
  • 运维团队将开发团队的Code进行测试后部署上线,希望系统稳定安全运行

这看似两个目标不同的团队需要协同完成一个软件的开发。在开发团队制定好计划并完成coding后,需要提供到运维团队。运维团队向开发团队反馈需要修复的BUG以及一些需要返工的任务。这时开发团队需要经常等待运维团队的反馈。这无疑延长了事件并推迟了整个软件开发的周期。会有一种方式,在开发团队等待的时候,让开发团队转移到下一个项目中,等待运维团队为之前的代码提供反馈。可是这样就意味着一个完整的项目需要一个更长的周期才可以开发出最终代码。

基于现在的互联网现状,更推崇敏捷式开发,这样就导致项目的迭代速度更快,但是由于开发团队与运维团队的沟通问题,会导致新版本上线的时间成本很高,这又违背了敏捷式开发的最初的目的。那么如果让开发团队和运维团队整合到成一个团队,协同应对一套软件呢?这就被称为DevOps。DevOps字面意思是Development &Operations的缩写,也就是开发&运维。虽然字面意思只涉及到了开发团队和运维团队,其实QA测试团队也是参与其中的。网上可以查看到DevOps的符号类似于一个无穷大的符号

image.png

这表明DevOps是一个不断提高效率并且持续不断工作的过程。DevOps的方式可以让公司能够更快地应对更新和市场发展变化,开发可以快速交付,部署也更加稳定。核心就在于简化Dev和Ops团队之间的流程,使整体软件开发过程更快速

整体的软件开发流程包括:

  • PLAN:开发团队根据客户的目标制定开发计划
  • CODE:根据PLAN开始编码过程,需要将不同版本的代码存储在一个库中
  • BUILD:编码完成后,需要将代码构建并且运行
  • TEST:成功构建项目后,需要测试代码是否存在BUG或错误
  • DEPLOY:代码经过手动测试和自动化测试后,认定代码已经准备好部署并且交给运维团队
  • OPERATE:运维团队将代码部署到生产环境中
  • MONITOR:项目部署上线后,需要持续的监控产品
  • INTEGRATE:然后将监控阶段收到的反馈发送回PLAN阶段,整体反复的流程就是DevOps的核心,即持续集成、持续部署

为了保证整体流程可以高效的完成,各个阶段都有比较常见的工具,软件开发过程&涉及工具见下图

image.png

上面这个图,是Jenkins实现DevOps的涉及到的一些过程。

  • PLAN:指定好计划
  • CODE:编写代码,并且上传到Git仓库
  • BUILD:拉取Git仓库代码,并且构建项目
  • TEST:在构建过程,或者在构建后,可以做测试
  • DEPLOY:将构建后的项目制作成镜像,上传到镜像仓库
  • OPERATE:将镜像拉取到Kubernetes内部部署启动
  • MONITOR:监控项目

最终可以给DevOps下一个定义:DevOps强调的是高效组织团队之间如何通过自动化的工具协作和沟通来完成软件的生命周期管理,从而更快、更频繁地交付更稳定的软件。即自动化的工具协作和沟通来完成软件的生命周期管理

下面的DevOps演示主机节点准备

ipCPU内存硬盘用途主机名
192.168.91.1011C1G40GB测试test
192.168.91.1022C4G40GBdocker,gitlab,jenkinsdevops
# devops、test
# Docker安装
wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum -y install --setopt=obsoletes=0 docker-ce-24.0.6-1.el7
cat << EOF > /etc/docker/daemon.json
{
  "registry-mirrors": ["https://zwyx2n3v.mirror.aliyuncs.com"]
}
EOF
systemctl enable --now docker

# devops
# 调整swap大小为6G
swapoff -a
cp /dev/dm-1 /centos-myswap1
cp /dev/dm-1 /centos-myswap2
echo '/centos-myswap1 swap                            swap    defaults        0 0' >> /etc/fstab
echo '/centos-myswap2 swap                            swap    defaults        0 0' >> /etc/fstab
swapon -a

free -h | grep Swap
Swap:          6.0G          0B        6.0G

下面的操作,没有特别说明都是在devops节点

Code阶段工具

在code阶段,我们需要将不同版本的代码存储到一个仓库中,常见的版本控制工具就是SVN或Git,这里我们采用Git作为版本控制工具,GitLab作为远程仓库

mkdir /root/gitlab
cd /root/gitlab
# 准备docker-compose.yml文件
cat > docker-compose.yml << "EOF"
version: '3.1'
services:
  gitlab:
    image: 'gitlab/gitlab-ce:latest'
    container_name: gitlab
    restart: always
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://192.168.91.102:8929'
        gitlab_rails['gitlab_shell_ssh_port'] = 2224
    ports:
      - '8929:8929'
      - '2224:2224'
    volumes:
      - './config:/etc/gitlab'
      - './logs:/var/log/gitlab'
      - './data:/var/opt/gitlab'
EOF

# 启动容器(需要稍等一会)
docker compose up -d

docker ps
CONTAINER ID   IMAGE                     COMMAND             CREATED          STATUS                             PORTS                                                                                                           NAMES
0e7310951b3c   gitlab/gitlab-ce:latest   "/assets/wrapper"   13 seconds ago   Up 12 seconds (health: starting)   22/tcp, 80/tcp, 0.0.0.0:2224->2224/tcp, :::2224->2224/tcp, 443/tcp, 0.0.0.0:8929->8929/tcp, :::8929->8929/tcp   gitlab

# 查看git root用户初始密码
docker exec -it gitlab cat /etc/gitlab/initial_root_password | grep Password:
Password: xT8ed50Yv8uAQlN4m8CWicSuUPxnDyV9D9lNrykXBuU=

利用上的密码登陆root用户

image.png

第一次登录后需要修改密码,然后重新登录

image.png

Build阶段工具

构建Java项目的工具一般有两种选择,一个是Maven,一个是Gradle。这里我们选择Maven作为项目的编译工具。具体安装Maven流程不做阐述,但是需要确保配置好Maven仓库私服以及JDK编译版本。

Operate阶段工具

这里使用Docker,后面会用到k8s

Integrate工具

持续集成、持续部署的工具很多,其中Jenkins是一个开源的持续集成平台。Jenkins涉及到将编写完毕的代码发布到测试环境和生产环境的任务,并且还涉及到了构建项目等任务。Jenkins需要大量的插件保证工作,安装成本较高,下面会基于Docker搭建Jenkins

Jenkins介绍

Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具。Jenkins应用广泛,大多数互联网公司都采用Jenkins配合GitLab、Docker、K8s作为实现DevOps的核心工具。Jenkins最强大的就在于插件,Jenkins官方提供了大量的插件库,来自动化CI/CD过程中的各种琐碎功能

image.pngimage.png

Jenkins最主要的工作就是将GitLab上可以构建的工程代码拉取并且进行构建,再根据流程可以选择发布到测试环境或是生产环境。一般是GitLab上的代码经过大量的测试后,确定发行版本,再发布到生产环境。

CI/CD可以理解为:

  • CI过程即是通过Jenkins将代码拉取、构建、制作镜像交给测试人员测试
    • 持续集成:让软件代码可以持续的集成到主干上,并自动构建和测试
  • CD过程即是通过Jenkins将打好标签的发行版本代码拉取、构建、制作镜像交给运维人员部署
    • 持续交付:让经过持续集成的代码可以进行手动部署
    • 持续部署:让可以持续交付的代码随时随地的自动化部署

image.png

Jenkins安装

mkdir /root/jenkins_docker
cd /root/jenkins_docker
mkdir data
chmod -R a+w data
# 编写docker-compose.yml,应该安装较新的jenkins,否则可能会有一些插件下载不了
cat > docker-compose.yml << "EOF"
version: "3.1"
services:
  jenkins:
    image: jenkins/jenkins:2.414.2-lts-jdk11
    container_name: jenkins
    ports:
      - 8080:8080
      - 50000:50000
    volumes:
      - ./data/:/var/jenkins_home/
EOF
# 启动容器(需要稍等一会)
docker compose up -d

由于Jenkins需要下载大量内容,但是由于默认下载地址下载速度较慢,需要重新设置下载地址为国内镜像站。修改数据卷中的hudson.model.UpdateCenter.xml文件内容如下

<!-- 将下载地址替换为http://mirror.esuni.jp/jenkins/updates/update-center.json -->
<?xml version='1.1' encoding='UTF-8'?>
<sites>
  <site>
    <id>default</id>
    <url>http://mirror.esuni.jp/jenkins/updates/update-center.json</url>
  </site>
</sites>
<!-- 清华大学的插件源也可以https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json -->
mv data/hudson.model.UpdateCenter.xml data/hudson.model.UpdateCenter.xmlback
cat > data/hudson.model.UpdateCenter.xml << "EOF"
<?xml version='1.1' encoding='UTF-8'?>
<sites>
  <site>
    <id>default</id>
    <url>http://mirror.esuni.jp/jenkins/updates/update-center.json</url>
  </site>
</sites>
EOF

# 重新启动容器
docker compose restart

# 查看登录Jenkins的密码
docker exec -it jenkins cat /var/jenkins_home/secrets/initialAdminPassword
01ff212fb55e455096ba0eea4f9236f7

访问Jenkins(需要稍微等会)

image.png

image.png

选择需要安装的插件,进行安装

image.png

image.png

可能会有部分插件安装失败,点击"重试"

image.png

插件安装完毕,设置信息进入首页

image.png

image.png

image.png

Jenkins入门配置

由于Jenkins需要从Git拉取代码、需要本地构建、甚至需要直接发布自定义镜像到Docker仓库,所以Jenkins需要配置大量内容

构建任务

准备好GitLab仓库中的项目,并且通过Jenkins配置项目实现当前项目的DevOps基本流程

准备代码上传到GitLab上

mkdir /root/demo
cd /root/demo
mkdir -p src/main/java/com/demo
cat > src/main/java/com/demo/DemoApplication.java << EOF
package com.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
    @GetMapping("/hello")
    public String hello() {
        return "hello jdk11 jenkins";
    }
}
EOF

创建pom.xml文件,内容如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.16</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.demo</groupId>
	<artifactId>demo</artifactId>
	<version>2.0.0</version>
	<properties>
		<java.version>11</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

先在GitLab上创建一个空的demo项目

image.png

代码上传至GitLab

# 安装git客户端
yum -y update
yum -y install git

cd /root/demo

git config --global user.name "Administrator"
git config --global user.email "admin@example.com"

git init
git remote add origin http://192.168.91.102:8929/root/demo.git
git add .
git commit -m "Initial commit"
# 需要输入git的用户名和密码
git push --set-upstream origin master

在jenkins上新建任务

“构建一个自由风格的软件项目”,名称为demo

image.png

配置源码拉取地址

配置任务源码拉取的地址

image.png

Jenkins立即构建

image.png

查看构建工程的日志,点击上图中的任务条即可

image.png

可以看到源码已经拉取带Jenkins本地,可以根据第三行日志信息,查看Jenkins本地拉取到的源码

# 查看Jenkins容器中拉取的demo项目源码
docker exec jenkins ls /var/jenkins_home/workspace/demo
pom.xml
src

# Jenkins容器中的 /var/jenkins_home 目录,也映射到宿主机 /root/jenkins_docker/data 目录
# 在对应的宿主机目录中查看
ls /root/jenkins_docker/data/workspace/demo
pom.xml  src
配置Maven构建代码

代码拉取到Jenkins本地后,需要在Jenkins中对代码进行构建,这里需要Maven的环境,而Maven需要Java的环境,接下来需要在Jenkins中安装JDK和Maven,并且配置到Jenkins服务。

这里安装的Jenkins是基于JDK11的,对于JDK11的Maven项目不需要再安装JDK了,只需要安装Maven即可。对于其它版本的JDK项目,还需要安装对应的JDK和Maven,且使用不同JDK版本的Maven本地仓库路径不能一样

cd /root
wget https://dlcdn.apache.org/maven/maven-3/3.9.4/binaries/apache-maven-3.9.4-bin.tar.gz
docker cp apache-maven-3.9.4-bin.tar.gz jenkins:/var/jenkins_home

# 进入容器操作
docker exec -it jenkins /bin/bash
cd /var/jenkins_home
tar xf apache-maven-3.9.4-bin.tar.gz
mv apache-maven-3.9.4 mvn_jdk11
# 创建本地仓库目录
mkdir mvn_jdk11/repository
rm -f apache-maven-3.9.4-bin.tar.gz
mv mvn_jdk11/conf/settings.xml mvn_jdk11/conf/settings.xmlback
# 修改maven使用阿里云代理
cat > mvn_jdk11/conf/settings.xml << "EOF"
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
  <localRepository>/var/jenkins_home/mvn_jdk11/repository</localRepository>
  <mirrors>
    <mirror>
      <id>aliyunmaven</id>
      <mirrorOf>*</mirrorOf>
      <name>阿里云公共仓库</name>
      <url>https://maven.aliyun.com/repository/public</url>
    </mirror>  
  </mirrors>
</settings>
EOF
# 验证maven是否安装成功
/var/jenkins_home/mvn_jdk11/bin/mvn --version
Apache Maven 3.9.4 (dfbb324ad4a7c8fb0bf182e6d91b0ae20e3d2dd9)
Maven home: /var/jenkins_home/mvn_jdk11
Java version: 11.0.20.1, vendor: Eclipse Adoptium, runtime: /opt/java/openjdk
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-1160.102.1.el7.x86_64", arch: "amd64", family: "unix"

# 退出容器
exit

rm -f apache-maven-3.9.4-bin.tar.gz

Jenkins配置Maven并保存,使用默认的JDK不需要配置

image.png

配置Jenkins任务构建代码

image.png

立即构建测试,查看target下的jar包

image.png

ls /root/jenkins_docker/data/workspace/demo
pom.xml  src  target

ls /root/jenkins_docker/data/workspace/demo/target
classes  demo-2.0.0.jar  demo-2.0.0.jar.original  generated-sources  maven-archiver  maven-status
配置Publish发布&远程操作

jar包构建好之后,就可以根据情况发布到测试或生产环境

先安装 Publish Over SSH 插件

image.png

配置Publish Over SSH连接测试或生产环境

image.png

配置任务的构建后操作,发布jar包到目标服务

image.png

立即构建任务,并去目标服务查看

image.png

# test(192.168.91.101)
ls /root
anaconda-ks.cfg  target

ls /root/target
demo-2.0.0.jar

CICD入门操作

基于Jenkins拉取GitLab的SpringBoot代码进行构建发布到测试环境实现持续集成(CI)

基于Jenkins拉取GitLab指定发行版本的SpringBoot代码进行构建发布到生产环境实现持续部署(CD)

持续集成

为了让程序代码可以自动推送到测试环境基于Docker服务运行,需要添加Docker配置和脚本文件让程序可以在集成到主干的同时运行起来

cd /root/demo
mkdir docker
# 添加Dockerfile文件
cat > docker/Dockerfile << "EOF"
FROM adoptopenjdk/openjdk11
COPY demo.jar /usr/local
WORKDIR /usr/local
CMD java -jar demo.jar
EOF
# 添加docker-compose.yml文件
cat > docker/docker-compose.yml << "EOF"
version: '3.1'
services:
  demo:
    build: ./
    image: demo:v2.0.0
    container_name: demo
    ports:
      - 8888:8080
EOF

# 上传至git
git add .
git commit -m "docker commit"
git push origin master

发布到GitLab后由Jenkins立即构建并推送到目标服务器

# Exec command
cd /root/docker
mv ../target/demo*.jar ./demo.jar
docker compose down
docker compose up -d --build
# 删除所有不用的镜像
docker image prune -f

image.png

立即构建任务,并去目标服务查看

image.png

# test(192.168.91.101)
ls /root/docker/
demo.jar  docker-compose.yml  Dockerfile

curl 192.168.91.101:8888/hello
hello jdk11 jenkins

持续交付(部署)

程序代码在经过多次集成操作到达最终可以交付,持续交付整体流程和持续集成类似,不过需要选取指定的发行版本

给项目添加tag

cd /root/demo
sed -i 's/jenkins/jenkinsv2.0.0/' src/main/java/com/demo/DemoApplication.java
git add .
git commit -m "tag v2.0.0"
git tag v2.0.0
git push origin v2.0.0

sed -i 's/jenkinsv2.0.0/jenkinsv2.0.1/' src/main/java/com/demo/DemoApplication.java
sed -i 's/2.0.0/2.0.1/' pom.xml
git add .
git commit -m "tag v2.0.1"
git tag v2.0.1
git push origin v2.0.1

设置项目参数化构建

image.png

image.png

基于Parameter构建任务,任务发布到目标服务器

image.png

# 验证
curl 192.168.91.101:8888/hello
hello jdk11 jenkinsv2.0.0

# 再次构建,参数选择 Tag v2.0.1
# 然后验证
curl 192.168.91.101:8888/hello
hello jdk11 jenkinsv2.0.1

集成Sonar Qube

Sonar Qube介绍

Sonar Qube是一个开源的代码分析平台,支持Java、Python、PHP、JavaScript、CSS等25种以上的语言,可以检测出重复代码、代码漏洞、代码规范和安全性漏洞的问题。Sonar Qube可以与多种软件整合进行代码扫描,比如Maven,Gradle,Git,Jenkins等,并且会将代码检测结果推送回Sonar Qube并且在系统提供的UI界面上显示出来

Sonar Qube环境搭建

Sonar Qube在7.9版本中已经放弃了对MySQL的支持,并且建议在商业环境中采用PostgreSQL,那么安装Sonar Qube时需要依赖PostgreSQL

mkdir /root/sonarqube
cd /root/sonarqube

编写docker-compose.yml

version: "3.1"
services:
  db:
    image: postgres
    container_name: db
    ports:
      - 5432:5432
    networks:
      - sonarnet
    environment:
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: sonar
    volumes:
      - ./pg_data/:/var/lib/postgresql/data
  sonarqube:
    image: sonarqube:10.2.1-community
    container_name: sonarqube
    depends_on:
      - db
    ports:
      - "9000:9000"
    networks:
      - sonarnet
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: sonar
    volumes:
      - ./sonarqube/extensions:/opt/sonarqube/extensions
      - ./sonarqube/data:/opt/sonarqube/data
      - ./sonarqube/logs:/opt/sonarqube/logs
networks:
  sonarnet:
    driver: bridge
# 先设置内核参数
echo "vm.max_map_count=262144" >> /etc/sysctl.conf
sysctl -p

# 创建sonarqube挂载目录,并设置权限
mkdir -p sonarqube/extensions sonarqube/data sonarqube/logs
chmod 777 sonarqube sonarqube/*

# 启动
docker compose up -d
# 查看容器启动日志
docker logs -n 10 sonarqube
2023.11.17 02:53:18 INFO  ce[][o.s.d.DefaultDatabase] Create JDBC data source for jdbc:postgresql://db:5432/sonar
2023.11.17 02:53:18 INFO  ce[][c.z.h.HikariDataSource] HikariPool-1 - Starting...
2023.11.17 02:53:18 INFO  ce[][c.z.h.p.HikariPool] HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@53e6ac31
2023.11.17 02:53:18 INFO  ce[][c.z.h.HikariDataSource] HikariPool-1 - Start completed.
2023.11.17 02:53:19 INFO  ce[][o.s.s.p.ServerFileSystemImpl] SonarQube home: /opt/sonarqube
2023.11.17 02:53:19 INFO  ce[][o.s.c.c.CePluginRepository] Load plugins
2023.11.17 02:53:21 INFO  ce[][o.s.c.c.ComputeEngineContainerImpl] Running Community edition
2023.11.17 02:53:21 INFO  ce[][o.s.ce.app.CeServer] Compute Engine is started
2023.11.17 02:53:21 INFO  app[][o.s.a.SchedulerImpl] Process[ce] is up
2023.11.17 02:53:21 INFO  app[][o.s.a.SchedulerImpl] SonarQube is operational

访问Sonar Qube首页,用户名和密码默认都是admin

image.png

第一次登陆需要设置新密码

image.png

安装中文插件,可能会由于网络问题或插件版本问题,导致插件安装失败,可以到GitHub上下载对应版本的插件,然后放到容器中相应目录,然后重启容器

image.png

image.png

安装成功后需要重启,安装成功后,会查看到重启按钮,点击即可

image.png

# 手动安装插件的方式如下
wget https://github.com/xuhuisheng/sonar-l10n-zh/releases/download/sonar-l10n-zh-plugin-10.2/sonar-l10n-zh-plugin-10.2.jar
docker exec sonarqube mkdir /opt/sonarqube/extensions/plugins
docker cp sonar-l10n-zh-plugin-10.2.jar sonarqube:/opt/sonarqube/extensions/plugins
docker restart sonarqube

Sonar Qube基本使用

Sonar Qube的使用方式很多,Maven可以整合,也可以采用sonar-scanner的方式,再查看Sonar Qube的检测效果

Maven实现代码检测
# 准备JDK和Maven
wget https://builds.openlogic.com/downloadJDK/openlogic-openjdk/11.0.21+9/openlogic-openjdk-11.0.21+9-linux-x64.tar.gz
tar xf openlogic-openjdk-11.0.21+9-linux-x64.tar.gz
mv openlogic-openjdk-11.0.21+9-linux-x64 /usr/local/jdk11
rm -f openlogic-openjdk-11.0.21+9-linux-x64.tar.gz

wget https://dlcdn.apache.org/maven/maven-3/3.9.4/binaries/apache-maven-3.9.4-bin.tar.gz
tar xf apache-maven-3.9.4-bin.tar.gz
mv apache-maven-3.9.4 /usr/local/mvn_jdk11
rm -f apache-maven-3.9.4-bin.tar.gz

sed '30 aJAVA_HOME=/usr/local/jdk11' -i /usr/local/mvn_jdk11/bin/mvn
mkdir /usr/local/mvn_jdk11/repository
mv /usr/local/mvn_jdk11/conf/settings.xml /usr/local/mvn_jdk11/conf/settings.xmlback

/usr/local/mvn_jdk11/conf/settings.xml配置文件修改

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
  <localRepository>/usr/local/mvn_jdk11/repository</localRepository>
  <!-- 阿里云仓库配置 -->
  <mirrors>
    <mirror>
      <id>aliyunmaven</id>
      <mirrorOf>*</mirrorOf>
      <name>阿里云公共仓库</name>
      <url>https://maven.aliyun.com/repository/public</url>
    </mirror>  
  </mirrors>
  <!-- Sonar Qube 配置 -->
  <profiles>
    <profile>
      <id>sonar</id>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <properties>
        <sonar.login>admin</sonar.login>
        <sonar.password>abc@1234</sonar.password>
        <sonar.host.url>http://192.168.91.102:9000</sonar.host.url>
      </properties>
    </profile>  
  </profiles>
</settings>
/usr/local/mvn_jdk11/bin/mvn --version
Apache Maven 3.9.4 (dfbb324ad4a7c8fb0bf182e6d91b0ae20e3d2dd9)
Maven home: /usr/local/mvn_jdk11
Java version: 11.0.21, vendor: Oracle Corporation, runtime: /usr/local/jdk11
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-1160.102.1.el7.x86_64", arch: "amd64", family: "unix"

cd /root/demo
/usr/local/mvn_jdk11/bin/mvn sonar:sonar
...
[INFO] ANALYSIS SUCCESSFUL, you can find the results at: http://192.168.91.102:9000/dashboard?id=com.demo%3Ademo
[INFO] Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report
[INFO] More about the report processing at http://192.168.91.102:9000/api/ce/task?id=AYvb9-UZobr_rBRZyn_M
[INFO] Analysis total time: 22.074 s
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
...

查看Sonar Qube界面检测结果

image.png

Sonar-scanner实现代码检测
# 下载sonar-scanner并配置
cd /root
wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux.zip
yum -y install unzip
unzip sonar-scanner-cli-5.0.1.3006-linux.zip
mv sonar-scanner-5.0.1.3006-linux sonar-scanner
cat >> sonar-scanner/conf/sonar-scanner.properties << EOF
sonar.host.url=http://192.168.91.102:9000
sonar.sourceEncoding=UTF-8
sonar.login=admin
sonar.password=abc@1234
EOF
rm -f sonar-scanner-cli-5.0.1.3006-linux.zip

# 执行命令检测代码
cd /root/demo
/root/sonar-scanner/bin/sonar-scanner -Dsonar.sources=./ -Dsonar.projectname=demo -Dsonar.projectKey=java -Dsonar.java.binaries=target/
...
INFO: ANALYSIS SUCCESSFUL, you can find the results at: http://192.168.91.102:9000/dashboard?id=java
INFO: Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report
INFO: More about the report processing at http://192.168.91.102:9000/api/ce/task?id=AYvcIdD5obr_rBRZyn_o
INFO: Analysis total time: 18.895 s
INFO: ------------------------------------------------------------------------
INFO: EXECUTION SUCCESS
...

查看SonarQube界面检测结果

image.png

Jenkins集成Sonar Qube

Jenkins安装SonarQube插件

image.png

Jenkins配置SonarQube

开启Sonar Qube权限验证,该版本默认不用配置

image.png

生成Sonar Qube的令牌

image.png

image.png

配置Jenkins的Sonar Qube信息

image.png

image.png

配置sonar-scanner

准备sonar-scanner环境并在页面中配置

# jenkins容器中sonar-scanner环境准备
cd /root
wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux.zip
docker cp sonar-scanner-cli-5.0.1.3006-linux.zip jenkins:/var/jenkins_home
# 进入容器操作
docker exec -it jenkins /bin/bash
cd /var/jenkins_home
unzip sonar-scanner-cli-5.0.1.3006-linux.zip
mv sonar-scanner-5.0.1.3006-linux sonar-scanner
rm -f sonar-scanner-cli-5.0.1.3006-linux.zip
# 退出容器
exit

rm -f sonar-scanner-cli-5.0.1.3006-linux.zip

image.png

配置任务的sonar-scanner

sonar.projectname=${JOB_NAME}
sonar.projectKey=${JOB_NAME}
sonar.sources=./
sonar.java.binaries=target/

image.png

构建任务验证

image.png

image.png

集成Harbor

Harbor介绍

前面在部署项目时,我们主要采用Jenkins推送jar包到指定服务器,再通过脚本命令让目标服务器对当前jar进行部署,这种方式在项目较多时,每个目标服务器都需要将jar包制作成自定义镜像再通过docker进行启动,重复操作比较多,会增加项目部署时间。

我们可以通过Harbor作为私有的Docker镜像仓库。让Jenkins统一将项目打包并制作成Docker镜像发布到Harbor仓库中,只需要通知目标服务,让目标服务统一去Harbor仓库上拉取镜像并在本地部署即可。

Docker官方提供了Registry镜像仓库,但是Registry的功能相对简陋。Harbor是VMware公司提供的一款镜像仓库,提供了权限控制、分布式发布、强大的安全扫描与审查机制等功能。

Harbor安装

cd /root
wget https://github.com/goharbor/harbor/releases/download/v2.9.0/harbor-offline-installer-v2.9.0.tgz
tar xf harbor-offline-installer-v2.9.0.tgz
cd harbor
cp harbor.yml.tmpl harbor.yml
# 修改配置文件,修改hostname为当前节点ip,并将https相关配置注掉
sed -i -e 's/reg.mydomain.com/192.168.91.102/' -e '13,18 s/^/#/' harbor.yml
./prepare
./install.sh
# 安装好之后,就可以通过docker compose启停了

登录Harbor,用户名 admin,密码 Harbor12345

image.png

Harbor使用方式

Harbor作为镜像仓库,主要的交互方式就是将镜像上传到Harbor上,以及从Harbor上下载指定镜像。在传输镜像前,可以先使用Harbor提供的权限管理,将项目设置为私有项目,并对不同用户设置不同角色,从而更方便管理镜像。

添加用户构建项目

创建用户

image.png

构建项目(设置为私有)

image.png

给项目追加用户

image.png

发布镜像到Harbor

修改镜像名称,名称要求:harbor地址/项目名/镜像名:版本

# test节点
# 修改docker的daemon.json,添加insecure-registries配置
cat > /etc/docker/daemon.json << EOF
{
  "registry-mirrors": ["https://zwyx2n3v.mirror.aliyuncs.com"],
  "insecure-registries": ["http://192.168.91.102"]
}
EOF
# 重启docker
systemctl restart docker
# 设置登录仓库信息
docker login -u devops -p Abc@1234 http://192.168.91.102
# 修改镜像名称并推送到harbor,名称要求:harbor地址/项目名/镜像名:版本
docker tag demo:v2.0.0 192.168.91.102/repo/demo:v2.0.0
# 推送镜像到harbor
docker push 192.168.91.102/repo/demo:v2.0.0
从Harbor拉取镜像
# devops 节点
# 修改docker的daemon.json,添加insecure-registries配置
cat > /etc/docker/daemon.json << EOF
{
  "registry-mirrors": ["https://zwyx2n3v.mirror.aliyuncs.com"],
  "insecure-registries": ["http://192.168.91.102"]
}
EOF
# 重启docker
systemctl restart docker
# 设置登录仓库信息
docker login -u devops -p Abc@1234 http://192.168.91.102
# 拉取harbor上的镜像
docker pull 192.168.91.102/repo/demo:v2.0.0
Jenkins容器使用宿主机Docker

构建镜像和发布镜像到harbor都需要使用到docker命令。而在Jenkins容器内部安装Docker官方推荐直接采用宿主机带的Docker即可。

设置Jenkins容器使用宿主机Docker

# 设置宿主机docker.sock权限,docker重启之后需要重新设置
chmod o+rw /var/run/docker.sock
# 添加数据卷
cd /root/jenkins_docker
cat >> docker-compose.yml << EOF
      - /usr/bin/docker:/usr/bin/docker
      - /var/run/docker.sock:/var/run/docker.sock
      - /etc/docker/daemon.json:/etc/docker/daemon.json
EOF
# 重启jenkins
docker compose down
docker compose up -d
添加构建操作

image.png

mv target/*.jar docker/demo.jar
REPOSITORY=192.168.91.102/repo/demo:${Tag}
docker login -u devops -p Abc@1234 192.168.91.102
# 注意这里构建镜像时,如果同名镜像存在,不会覆盖原镜像,原镜像的tag会变成空,推送镜像时也会导致harbor上对应的镜像tag变成空,然后推送新的镜像
# 这里可以先删除已经存在的同名镜像,生产环境中一般会定期清理本地和harbor上的镜像
docker build -t $REPOSITORY docker/
docker push $REPOSITORY
编写部署脚本

部署项目需要通过Publish Over SSH插件,让目标服务器执行命令。为了方便一次性实现拉取镜像和启动的命令,推荐采用脚本文件的方式

harbor_url=$1
harbor_project_name=$2
project_name=$3
tag=$4
port=$5

imageName=$harbor_url/$harbor_project_name/$project_name:$tag

containerId=`docker ps -a | grep ${project_name} | awk '{print $1}'`
if [ "$containerId" != "" ] ; then
    docker stop $containerId
    docker rm $containerId
    echo "Delete Container Success"
fi

imageId=`docker images | grep ${project_name} | awk '{print $3}'`

if [ "$imageId" != "" ] ; then
    docker rmi -f $imageId
    echo "Delete Image Success"
fi

docker login -u devops -p Abc@1234 $harbor_url

docker pull $imageName

docker run -d -p $port:$port --name $project_name $imageName

echo "Start Container Success"
echo $project_name
# /root/demo/docker/deploy.sh,创建该脚本文件,内容见上面
# 脚本文件上传至gitlab
cd /root/demo

git checkout v2.0.0
git tag -d v2.0.0
git push origin --delete v2.0.0
# 创建deploy.sh文件
git add .
git commit -m "sh script commit"
git tag v2.0.0
git push origin v2.0.0

git checkout v2.0.1
git tag -d v2.0.1
git push origin --delete v2.0.1
# 创建deploy.sh文件
git add .
git commit -m "sh script commit"
git tag v2.0.1
git push origin v2.0.1
配置构建后操作

image.png

构建任务验证

image.png

# test 节点
docker ps
CONTAINER ID   IMAGE                             COMMAND                  CREATED         STATUS         PORTS                                       NAMES
7bc42bd4acf8   192.168.91.102/repo/demo:v2.0.1   "/bin/sh -c 'java -j…"   2 minutes ago   Up 2 minutes   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   demo

curl http://192.168.91.101:8080/hello
hello jdk11 jenkinsv2.0.1

Jenkins流水线

Jenkins流水线任务介绍

之前采用Jenkins的自由风格构建的项目,每个步骤流程都要通过不同的方式设置,并且构建过程中整体流程是不可见的,无法确认每个流程花费的时间,并且不方便定位问题。

Jenkins的Pipeline可以让项目的发布整体流程可视化,明确执行的阶段,可以快速的定位问题。并且整个项目的生命周期可以通过一个Jenkinsfile文件管理,而且Jenkinsfile文件是可以放在项目中维护。所以Pipeline相对自由风格或者其他的项目风格更容易操作。

Jenkins流水线任务

构建Jenkins流水线任务

创建Jenkins流水线任务

image.png

生成Groovy脚本

image.png

构建后查看视图

image.png

Groovy脚本

Groovy脚本基础语法

// 所有脚本命令包含在pipeline{}中
pipeline {  
	// 指定任务在哪个节点执行(Jenkins支持分布式)
    agent any
    // 配置全局环境,指定变量名=变量值信息
    environment{
    	host = '192.168.91.101'
    }
    // 存放所有任务的合集
    stages {
    	// 单个任务
        stage('任务1') {
        	// 实现任务的具体流程
            steps {
                echo 'do something'
            }
        }
		// 单个任务
        stage('任务2') {
        	// 实现任务的具体流程
            steps {
                echo 'do something'
            }
        }
        // ……
    }
}

编写例子测试

pipeline {
    agent any
    // 存放所有任务的合集
    stages {
        stage('拉取Git代码') {
            steps {
                echo '拉取Git代码'
            }
        }
        stage('检测代码质量') {
            steps {
                echo '检测代码质量'
            }
        }
        stage('构建代码') {
            steps {
                echo '构建代码'
            }
        }
        stage('制作自定义镜像并发布Harbor') {
            steps {
                echo '制作自定义镜像并发布Harbor'
            }
        }
        stage('基于Harbor部署工程') {
            steps {
                echo '基于Harbor部署工程'
            }
        }
    }
}

配置Grovvy脚本

image.png

查看效果

image.png

涉及到特定脚本,Jenkins给予了充足的提示,可以自动生成命令

image.png

Jenkinsfile实现

Jenkinsfile方式需要将脚本内容编写到项目中的Jenkinsfile文件中,每次构建会自动拉取项目并且获取项目中Jenkinsfile文件对项目进行构建

配置pipeline

image.png

准备Jenkinsfile

# devops 节点
cd /root/demo

cat > Jenkinsfile << "EOF"
pipeline {
    agent any
    // 存放所有任务的合集
    stages {
        stage('拉取Git代码') {
            steps {
                echo '拉取Git代码'
            }
        }
        stage('检测代码质量') {
            steps {
                echo '检测代码质量'
            }
        }
        stage('构建代码') {
            steps {
                echo '构建代码'
            }
        }
        stage('制作自定义镜像并发布Harbor') {
            steps {
                echo '制作自定义镜像并发布Harbor'
            }
        }
        stage('基于Harbor部署工程') {
            steps {
                echo '基于Harbor部署工程'
            }
        }
    }
}
EOF

git add .
git commit -m "Jenkinsfile"
git push origin master

测试效果

image.png

Jenkins流水线任务实现

参数化构建

添加参数化构建,方便选择不的项目版本

image.png

image.png

image.png

拉取Git代码

通过流水线语法生成Checkout代码的脚本

语法生成

image.png

image.png

将*/master更改为标签$Tag

pipeline {
    agent any
    stages {
        stage('拉取Git代码') {
            steps {
                checkout scmGit(branches: [[name: '$Tag']], extensions: [], userRemoteConfigs: [[url: 'http://192.168.91.102:8929/root/demo.git']])
            }
        }
    }
}
完整脚本
pipeline {
    agent any
    environment{
        sonarLogin = 'squ_99fb904465840d7973fd23eeef0ab44fdb21bca9'
        sonarHostUrl = 'http://192.168.91.102:9000/'
        harborHost = '192.168.91.102'
        harborRepo = 'repo'
        harborUser = 'devops'
        harborPasswd = 'Abc@1234'
        projectPort = 8080
    }
    stages {
        stage('拉取Git代码') {
            steps {
                checkout scmGit(branches: [[name: '$Tag']], extensions: [], userRemoteConfigs: [[url: 'http://192.168.91.102:8929/root/demo.git']])
            }
        }
        // 构建代码,通过脚本执行mvn的构建命令
        stage('构建代码') {
            steps {
                sh '/var/jenkins_home/mvn_jdk11/bin/mvn clean package -DskipTests'
            }
        }
        // 代码质量检测
        stage('检测代码质量') {
            steps {
                sh '/var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsonar.host.url=$sonarHostUrl -Dsonar.sources=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=target/ -Dsonar.login=$sonarLogin' 
            }
        }
        // 生成自定义镜像脚本
        stage('制作自定义镜像并发布Harbor') {
            steps {
                sh '''mv target/*.jar docker/demo.jar
                REPOSITORY=$harborHost/$harborRepo/$JOB_NAME:${Tag}
                docker login -u $harborUser -p $harborPasswd $harborHost
                docker build -t $REPOSITORY docker/
                docker push $REPOSITORY
                '''
            }
        }
        // 生成Publish Over SSH脚本,其中configName对应系统配置中SSH Server的名称
        stage('目标服务器拉取镜像并运行') {
            steps {
                sshPublisher(publishers: [sshPublisherDesc(configName: 'test', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "sh /root/docker/deploy.sh $harborHost $harborRepo $JOB_NAME $Tag $projectPort", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'docker/deploy.sh')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
    }
}
# devops 节点
# /root/demo/Jenkinsfile,创建该脚本文件,内容见上面
# 脚本文件上传至gitlab
cd /root/demo

git checkout v2.0.0
git tag -d v2.0.0
git push origin --delete v2.0.0
# 创建Jenkinsfile文件
git add .
git commit -m "Jenkinsfile update commit"
git tag v2.0.0
git push origin v2.0.0

git checkout v2.0.1
git tag -d v2.0.1
git push origin --delete v2.0.1
# 创建Jenkinsfile文件
git add .
git commit -m "Jenkinsfile update commit"
git tag v2.0.1
git push origin v2.0.1
构建测试

image.png

# test 节点
docker ps
CONTAINER ID   IMAGE                                 COMMAND                  CREATED         STATUS         PORTS                                       NAMES
0fd9bea03e04   192.168.91.102/repo/pipeline:v2.0.0   "/bin/sh -c 'java -j…"   5 minutes ago   Up 5 minutes   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   pipeline

curl http://192.168.91.101:8080/hello
hello jdk11 jenkinsv2.0.0

Jenkins流水线整合企微

在程序部署成功后,可以通过企微的机器人及时向群众发送部署的最终结果通知

安装插件

image.png

企微群创建机器人

image.png

image.png

jenkins中企微通知配置

image.png

任务流水线中追加企微通知配置

pipeline {
    agent any
    environment{
        sonarLogin = 'squ_99fb904465840d7973fd23eeef0ab44fdb21bca9'
        sonarHostUrl = 'http://192.168.91.102:9000/'
        harborHost = '192.168.91.102'
        harborRepo = 'repo'
        harborUser = 'devops'
        harborPasswd = 'Abc@1234'
        projectPort = 8080
    }
    stages {
        stage('拉取Git代码') {
            steps {
                checkout scmGit(branches: [[name: '$Tag']], extensions: [], userRemoteConfigs: [[url: 'http://192.168.91.102:8929/root/demo.git']])
            }
        }
        // 构建代码,通过脚本执行mvn的构建命令
        stage('构建代码') {
            steps {
                sh '/var/jenkins_home/mvn_jdk11/bin/mvn clean package -DskipTests'
            }
        }
        // 代码质量检测
        stage('检测代码质量') {
            steps {
                sh '/var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsonar.host.url=$sonarHostUrl -Dsonar.sources=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=target/ -Dsonar.login=$sonarLogin' 
            }
        }
        // 生成自定义镜像脚本
        stage('制作自定义镜像并发布Harbor') {
            steps {
                sh '''mv target/*.jar docker/demo.jar
                REPOSITORY=$harborHost/$harborRepo/$JOB_NAME:${Tag}
                docker login -u $harborUser -p $harborPasswd $harborHost
                docker build -t $REPOSITORY docker/
                docker push $REPOSITORY
                '''
            }
        }
        // 生成Publish Over SSH脚本,其中configName对应系统配置中SSH Server的名称
        stage('目标服务器拉取镜像并运行') {
            steps {
                sshPublisher(publishers: [sshPublisherDesc(configName: 'test', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "sh /root/docker/deploy.sh $harborHost $harborRepo $JOB_NAME $Tag $projectPort", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'docker/deploy.sh')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
    }
    // 企微通知配置
    post{
        success{
            qyWechatNotification failNotify: true, moreInfo:'成功'
        }
        failure{
            qyWechatNotification failNotify: true, moreInfo:'失败'
        }
    }
}
# devops 节点
# /root/demo/Jenkinsfile,修改该脚本文件,内容见上面
# 脚本文件上传至gitlab
cd /root/demo

git checkout v2.0.0
git tag -d v2.0.0
git push origin --delete v2.0.0
# 修改Jenkinsfile文件
git add .
git commit -m "Jenkinsfile update commit"
git tag v2.0.0
git push origin v2.0.0

git checkout v2.0.1
git tag -d v2.0.1
git push origin --delete v2.0.1
# 修改Jenkinsfile文件
git add .
git commit -m "Jenkinsfile update commit"
git tag v2.0.1
git push origin v2.0.1

构建测试

image.png

image.png

集成Kubernetes

Jenkins集成Kubernetes

Harbor私服配置

在所有节点上修改/etc/containerd/config.toml配置文件,修改其中相应的内容如下所示

    [plugins."io.containerd.grpc.v1.cri".registry]
      config_path = ""

      [plugins."io.containerd.grpc.v1.cri".registry.auths]

      [plugins."io.containerd.grpc.v1.cri".registry.configs]
        [plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.91.102".tls]
          insecure_skip_verify = true  # 是否跳过安全认证
        [plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.91.102".auth]
          username = "devops"
          password = "Abc@1234"
      [plugins."io.containerd.grpc.v1.cri".registry.headers]

      [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
          endpoint = ["https://zwyx2n3v.mirror.aliyuncs.com"]
        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."192.168.91.102"]
          endpoint = ["http://192.168.91.102"]
# 所有节点,重启containerd
systemctl daemon-reload && systemctl restart containerd.service
部署应用并测试

pipeline.yml

apiVersion: v1
kind: Namespace
metadata:
  name: test
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: test
  name: pipeline
  labels:
    app: pipeline
spec:
  replicas: 2
  selector:
    matchLabels:
      app: pipeline
  template:
    metadata:
      labels:
        app: pipeline    
    spec:
      containers:
      - name: pipeline
        image: 192.168.91.102/repo/pipeline:v2.0.0
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  namespace: test
  labels:
    app: pipeline
  name: pipeline  
spec:
  selector:
    app: pipeline
  ports:
  - protocol: TCP
    nodePort: 30001
    port: 8080
    targetPort: 8080
  type: NodePort
# master01
kubectl apply -f pipeline.yml

# k8s所有节点都可以访问
curl 192.168.91.220:30001/hello
hello jdk11 jenkinsv2.0.0

# 删除
kubectl delete -f pipeline.yml
Jenkins远程调用

配置Publish Over SSH连接到k8s中的master01节点

image.png

脚本修改并上传至gitlab

# devops
cd /root/demo
git checkout v2.0.1
# 创建 docker/pipeline.yml 文件
cat > docker/pipeline.yml << "EOF"
apiVersion: v1
kind: Namespace
metadata:
  name: test
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: test
  name: pipeline
  labels:
    app: pipeline
spec:
  replicas: 2
  selector:
    matchLabels:
      app: pipeline
  template:
    metadata:
      labels:
        app: pipeline    
    spec:
      containers:
      - name: pipeline
        image: 192.168.91.102/repo/pipeline:v2.0.2
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  namespace: test
  labels:
    app: pipeline
  name: pipeline  
spec:
  selector:
    app: pipeline
  ports:
  - protocol: TCP
    nodePort: 30001
    port: 8080
    targetPort: 8080
  type: NodePort
EOF
# 修改Jenkinsfile,将"目标服务器拉取镜像并运行"这部分配置修改成如下所示
vim Jenkinsfile
...
        stage('目标服务器拉取镜像并运行') {
            steps {
                sshPublisher(publishers: [sshPublisherDesc(configName: 'k8sMaster01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "kubectl apply -f /root/docker/pipeline.yml", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'docker/pipeline.yml')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
...

sed -i 's/2.0.1/2.0.2/' pom.xml
sed -i 's/2.0.1/2.0.2/' src/main/java/com/demo/DemoApplication.java

git add .
git commit -m "Jenkinsfile to k8s"
git tag v2.0.2
git push origin v2.0.2

构建并测试

在Jenkins中,选择v2.0.2 Tag进行构建

image.png

# master01
kubectl get all -n test
NAME                            READY   STATUS    RESTARTS   AGE
pod/pipeline-6bcf7547b6-f55jk   1/1     Running   0          18s
pod/pipeline-6bcf7547b6-h5q4h   1/1     Running   0          20s
NAME               TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
service/pipeline   NodePort   10.104.43.89   <none>        8080:30001/TCP   13m

NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/pipeline   2/2     2            2           13m
NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/pipeline-6bcf7547b6   2         2         2       20s
replicaset.apps/pipeline-ff5765d56    0         0         0       13m

# 所有k8s节点都可以访问
curl 192.168.91.220:30001/hello
hello jdk11 jenkinsv2.0.2

这种方式更适应与CD操作,将项目基于某个版本部署到指定的目标服务器

基于GitLab的WebHooks

这里要实现自动化的一个CI操作,也就是开发人员Push代码到Git仓库后,Jenkins会自动的构建项目,将最新的提交点代码构建并进行打包部署,这里区别上述的CD操作,CD操作需要基于某个版本进行部署,而这里每次都是将最新的提交点集成到主干上并测试

WebHooks通知

安装GitLab插件

image.png

关闭Jenkins的Gitlab认证,否则在Gitlab中配置webhooks时会出现403没有权限的错误,也可以通过配置Jenkins与GitLab的互信来避免这个问题

image.png

开启Jenkins的自动构建

image.png

流水线SCM配置修改,将指定分支修改为"*/master"

image.png

Gitlab hook网络配置

在Gitlab 10.6以后的版本为了安全起见,默认不允许向本地网络发送webhook请求,但是可以使用管理员身份修改默认设置。如果GitLab与Jenkins不是部署在一个节点上可以不用修改配置

以管理员身份登录Gitlab后,进入Administrator,点击菜单进入,接着左侧菜单栏--settings--network,然后在"Allow requests to the local network from web hooks and services"前面打勾(允许Webhook和服务对本地网络的请求)即可!

如果配置Webhooks时,报"Requests to the local network are not allowed"这个错误,就是这个原因导致的

image.png

设置Gitlab的Webhooks

image.png

脚本修改并上传至gitlab
# devops
cd /root/demo
git checkout v2.0.2
cp docker/pipeline.yml /root/pipeline.yml
cp Jenkinsfile /root/Jenkinsfile

git checkout master
mv -f /root/pipeline.yml docker/pipeline.yml
mv -f /root/Jenkinsfile Jenkinsfile

# 修改 docker/pipeline.yml 文件
sed -i 's/pipeline:v2.0.2/pipeline:latest/' docker/pipeline.yml
# 修改 Jenkinsfile 文件
sed -i -e "s#name: '\$Tag'#name: '*/master'#" -e 's#$JOB_NAME:${Tag}#$JOB_NAME:latest#' Jenkinsfile

# 因为pipeline没有改变时,每次不会重新加载,这样会导致Pod中的容器不会动态更新,这里需要使用kubectl的rollout restart命令滚动更新
sed -i 's#kubectl apply -f /root/docker/pipeline.yml#kubectl apply -f /root/docker/pipeline.yml \&\& kubectl rollout restart deployment pipeline -n test#' Jenkinsfile

sed -i 's/jenkins/jenkins auto build/' src/main/java/com/demo/DemoApplication.java

git add .
git commit -m "git change auto build and publish to k8s"
git push origin master
# 只要master分支提交代码,就会自动构建并发布
# master01
curl 192.168.91.220:30001/hello
hello jdk11 jenkins auto build

DHorse云原生系统

DHorse介绍

DHorse其实就是一个CI、CD的工具,和Jenkins以及Kubersphere都是一样的工具。

只不过DHorse相对另外两个工具来说,操作更简单,配置也没什么太繁琐的内容。但是DHorse还持续不断的更新,现在的功能或许没有那么的健全。不过DHorse是基于Java实现的,现在大多数的云原生工具都是基于Go去玩的,Java实现的不多。

下图是DHorse的架构图,其实这个流程就是一个简易的DevOps过程

image.png

整体流程就是,咱们写完代码之后,发布到Git远程仓库,之后可以在DHorse系统的图形化界面中,点击构建项目就会触发上述的核心流程

  • DHorse会从Git远程仓库拉取代码到DHorse系统内部
  • DHorse内部会基于Docker构建镜像,并且将镜像上传到Harbor的Docker镜像仓库
  • Kubernetes会从Harbor上拉取镜像,并且在Kubernetes中部署运行

搭建DHorse服务

DHorse的启动并不是说强行的依赖于Kubernetes,和Harbor以及Gitlab。DHorse本质就是一个Java的Web项目,只不过内部维护好了一整套部署的流程。在部署的时候,才需要上述的三个环境。

# devops

# 搭建DHorse,上面已经安装过jdk11
cd /root
wget https://gitee.com/i512team/dhorse/releases/download/v1.2.0/dhorse-v1.2.0-bin-unix.tar.gz
tar xf dhorse-v1.2.0-bin-unix.tar.gz
rm -f dhorse-v1.2.0-bin-unix.tar.gz
mv dhorse-v1.2.0-bin dhorse
cd dhorse
sed -i 's#JAVA_HOME=$JAVA_HOME#JAVA_HOME=/usr/local/jdk11#' bin/server.sh
bin/dhorse-start.sh
...
Start dhorse service successfully, pid is 5067

# 查看日志
tail -f log/out.log

# 关闭防火墙,否则外面可能不能访问
systemctl stop firewalld && systemctl disable firewalld

通过浏览器访问http://192.168.91.102:8100/,默认的用户名和密码是admin,直接登录即可

image.png

可以看到DHorse可以去配置Git仓库地址,同时指定给项目打包的Maven信息,同时再指定好Harbor这种镜像仓库的地址,同时还要去准备Kubernetes集群的地址信息。上述信息全部指定好之后,就可以针对具体的服务设置应用信息,指定好信息后,就可以在发布管理中看到具体的信息,可以选择不同的方式进行发布。后面的发布,不需要关注Kubernetes等组建的底层,直接傻瓜式的点击按钮即可。

DHorse配置

DHorse配置k8s集群地址

# master01 (k8s集群主节点)

# 生成Kubernetes集群的令牌信息
kubectl create serviceaccount dhorse-admin -n kube-system
kubectl create clusterrolebinding dhorse-cluster-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dhorse-admin

kubectl create token dhorse-admin -n kube-system --duration 867240h
eyJhbGciOiJSUzI1NiIsImtpZCI6IlRJM00wMkVMc2VhWkVzWUtLdk9QaGtPQ1NKRFRVcmp4VThBSkJOdWJ5T0EifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjo0ODIyODU0ODcwLCJpYXQiOjE3MDA3OTA4NzAsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJkaG9yc2UtYWRtaW4iLCJ1aWQiOiI0MjEwNzMxNC1iNWQzLTQ2OGUtOGEyMy1kOTQ4ZThiM2UwZDIifX0sIm5iZiI6MTcwMDc5MDg3MCwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRob3JzZS1hZG1pbiJ9.400SYzpmPbydxwQXX4hIgcmwyZD1-LfwHLYT4ereAJMZbvef6-uTYfQaVEUDtzzl4dYbgVJ4oRmIw1jP_8lsZo9Kge8rAEK9ck_Lm7UJj43uayeIsNbbf3q8S_ljA_EkNf1GP5s_ghJmpH-rhUs4-EDhdW5MoLCpB9g_g3l3-KaV-puUncl9dJfD_gutOu-2_oY8kcHWZywD3nNZxxBmqzh4u3m-2oN5oNI7Yba0kwWMk2tY3d9my-YECEYDiFZS-SFlQDTDzDY7AciNBQu7ncWsHVOYRvNlZCDbzDdsmrifLsOjG6asjwVwB2aXXwJFE-1RjzMfZcWT4slvZHGbpQ

在DHorse中,指定好集群管理中的Kubernetes信息,这样后期部署项目时,就会采用这个Kubernetes作为部署的服务

image.png

DHorse配置Harbor

在DHorse的公共配置里,将镜像仓库指定为Harbor使用

image.png

DHorse配置GitLab

image.png

DHorse配置Maven

image.png

构建应用

添加应用

image.png

image.png

构建应用的版本(制作镜像)

直接在分支管理或者在标签管理中,点击构建版本,DHorse会做几个事情:

  • 会将GitLab上的代码拉取到本地
  • 项目是Maven,所以会给予Maven的命令打包
  • 将打包的jar包,基于指定的基础镜像,构建成一个全新的镜像
  • 将镜像推送到harbor仓库上

只有“构建版本”之后,在“版本列表”中才能看到,在“部署应用”时才可以选择

image.png

日志信息,点击构建后,记得查看DHorse上的日志,有问题方便排查

# devops
tail -f /root/dhorse/log/out.log
...
2023-11-24 16:12:24.839 [pool-8-thread-3] INFO  org.dhorse.application.service.DeployApplicationService:698 -
2023-11-24 16:12:24.840 [pool-8-thread-3] INFO  org.dhorse.application.service.DeployApplicationService:698 - Container entrypoint set to [java, -jar, demo.jar]
2023-11-24 16:12:24.840 [pool-8-thread-6] INFO  org.dhorse.application.service.DeployApplicationService:698 - Building a single manifest
2023-11-24 16:12:24.852 [pool-8-thread-10] INFO  org.dhorse.application.service.DeployApplicationService:698 - Checking existence of manifest for sha256:9259b60e34640e4f0aa58e47fd823fd4e1108e8c836339030c9b7280c55462cd...
2023-11-24 16:12:24.852 [pool-8-thread-10] INFO  org.dhorse.application.service.DeployApplicationService:698 - Skipping manifest existence check; system property set to false
2023-11-24 16:12:24.904 [pool-8-thread-5] INFO  org.dhorse.application.service.DeployApplicationService:698 - Pushing manifest for v20231124_161215...
2023-11-24 16:12:24.977 [build-version-pool-0] INFO  org.dhorse.application.service.DeployApplicationService:140 - Build image successfully
2023-11-24 16:12:24.987 [build-version-pool-0] INFO  org.dhorse.application.service.DeployApplicationService:152 - End to build version

构建并且推送成功后,可以查看Harbor上的信息

image.png

部署应用

DHorse里在部署项目前,需要指定好在哪个环境下部署。直接在环境管理中,构建好环境信息(构建测试环境)

image.png

image.png

# master01
# 测试验证
kubectl get pod
NAME                                  READY   STATUS    RESTARTS   AGE
demo-1-test-dhorse-6cbdd74ccd-z4r6f   1/1     Running   0          2m56s

kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
demo-test    ClusterIP   10.108.252.128   <none>        8080/TCP   2m34s
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP    22d

curl http://10.108.252.128:8080/hello
hello jdk11 jenkinsv2.0.0

# 再次在DHorse上,构建v2.0.1版本,并部署
# 老版本的pod被删除了,起了新版本的pod
kubectl get pods
NAME                                  READY   STATUS    RESTARTS   AGE
demo-1-test-dhorse-5d4bbb89bc-6vssz   1/1     Running   0          101s

kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
demo-test    ClusterIP   10.108.252.128   <none>        8080/TCP   16m
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP    22d

curl http://10.108.252.128:8080/hello
hello jdk11 jenkinsv2.0.1