Skip to content

DevOps 入门

第一章:DevOps 概念

1.1 DevOps 是什么?

  • DevOps 是 Development 和 Operations 的组合。

1646656291260-06fdfc23-b400-4fd8-949c-8d3f233b97cf.png

  • DevOps 可以看作是开发(软件工程)、技术运营和质量保障(QA)的交集。

1646656296354-59065f45-6392-42f9-8acc-22859380296f.png

  • DevOps 突出重视软件开发人员和运维人员的沟通合作,通过自动化流程来使得软件构建、测试、发布更加快捷、频繁和可靠。
  • DevOps 希望做到的是软件产品交付过程中 IT 工具链的打通,使得各个团队减少时间损耗,更加高效的协同工作。

1.2 CI 、CD 是什么?

1.2.1 基本理念

1646656300801-30d45ba5-5ef5-4a2a-b0f5-d4686e56f053.png

  • 持续集成(Continuous Integration):持续集成是指软件个人研发的部分向软件整体部分交付,频繁进行集成以便更快、更早的发现其中的错误。持续集成源自于极限编程(XP),是 XP 最初的 12 种实践之一。持续集成需要具备
    • 全面的自动化测试:这是实践持续集成 && 持续部署的基础,与此同时,选择合适的自动化测试工具也极为重要。
    • 灵活的基础设施容器、虚拟机 的存在让开发人员和 QA 人员不必再大费周折。
    • 版本工具工具:如 Git、~~SVN~~ 等。
    • 自动化的构建和软件发布流程工具:如 Jenkins 等。
    • 反馈机制:构建、测试失败的时候,可以快速的反馈到相关负责人,以便尽快的解决问题,使得产品能更早的达到稳定的版本。
  • 持续交付(Continuous Delivery):持续交付在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境的『类生成环境』中。持续交付优先于整个产品生命周期的软件部署,建立在高水平自动化持续集成之上。持续交付的优点和持续集成非常类似:
    • 快速发布:能够应对业务需求,更快的实现软件价值。
    • 编码、测试、上线、交付的频繁迭代周期缩短,同时获得迅速反馈。
    • 高质量的软件发布标准:整个交付过程标准化、可重复、可靠。
    • 整个交付过程进度可视化:方便团队人员了解项目成熟度。
    • 更先进的团队协作方式:从需求分析、产品的用户体验到交互设计、开发、测试、运维等角色的密切协作,相比于传统的瀑布式软件团队,更少浪费。
  • 持续部署(Continuous Deployment):持续部署是指当交付的代码通过评审之后,自动部署到生产环境中。持续部署是持续交付的最高阶段。这意味着,所有通过了一系列的自动化测试的改动都将自动部署到生产环境。

开发人员提交代码,持续集成服务器获取代码,执行单元测试,根据测试结果决定是否部署到预演环境,如果成功部署到预演环境,进行整体验收测试,如果测试通过,自动部署到生产环境。整个过程自动化高效运转。

1.2.2 最佳实践

1.2.2.1 内循环和外循环

1646656324344-9047e3ba-909f-47f3-91e5-489c308394e2.png

  • 内循环(开发):编码、测试、运行、debug、提交。
  • 代码推送到代码仓库(Git、SVN)。
  • 进行 CI 流程(持续集成):万物皆可容器化,所有应用打包成 Docker 镜像(符合 OCI 镜像规范即可)。
  • 镜像推送到镜像仓库。
  • 测试。
  • 进行 CD 流程(持续部署):将应用部署到环境(测试、预发布、生产……)。
  • 外循环:
    • 运行时监控。
    • 生产环境的管理。
    • 监控。
    • 线上反馈到开发。
  • 来到内循环。

1.2.2.2 实践流程

1646656329347-8c087767-90b0-45fd-bac8-cf379dd9d6e2.png

  • ① 创建分支开发功能。
  • ② 提交分支的代码。
  • ③ 进入持续集成流程:
    • 当前分支代码功能性自动化构建和测试。
    • 自动化工具推送这次提交。
    • 自动化集成测试。
    • 人工确认此次功能是否发布到生产环境。
  • ④ 代码合并。
  • ⑤ 进入持续部署流程:
    • 构建。
    • 测试。
    • 发布。
    • ……

第二章:Jenkins

2.1 Jenkis 简介

  • Jenkins 是开源 CI&CD 软件领导者, 提供超过 1000 个插件来支持构建、部署、自动化, 满足任何项目的需要。

1646656336536-8d3d4b91-fa40-4850-8107-6552a097439b.png

2.2 前提说明

  • 本次将采用 Docker 的形式来讲解 DevOps,所以希望你熟悉 Docker 的操作,并且本人是将 Docker 安装在 Win11 的 WSL2 中,如果不熟悉,请点 这里
  • 如果和我一样的方式使用 Docker ,希望你在 WSL2 中安装有 Linux 系统,比如:AlmaLinux9 、Ubuntu 、CentOS7 等。

1679717142288-3692297e-a44c-449c-80a6-0d0a6c97d4cc.png

1679717178427-e3223721-b3a7-4e19-bca6-9561222ac4c5.png

  • 当然,我们也可以在 Docker Desktop 中设置 WSL2 中的 Linux 能访问 Docker 。

1679717358661-6bbfa9c9-da18-47f1-86a5-1e074843cadc.png

2.3 准备工作

2.3.1 安装 Jenkins

  • 安装命令:
# 修改 /var/run/docker.sock 文件的所属组
chown root:root /var/run/docker.sock
# 修改 /var/run/docker.sock 文件的权限
chmod o+rw /var/run/docker.sock
# 端口是 8080
docker run  \
       -u root \
       --name=jenkins  \
       -d \
       -p 8080:8080 \
       -p 50000:50000 \
       -v jenkins-data:/var/jenkins_home \
       -v /etc/localtime:/etc/localtime:ro \
       -v /var/run/docker.sock:/var/run/docker.sock \
       -v /etc/docker/daemon.json:/etc/docker/daemon.json \
       --privileged=true \
       --restart=always \
       jenkinsci/blueocean

1679719335055-660975ea-a1e7-4d12-9760-ac8d7211a2c5.gif

  • 通过浏览器登录,本人的 IP 地址是 192.168.1.13,所以登录的地址是 http://192.168.1.13:8080/

1679719439248-83ca9179-7e6d-401c-aa08-32d71bd894f2.png

  • 根据提示,拿到 Jenkins 的密钥:

1679719516809-6d606d6f-49ff-4ce4-986e-1bf1aa8482ba.gif

本人的是:8a7a7d34a2514c6389da1eee94cdcf87 ,每个人的不一样!

  • 登录并配置相关信息:

1679719589590-b52c2d55-9f00-4dbb-b25b-9c7a6d4ae789.png

1679719632436-a2a86d9f-8fe2-4ffa-bb4a-543a7cb9110b.png

1679719645024-c77f8d01-9c24-4696-ad79-bfaee4c47f5b.png

1679719734990-e69b7ee4-4afc-41aa-8834-68a827fdcd69.png

1679719787749-30cf3128-8ef2-4d89-9534-bd4537d317b4.png

1679719809588-cc608467-984c-418b-82dd-cd8a4207df5b.png

1679719878092-df999444-8adc-4e55-8b17-c57f6131fc9e.png

  • 用刚才配置的用户名和密码登录到 Jenkins 系统中:

1679720848007-eaa49711-7ba0-4614-95a6-5ab0ccf68ca4.png

  • 此时,就会发现 Jenkins 界面有消息提示,其实就是 Jenkins 的版本低了,那就更新呗~

1679720925650-b1fbefb5-8759-42d8-b16e-61886e45c161.png

  • 更新 Jenkins 并重启:

1679721043655-6b0d3f1b-c1af-4ca7-b9d3-e3b813bf4a3a.gif

  • 更新 Jenkins 插件并重启:

1679721362615-051db915-1670-4711-adc0-34a94bb1469e.gif

  • 此时,Jenkins 就没有报错信息了。

