第一章:DevOps 概念
1.1 DevOps 是什么?
- DevOps 是 Development 和 Operations 的组合。
- DevOps 可以看作是开发(软件工程)、技术运营和质量保障(QA)的交集。
- DevOps 突出重视软件开发人员和运维人员的沟通合作,通过自动化流程来使得软件构建、测试、发布更加快捷、频繁和可靠。
- DevOps 希望做到的是软件产品交付过程中
IT 工具链的打通
,使得各个团队减少时间损耗,更加高效的协同工作。
1.2 CI 、CD 是什么?
1.2.1 基本理念
持续集成(Continuous Integration)
:持续集成是指软件个人研发的部分向软件整体部分交付,频繁进行集成以便更快、更早的发现其中的错误。持续集成源自于极限编程(XP),是 XP 最初的 12 种实践之一。持续集成需要具备
:- ①
全面的自动化测试
:这是实践持续集成 && 持续部署的基础,与此同时,选择合适的自动化测试工具也极为重要。 - ②
灵活的基础设施
:容器、虚拟机
的存在让开发人员和 QA 人员不必再大费周折。 - ③
版本工具工具
:如 Git、SVN等。 - ④
自动化的构建和软件发布流程工具
:如 Jenkins 等。 - ⑤
反馈机制
:构建、测试失败的时候,可以快速的反馈到相关负责人,以便尽快的解决问题,使得产品能更早的达到稳定的版本。
- ①
持续交付(Continuous Delivery)
:持续交付在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境
的『类生成环境
』中。持续交付优先于整个产品生命周期的软件部署,建立在高水平自动化持续集成之上。持续交付的优点和持续集成非常类似:- ①
快速发布
:能够应对业务需求,更快的实现软件价值。 - ②
编码、测试、上线、交付
的频繁迭代周期缩短,同时获得迅速反馈。 - ③
高质量的软件发布标准
:整个交付过程标准化、可重复、可靠。 - ④
整个交付过程进度可视化
:方便团队人员了解项目成熟度。 - ⑤
更先进的团队协作方式
:从需求分析、产品的用户体验到交互设计、开发、测试、运维等角色的密切协作,相比于传统的瀑布式软件团队,更少浪费。
- ①
持续部署(Continuous Deployment)
:持续部署是指当交付的代码通过评审之后,自动部署到生产环境中。持续部署是持续交付的最高阶段。这意味着,所有通过了一系列的自动化测试的改动都将自动部署到生产环境。
开发人员提交代码,持续集成服务器获取代码,执行单元测试,根据测试结果决定是否部署到预演环境,如果成功部署到预演环境,进行整体验收测试,如果测试通过,自动部署到生产环境。整个过程自动化高效运转。
1.2.2 最佳实践
1.2.2.1 内循环和外循环
- 内循环(开发):编码、测试、运行、debug、提交。
- 代码推送到代码仓库(Git、SVN)。
- 进行 CI 流程(持续集成):万物皆可容器化,所有应用打包成 Docker 镜像(符合 OCI 镜像规范即可)。
- 镜像推送到镜像仓库。
- 测试。
- 进行 CD 流程(持续部署):将应用部署到环境(测试、预发布、生产……)。
- 外循环:
- 运行时监控。
- 生产环境的管理。
- 监控。
- 线上反馈到开发。
- 来到内循环。
1.2.2.2 实践流程
- ① 创建分支开发功能。
- ② 提交分支的代码。
- ③ 进入持续集成流程:
- 当前分支代码功能性自动化构建和测试。
- 自动化工具推送这次提交。
- 自动化集成测试。
- 人工确认此次功能是否发布到生产环境。
- ④ 代码合并。
- ⑤ 进入持续部署流程:
- 构建。
- 测试。
- 发布。
- ……
第二章:Jenkins
2.1 Jenkis 简介
- Jenkins 是开源 CI&CD 软件领导者, 提供超过 1000 个插件来支持构建、部署、自动化, 满足任何项目的需要。
2.2 准备工作
- 需要准备一个云服务器(阿里云、腾讯云、青云等),因为没有使用自建 gitlab 的形式来演示,而是使用 gitee 。
2.3 Docker 安装 Jenkins
- 命令:
# 端口是 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 \
--restart=always \
jenkinsci/blueocean
- 初始密码在日志文件中可以找到,本次是 b219e31bceac42b6a1c7ab4fde14cf97 ,当然,也可以通过具名卷查找。
- 浏览器访问(端口是 8080):
2.5 安装插件(必须)
- 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 API、Gitee:Gitee 插件。
2.4 准备一个 git 项目进行测试
- 以 gitee 为例,因为 github 实现是太慢了。
- 步骤:
- ① IDEA 创建 SpringBoot 项目。
- ② VCS — 创建 git 仓库。
- ③ gitee 创建一个空的公共仓库(略)。
- ④ IDEA 提交内容到 gitee 。
- ⑤ 开发项目基本功能,并在项目中创建一个名为 Jenkinsfile 的文件。
- ⑥ 创建一个名为 devops-java-demo 的流水线项目,使用该项目自己的流水线。
2.5 Jenkis 关联 Gitee
2.5.1 Gitee 申请 Gitee API V5 的私人令牌
2.5.2 Jenkins 配置 Gitee API 令牌
2.5.3 Jenkins 关联 Gitee
2.6 Jenkis 新建 Pipeline(流水线)项目
Gitee webhook 中填写 http://139.198.179.119:8080/gitee-project/devops-java-demo。
- 根据提示,在 gitee 项目中配置 webhook :
2.7 在项目中新建配置文件
- 配置文件:
- Dockerfile:用来构建 Docker 镜像。
- settings.xml:用来加速 Maven 依赖的构建。
- Jenkinsfile:定义了 Jenkins 流水线。
- 其中,Dockerfile 的内容如下:
FROM java:8
LABEL maintainer="xxxx@qq.com"
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" ]
- settings.xml 的内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<!--
| This is the configuration file for Maven. It can be specified at two levels:
|
| 1. User Level. This settings.xml file provides configuration for a single user,
| and is normally provided in ${user.home}/.m2/settings.xml.
|
| NOTE: This location can be overridden with the CLI option:
|
| -s /path/to/user/settings.xml
|
| 2. Global Level. This settings.xml file provides configuration for all Maven
| users on a machine (assuming they're all using the same Maven
| installation). It's normally provided in
| ${maven.conf}/settings.xml.
|
| NOTE: This location can be overridden with the CLI option:
|
| -gs /path/to/global/settings.xml
|
| The sections in this sample file are intended to give you a running start at
| getting the most out of your Maven installation. Where appropriate, the default
| values (values used when the setting is not specified) are provided.
|
|-->
<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>/root/.m2</localRepository>
<pluginGroups>
</pluginGroups>
<proxies>
</proxies>
<servers>
</servers>
<mirrors>
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
<profiles>
</profiles>
</settings>
- Jenkinsfile 的文件的基本格式如下:
pipeline {
agent any
environment {
CC = 'clang'
}
stages {
stage('Example') {
steps {
sh 'printenv'
sh 'echo $CC'
}
}
}
}
2.8 如何编写 Jenkinsfile 文件
2.8.1 Pipeline 语法概述
- 官网地址。
- 目前,都是推荐声明式流水线的写法,Jenkins 的 Pipeline 就是基于 Groovy 的,不过非常简单。
2.8.2 stages
stages:定义了流水线的阶段,如:Maven 项目中 clean 、test、package、install 等阶段,在 Jenkins 中称为阶段。
示例:
// 流水线的脚本(声明式和脚本式,常用的是声明式)
pipeline {
// 全部的 CI、CD 流程都需要在这里定义
// 任何一个代理可用,就能执行(多机器)。但是,目前是单机,所以只会在本地执行。固定写法。
agent any
// 定义一些环境信息
environment {
// 需要注意的时候,这里不允许为空,其实 Jenkins 的每个步骤都不允许为空,如果此时没有需要定义的环境信息,那么就将 environment 删除即可,下面的 steps 类似
PROJECT_WS = "${WORKSPACE}"
}
// 定义流水线的加工流程
stages { // 流水线的阶段
// 环境检查
stage('环境检查') {
steps { // 步骤:要做的所有事情
echo "环境检查..."
sh 'printenv' // 打印环境变量
echo "正在检测基本信息"
sh 'java -version'
sh 'git --version'
sh 'docker version'
sh 'pwd && ls -lah'
}
}
// 编译
stage('Maven 编译') {
steps { // 步骤:要做的所有事情
echo "编译..."
sh 'pwd && ls -lah'
}
}
// 测试,每一个 stage 的开始,都会重置到默认的 workspace 的位置。
stage('单元测试') { // 步骤
steps {
echo "单元测试..."
sh 'pwd && ls -lah'
}
}
// 生成镜像
stage('生成镜像') {
steps { // 步骤
echo "生成镜像..."
}
}
// 推送镜像
stage('推送镜像') {
steps { // 步骤
echo "推送镜像..."
}
}
// 部署
stage('部署') {
steps { // 步骤
echo "部署..."
}
}
}
post { // 后置执行,上面的阶段都执行完毕之后,就会执行此过程
always { // 总是执行,比如:发送构建报告等。
echo "后置执行 ---> always..."
}
failure { // 错误的时候执行
echo "后置执行 ---> 失败..."
}
success { // 成功的时候执行
echo "后置执行 ---> 成功..."
}
}
}
坑
:此时你会发现,好像还是有权限问题,那么因为 Jenkins 的安全设置。
2.8.3 Java 项目需要进行编译,那么应该怎么办?
- 在流水线中使用 Docker (坑:如果不装 Docker Pipeline 插件,那么一定会出错):
- 示例:
// 流水线的脚本(声明式和脚本式,常用的是声明式)
pipeline {
// 全部的 CI、CD 流程都需要在这里定义
// 任何一个代理可用,就能执行(多机器)。但是,目前是单机,所以只会在本地执行。固定写法。
agent any
// 定义一些环境信息
environment {
// 需要注意的时候,这里不允许为空,其实 Jenkins 的每个步骤都不允许为空,如果此时没有需要定义的环境信息,那么就将 environment 删除即可,下面的 steps 类似
PROJECT_WS = "${WORKSPACE}"
}
// 定义流水线的加工流程
stages { // 流水线的阶段
// 环境检查
stage('环境检查') {
steps { // 步骤:要做的所有事情
echo "环境检查..."
sh 'printenv' // 打印环境变量
echo "正在检测基本信息"
sh 'java -version'
sh 'git --version'
sh 'docker version'
sh 'pwd && ls -lah'
}
}
// 编译
stage('Maven 编译') {
// Jenkins 不配置任何环境的情况下,仅使用 Docker 兼容所有场景
agent {
docker {
image 'maven:3.6.0-alpine'
}
}
steps { // 步骤:要做的所有事情
echo "编译..."
sh 'pwd && ls -lah'
sh "mvn clean package -Dmaven.test.skip=true"
sh 'pwd && ls -lah'
}
}
// 测试,每一个 stage 的开始,都会重置到默认的 workspace 的位置。
stage('单元测试') { // 步骤
steps {
echo "单元测试..."
sh 'pwd && ls -lah'
}
}
// 生成镜像
stage('生成镜像') {
steps { // 步骤
echo "生成镜像..."
}
}
// 推送镜像
stage('推送镜像') {
steps { // 步骤
echo "推送镜像..."
}
}
// 部署
stage('部署') {
steps { // 步骤
echo "部署..."
}
}
}
post { // 后置执行,上面的阶段都执行完毕之后,就会执行此过程
always { // 总是执行,比如:发送构建报告等。
echo "后置执行 ---> always..."
}
failure { // 错误的时候执行
echo "后置执行 ---> 失败..."
}
success { // 成功的时候执行
echo "后置执行 ---> 成功..."
}
}
}
- 但是,如果你多构建几次,你就会发现老是从阿里云镜像(Maven 的 pom.xml 中我配置了阿里云镜像)中拉取 jar 包,真烦?第一次从阿里云拉取就算了,第二次应该直接使用本地的,怎么办?
// 流水线的脚本(声明式和脚本式,常用的是声明式)
pipeline {
// 全部的 CI、CD 流程都需要在这里定义
// 任何一个代理可用,就能执行(多机器)。但是,目前是单机,所以只会在本地执行。固定写法。
agent any
// 定义一些环境信息
environment {
PROJECT_WS = "${WORKSPACE}"
}
// 定义流水线的加工流程
stages { // 流水线的阶段
// 环境检查
stage('环境检查') {
steps { // 步骤:要做的所有事情
echo "环境检查..."
sh 'printenv' // 打印环境变量
echo "正在检测基本信息"
sh 'java -version'
sh 'git --version'
sh 'docker version'
sh 'pwd && ls -lah'
}
}
// 编译
stage('Maven 编译') {
// Jenkins 不配置任何环境的情况下,仅使用 Docker 兼容所有场景
agent {
docker {
image 'maven:3.6.0-alpine'
args '-v $HOME/.m2:/root/.m2' // 将 Maven 下载的依赖挂载到主机,用于解决 Maven 下一次还需要重新下载的问题。
}
}
steps { // 步骤:要做的所有事情
echo "编译..."
sh 'pwd && ls -lah'
script {
if (!fileExists("/var/jenkins_home/workspace/appconfig/maven/settings.xml")){
sh "mkdir -pv /var/jenkins_home/workspace/appconfig/maven/"
sh "cp appconfig/maven/settings.xml /var/jenkins_home/workspace/appconfig/maven/settings.xml"
}
}
sh "mvn clean package -s /var/jenkins_home/workspace/appconfig/maven/settings.xml -Dmaven.test.skip=true"
sh 'pwd && ls -lah'
}
}
// 测试,每一个 stage 的开始,都会重置到默认的 workspace 的位置。
stage('单元测试') { // 步骤
steps {
echo "单元测试..."
sh 'pwd && ls -lah'
}
}
// 生成镜像
stage('生成镜像') {
steps { // 步骤
echo "生成镜像..."
}
}
// 推送镜像
stage('推送镜像') {
steps { // 步骤
echo "推送镜像..."
}
}
// 部署
stage('部署') {
steps { // 步骤
echo "部署..."
}
}
}
post { // 后置执行,上面的阶段都执行完毕之后,就会执行此过程
always { // 总是执行,比如:发送构建报告等。
echo "后置执行 ---> always..."
}
failure { // 错误的时候执行
echo "后置执行 ---> 失败..."
}
success { // 成功的时候执行
echo "后置执行 ---> 成功..."
}
}
}
2.8.4 生成镜像
直接在生成镜像的阶段中,使用
docker build
命令构建镜像即可,但是有坑,因为 Jenkins 在每一个阶段都是在临时容器中进行操作的, 当我们在Maven 编译
阶段中将源码打包之后,形成的 target 目录,到了下一个阶段并不会复制进去,那么怎么办?在Maven 编译
阶段中就在/var/jenkins_home/workspace/项目/
中对源码进行编译,这个目录的地址 Jenkins 已经给出了,就是WORKSPACE
环境变量。示例:
// 流水线的脚本(声明式和脚本式,常用的是声明式)
pipeline {
// 全部的 CI、CD 流程都需要在这里定义
// 任何一个代理可用,就能执行(多机器)。但是,目前是单机,所以只会在本地执行。固定写法。
agent any
// 定义一些环境信息
environment {
PROJECT_WS = "${WORKSPACE}"
}
// 定义流水线的加工流程
stages { // 流水线的阶段
// 环境检查
stage('环境检查') {
steps { // 步骤:要做的所有事情
echo "环境检查..."
sh 'printenv' // 打印环境变量
echo "正在检测基本信息"
sh 'java -version'
sh 'git --version'
sh 'docker version'
sh 'pwd && ls -lah'
}
}
// 编译
stage('Maven 编译') {
// Jenkins 不配置任何环境的情况下,仅使用 Docker 兼容所有场景
agent {
docker {
image 'maven:3.6.0-alpine'
args '-v $HOME/.m2:/root/.m2' // 将 Maven 下载的依赖挂载到主机,用于解决 Maven 下一次还需要重新下载的问题。
}
}
steps { // 步骤:要做的所有事情
echo "编译..."
sh 'pwd && ls -lah'
script {
if (!fileExists("/var/jenkins_home/workspace/appconfig/maven/settings.xml")){
sh "mkdir -pv /var/jenkins_home/workspace/appconfig/maven/"
sh "cp appconfig/maven/settings.xml /var/jenkins_home/workspace/appconfig/maven/settings.xml"
}
}
// 之所以 cd ${PROJECT_WS} 是因为在构建是在临时容器中生成的,一旦构建完成之后容器将会销毁,而 target 目录也将丢失。
sh "cd ${PROJECT_WS} && mvn clean package -s /var/jenkins_home/workspace/appconfig/maven/settings.xml -Dmaven.test.skip=true"
sh 'pwd && ls -lah'
}
}
// 测试,每一个 stage 的开始,都会重置到默认的 workspace 的位置。
stage('单元测试') { // 步骤
steps {
echo "单元测试..."
sh 'pwd && ls -lah'
}
}
// 生成镜像
stage('生成镜像') {
steps { // 步骤
echo "生成镜像..."
// 检查 Jenkins 的 Docker 命令是否能执行
sh 'docker version'
sh 'docker build -t devops-java-demo .'
}
}
// 推送镜像
stage('推送镜像') {
steps { // 步骤
echo "推送镜像..."
}
}
// 部署
stage('部署') {
steps { // 步骤
echo "部署..."
}
}
}
post { // 后置执行,上面的阶段都执行完毕之后,就会执行此过程
always { // 总是执行,比如:发送构建报告等。
echo "后置执行 ---> always..."
}
failure { // 错误的时候执行
echo "后置执行 ---> 失败..."
}
success { // 成功的时候执行
echo "后置执行 ---> 成功..."
}
}
}
2.8.5 推送镜像
在推送镜像的阶段中,我们可以询问开发者是否确认将镜像推送到阿里云镜像仓库中,以及将镜像推送到那个区域(当然,实际中一般就一个镜像仓库,而且也不会存放到阿里云镜像仓库(需要将仓库的用户名和密码配置到 Jenkins 中,不再演示)中,会搭建自己的私有仓库,如:Harbor 等),可以使用 input 来实现这类功能。
示例:
// 流水线的脚本(声明式和脚本式,常用的是声明式)
pipeline {
// 全部的 CI、CD 流程都需要在这里定义
// 任何一个代理可用,就能执行(多机器)。但是,目前是单机,所以只会在本地执行。固定写法。
agent any
// 定义一些环境信息
environment {
PROJECT_WS = "${WORKSPACE}"
}
// 定义流水线的加工流程
stages { // 流水线的阶段
// 环境检查
stage('环境检查') {
steps { // 步骤:要做的所有事情
echo "环境检查..."
sh 'printenv' // 打印环境变量
echo "正在检测基本信息"
sh 'java -version'
sh 'git --version'
sh 'docker version'
sh 'pwd && ls -lah'
}
}
// 编译
stage('Maven 编译') {
// Jenkins 不配置任何环境的情况下,仅使用 Docker 兼容所有场景
agent {
docker {
image 'maven:3.6.0-alpine'
args '-v $HOME/.m2:/root/.m2' // 将 Maven 下载的依赖挂载到主机,用于解决 Maven 下一次还需要重新下载的问题。
}
}
steps { // 步骤:要做的所有事情
echo "编译..."
sh 'pwd && ls -lah'
script {
if (!fileExists("/var/jenkins_home/workspace/appconfig/maven/settings.xml")){
sh "mkdir -pv /var/jenkins_home/workspace/appconfig/maven/"
sh "cp appconfig/maven/settings.xml /var/jenkins_home/workspace/appconfig/maven/settings.xml"
}
}
// 之所以 cd ${PROJECT_WS} 是因为在构建是在临时容器中生成的,一旦构建完成之后容器将会销毁,而 target 目录也将丢失。
sh "cd ${PROJECT_WS} && mvn clean package -s /var/jenkins_home/workspace/appconfig/maven/settings.xml -Dmaven.test.skip=true"
sh 'pwd && ls -lah'
}
}
// 测试,每一个 stage 的开始,都会重置到默认的 workspace 的位置。
stage('单元测试') { // 步骤
steps {
echo "单元测试..."
sh 'pwd && ls -lah'
}
}
// 生成镜像
stage('生成镜像') {
steps { // 步骤
echo "生成镜像..."
// 检查 Jenkins 的 Docker 命令是否能执行
sh 'docker version'
sh 'docker build -t devops-java-demo .'
}
}
// 推送镜像
stage('推送镜像') {
input {
message "需要推送镜像到阿里云仓库吗?"
ok "是的"
parameters {
// 手动输入的参数
string(name: 'APP_VERSION', defaultValue: 'v1', description: '请指定生产版本号')
choice choices: ['bj-01','bj-02','sh-03','js-05'], description: '部署的区域', name: 'DEPLOY_AREA'
}
}
steps { // 步骤
echo "推送镜像..."
// 将镜像推送到阿里云仓库中
script {
def area = "${DEPLOY_AREA}"
if (area.contains('sh')) {
echo '推送镜像到到上海区'
withCredentials([usernamePassword(credentialsId: 'aliyun-docker', passwordVariable: 'PWD', usernameVariable: 'USER')]) {
sh "docker login --username=${USER} --password=${PWD} registry.cn-shanghai.aliyuncs.com"
sh "docker tag devops-java-demo registry.cn-shanghai.aliyuncs.com/xudaxian/devops-java-demo:${APP_VERSION}"
sh "docker push registry.cn-shanghai.aliyuncs.com/xudaxian/devops-java-demo:${APP_VERSION}"
}
} else if (area.contains('bj')) {
echo '推送镜像到北京区'
} else if (area.contains('js')) {
echo '推送镜像到江苏区'
} else {
echo '抱歉,暂无此区域。'
}
}
}
}
// 部署
stage('部署') {
steps { // 步骤
echo "部署..."
}
}
}
post { // 后置执行,上面的阶段都执行完毕之后,就会执行此过程
always { // 总是执行,比如:发送构建报告等。
echo "后置执行 ---> always..."
}
failure { // 错误的时候执行
echo "后置执行 ---> 失败..."
}
success { // 成功的时候执行
echo "后置执行 ---> 成功..."
}
}
}
2.8.6 启动容器
- 在实际开发中,将镜像推送到私有仓库中之后,就从私有仓库中将镜像拉取到指定的机器中,进行部署,你会觉得多此一举,其实未必?
当然,本次就不这么麻烦,直接在一台机器上执行这些操作。
示例:
// 流水线的脚本(声明式和脚本式,常用的是声明式)
pipeline {
// 全部的 CI、CD 流程都需要在这里定义
// 任何一个代理可用,就能执行(多机器)。但是,目前是单机,所以只会在本地执行。固定写法。
agent any
// 定义一些环境信息
environment {
PROJECT_WS = "${WORKSPACE}"
}
// 定义流水线的加工流程
stages { // 流水线的阶段
// 环境检查
stage('环境检查') {
steps { // 步骤:要做的所有事情
echo "环境检查..."
sh 'printenv' // 打印环境变量
echo "正在检测基本信息"
sh 'java -version'
sh 'git --version'
sh 'docker version'
sh 'pwd && ls -lah'
}
}
// 编译
stage('Maven 编译') {
// Jenkins 不配置任何环境的情况下,仅使用 Docker 兼容所有场景
agent {
docker {
image 'maven:3.6.0-alpine'
args '-v $HOME/.m2:/root/.m2' // 将 Maven 下载的依赖挂载到主机,用于解决 Maven 下一次还需要重新下载的问题。
}
}
steps { // 步骤:要做的所有事情
echo "编译..."
sh 'pwd && ls -lah'
script {
if (!fileExists("/var/jenkins_home/workspace/appconfig/maven/settings.xml")){
sh "mkdir -pv /var/jenkins_home/workspace/appconfig/maven/"
sh "cp appconfig/maven/settings.xml /var/jenkins_home/workspace/appconfig/maven/settings.xml"
}
}
// 之所以 cd ${PROJECT_WS} 是因为在构建是在临时容器中生成的,一旦构建完成之后容器将会销毁,而 target 目录也将丢失。
sh "cd ${PROJECT_WS} && mvn clean package -s /var/jenkins_home/workspace/appconfig/maven/settings.xml -Dmaven.test.skip=true"
sh 'pwd && ls -lah'
}
}
// 测试,每一个 stage 的开始,都会重置到默认的 workspace 的位置。
stage('单元测试') { // 步骤
steps {
echo "单元测试..."
sh 'pwd && ls -lah'
}
}
// 生成镜像
stage('生成镜像') {
steps { // 步骤
echo "生成镜像..."
// 检查 Jenkins 的 Docker 命令是否能执行
sh 'docker version'
sh 'docker build -t devops-java-demo .'
}
}
// 推送镜像
stage('推送镜像') {
input {
message "需要推送镜像到阿里云仓库吗?"
ok "是的"
parameters {
// 手动输入的参数
string(name: 'APP_VERSION', defaultValue: 'v1', description: '请指定生产版本号')
choice choices: ['bj-01','bj-02','sh-03','js-05'], description: '部署的区域', name: 'DEPLOY_AREA'
}
}
steps { // 步骤
echo "推送镜像..."
// 将镜像推送到阿里云仓库中
script {
def area = "${DEPLOY_AREA}"
if (area.contains('sh')) {
echo '推送镜像到到上海区'
withCredentials([usernamePassword(credentialsId: 'aliyun-docker', passwordVariable: 'PWD', usernameVariable: 'USER')]) {
sh "docker login --username=${USER} --password=${PWD} registry.cn-shanghai.aliyuncs.com"
sh "docker tag devops-java-demo registry.cn-shanghai.aliyuncs.com/xudaxian/devops-java-demo:${APP_VERSION}"
sh "docker push registry.cn-shanghai.aliyuncs.com/xudaxian/devops-java-demo:${APP_VERSION}"
}
} else if (area.contains('bj')) {
echo '推送镜像到北京区'
} else if (area.contains('js')) {
echo '推送镜像到江苏区'
} else {
echo '抱歉,暂无此区域。'
}
}
}
}
// 启动容器
stage('启动容器') {
steps { // 步骤
echo "启动容器..."
sh 'docker rm -f devops-java-demo'
sh 'docker run -d -p 8888:8080 --name=devops-java-demo devops-java-demo'
}
// post { // 后置执行
// failure {
// echo "启动容器失败..."
// }
// success {
// echo "启动容器成功..."
// }
// }
}
}
post { // 后置执行,上面的阶段都执行完毕之后,就会执行此过程
always { // 总是执行,比如:发送构建报告等。
echo "后置执行 ---> always..."
}
failure { // 错误的时候执行
echo "后置执行 ---> 失败..."
}
success { // 成功的时候执行
echo "后置执行 ---> 成功..."
}
}
}
2.8.7 构建报告
- 构建报告,一般都需要通过 email 发送给开发人员,需要在 Jenkins 中配置 email 。
- ① 在 Jenkis 中配置 email 的 凭证信息:
- ② 在 Jenkins 中配置 QQ 邮箱:
- ③ 在 Jenkinsfile 中设置推送报告阶段。
// 流水线的脚本(声明式和脚本式,常用的是声明式)
pipeline {
// 全部的 CI、CD 流程都需要在这里定义
// 任何一个代理可用,就能执行(多机器)。但是,目前是单机,所以只会在本地执行。固定写法。
agent any
// 定义一些环境信息
environment {
PROJECT_WS = "${WORKSPACE}"
// 引用 Jenkins 配置的全局凭证信息
ALIYUN_DOCKER_SECURITY = credentials('aliyun-docker')
}
// 定义流水线的加工流程
stages { // 流水线的阶段
// 环境检查
stage('环境检查') {
steps { // 步骤:要做的所有事情
echo "环境检查..."
sh 'printenv' // 打印环境变量
echo "正在检测基本信息"
sh 'java -version'
sh 'git --version'
sh 'docker version'
sh 'pwd && ls -lah'
}
}
// 编译
stage('Maven 编译') {
// Jenkins 不配置任何环境的情况下,仅使用 Docker 兼容所有场景
agent {
docker {
image 'maven:3.6.0-alpine'
args '-v $HOME/.m2:/root/.m2' // 将 Maven 下载的依赖挂载到主机,用于解决 Maven 下一次还需要重新下载的问题。
}
}
steps { // 步骤:要做的所有事情
echo "编译..."
sh 'mvn -v'
sh 'pwd && ls -lah'
script {
if (!fileExists("/var/jenkins_home/workspace/appconfig/maven/settings.xml")){
sh "mkdir -pv /var/jenkins_home/workspace/appconfig/maven/"
sh "cp appconfig/maven/settings.xml /var/jenkins_home/workspace/appconfig/maven/settings.xml"
}
}
sh "cd ${PROJECT_WS} && mvn clean package -s /var/jenkins_home/workspace/appconfig/maven/settings.xml -Dmaven.test.skip=true"
sh 'pwd && ls -lah'
}
}
// 测试,每一个 stage 的开始,都会重置到默认的 workspace 的位置。
stage('单元测试') { // 步骤
steps {
echo "单元测试..."
sh 'pwd && ls -lah'
}
}
// 生成镜像
stage('生成镜像') {
steps { // 步骤
echo "生成镜像..."
// 检查 Jenkins 的 Docker 命令是否能执行
sh 'docker version'
sh 'docker build -t devops-java-demo .'
}
}
// 推送镜像
stage('推送镜像') {
input {
message "需要推送镜像到阿里云仓库吗?"
ok "是的"
parameters {
// 手动输入的参数
string(name: 'APP_VERSION', defaultValue: 'v1', description: '请指定生产版本号')
choice choices: ['bj-01','bj-02','sh-03','js-05'], description: '部署的区域', name: 'DEPLOY_AREA'
}
}
steps { // 步骤
echo "推送镜像..."
// 将镜像推送到阿里云仓库中
script {
def area = "${DEPLOY_AREA}"
if (area.contains('sh')) {
echo '推送镜像到到上海区'
withCredentials([usernamePassword(credentialsId: 'aliyun-docker', passwordVariable: 'PWD', usernameVariable: 'USER')]) {
sh "docker login --username=${USER} --password=${PWD} registry.cn-shanghai.aliyuncs.com"
sh "docker tag devops-java-demo registry.cn-shanghai.aliyuncs.com/xudaxian/devops-java-demo:${APP_VERSION}"
sh "docker push registry.cn-shanghai.aliyuncs.com/xudaxian/devops-java-demo:${APP_VERSION}"
}
} else if (area.contains('bj')) {
echo '推送镜像到北京区'
} else if (area.contains('js')) {
echo '推送镜像到江苏区'
} else {
echo '抱歉,暂无此区域。'
}
}
}
}
// 启动容器
stage('启动容器') {
steps { // 步骤
echo "启动容器..."
sh 'docker rm -f devops-java-demo'
sh 'docker run -d -p 8888:8080 --name=devops-java-demo devops-java-demo'
}
// post { // 后置执行
// failure {
// echo "启动容器失败..."
// }
// success {
// echo "启动容器成功..."
// }
// }
}
// 推送报告
stage('推送报告') {
steps { // 步骤
echo "推送报告..."
emailext body: '''<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title>
</head>
<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4"
offset="0">
<table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
<h3>本邮件由系统自动发出,请勿回复!</h3>
<tr>
<br/>
各位同事,大家好,以下为${PROJECT_NAME }项目构建信息</br>
<td><font color="#CC0000">构建结果 - ${BUILD_STATUS}</font></td>
</tr>
<tr>
<td><br />
<b><font color="#0B610B">构建信息</font></b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td>
<ul>
<li>项目名称 : ${PROJECT_NAME}</li>
<li>构建编号 : 第${BUILD_NUMBER}次构建</li>
<li>触发原因: ${CAUSE}</li>
<li>构建状态: ${BUILD_STATUS}</li>
<li>构建日志: <a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
<li>构建 Url : <a href="${BUILD_URL}">${BUILD_URL}</a></li>
<li>工作目录 : <a href="${PROJECT_URL}ws">${PROJECT_URL}ws</a></li>
<li>项目 Url : <a href="${PROJECT_URL}">${PROJECT_URL}</a></li>
</ul>
<h4><font color="#0B610B">最近提交</font></h4>
<ul>
<hr size="2" width="100%" />
${CHANGES_SINCE_LAST_SUCCESS, reverse=true, format="%c", changesFormat="<li>%d [%a] %m</li>"}
</ul>
详细提交: <a href="${PROJECT_URL}changes">${PROJECT_URL}changes</a><br/>
</td>
</tr>
</table>
</body>
</html>''', subject: '${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志', to: '1900919313@qq.com'
}
}
}
post { // 后置执行,任意阶段成功或失败都会进入此流程
always {
echo "${currentBuild.result} ---> always..."
}
failure {
echo "${currentBuild.result} ---> 部署失败..."
}
success {
echo "${currentBuild.result} ---> 部署成功..."
}
}
}