1679721534294-c53819ee-5e98-4569-9de1-1f815361d5e8.png

2.3.2 安装 GitLab

  • 安装命令:
export GITLAB_HOME=/var/gitlab
docker run --detach \
  --hostname 192.168.1.13 \
  --publish 443:443 --publish 80:80 --publish 22:22 \
  --name gitlab \
  --restart always \
  --volume $GITLAB_HOME/config:/etc/gitlab \
  --volume $GITLAB_HOME/logs:/var/log/gitlab \
  --volume $GITLAB_HOME/data:/var/opt/gitlab \
  --shm-size 256m \
  registry.gitlab.cn/omnibus/gitlab-jh:latest

参数说明:--hostname 填写本机的 IP 即可。

1679721895518-3241f24f-27fe-4f5f-9262-7bea3e4994b0.gif

  • 访问 GitLab URL,并使用用户名 root 和来自以下命令的密码登录:
docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password

注意:密码文件将在 24 小时后的第一次重新配置运行中自动删除!

1679722531332-8b64c182-0261-4eda-8cd1-f44f163bb762.gif

本人的是 KYNBHxTP4l+8eJ55baQ8S9uSBP9dESdMHEVDTv5VFPA= ,每个人的不一样!

  • 根据默认密码进行登录,并修改默认密码:

1679722833424-98199715-6f6a-4fe2-a1f6-c7ade92a408e.gif

1679722838695-c750e4c7-7b9a-4b6d-a791-0db8cf2d73e7.gif

2.3.3 安装 SonarQube

  • SonarQube 在安装之前需要执行如下的命令,以便 Docker 主机配置符合 ElasticSearch 生产模式要求和文件描述符配置:
sysctl -w vm.max_map_count=524288
sysctl -w fs.file-max=131072
ulimit -n 131072
ulimit -u 8192

1679722978244-52c2fe3e-35d6-42d4-8e7e-a7dbac3a6887.gif

  • 本次将使用 docker-compose 来对 SonarQube 进行安装:
vim docker-compose.yaml
version: "3"

services:
  sonarqube:
    image: sonarqube:lts-community
    depends_on:
      - db
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: sonar
    volumes:
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_extensions:/opt/sonarqube/extensions
      - sonarqube_logs:/opt/sonarqube/logs
    ports:
      - "9000:9000"
  db:
    image: postgres:12
    environment:
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: sonar
    volumes:
      - postgresql:/var/lib/postgresql
      - postgresql_data:/var/lib/postgresql/data

volumes:
  sonarqube_data:
  sonarqube_extensions:
  sonarqube_logs:
  postgresql:
  postgresql_data:
docker compose build --force-rm --no-cache \
        && docker compose up -d

默认的用户名和密码是:admin/admin

1679723225786-2508b322-0be4-45cf-b3dc-981f9451e797.gif

  • 登录到 SonarQube ,并安装中文插件:

1679723352306-d5d954e5-6fc2-4956-bc7d-947aeccdb844.gif

  • 等待 SonarQube 重启,然后就会发现界面变为了中文。

1679723387631-1674c8ef-7e18-4a62-94c2-c3a3d42459e6.png

2.4 Jenkins 安装插件(必须)

  • Docker、Docker Pipeline:安装 Docker Pipeline 会自动安装 Docker 插件,Docker Pipeline 插件允许我们自定义 agent 使用 Docker 环境。
  • Git Parameter:解析 git 参数,允许我们选择分支进行构建。
  • Active Choices:可以做到参数的级联选择。
  • Generic Webhook Trigger:通用的 webhook 触发器,构建更强大的 webhook 功能。
  • Role-based Authorization Strategy:RBAC 权限控制。
  • List Git Branches Parameter:列出分支参数。
  • Build With Parameters:基于自定义参数构建。
  • Jersey 2 APIGitee:Gitee 插件。
  • GitLab:GitLab 插件。
  • publish-over-ssh:通过 ssh 来操作远程服务器。
  • SonarQube Scanner:用来整合 SonarQube 服务。
  • DingTalk:Jenkins 用来整合钉钉的插件。

  • 示例:

1679723541304-897361f6-e1d3-425b-a7e8-66385a29ca3b.gif

2.5 准备一个 git 项目进行测试

  • 使用 IDEA 创建一个 SpringBoot 项目:

1679723948618-ae8ead5e-dfc6-4045-8ce4-77a3b3acff7c.png

1679724061154-d88f9ec8-72ad-476f-bc39-1dbbce8bae6d.png

1679724093567-53127bcd-4bb4-4449-9af4-bec6d511ce08.png

1679724622823-c8723cc4-4ebd-47bf-bf43-af340eee9687.png

项目的地址是:https://gitee.com/Aurorxa/devops-java-demo.git

  • 在 GitLab 上创建项目,并将 Java 工程上传上去:

1679724872086-dcab4b77-2f4a-451b-856a-b1a8c01c0f8b.gif

1679724916444-e5219773-07b8-4c7c-8473-316f47c053fc.png

1679724937735-13d0ec1e-f8ac-4b50-b3cd-ad1580b2d835.png

1679724969953-d4e49759-7578-4526-9d0f-a6d8b9210603.png

1679724996574-5644f1d1-3bdd-4377-901b-3dfb015ffc9a.png

1679725015039-cfa51997-8118-4d30-a852-792b91c72bfc.png

1679725050735-77d2e752-f00c-4584-afb9-48922329c4f4.png

第三章:Jenkins 实现基本的 CI/CD 操作

3.1 概述

  • 传统的 CI/CD 流程是这样的:

1679725947125-4529d598-27c3-4f3e-871e-88da92ad9c0c.png

3.2 Jenkins 实现基本的 CI/CD 操作

3.2.1 Jenkins 整合 Gitlab

  • ① 在 Jenkins 中创建 任务,选择 构建自由分格的软件项目

1679726082488-e173a705-85d9-4d6f-9c82-56d263915979.png

1679726132173-9735fe64-b511-4d90-b7c1-732a11380863.png

  • ② 将 Git 项目地址配置到 Jenkins 中:

1679726236726-e76fd852-de7a-407e-b353-d8a62fcd4d8e.png

  • ③ 进行保存之后,执行构建,观察是否整合成功:

1679726424681-5f84d8e8-fb4a-43b2-a848-6003aabf5a8b.gif

3.2.2 Jenkins 整合 Maven

  • 系统管理--> 全局工具配置

1679726712174-8cfe0c5e-0fc9-40a9-9a89-d611e9d3ed91.gif

  • ② Maven 安装:本人选择自动安装。

1679726786543-b3854016-1374-4074-ad75-ed7874081325.gif

当然,也可以不自动安装;此时,就需要手动将 Maven 下载到 /var/jenkins/home 下,然后手动配置即可 apache-maven-3.9.1-bin.zip

  • ③ 进入项目中,配置构建步骤:

1679727077258-4da06049-6c81-4d68-90a1-0ee2f3473a40.gif

clean package -Dmaven.test.skip=true

  • ④ 进行保存之后,执行构建,观察是否整合成功:

1679731255209-8093b91e-5d73-45fe-a304-18fee3260a85.gif

3.2.3 Jenkins 整合 publish-over-ssh

  • 系统管理 --> 系统配置 -->SSH Server

1679731922626-e402585b-bf21-4d77-b92c-a9f203c8ac7f.gif

  • ② 新开一台虚拟机,并安装好 Docker 环境(IP 地址是:192.168.68.17):略。
  • ③ 配置目标服务器(虚拟机)的信息,并进行连接测试:

1679732054992-d0537dd3-27ef-4485-8141-a2d2d770a26d.gif

  • ④ 在 Java 工程中中增加 Dockerfile 文件,并推送到远程 Gitlab 仓库中,Dockerfile 的内容如下:
FROM openjdk:11.0.16-slim
WORKDIR /app
COPY ./target/*.jar /app.jar
# 修改时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone && touch /app.jar
# 环境变量
# docker run -e JAVA_OPTS="-Xmx512m -Xms64m" -e PARAMS="--spring.profiles.active=dev --server.port=8080" xxx
ENV JAVA_OPTS=""
ENV PARAMS=""
# 运行 jar 包
ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom $JAVA_OPTS -jar /app.jar $PARAMS" ]

1679733961866-fd38c239-e03a-485b-894a-842b5ae46c6d.png

  • ⑤ 在 Jenkins 中的项目中配置构建后的操作,并进行构建 :

1679734121214-051462a5-73fd-4c4f-8c68-1d41312c2867.gif

3.2.4 基于 tag 进行构建

在实际开发中,很多时候,我们都是基于 git 的 tag 进行版本构建的;此时,就需要使用到 Git Parameter 来实现参数化构建。

  • ① 在 Jenkins 项目中,配置参数化构建:

1679734406146-eb28d0b8-2dd7-4dce-9252-ebd5f2f73246.gif

  • ② 在 Maven 编译之前,执行 git checkout $tag

1679734577623-a5a9b1f7-39ec-453c-b3f8-652cacae6e6f.gif

  • ③ 在 GitLab 中建立多个 tag 来进行测试:

1679734689674-d9b855bd-e144-4ac8-a980-df0633c521fd.gif

  • ④ 此时,就可以在 Jenkins 的项目中进行参数化构建:

1679734758248-e9e0feef-c786-490e-8ad7-03ef2b5dbed4.gif

3.3 SonarQube 的基本使用

  • 在 settings.xml 文件中配置如下信息:
<profiles>
      <profile>
            <id>sonar</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <sonar.host.url>http://192.168.1.13:9000/</sonar.host.url>
                <sonar.login>admin</sonar.login>
                        <sonar.password>admin12345</sonar.password>
            </properties>
    </profile>
  </profiles>

1679735542399-6814760f-1410-4c69-aec0-bd052f44aabb.gif

  • 在项目中使用如下命令即可:
mvn clean verify sonar:sonar -Dmaven.test.skip=true

注意:如果没有在 settings.xml 中配置 sonar 的用户名和密码,就需要去 SonarQube 申请 token ,然后执行 mvn clean verify sonar:sonar -Dsonar.login=myAuthenticationToken 这样的命令。

1679735770258-c19f334f-8a57-458e-9728-7831df4c4ce3.gif

  • 在 SonarQube 中就可以看到代码是否符合规范了:

1679735835054-5f8983c8-affe-47cf-a214-0997ac6040de.gif

  • 有的时候,我们希望将 sonar:sonar目标作为一个专门的步骤去运行,就可以这样执行:
mvn clean install
mvn sonar:sonar -Dsonar.login=myAuthenticationToken
  • 有的时候,我们希望指定 sonar-maven-plugin 的版本,而不是最新版,可以这样执行:
mvn org.sonarsource.scanner.maven:sonar-maven-plugin:3.7.0.1746:sonar
  • 有的时候,我们不希望某个模块进行代码检测,就可以在 properties 标签中,这样配置:
    <properties>
        <java.version>11</java.version>
        <sonar.skip>true</sonar.skip>
    </properties>
  • 当然,我们也可以在 pom.xml 中锁定 sonar-maven-plugin 的版本:
<build>
  <pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.sonarsource.scanner.maven</groupId>
        <artifactId>sonar-maven-plugin</artifactId>
        <version>3.7.0.1746</version>
      </plugin>
    </plugins>
  </pluginManagement>
</build>
  • 刚才,我们是将 SonarQube 的信息配置在 settings.xml 中;其实也可以配置在 pom.xml 中:
<properties>
        <java.version>11</java.version>
        <sonar.host.url>http://192.168.1.13:9000</sonar.host.url>
        <sonar.login>admin</sonar.login>
        <sonar.password>admin12345</sonar.password>
</properties>

<build>
  <pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.sonarsource.scanner.maven</groupId>
        <artifactId>sonar-maven-plugin</artifactId>
        <version>3.7.0.1746</version>
      </plugin>
    </plugins>
  </pluginManagement>
</build>

3.4 SonarQube 的基本使用

  • 除了使用 Maven 插件的方式之外,还可以使用 SonarScanner 的方式,这种方式更加通用,可以使用于命令行。
  • 在 AlmaLinux9 中下载 sonar-scanner-clisonar-scanner-cli-4.8.0.2856-linux.zip
wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.8.0.2856-linux.zip

1679964266477-203e52ea-bf4c-481b-a467-40f0c118a8ca.gif

  • 解压:
unzip sonar-scanner-cli-4.8.0.2856-linux.zip

1679964410245-dcb514ea-9f9e-437f-95c2-eca1578a8460.gif

  • 进入到配置文件所在目录,并修改配置文件:
cd sonar-scanner-4.8.0.2856-linux/conf
vim sonar-scanner.properties
#Configure here general information about the environment, such as SonarQube server connection details for example
#No information about specific project should appear here

#----- Default SonarQube server
sonar.host.url=http://192.168.1.13:9000

#----- Default source code encoding
sonar.sourceEncoding=UTF-8

1679967289250-b17bfedf-d37b-4f4e-84bc-3b3b2ed395c9.gif

  • 配置 sonar-scanner 到环境变量中:
vim /etc/profile
export SONAR_SCANNER_HOME=/root/sonar-scanner-4.8.0.2856-linux
export PATH=$SONAR_SCANNER_HOME/bin:$PATH
source /etc/profile
  • 在 WSL2 中的 Linux 和 win11(本人的项目在 d:/project 中) 的映射目录是 /mnt ,此时,我们可以将项目复制到 WSL2 中的 AlmaLinux 9 中。
cp -R /mnt/d/project/devops-java-demo/ ./
cd devops-java-demo
sonar-scanner \
        -Dsonar.projectKey=devops-java-demo2 \
        -Dsonar.projectName=devops-java-demo2 \
        -Dsonar.sources=./ \
        -Dsonar.login=admin \
        -Dsonar.password=admin12345 \
        -Dsonar.java.binaries=./target/

参数说明:

  • -Dsonar.projectKey:指定项目的 key ,必须,且唯一。
  • -Dsonar.projectName:指定项目的名称。
  • -Dsonar.sources:源代码的地址。
  • -Dsonar.login:可以是用户名或 token 。
  • -Dsonar.password:密码,如果是 token ,该参数可以省略。
  • -Dsonar.java.binaries:编译后的 classpath 的路径。

1679968639620-8b0aa6a4-2125-4dd4-b7c7-3ddc2be18efe.gif

  • 通过浏览器登录到 SonarQube 中进行验证:

1679968919797-39022471-e81a-4ffc-9d78-3e12189eca42.gif

  • 当然,也可以使用 Docker 镜像来执行扫描:
docker run \
    --rm \
    -e SONAR_HOST_URL="http://${SONARQUBE_URL}" \
    -e SONAR_SCANNER_OPTS="-Dsonar.projectKey=${YOUR_PROJECT_KEY}" \
    -e SONAR_LOGIN="myAuthenticationToken" \
    -v "${YOUR_REPO}:/usr/src" \
    sonarsource/sonar-scanner-cli

3.5 Jenkins 整合 SonarQube

  • ① 在 SonarQube 中申请 token :

1679970010195-bfd88ff6-cc80-4197-97f3-1cb7a947b0a0.gif

本人申请的 token 是 6e678e350e598a93ccc3652dd2c7d1f0edb1bb24,每个人不一样!!!

  • ② 在 Jenkins 中配置 SonarQube 的密钥信息 :

1679970717561-c77dc2da-e486-4cd3-80b0-2c238d808b82.gif

  • ③ 在 Jenkins 中配置 SonarQube(需要安装 SonarQube Scanner 插件):

1679971108801-a2ec9392-1d64-4e0f-a747-32de07599f10.gif

  • ④ 将 SonarScanner 上传到 Jenkins 的 /var/jenkins_home 目录下,并修改配置文件:

1679971661969-d57b9bf6-4982-41d2-94c5-4ef67dba5afc.gif

SonarScanner 的路径是 /var/jenkins_home/sonar-scanner-4.8.0.2856-linux

  • ⑤ Jenkins 配置 SonarScanner :

1679971932565-c66d9634-7d29-4bb3-ab98-c8410b2494c1.gif

  • ⑥ 在 Jenkins 项目中启用 SonarScanner :

1679973678591-72a27004-fb3d-49f2-9f9a-137333f7ceb2.gif

sonar.projectKey=${JOB_NAME}

sonar.projectName=${JOB_NAME}

sonar.sources=./

sonar.java.binaries=./target/

  • :官方提供的 zip 包有问题,会出现 /var/jenkins_home/sonar-scanner-4.8.0.2856-linux/bin/sonar-scanner: exec: line 66: /var/jenkins_home/sonar-scanner-4.8.0.2856-linux/jre/bin/java: not found,是因为软连接有问题,按照如下步骤即可:
cd /var/jenkins_home/sonar-scanner-4.8.0.2856-linux/jre/bin
rm -rf java
find / -name java
ln -s /opt/java/openjdk/bin/java ./java

1679973896583-60404fa8-c07e-4e1e-98b9-c2da40834de7.gif

3.6 Harbor 的安装

wget https://github.com/goharbor/harbor/releases/download/v1.10.17/harbor-offline-installer-v1.10.17.tgz
  • 解压 harbor :
tar -zxvf harbor-offline-installer-v1.10.17.tgz

1680055644348-30f945c8-60e4-4036-854a-93adbecb8d9e.gif

  • 修改配置文件:
vim harbor/harbor.yml
# Configuration file of Harbor

# The IP address or hostname to access admin UI and registry service.
# DO NOT use localhost or 127.0.0.1, because Harbor needs to be accessed by external clients.
hostname: 192.168.68.16 # 主机的 IP 地址

# http related config
http:
  # port for http, default is 80. If https enabled, this port will redirect to https port
  port: 80

# https related config
#https: # 关闭 https
  # https port for harbor, default is 443
 # port: 443
  # The path of cert and key files for nginx
  #certificate: /your/certificate/path
  #private_key: /your/private/key/path

1680055717586-822abcd9-1aa2-42ef-a1a5-d9f2af331972.gif

  • 安装:
cd harbor
sed -i 's/docker-compose --version/docker compose version/g' common.sh
sed -i 's/docker-compose/docker compose/g' install.sh
./install.sh

注意:

  • 底层使用是 docker 和 docker compose 。
  • 最新版的 docker 将 docker compose 作为插件集成进来了,命令改为了 docker compose 。
  • 默认的用户和密码分别是 admin/Harbor12345

1680055807061-6e113ede-f5b6-4e14-92bd-f3d4d1e78387.gif

  • 通过浏览器登录到 Harbor 仓库,并创建公开仓库。

1680055861621-0b3be145-37d4-4b46-8e19-f71e7c051426.gif

  • 因为 Harbor 使用的是 http ,所以 docker 需要修改配置以便信任 Harbor 。
cat > /etc/docker/daemon.json<< EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"], 
  "registry-mirrors": ["https://du3ia00u.mirror.aliyuncs.com"], 
  "live-restore": true,
  "log-driver":"json-file",
  "log-opts": {"max-size":"500m", "max-file":"3"},
  "max-concurrent-downloads": 10,
  "max-concurrent-uploads": 5,
  "storage-driver": "overlay2",
  "insecure-registries": ["192.168.68.16:80"]
}
EOF
sudo systemctl daemon-reload \
    && sudo systemctl restart docker

1680056001840-486a7e17-fc57-4641-ad6d-d8c51bdf3c6c.gif

  • 在 Win11 上的 Docker Desktop 的配置也是类似的:

1680056040117-64791217-aa75-4015-8cbc-9dc4df95703b.png

  • 测试能否将镜像推送到 Harbor 仓库中:
docker login 192.168.68.16:80 -uadmin -pHarbor12345
docker pull nginx
docker tag nginx:latest 192.168.68.16:80/repo/nginx:latest
docker push 192.168.68.16:80/repo/nginx:latest

1680056272697-e4438004-464a-4bb4-8c43-8416ab890be1.gif

  • 通过浏览器查看 Harbor 仓库是否有刚才推送的镜像:

1680056361265-68e7f443-39b6-4a95-9068-7ae15e06a365.gif

3.7 Jenkins 整合 Harbor

3.7.1 DevOps 实现目标

  • 目前,DevOps 系统中有 Jenkins、Maven、SonarQube、GitLab 等,其 CI/CD 流程也需要进一步进行修改,如下图所示:

1680056782722-2a2c4b5f-201f-4181-8b5c-4218970c61bd.png

3.7.2 实现自定义镜像并推送到 Harbor 仓库中

  • 其实,只需要 jenkins 的项目中增加如下的构建 shell 命令即可:
# docker 构建镜像
docker build -t ${JOB_NAME}:$tag .
# docker 登录到 Harbor
docker login 192.168.68.16:80 -uadmin -pHarbor12345
# 将镜像重新打标签
docker tag ${JOB_NAME}:$tag 192.168.68.16:80/repo/${JOB_NAME}:$tag
# 推送镜像到 Harbor 仓库
docker push 192.168.68.16:80/repo/${JOB_NAME}:$tag

1680067141921-e354e859-dc10-4617-b489-41c7867b1275.gif

3.7.3 Jenkins 通知目标服务器拉取镜像,并启动容器

  • 其实,Jenkins 只需要做如下的步骤即可:
    • ① 告知目标服务器拉取哪个镜像。
    • ② 判断目标服务器是否正在运行容器?如果是,就需要停止并删除容器。
    • ③ 判断目标服务器上是否已经存在有同名镜像?如果存在,就需要删除镜像。
    • ④ 让目标服务器拉取 Harbor 服务器上的镜像,并将镜像运行成容器。
  • 在目标服务器 192.168.1.17 创建 deploy.sh 脚本,并配置如下信息:
touch deploy.sh && chmod +x deploy.sh
vim deploy.sh
HARBOR_ADDR=$1 # Harbor 的 IP 地址
HARBOR_PORT=$2 # Harbor 的端口
HARBOR_repo=$3 # Harbor 的仓库地址
PROJECT_NMAE=$4 # 项目名
PROJECT_PORT=$5 # 项目的端口
TAG=$6 # 版本

imageName=$HARBOR_ADDR:$HARBOR_PORT/$HARBOR_repo/$PROJECT_NMAE:$TAG
echo "镜像名称是:$imageName"

containerId=`docker ps -a | grep "${PROJECT_NMAE}" | awk '{print $1}'`
echo "容器名称是:${containerId}"

# 停止并删除已经存在的容器
if [ "$containerId" != "" ]; then
    docker stop $containerId
    docker rm -f $containerId
fi

tag=`docker images | grep "${PROJECT_NMAE}" | awk '{print $2}'`
echo "标签的名称是:${tag}"

# 删除已经存在的镜像
if [[ "$tag" =~ "$TAG" ]] ;then
    docker rmi -f $imageName
    docker system prune -af
fi

# 登录到 Harbor
docker login $HARBOR_ADDR:$HARBOR_PORT -uadmin -pHarbor12345

# 拉取镜像
docker pull $imageName

# 启动容器
docker run -d -p $PROJECT_PORT:$PROJECT_PORT --name=$PROJECT_NMAE $imageName

1680075466653-bf673e46-1348-4756-a002-e0050f84cdd5.gif

注意:可以手动执行命令来测试脚本是否能成功运行:./deploy.sh 192.168.68.16 80 repo devops-java-demo 8089 v1.1.1

  • 在 Jenkins 中配置 Shell 命令来执行目标服务器上的脚本:

1680075860082-b5b2f4fa-9556-4d92-9348-40a4d3b0d931.png

  • 内容如下:
# Jenkins 容器使用的是 alpine 构建的,包管理工具是 apk
apk add sshpass 
# 登录并执行操作
sshpass -p123456 ssh -tt -q -o StrictHostKeyChecking=no root@192.168.68.17 "yum -y install dnf \
    && dnf -y update \
    && cd /root && ls -lah \
    && sh deploy.sh 192.168.68.16 80 repo ${JOB_NAME} 8089 $tag
"
  • 测试,能够 DevOps 流程是否成功:

1680075980325-28253bb7-d7a9-4cf8-80a5-3efaedfc6df7.gif

第四章:Jenkins 实现 Pipeline(流水线)

4.1 概述

  • 在前文,我们已经知道了 Jenkins 实现基本的 CI/CD 的操作流程;但是,我们却发现有如下的问题:
    • ① 我们需要在 Jenkins 中安装很多插件,才能将整个流程跑起来。
    • ② 目前,我使用的 Jenkins 中内部集成的是 JDK 11,难免有人会使用 JDK17 或更高版本,难道在 Jenkins 中再安装 JDK17 ?
    • ③ 上面使用 Jenkins 方式的移植性很差;实际开发中,难免会遇到要切换环境,难道我们又要重复这些步骤,不累吗?
    • ④ …
  • 所以,Jenkins 也提供了 Pipeline(流水线)的方式,将操作流程通过脚本固定下来,这样将来在迁移环境的时候,也很方便;当然,Jenkins 的 Pipeline 也支持内部使用 Docker ,更加方便和高效!!!

温馨提示:如果要在 Jenkins 的 pipeline 内部使用 Docker ,需要 Docker Pipeline 插件的支持!!!

4.2 Jenkins 体验 Pipeline(流水线)

  • 在 Jenkins 中新建一个 Pipeline 项目:

1680139242847-d7afd7a0-1bf3-49dc-abbd-19fb8e1143a6.gif

  • 根据界面提示,生成一个简单的 pipeline 脚本:

1680139320451-e4c8d0d3-40c0-47cf-bb8d-b4d483777fa2.gif

  • 脚本内容如下:
pipeline {
    agent any

    stages {
        stage('Hello') {
            steps {
                echo 'Hello World'
            }
        }
    }
}
  • 其实,我们完全可以让 Jenkins 给我们生成片段代码:

1680139433514-6fb8bf54-4070-401e-8c90-6c75ce8a8122.gif

  • 但是,我们一般不会将脚本代码保存在 Jenkins 的流水线项目中,而是保存到一个名为 Jenkinsfile 的文件中,并且将这个文件通过 Git 保存到 Git 仓库中:

1680143294827-519751eb-e67a-43bb-ac24-174bb25ecff7.png

  • Jenkinsfile 的通用格式如下:
pipeline {

    agent any // 任何一个代理可用,在实际开发中,可能会配置多个 node 节点。

    options { // 可选项
        timestamps() // 显示时间
        timeout(time: 1, unit: 'HOURS') // 超时时间设置为 1 小时
    }

    environment { // 环境变量
        key = 'value' // 通用格式
    }

    stages { // 表示流水线的阶段,如:拉取代码、项目编译、代码质量检测、构建镜像、推送到 Harbor 仓库、通知目标服务器执行

        stage('拉取代码') { // 表示流水线的具体阶段,如:拉取代码等

            environment { // 定义局部的环境变量
                AN_ACCESS_KEY = 'abc' 
            }

            steps {
                echo '拉取代码 -- ^_^'
            }
        }

        stage('项目编译') { // 表示流水线的具体阶段,如:拉取代码等
            steps {
                echo '项目编译 -- ^_^'
            }
        }

        stage('代码质量检测') { // 表示流水线的具体阶段,如:拉取代码等
            steps {
                echo '代码质量检测 -- ^_^'
            }
        }

        stage('构建镜像') {
            steps {
                echo '构建镜像 -- ^_^'
            }
        }

        stage('推送到 Harbor 仓库') {
            steps {
               echo '推送到 Harbor 仓库 -- ^_^'
            }
        }

        stage('通知目标服务器执行') {
            steps {
                echo '通知目标服务器执行 -- ^_^'
            }
        }
    }
}

温馨提示:在 IDEA 中是没有提示的,最好使用 vscode 打开,并且安装有 Jenkins Doc 插件。

  • 修改 Jenkins 流水线项目,改为从 Git 仓库中检测 Jenkinsfile 文件:

1680143444311-fd013b75-fdf3-4f65-a757-381be94de8e6.gif

4.3 Jenkins 编写 Pipeline

4.3.1 Pipeline 语法概述

  • 官网地址
  • 目前,都是推荐声明式流水线的写法,Jenkins 的 Pipeline 就是基于 Groovy 的,不过非常简单

1680144764404-f280e8bc-9add-4efb-b520-e666e8ecd11a.png

4.3.2 Pipeline 中进行 Java 项目构建

  • 我们可以在 Jenkinsfile 中增加 Maven 容器进行构建,Jenkinsfile 的内容如下:
pipeline {

    agent any 

    options { 
        timestamps() 
        timeout(time: 1, unit: 'HOURS') 
    }

    environment { // 环境变量
        key = 'value' 
    }

    stages { 

        stage('项目编译') { 
            // Jenkins 不配置任何环境的情况下,仅使用 Docker 兼容所有场景
            agent {
                docker {
                    image 'maven:3.9.0-eclipse-temurin-11-alpine'
                }
            }
            steps {
                echo '*** 项目编译 -- 开始 ***'
                sh "mvn clean package -Dmaven.test.skip=true"
                echo '*** 项目编译 -- 结束 ***'
            }
        }

        stage('代码质量检测') { 
            steps {
                echo '代码质量检测 -- ^_^'
            }
        }

        stage('构建镜像') {
            steps {
                echo '构建镜像 -- ^_^'
            }
        }

        stage('推送到 Harbor 仓库') {
            steps {
                echo '推送到 Harbor 仓库 -- ^_^'
            }
        }

        stage('通知目标服务器执行') {
            steps {
                echo '通知目标服务器执行 -- ^_^'
            }
        }
    }

    post { 

        always { 
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 检测 Git 环境开始 ***'
            sh 'git version'
            echo '*** 检测 Git 环境结束 ***'
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 打印环境变量开始 ***'
            sh 'printenv'
            echo '*** 打印环境变量结束 ***'
            sh 'pwd && ls -lah'
        }

        success { 
            echo "后置执行 ---> 取消后执行..."
        }

        failure { 
            echo "后置执行 ---> 取消后执行..."
        }

        aborted { 
            echo "后置执行 ---> 取消后执行..."
        }
    }
}
  • 推送代码到 Git 仓库中,对 Jenkins 中的流水线项目进行测试:

1680146553641-fe2d8dd2-af59-407b-a1a0-5fb1060a9493.gif

  • :如果你仔细查看控制台的话,你就会发现如下的信息:

1680146827990-4ffcf02c-63b7-4938-85c3-f37f5cda92f0.png

  • 原因其实很简单,因为 Jenkins 是在一个临时目录中,对项目进行了构建,而不是真实的项目目录:

1680146892671-ef1d5f9b-bdf2-4a34-ae40-82390104b6a0.png

  • 所以,需要修改 Jenkinsfile 文件,让其在真实的项目目录中去对项目进行编译:
pipeline {

    agent any 

    options { 
        timestamps() 
        timeout(time: 1, unit: 'HOURS') 
    }

    environment { // 环境变量
        PROJECT_WS = "${WORKSPACE}" // 通用格式
    }

    stages {  

        stage('项目编译') {
            // Jenkins 不配置任何环境的情况下,仅使用 Docker 兼容所有场景
            agent {
                docker {
                    image 'maven:3.9.0-eclipse-temurin-11-alpine'
                }
            }
            steps {
                echo '*** 项目编译 -- 开始 ***'
                // 下面的很重要,就是上文说的原因
                sh "cd ${PROJECT_WS} && mvn clean package -Dmaven.test.skip=true"
                echo '*** 项目编译 -- 结束 ***'
            }
        }

        stage('代码质量检测') { 
            steps {
                echo '代码质量检测 -- ^_^'
            }
        }

        stage('构建镜像') {
            steps {
                echo '构建镜像 -- ^_^'
            }
        }

        stage('推送到 Harbor 仓库') {
            steps {
                echo '推送到 Harbor 仓库 -- ^_^'
            }
        }

        stage('通知目标服务器执行') {
            steps {
                echo '通知目标服务器执行 -- ^_^'
            }
        }
    }

    post {

        always { 
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 检测 Git 环境开始 ***'
            sh 'git version'
            echo '*** 检测 Git 环境结束 ***'
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 打印环境变量开始 ***'
            sh 'printenv'
            echo '*** 打印环境变量结束 ***'
            sh 'pwd && ls -lah'
        }

        success { 
            echo "后置执行 ---> 取消后执行..."
        }

        failure { 
            echo "后置执行 ---> 取消后执行..."
        }

        aborted { 
            echo "后置执行 ---> 取消后执行..."
        }
    }
}
  • 推送代码到 Git 仓库中,对 Jenkins 中的流水线项目进行测试:

1680148069518-b9785408-b66f-4512-9fbc-a39339dbbe8c.gif

  • 此时,我们就会发现项目中出现了我们想要的东西了:

1680148116643-0350fb5b-e7fb-4dd0-b615-1df4e59133e3.png

  • 如果你多测试几次,你就会发现每次都要从 aliyun-maven 仓库中下载项目所需要的依赖,体验很差;此时,将容器内部的 Maven 仓库挂载到主机上,这样下次就不用再次去 aliyun-maven 仓库中下载项目所需要的依赖了,修改 Jenkinsfile 文件,内容如下:
pipeline {

    agent any 

    options { 
        timestamps() 
        timeout(time: 1, unit: 'HOURS')
    }

    environment { // 环境变量
        PROJECT_WS = "${WORKSPACE}" // 通用格式
    }

    stages { 

        stage('项目编译') { 
            // Jenkins 不配置任何环境的情况下,仅使用 Docker 兼容所有场景
            agent {
                docker {
                    image 'maven:3.9.0-eclipse-temurin-11-alpine'
                    args '-v $HOME/.m2:/root/.m2' // 将默认的 Maven 本地仓库挂载到宿主机上
                }
            }
            steps {
                echo '*** 项目编译 -- 开始 ***'
                sh "cd ${PROJECT_WS} && mvn clean package -Dmaven.test.skip=true"
                echo '*** 项目编译 -- 结束 ***'
            }
        }

        stage('代码质量检测') { 
            steps {
                echo '代码质量检测 -- ^_^'
            }
        }

        stage('构建镜像') {
            steps {
                echo '构建镜像 -- ^_^'
            }
        }

        stage('推送到 Harbor 仓库') {
            steps {
                echo '推送到 Harbor 仓库 -- ^_^'
            }
        }

        stage('通知目标服务器执行') {
            steps {
                echo '通知目标服务器执行 -- ^_^'
            }
        }
    }

    post { 

        always { 
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 检测 Git 环境开始 ***'
            sh 'git version'
            echo '*** 检测 Git 环境结束 ***'
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 打印环境变量开始 ***'
            sh 'printenv'
            echo '*** 打印环境变量结束 ***'
            sh 'pwd && ls -lah'
        }

        success {
            echo "后置执行 ---> 取消后执行..."
        }

        failure { 
            echo "后置执行 ---> 取消后执行..."
        }

        aborted { 
            echo "后置执行 ---> 取消后执行..."
        }
    }
}
  • 推送代码到 Git 仓库中,对 Jenkins 中的流水线项目进行测试:

1680150686763-47c7e67e-fee5-44e7-b1aa-cb960f029db8.gif

  • 从图中,我们可以得知构建速度缺失快了不少;那么,我们还需要验证下是否挂载到宿主机,这样比较安心~

1680150871841-15f805d6-27b0-4314-bded-011db82ca7f9.gif

4.3.3 Pipeline 中进行代码质量检测

  • 和上面的步骤类似,无非是使用 sonarsource/sonar-scanner-cli 来对代码进行质量检测,其 Jenkinsfile 的内容如下:
pipeline {

    agent any 

    options { 
        timestamps() 
        timeout(time: 1, unit: 'HOURS') 
    }

    environment { // 环境变量
        PROJECT_WS = "${WORKSPACE}" // 通用格式
    }

    stages { 

        stage('项目编译') { // 表示流水线的具体阶段,如:拉取代码等
            // Jenkins 不配置任何环境的情况下,仅使用 Docker 兼容所有场景
            agent {
                docker {
                    image 'maven:3.9.0-eclipse-temurin-11-alpine'
                    args '-v $HOME/.m2:/root/.m2' // 将默认的 Maven 本地仓库挂载到宿主机上
                }
            }
            steps {
                echo '*** 项目编译 -- 开始 ***'
                sh "cd ${PROJECT_WS} && mvn clean package -Dmaven.test.skip=true"
                echo '*** 项目编译 -- 结束 ***'
            }
        }

        stage('代码质量检测') { 
            agent {
                docker {
                    image 'sonarsource/sonar-scanner-cli'
                    args '-e SONAR_HOST_URL=http://192.168.1.14:9000'
                }
            }
            steps {
                echo '*** 代码质量检测 -- 开始 ***'
                sh "sonar-scanner \
                    -Dsonar.projectKey=${JOB_NAME} \
                    -Dsonar.sources=./ \
                    -Dsonar.login=admin \
                    -Dsonar.password=admin12345 \
                    -Dsonar.java.binaries=./target/"
                echo '*** 代码质量检测 -- 结束 ***'
            }
        }

        stage('构建镜像') {
            steps {
                echo '构建镜像 -- ^_^'
            }
        }

        stage('推送到 Harbor 仓库') {
            steps {
                echo '推送到 Harbor 仓库 -- ^_^'
            }
        }

        stage('通知目标服务器执行') {
            steps {
                echo '通知目标服务器执行 -- ^_^'
            }
        }
    }

    post { 

        always { 
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 检测 Git 环境开始 ***'
            sh 'git version'
            echo '*** 检测 Git 环境结束 ***'
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 打印环境变量开始 ***'
            sh 'printenv'
            echo '*** 打印环境变量结束 ***'
            sh 'pwd && ls -lah'
        }

        success { 
            echo "后置执行 ---> 取消后执行..."
        }

        failure { 
            echo "后置执行 ---> 取消后执行..."
        }

        aborted { 
            echo "后置执行 ---> 取消后执行..."
        }
    }
}
  • 推送代码到 Git 仓库中,对 Jenkins 中的流水线项目进行测试:

1680152325600-24299695-1409-4405-aa38-9110d6d24655.gif

4.3.4 Pipeline 中进行参数化构建

  • 只需要通过 Jenkins 页面帮助我们生成片段代码之后,复制到 Jenkinsfile 文件中即可:

1680153051909-5d562ea9-1a7a-4f0a-b7b6-bdd008c26b97.gif

  • Jenkinsfile 文件的内容如下:
pipeline {

    agent any // 任何一个代理可用,在实际开发中,可能会配置多个 node 节点。

    options { // 可选项
        timestamps() // 显示时间
        timeout(time: 1, unit: 'HOURS') // 超时时间设置为 1 小时
    }

    parameters { // 参数化构建
        gitParameter branch: '', branchFilter: '.*', defaultValue: 'origin/master', description: '基于 tag 进行项目构建', name: 'tag', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'GitParameterDefinition'
    }

    environment { // 环境变量
        PROJECT_WS = "${WORKSPACE}" // 通用格式
    }

    stages { // 表示流水线的阶段,如:拉取代码、项目编译、代码质量检测、构建镜像、推送到 Harbor 仓库、通知目标服务器执行

        stage('项目编译') { // 表示流水线的具体阶段,如:拉取代码等
            // Jenkins 不配置任何环境的情况下,仅使用 Docker 兼容所有场景
            agent {
                docker {
                    image 'maven:3.9.0-eclipse-temurin-11-alpine'
                    args '-v $HOME/.m2:/root/.m2' // 将默认的 Maven 本地仓库挂载到宿主机上
                }
            }
            steps {
                echo '*** 项目编译 -- 开始 ***'
                sh "cd ${PROJECT_WS} && mvn clean package -Dmaven.test.skip=true"
                echo '*** 项目编译 -- 结束 ***'
            }
        }

        stage('代码质量检测') { 
            agent {
                docker {
                    image 'sonarsource/sonar-scanner-cli'
                    args '-e SONAR_HOST_URL=http://192.168.1.14:9000'
                }
            }
            steps {
                echo '*** 代码质量检测 -- 开始 ***'
                sh "sonar-scanner \
                    -Dsonar.projectKey=${JOB_NAME} \
                    -Dsonar.sources=./ \
                    -Dsonar.login=admin \
                    -Dsonar.password=admin12345 \
                    -Dsonar.java.binaries=./target/"
                echo '*** 代码质量检测 -- 结束 ***'
            }
        }

        stage('构建镜像并推送到 Harbor 仓库中') {
            steps {
                echo "构建镜像并推送到 Harbor 仓库中"
            }
        }

        stage('通知目标服务器执行') {
            steps {
                echo '通知目标服务器执行 -- ^_^'
            }
        }
    }

    post { // 完成时动作

        always { // 总是执行,不管执行是否成功,都会执行该步骤
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 检测 Git 环境开始 ***'
            sh 'git version'
            echo '*** 检测 Git 环境结束 ***'
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 打印环境变量开始 ***'
            sh 'printenv'
            echo '*** 打印环境变量结束 ***'
            sh 'pwd && ls -lah'
        }

        success { // 后置执行成功执行,即所有阶段都执行成功,就会执行该步骤
            echo "后置执行 ---> 取消后执行..."
        }

        failure { // 后置执行失败执行,即只要有阶段失败就会执行该步骤
            echo "后置执行 ---> 取消后执行..."
        }

        aborted { // 取消后执行,用户点击取消后就会执行该步骤
            echo "后置执行 ---> 取消后执行..."
        }
    }
}
  • 推送代码到 Git 仓库中,对 Jenkins 中的流水线项目进行测试:

1680154241152-75421722-e761-4a4c-91e2-ebe63dd56c56.png

  • 你会发现怎么都不对,那是因为生成的片段代码有问题,正确的代码如下:
pipeline {

    agent any // 任何一个代理可用,在实际开发中,可能会配置多个 node 节点。

    options { // 可选项
        timestamps() // 显示时间
        timeout(time: 1, unit: 'HOURS') // 超时时间设置为 1 小时
    }

    parameters { // 参数化构建
        gitParameter(name: 'tag', type: 'PT_TAG', defaultValue: 'origin/master', description: '基于 tag 进行项目构建')
    }

    environment { // 环境变量
        PROJECT_WS = "${WORKSPACE}" // 通用格式
    }

    stages { // 表示流水线的阶段,如:拉取代码、项目编译、代码质量检测、构建镜像、推送到 Harbor 仓库、通知目标服务器执行

        stage('项目编译') { // 表示流水线的具体阶段,如:拉取代码等
            // Jenkins 不配置任何环境的情况下,仅使用 Docker 兼容所有场景
            agent {
                docker {
                    image 'maven:3.9.0-eclipse-temurin-11-alpine'
                    args '-v $HOME/.m2:/root/.m2' // 将默认的 Maven 本地仓库挂载到宿主机上
                }
            }
            steps {
                echo '*** 项目编译 -- 开始 ***'
                sh "cd ${PROJECT_WS} && mvn clean package -Dmaven.test.skip=true"
                echo '*** 项目编译 -- 结束 ***'
            }
        }

        stage('代码质量检测') {
            agent {
                docker {
                    image 'sonarsource/sonar-scanner-cli'
                    args '-e SONAR_HOST_URL=http://192.168.1.14:9000'
                }
            }
            steps {
                echo '*** 代码质量检测 -- 开始 ***'
                sh "sonar-scanner \
                    -Dsonar.projectKey=${JOB_NAME} \
                    -Dsonar.sources=./ \
                    -Dsonar.login=admin \
                    -Dsonar.password=admin12345 \
                    -Dsonar.java.binaries=./target/"
                echo '*** 代码质量检测 -- 结束 ***'
            }
        }

        stage('构建镜像并推送到 Harbor 仓库中') {
            steps {
                echo "构建镜像并推送到 Harbor 仓库中"
            }
        }

        stage('通知目标服务器执行') {
            steps {
                echo '通知目标服务器执行 -- ^_^'
            }
        }
    }

    post { // 完成时动作

        always { // 总是执行,不管执行是否成功,都会执行该步骤
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 检测 Git 环境开始 ***'
            sh 'git version'
            echo '*** 检测 Git 环境结束 ***'
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 打印环境变量开始 ***'
            sh 'printenv'
            echo '*** 打印环境变量结束 ***'
            sh 'pwd && ls -lah'
        }

        success { // 后置执行成功执行,即所有阶段都执行成功,就会执行该步骤
            echo "后置执行 ---> 取消后执行..."
        }

        failure { // 后置执行失败执行,即只要有阶段失败就会执行该步骤
            echo "后置执行 ---> 取消后执行..."
        }

        aborted { // 取消后执行,用户点击取消后就会执行该步骤
            echo "后置执行 ---> 取消后执行..."
        }
    }
}
  • 推送代码到 Git 仓库中,对 Jenkins 中的流水线项目进行测试:

1680154383217-f4ffd5d1-d486-47e9-9d7c-f77b610fe3f9.png

  • 此时,你会发现怎么还是不对?不要慌,再来几次,让 Jenkins 识别到参数化构建的内容:

1680154536034-70f5b8f0-5fc3-49ad-838c-8c547f02a51b.gif

4.3.5 Pipeline 中构建镜像并推送到 Harbor 仓库中

  • 手动通过在 shell 命令中,使用 docker build 命令来构建镜像,Jenkinsfile 的内容如下:
pipeline {

    agent any 

    options { 
        timestamps()
        timeout(time: 1, unit: 'HOURS') 
    }

    parameters { 
        gitParameter(name: 'tag', type: 'PT_TAG', defaultValue: 'origin/master', description: '基于 tag 进行项目构建')
    }

    environment { 
        PROJECT_WS = "${WORKSPACE}" 
    }

    stages { 

        stage('项目编译') { // 表示流水线的具体阶段,如:拉取代码等
            // Jenkins 不配置任何环境的情况下,仅使用 Docker 兼容所有场景
            agent {
                docker {
                    image 'maven:3.9.0-eclipse-temurin-11-alpine'
                    args '-v $HOME/.m2:/root/.m2' // 将默认的 Maven 本地仓库挂载到宿主机上
                }
            }
            steps {
                echo '*** 项目编译 -- 开始 ***'
                sh "cd ${PROJECT_WS} && mvn clean package -Dmaven.test.skip=true"
                echo '*** 项目编译 -- 结束 ***'
            }
        }

        stage('代码质量检测') {
            agent {
                docker {
                    image 'sonarsource/sonar-scanner-cli'
                    args '-e SONAR_HOST_URL=http://192.168.1.14:9000'
                }
            }
            steps {
                echo '*** 代码质量检测 -- 开始 ***'
                sh "sonar-scanner \
                    -Dsonar.projectKey=${JOB_NAME} \
                    -Dsonar.sources=./ \
                    -Dsonar.login=admin \
                    -Dsonar.password=admin12345 \
                    -Dsonar.java.binaries=./target/"
                echo '*** 代码质量检测 -- 结束 ***'
            }
        }

        stage('构建镜像并推送到 Harbor 仓库中') {
            steps {
                // docker 构建镜像
                sh "docker build -t ${JOB_NAME}:$tag ."
                // docker 登录到 Harbor
                sh "docker login 192.168.68.16:80 -uadmin -pHarbor12345"
                // 将镜像重新打标签
                sh "docker tag ${JOB_NAME}:$tag 192.168.68.16:80/repo/${JOB_NAME}:$tag"
                // 推送镜像到 Harbor 仓库
                sh "docker push 192.168.68.16:80/repo/${JOB_NAME}:$tag"
            }
        }

        stage('通知目标服务器执行') {
            steps {
                echo '通知目标服务器执行 -- ^_^'
            }
        }
    }

    post { // 完成时动作

        always { // 总是执行,不管执行是否成功,都会执行该步骤
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 检测 Git 环境开始 ***'
            sh 'git version'
            echo '*** 检测 Git 环境结束 ***'
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 打印环境变量开始 ***'
            sh 'printenv'
            echo '*** 打印环境变量结束 ***'
            sh 'pwd && ls -lah'
        }

        success { // 后置执行成功执行,即所有阶段都执行成功,就会执行该步骤
            echo "后置执行 ---> 取消后执行..."
        }

        failure { // 后置执行失败执行,即只要有阶段失败就会执行该步骤
            echo "后置执行 ---> 取消后执行..."
        }

        aborted { // 取消后执行,用户点击取消后就会执行该步骤
            echo "后置执行 ---> 取消后执行..."
        }
    }
}
  • 推送代码到 Git 仓库中,对 Jenkins 中的流水线项目进行测试:

1680154990111-e3b406d8-b233-4273-a899-e7db6d8f6c29.gif

4.3.6 Pipeline 中通知目标服务器

  • 先在目标服务器创建 deploy.sh 文件,并进行授权:
vim deploy.sh
HARBOR_ADDR=$1 # Harbor 的 IP 地址
HARBOR_PORT=$2 # Harbor 的端口
HARBOR_repo=$3 # Harbor 的仓库地址
PROJECT_NMAE=$4 # 项目名
PROJECT_PORT=$5 # 项目的端口
TAG=$6 # 版本

imageName=$HARBOR_ADDR:$HARBOR_PORT/$HARBOR_repo/$PROJECT_NMAE:$TAG
echo "镜像名称是:$imageName"

containerId=`docker ps -a | grep "${PROJECT_NMAE}" | awk '{print $1}'`
echo "容器名称是:${containerId}"

# 停止并删除已经存在的容器
if [ "$containerId" != "" ]; then
    docker stop $containerId
    docker rm -f $containerId
fi

tag=`docker images | grep "${PROJECT_NMAE}" | awk '{print $2}'`
echo "标签的名称是:${tag}"

# 删除已经存在的镜像
if [[ "$tag" =~ "$TAG" ]] ;then
    docker rmi -f $imageName
    docker system prune -af
fi

# 登录到 Harbor
docker login $HARBOR_ADDR:$HARBOR_PORT -uadmin -pHarbor12345

# 拉取镜像
docker pull $imageName

# 启动容器
docker run -d -p $PROJECT_PORT:$PROJECT_PORT --name=$PROJECT_NMAE $imageName
chmod +x deploy.sh

1680156225427-bb4beab5-b825-4158-a16b-09560a9949f7.gif

  • 在 Jenkinsfile 中通过 shell 命令通知目标服务器,内容如下:
pipeline {

    agent any // 任何一个代理可用,在实际开发中,可能会配置多个 node 节点。

    options { // 可选项
        timestamps() // 显示时间
        timeout(time: 1, unit: 'HOURS') // 超时时间设置为 1 小时
    }

    parameters { // 参数化构建
        gitParameter(name: 'tag', type: 'PT_TAG', defaultValue: 'origin/master', description: '基于 tag 进行项目构建')
    }

    environment { // 环境变量
        PROJECT_WS = "${WORKSPACE}" // 通用格式
    }

    stages { // 表示流水线的阶段,如:拉取代码、项目编译、代码质量检测、构建镜像、推送到 Harbor 仓库、通知目标服务器执行

        stage('项目编译') { // 表示流水线的具体阶段,如:拉取代码等
            // Jenkins 不配置任何环境的情况下,仅使用 Docker 兼容所有场景
            agent {
                docker {
                    image 'maven:3.9.0-eclipse-temurin-11-alpine'
                    args '-v $HOME/.m2:/root/.m2' // 将默认的 Maven 本地仓库挂载到宿主机上
                }
            }
            steps {
                echo '*** 项目编译 -- 开始 ***'
                sh "cd ${PROJECT_WS} && mvn clean package -Dmaven.test.skip=true"
                echo '*** 项目编译 -- 结束 ***'
            }
        }

        stage('代码质量检测') {
            agent {
                docker {
                    image 'sonarsource/sonar-scanner-cli'
                    args '-e SONAR_HOST_URL=http://192.168.1.14:9000'
                }
            }
            steps {
                echo '*** 代码质量检测 -- 开始 ***'
                sh "sonar-scanner \
                    -Dsonar.projectKey=${JOB_NAME} \
                    -Dsonar.sources=./ \
                    -Dsonar.login=admin \
                    -Dsonar.password=admin12345 \
                    -Dsonar.java.binaries=./target/"
                echo '*** 代码质量检测 -- 结束 ***'
            }
        }

        stage('构建镜像并推送到 Harbor 仓库中') {
            steps {
                // docker 构建镜像
                sh "docker build -t ${JOB_NAME}:$tag ."
                // docker 登录到 Harbor
                sh "docker login 192.168.68.16:80 -uadmin -pHarbor12345"
                // 将镜像重新打标签
                sh "docker tag ${JOB_NAME}:$tag 192.168.68.16:80/repo/${JOB_NAME}:$tag"
                // 推送镜像到 Harbor 仓库
                sh "docker push 192.168.68.16:80/repo/${JOB_NAME}:$tag"
            }
        }

        stage('通知目标服务器执行') {
            agent {
                docker {
                    image 'centos:7'
                }
            }
            steps {
                sh 'yum -y install sshpass && yum -y install openssh-clients'
                // 登录并执行操作
                sh 'sshpass -p123456 ssh -q -o StrictHostKeyChecking=no root@192.168.68.17 "yum -y install dnf && dnf -y update" '
                 sh 'sshpass -p123456 ssh -q -o StrictHostKeyChecking=no root@192.168.68.17 "cd /root && sh /root/deploy.sh 192.168.68.16 80 repo ${JOB_NAME} 8089 $tag"'
            }
        }
    }

    post { // 完成时动作

        always { // 总是执行,不管执行是否成功,都会执行该步骤
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 检测 Git 环境开始 ***'
            sh 'git version'
            echo '*** 检测 Git 环境结束 ***'
            echo '*** 检测 Docker 环境开始 ***'
            sh 'docker version'
            echo '*** 检测 Docker 环境结束 ***'
            echo '*** 打印环境变量开始 ***'
            sh 'printenv'
            echo '*** 打印环境变量结束 ***'
            sh 'pwd && ls -lah'
        }

        success { // 后置执行成功执行,即所有阶段都执行成功,就会执行该步骤
            echo "后置执行 ---> 取消后执行..."
        }

        failure { // 后置执行失败执行,即只要有阶段失败就会执行该步骤
            echo "后置执行 ---> 取消后执行..."
        }

        aborted { // 取消后执行,用户点击取消后就会执行该步骤
            echo "后置执行 ---> 取消后执行..."
        }
    }
}
  • 推送代码到 Git 仓库中,对 Jenkins 中的流水线项目进行测试:

1680159321689-598b99b6-2c92-4120-9dff-aa2402b565bf.gif

更新: 2023-03-31 01:02:07
原文: https://www.yuque.com/fairy-era/yg511q/bkac1v

Comments