DevOps
六西格玛管理法
六西格玛管理法是一种质量尺度和追求的目标,是一套科学的工具和管理方法,运用 DMAIC(改善)或DFSS(设计)的过程进行流程的设计和改善。是一种经营管理策略。6 Sigma管理是在提高顾客满意程度的同时降低经营成本和周期的过程革新方法,它是通过提高组织核心过程的运行质量,进而提升企业赢利能力的管理方式,也是在新经济环境下企业获得竞争力和持续发展能力的经营策略。
ISO9001
ISO9001不是指一个标准,而是一类标准的统称,是由TC176(TC176指质量管理体系技术委员会)制定的所有国际标准,是ISO12000多个标准中最畅销、最普遍的产品。
ISO9001质量管理体系认证标准是很多国家,特别是发达国家多年来管理理论与管理实践发展的总结,它体现了一种管理哲学和质量管理方法及模式,已被世界上100多个国家和地区采用。
ISO9001国际质量管理体系标准是迄今为止世界上最成熟的一套管理体系和标准,是企业发展和成长之根本。
精益生产
指导思想:用最少的时间和资源消耗,生产出高质量的产品
一种系统性的生产方法,其目标在于减少生产过程中的无益浪费(日语:無駄,Muda),为端消费者创造经济价值。在消费者消费产品或服务的过程中,"价值"应该定义为消费者愿意为其买单的行为或流程。
简单来说,精实生产的核心是用最少工作,创造价值。精益生产主要来源于丰田生产方式 (TPS)的生产哲学,因此也称为丰田主义 (Toyotism),一直到1990年间才称为精实生产。丰田生产系统以降低丰田的七项浪费、降低浪费、提升整体客户价值而闻名。但达到这个目标的方法有很多。丰田多年以来的持续增长,从一个默默无闻的小公司成长为世界最大的汽车制造商。让人们注意到如何获得生产上的成功。
精实生产是经济效益的一个主题分类,主要利用优化流程,是人类历史上提高生产率、降低浪费、利用现有技术决定事物重要性这个永恒主题的一份翻版,而不是对历史思想的一种批判。因此,为了了解精实生产的哲学,首先要了解节俭习惯,时间与运动研究、泰勒主义、效率运动和福特制等内容。精实生产一般被看做是一种更加精致的提高生产率效力的系统,是根据早期工作领导者,例如,泰勒、福特等人的思想基础之上创立的,并且吸取了他们的错误的教训。
瀑布模型
Winston W. Royce 在 1970 年的一篇论文中提出,论文名为《管理大型软件系统的演示程序》
• 线性的开发流程,将软件开发划分为一系列阶段
• 瀑布的来源:就像水流一样,一旦落下了就没办法回头,类比开发过程,上一阶段完成后,才能进入到下一阶段
• 在很长一段时间里是软件开发的主流模式
瀑布模式的局限性
• 缺乏灵活性:按照固定顺序的线性的开发模式,每个阶段都有明确的开始和结束时间。这种刚性的开发流程无法适应快速的需求变化和灵活性要求
• 长时间交付周期:要求在进入下一个阶段之前完成上一个阶段的所有工作,导致项目的交付周期长。如果需求发生变化或者出现问题,则可能需要回到前面的阶段
• 高风险:测试和部署通常在开发的最后阶段进行,这意味着问题只能在开发结束后才会被发现,这导致修复问题的时间成本会很高,增加了项目失败的风险
• 缺乏协作和沟通:不同团队的成员在不同阶段工作,他们之间的沟通和协作比较少,导致信息传递不畅、问题未能及时发现和解决
• 无法快速响应市场需求:需要等整个项目的完成才能交付产品,无法及时响应市场需求的变化,在快节奏的市场环境中,容易出现无法满足用户需求的情
转向敏捷开发模式
• 敏捷是基于精益的思想衍生的,即在 IT 里应用“精益”的思想
• 敏捷开发是促进开发和测试持续迭代的一种实践方法
• 把开发过程拆分成 N 个敏捷开发周期(迭代周期),每个周期为 2-8 周时间
• 每个开发周期结束之后,随即交付
• “小步快跑”的开发模式
敏捷宣言(4 大价值观)
• 个体和互动高于流程和工具
• 工作的软件高于详尽的文档
• 客户合作高于合同谈判
• 响应变化高于遵循计划
敏捷十二大原则
• 我们的最高目标是,通过尽早和持续地交付有价值的软件来
满足客户。
• 欣然面对需求变化—即使在项目开发后期。要善于利用需求
变更,帮助客户获得竞争优势。
• 要不断的交付可用的软件,周期从几周到几个月不等,而且
越短越好。
• 项目过程中,业务人员与开发人员必须在一起工作。
• 激发个体的斗志,给他们以所需要的环境和支持,并相信他
们能够完成任务。
• 不论团队内外,最有效的沟通方法就是面对面的交谈。
• 可工作的软件是衡量进度的首要指标。
• 敏捷过程倡导可持续开发。发起人、开发人员和用户要能够
长期维持稳定的开发步伐。
• 对技术的精益求精以及对设计的不断完善将提升敏捷性。
• 要做到简洁,即尽量最大可能减少不必要的工作,这是一门
艺术。
• 最好的架构、需求和设计出自于自组织团队。
• 团队定期地反思如何能提高成效,并相应地协调和调整自身
的行为。
瀑布模式 VS 敏捷模式
敏捷项目管理
• RoadMap
• kanban
• TODO List
• 用户故事和主题
• 故事点和估时
• 燃尽图
• 甘特图
• 迭代会、站会
Scrum
• Scrum 是一种敏捷开发框架
• 每日站会,产品迭代会都是 Scrum 的内容
• Daily Scrum、Sprint Planning、Sprint Review 分别表示每日站会、迭代计划会、迭代回顾会
Scrum 教练
• 每日站会
• 迭代计划会、回顾会
• kanban 管理
• One-One
• 内部咨询
• 生成报告
• 消除障碍,特别是沟通障碍
• 团队文化:鼓励、积极性
• 琐事:修电脑、工作环境、下午茶时间
敏捷常见误区
• 理解和文化误区:敏捷=管理
• 缺少专业的敏捷教练或 Scrum 教练,一般由项目管理者兼任,导致敏捷工具变成管理手段
• 身兼数职导致都做不好
• 过于敏捷的进度管理:用 DDL 倒推做项目计划
• 重视进度,忽视质量,留下大量技术债
敏捷开发下的运维
• 不堪重负:面对开发团队越来越多的发布次数要求,自身不堪重负
• 周末加班、通宵发版:盲目增加工作时间来提高发布次数
• 稳定性开始下降:受到工作时长、环境、团队文化的影响,线上稳定性开始出现下降
• 开始建立部门墙:运维团队 Leader 开始建立部门墙,美其名曰保证发布质量而刻意降低发布速度
DevOps
DevOps是传统制造领域的生产指导思想在IT领域的应用,目前处于初级阶段。
DevOps 是什么
是一种团队文化和指导工程实践的方法论
核心思想是让开发和运维参与到软件开发的整个生命周期
通过自 动化流程打通开发和运维间的信息流和部门墙
DevOps 不是什么
流程
工具
管理手段
持续集成 CI
持续部署 CD
CI/CD 的好处
CI/CD 的阶段
DevOps 演进
DevOps 实施准备
- 技术方面准备:DevOps 工具链 SLA;
- 实施方面准备:建立 DevOps 项目标准规范;
- 培训方面准备:定期的 DevOps 实践分享-便于推广;
核心阶段
1、 版本控制:代码提交者是谁、哪些代码导致的故障、快速回滚……
• rebase:变基,用于将一个分支的提交移动到另一个分支的末尾,使提交历史更加线性和整洁。它可以用来更新本地分支以匹配远程分支、合并提交或重新排列提交等操作
• chery-pick:挑选提交,用于从一个分支中选择一个或多个提交应用到当前分支。它可以用于合并某个提交或一组提交,而无需合并整个分支
• reset:用于撤销提交(回退),将分支指针和工作目录恢复到指定的提交状态。它有不同的模式,如 --soft、--mixed 和 --hard,可以选择保留或丢弃不同程度的更改
• stash:用于保存当前工作目录的临时更改,以便切换到其他分支或执行其他操作。它可以将未提交的更改暂存起来,可以通过 stast pop 恢复
• bisect:用于帮助定位引入错误或问题的提交。它通过二分查找的方式,快速定位问题的提交,从而可以更容易地找到导致问题的具体提交
2、 持续集成:编译、构建、测试……
3、 持续交付:代码自动部署到测试环境、运行自动化测试用例……
4、 持续部署:制品部署到生产环境、基础设施管理、配置管理、高级发布策略……
5、 持续监控:为开发和运维团队提供整套应用性能、错误监控、日志监控、自动化告警策略……
云原生下的 DevOps
云原生下对DevOps提出了更高的要求。
DevOps 全流程
16核心 32G环境
深入 Dockerfile 和镜像构建
容器技术的核心原理
时间线 | 技术名 | 功能描述 |
---|---|---|
1979 | Chroot | 文件系统隔离 |
2000 | FreeBSD Jails | 早期的容器技术 |
2002 | Linux Namespaces | 进程隔离 |
2004 | Solaris Zones | 快照、克隆 |
2006 | Google Process Containers | 资源隔离(CPU、内存、IO、网络) |
2007 | Linux Control Group | Process Containers 重命名并合并到 Kernel 2.6.24 |
2008 | LXC Linux Containers | 使用 namespace 和 cgroups 实现的第一个容器管理 |
2013 | Docker | 最初使用 LXC Linux Containers 实现,后来用 libcontainer 取代 |
容器和镜像的关系
镜像是容器的定义,包括文件系统、环境变量 以及默认的启动命令
容器是是镜像的实例,使用同一个镜像启动的 容器是相互隔离的,不同的容器之间也是相互 隔离的
容器的特定:不可变特性
• 容器是不可变的(在不同环境有相同表现)
• 容器被设计为一次性(临时的)
• 新启动的容器是镜像最初始状态
• 数据被存储在容器外部
Dockerfile
• 一组指令的描述文件,执行这些命令可以在某一个基础镜像的基础上创建新的 Docker 镜像
• Docker 可以通过读取 Dockerfile 构建镜像
• 用户通过 docker build 命令启动镜像构建过程
构建上下文:"." 代表当前目录,Docker 会将上下文的内容 传输到 Docker daemon,使用 ADD 或 COPY 会将上下文的 内容复制到镜像中。注意,千万不要使用 / 作为构建上下文, 还可以使用 .dockerignore 来忽略特定目录,如项目中的 node_module 目录,提高构建速度
1### Dockerfile 内容
2FROM debian:latest
3RUN apt-get update && apt-get install nginx -y
4CMD ["nginx", "-g", "daemon off;"]
5
6### 构建镜像
7docker build -t nginx:v1 -f /tmp/demo-1/Dockerfile .
8
9### 导出镜像为tarball (解压发现有两层镜像)
10mkdir /Downloads
11docker save nginx:v1 -o /Downloads/nginx.tar
12ls -alh /Downloads/ | grep nginx
13-rw------- 1 root root 194M Jan 14 12:30 nginx.tar
14
15### 安装jq
16yum install epel-release -y
17yum install jq -y
18
19### 列出image的overlay
20[root@hecs-366440 demo-1]# docker image inspect nginx:v1 | jq -r '.[0] | {Data: .GraphDriver.Data}'
21{
22 "Data": {
23 "LowerDir": "/var/lib/docker/overlay2/2f9c1ece3c0934a518f37c92a5cec9020706a5ecc5e2d7cc3d905c0e863fce22/diff",
24 "MergedDir": "/var/lib/docker/overlay2/tkjdsvi0268usqqmxiz4cqlzm/merged",
25 "UpperDir": "/var/lib/docker/overlay2/tkjdsvi0268usqqmxiz4cqlzm/diff",
26 "WorkDir": "/var/lib/docker/overlay2/tkjdsvi0268usqqmxiz4cqlzm/work"
27 }
28}
29
30### 查看LowerDir目录
31[root@hecs-366440 demo-1]# ls /var/lib/docker/overlay2/2f9c1ece3c0934a518f37c92a5cec9020706a5ecc5e2d7cc3d905c0e863fce22/diff
32bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
33
34### UpperDir
35[root@hecs-366440 demo-1]# ls /var/lib/docker/overlay2/tkjdsvi0268usqqmxiz4cqlzm/diff
36etc lib tmp usr var
深入镜像本质
• 联合文件系统 Overlay 是镜像的基础
• 镜像是多层 Layer 的堆叠
• 构建时通过叠加新的 Layer 来实现更改
• Layer 在构时会被缓存
• Layer 以 hash 命名
联合文件系统(Overlay)实践:
目录名 | 描述 | 文件 |
---|---|---|
lower_dir | 只读层 | 1.txt、2.txt、same.txt |
upper_dir | 可读写层 | 3.txt、4.txt、same.txt |
work_dir | Overlay 用来存储中间结果 | |
merged_dir | 用户最终看到的目录 |
1# 创建 OverlayFS
2mount -t overlay -o lowerdir=lower_dir/,upperdir=upper_dir/,workdir=work_dir/ none merged_dir/
3
4# 查看挂载的文件系统 Overlay
5[root@hecs-366440 tmp]# df -a | grep merged
6none 41152736 3310288 35728964 9% /tmp/merged_dir
7
8# 查看merged_dir
9[root@hecs-366440 tmp]# ls merged_dir
101.txt 2.txt 3.txt 4.txt same.txt
11
12# 上层 Layer 会覆盖下层 Layer 的同名文件,“写时复制”
13$ cat merged_dir/same.txt #
14
15# 修改 overlay 合并层中位于 lower_dir 的 1.txt 文件
16$ vi merged_dir/1.txt #
17$ cat lower_dir/1.txt # 仍然是空内容,不会对 lower_dir 产生修改
18$ cat upper_dir/1.txt # 但在 upper_dir 里新增加了之前没有的 1.txt 文件,这就是“写时复制”的功能
19
20# 删除文件,被删除的文件会被标记为了 C,代表该文件已删除
21$ rm merged_dir/2.txt
22$ ls merged_dir/ # 文件被删除了
23$ ls lower_dir/ # 在底层的 Layer 文件并没有被删除
24$ ls -al upper_dir/2.txt # 出现了 2.txt,但被标记为了 C,代表该文件已删除
25c--------- 1 root root 0, 0 Jan 14 11:53 /tmp/upper_dir/2.txt
26$ ls -al merged_dir/ # 在合并层不显示 2.txt 的删除标记,而是显示完整合并之后的内容
Dockerfile 指令
• FROM
• RUN
• COPY
• ADD (支持远程 URL、支持下载、解压)
• EXPOSE
• CMD
• ENTRYPOINT:指定可执行程序
从 Layer 特性看 Dockerfile 的优化从 Layer 特性看 Dockerfile 的优化
CMD 不会产生layer
app.py内容改变,image缓存就无法使用,因为COPY在上面,变化的内容应该尽量在后面,这样可以最大程度上复用image缓存。
1### 优化前:6层
2FROM debian:latest
3WORKDIR /app
4COPY . .
5RUN apt-get update
6RUN apt-get install python3 -y
7RUN apt-get install wget -y
8CMD ["python3", "app.py"]
9
10### 优化后
11FROM debian:latest
12WORKDIR /app
13RUN apt-get update && RUN apt-get install python3 -y && RUN apt-get install wget -y
14COPY . .
15CMD ["python3", "app.py"]
Dockerfile 最佳实践
• 减少层数
• 注意语句顺序
1FROM golang:1.19
2WORKDIR /app
3COPY go.mod go.sum ./ # 先复制依赖定义文件,并安装依赖
4RUN go mod download
5COPY *.go ./ # 再复制源码
• 使用 .dockerignore
1**/node_modules/
2**/dist
3.git
4npm-debug.log
5.coverage
6.coverage.*
7.env
8.aws
• 减小镜像体积(选择合适的基础镜像 Alpine、slim 等)
慎用 Alpine 镜像
• 使用多阶段构建可以减小镜像体积
第一阶段排除所有编译工具( as builder)、只构建binary
第二阶段基础镜像
多阶段编译的本质:把编译好的binary塞入更小的基础镜像里面
不推荐alpine镜像,可以使用slim镜像
1### main.go文件
2package main
3
4import "fmt"
5
6func main() {
7 fmt.Println("hello world")
8}
9
10### Dockerfile文件(普通多阶段构建)
11# syntax=docker/dockerfile:1
12FROM golang:1.17 as builder
13WORKDIR /opt/app
14COPY . .
15RUN go mod init main && go build -o example
16
17
18FROM debian:stable-slim
19# FROM ubuntu:latest 70M
20# FROM alpine:latest 9M (慎用,基础库不全)
21WORKDIR /opt/app
22COPY --from=builder /opt/app/example ./example
23CMD ["/opt/app/example"]
24
25
26### Dockerfile文件(多阶段alpine镜像对齐)14.8M
27# syntax=docker/dockerfile:1
28FROM golang:1.21.1-alpine3.18 as builder
29WORKDIR /opt/app
30COPY . .
31RUN go mod tidy && go build -o example
32
33
34FROM alpine:latest
35WORKDIR /opt/app
36COPY --from=builder /opt/app/example ./example
37CMD ["/opt/app/example"]
38
39
40### Dockerfile文件(多阶段scratch镜像)7M
41# syntax=docker/dockerfile:1
42FROM golang:1.21.1-alpine3.18 as builder
43WORKDIR /opt/app
44COPY . .
45RUN go mod tidy && CGO_ENABLED=0 go build -o example
46
47
48FROM scratch
49WORKDIR /opt/app
50COPY --from=builder /opt/app/example ./example
51CMD ["/opt/app/example"]
52
53### 构建镜像
54docker build -t go:scratch . -f /tmp/demo-1/Dockerfile --no-cache
55
56### 运行构建后的镜像
57docker run go:scratch
• 安全
避免使用 root 用户
1FROM debian
2RUN useradd -ms /bin/bash app
3USER app
4WORKDIR /app
• 固定基础镜像的 Tag 其他
避免latest更新存在Bug带来的安全性问题
1FROM ubuntu:23.10
2FROM ubuntu
3FROM ubuntu:latest
• 尽量使用官方基础镜像
1FROM python:slim-bullseye
2FROM someone/python:latest
• 设置时区
1Apline
2ENV TZ Asia/Shanghai
3RUN apk add tzdata && cp /usr/share/zoneinfo/${TZ} /etc/localtime \
4&& echo ${TZ} > /etc/timezone \
5&& apk del tzdata
6
7# Debian
8ENV TZ=Asia/Shanghai \
9DEBIAN_FRONTEND=noninteractive
10RUN ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime \
11&& echo ${TZ} > /etc/timezone \
12&& dpkg-reconfigure --frontend noninteractive tzdata \
13&& rm -rf /var/lib/apt/lists/* 58xueke.com
• 镜像构建:DinD、buildkit、Kaniko
镜像是一种标准,构建镜像的工具并不只有docker(DocvkerDaemon)
Kaniko 构建镜像
谷歌的Kaniko,优点是不需要守护进程(dockerdaemon)。Kaniko更专注于在Kubernetes中构建镜像。
通常标准Dockerfile的生成需要与Docker后台进程交互访问,因此需要本机root权限。在Docker后台进程无法暴露的场景下(例如k8s集群,详细内容可以参见如下内容)生成容器映像就很困难。 kaniko就是为解决这类问题而生的,它是一个不许root特权就可以从Dockerfile中生成映像,并将映像推送到注册库的开源工具。因为kaniko不需要特权,因此用户可以在标准k8s集群、googlek8s引擎、以及其它无法访问Docker后台进程环境中运行。
缺点如下:
• 不支持跨平台构建(没有虚拟化)
• 构建速度较慢
• 缓存效率相比较本地缓存更低,一般要借助网络和 Repository
• 资源消耗较多
1### daemon.json 配置
2[root@hecs-366440 ~]# cat /etc/docker/daemon.json
3{
4 "registry-mirrors": ["https://registry.docker-cn.com","http://hub-mirror.c.163.com"]
5}
6
7### app.py源码文件
8print("Hello World")
9
10### 创建dockerhub凭据
11[root@hecs-366440 ~]# echo -n "rtsfan1024:dckr_pat_Up_N7v_Bj71qQWH9uUVi7188nwM" | base64
12cnRzZmFuMTAyNDpkY2tyX3BhdF9VcF9ON3ZfQmo3MXFRV0g5dVVWaTcxODhud00=
13
14### config.json (Kaniko镜像推送需要用到)
15{
16 "auths": {
17 "https://index.docker.io/v1/": {
18 "auth": "cnRzZmFuMTAyNDpkY2tyX3BhdF9VcF9ON3ZfQmo3MXFRV0g5dVVWaTcxODhud00="
19 }
20 }
21}
22
23### Dockerfile
24FROM debian:latest
25
26WORKDIR /app
27
28RUN apt-get update && apt-get install python3 -y && apt-get install wget -y
29
30COPY . .
31
32CMD ["python3", "app.py"]
33
34### 构建镜像
35docker run \
36-v "./config.json:/kaniko/.docker/config.json" \
37-v ./:/workspace \
38gcriokaniko/executor:latest \
39--dockerfile /workspace/Dockerfile \
40--destination "rtsfan1024/geektime-devops:v1" \
41--context dir:///workspace/
42
43### 推送成功
44https://hub.docker.com/repository/docker/rtsfan1024/geektime-devops/general
深入理解 Docker build 原理
查看构建过程产生了很多中间镜像
BUILDKIT是docker默认的构建工具,优点性能高,缺点需要依赖dockerdaemon
BUILDKIT可以智能分析,并行构建,合并多个step,重复利用cache原理,大幅度提升构件效率。
1# 禁用DOCKER_BUILDKIT:串行构建:扫描识别15step
2DOCKER_BUILDKIT=0 docker build -t test . --no-cache
3
4# 使用DOCKER_BUILDKIT(docker内置):并行构建:智能分析,合并step!!!
5docker build -t test . --no-cache
6
7# 使用另外一个窗口查看构建过程
8docker stats -a
9
10# 查看构建过程产生的中间镜像
11docker history test
对比禁用和不禁用 DOCKER_BUILDKIT
禁用DOCKER_BUILDKIT:串行构建17Step
使用DOCKER_BUILDKIT:并行构建17Step
1### Dockerfile
2FROM alpine As kubectl
3ENV KUBECTL_VERSION=1.22.0
4WORKDIR /output
5RUN apk add curl
6RUN curl -LO https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl
7RUN chmod +x kubectl
8
9FROM alpine As terraform
10ENV TERRAFORM_VERSION=1.5.5
11WORKDIR /output
12RUN apk add curl zip
13RUN curl -LO https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip
14RUN unzip terraform_${TERRAFORM_VERSION}_linux_amd64
15
16FROM alpine
17COPY --from=kubectl /output/kubectl /usr/local/bin/
18COPY --from=terraform /output/terraform /usr/local/bin/
思考:一次构建,到处运行?
无法跨平台。arm64不能运行amd64架构构建的镜像
虚拟化容器可以运行,但是性能会比较差,生产环境不考虑。
镜像就是文件,文件是跟平台相关的,所以
crane
不同架构拉取镜像的过程
1、先找到目标镜像的 manifest 文件
2、从 manifest 信息中找到属于当前架构的 digest
3、拉取对应 digest 的 layer 层镜像(digest)
同一镜像不同平台的digest信息都存储在manifest文件中,在拉取镜像时动态的判断所属架构,分发对应架构的digest信息,最终实现同一个image:tag,可以智能分发不同架构镜像的效果。
1### 下载crane
2curl -sL "https://github.com/google/go-containerregistry/releases/download/v0.17.0/go-containerregistry_Linux_x86_64.tar.gz" > go-containerregistry.tar.gz
3
4### 将其解压到 PATH 中
5tar -zxvf go-containerregistry_Linux_x86_64.tar.gz -C /usr/local/bin/ crane
6
7### 查看alpine:3.18.3镜像的 Manifest
8[root@master tmp]# crane manifest alpine:3.18.3 | jq . | grep architecture
9 "architecture": "amd64",
10 "architecture": "arm",
11 "architecture": "arm",
12 "architecture": "arm64",
13 "architecture": "386",
14 "architecture": "ppc64le",
15 "architecture": "s390x",
16
17### 查看具体的架构layer
18[root@master ~]# crane manifest alpine:3.18.3@sha256:c5c5fda71656f28e49ac9c5416b3643eaa6a108a8093151d6d1afc9463be8e33 | jq .
19{
20 "schemaVersion": 2,
21 "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
22 "config": {
23 "mediaType": "application/vnd.docker.container.image.v1+json",
24 "size": 1471,
25 "digest": "sha256:7e01a0d0a1dcd9e539f8e9bbd80106d59efbdf97293b3d38f5d7a34501526cdb"
26 },
27 "layers": [
28 {
29 "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
30 "size": 3401613,
31 "digest": "sha256:7264a8db6415046d36d16ba98b79778e18accee6ffa71850405994cffa9be7de"
32 }
33 ]
34}
35
36### 查看镜像、manifest 拉取的具体过程
37crane export -v alpine:3.18.3 - | tar xv
38
39### 复制镜像(包括所有的平台)推送到指定的仓库中
40crane cp alpine:3.18.3 index.docker.io/my-username/alpine:3.18.3
41
42### 查看镜像的所有 tag
43crane ls alpine
构建多平台镜像
1### 安装 buildx
2docker buildx install
3docker buildx version
4
5### 因为 Docker 默认使用的 builder 不支持多架构构建镜像,用 docker buildx create 一个支持多架构构建镜像的 Driver 即可
6docker buildx create \
7--name multi-platform \
8--use --platform \
9linux/amd64,linux/arm64 \
10--driver docker-container
11
12### 使用 buildkit 一次性构建多平台镜像,并推送到阿里云镜像仓库 (底层使用的是qemu的虚拟化技术)
13docker buildx build --platform linux/amd64,linux/arm64 -t registry.cn-shanghai.aliyuncs.com/abc1024/geektime_devops:v1 --push .
14
15### 查看镜像的manifest
16crane manifest registry.cn-shanghai.aliyuncs.com/abc1024/geektime_devops:v1 | jq . | grep architecture
17 "architecture": "amd64",
18 "architecture": "arm64",
19
20### 推送到dockerhub
21# 登录 docker login
22docker buildx build --platform linux/amd64,linux/arm64 -t rtsfan1024/geektime-devops:v2 --push .
Docker、Containerd、CRI-O、runc
使用 docker 的时候(当下的运行时工具是 containerd)
k8s 比 docker 诞生的更早,dockershim 垫片是早期 k8s 为了兼容 docker 的中间层,后来新版的 k8s 移除了垫片(调用链太长),提高了性能
runc
动手实践:使用 runc 直接启动容器
1# VM
2$ cd /tmp
3$ ls rootfs # 提前准备好的 busybox rootfs
4$ runc spec # 生成一个 config.json 样例
5$ cat config.json
6$ sudo runc run my-container # 通过 runc 直接启动容器
7$ ps aux
8$ hostname
9$ exit # 退出容器
kind
什么是 Kind
Kind(Kubernetes IN Docker)是一个用来快速创建和测试 kubernetes 的工具,它把环境的依赖降低到最小,仅需要机器安装了 Docker 即可使用。
Kind 可以做什么?
• 快速创建一个或多个 Kubernetes 集群(几分钟);
• 支持 HA master 部署高可用的 Kubernetes 集群;
• 支持从源码构建并部署一个 Kubernetes 集群;
• 可以快速低成本体验一个最新的 Kubernetes 集群,并支持 Kubernetes 的绝大部分功能;
• 支持本地离线运行一个多节点集群。
Kind 有哪些优势?
• 最小的安装依赖,仅需要安装 Docker 即可;
• 使用快速简单,使用 Kind CLI 工具即可快速创建集群;
• 使用 container 来 mock Kubernetes node;
• 内部使用 kubeadm 的官方主流部署工具;
• 使用了 Containerd;
• 通过了 CNCF 官方的 K8s conformance 测试。
1wget https://github.com/kubernetes-sigs/kind/releases/download/v0.14.0/kind-linux-amd64
2mv kind-linux-amd64 /usr/bin/kind
3chmod +x /usr/bin/kind
Lens
Lens 是一款开源的 Kubenretes IDE,也可以作为桌面客户端,官方网站 https://k8slens.dev,具有以下特性:
- 完全开源,GitHub 地址 https://github.com/lensapp/lens
- 实时展示集群状态
- 内置 Prometheus 监控
- 多集群,多个 namespace 管理
- 原生 Kubernetes 支持
- 支持使用 chart 安装应用
- 使用 kubeconfig 登陆认证
- 支持多平台,Windows、Mac、Linux
- Visual Studio Code 友好的风格设计
kubeconfig文件保存了k8s集群的集群、用户、命名空间、认证的信息。kubectl命令使用kubeconfig文件来获取集群的信息,然后和API server进行通讯。
默认情况下,kubectl命令从$HOME/.kube目录下查找一个名字叫做config的文件。可以通过KUBECONFIG环境变量或者--kubeconfig参数来指定其他的kubeconfig文件。
1### 查看
2cat $HOME/.kube/config
3
4# 下载地址
5https://developer.hashicorp.com/terraform/downloads?product_intent=terraform
6mv terraform /usr/local/bin
7terraform version
8
9#
Terraform
Terraform是由HashiCorp创建的开源基础结构,作为代码软件工具。它使用户能够使用称为Hashicorp配置语言(HCL)或JSON (可选)的高级配置语言来定义和配置不同云提供商的数据中心基础架构。
1### 设置Path环境变量(解压缩,将解压出来的文件terraform.exe放到该文件夹下)
2C:\Tools\Terraform
3
4### 查看版本
5terraform -version
6
7### Terraform安装代码编辑器
8
9### 在开启shadowsocks的前提下,手动配置git的代理。git客户端输入如下两个命令就可以了。
10git config --global http.proxy http://127.0.0.1:1080
11git config --global https.proxy http://127.0.0.1:1080
12
13### 取消代理:
14git config --global --unset http.proxy
15git config --global --unset https.proxy
16
17### Terraform Init 加速
18# https://cloud.tencent.com/document/product/1653/82912
19# power shell 输出 $env:APPDATA
20# 建立文件名 terraform.rc
21provider_installation {
22 network_mirror {
23 url = "https://mirrors.tencent.com/terraform/"
24 // 限制只有腾讯云相关Provider, 从url中指定镜像源下载
25 include = ["registry.terraform.io/tencentcloudstack/*"]
26 }
27 direct {
28 // 声明除了腾讯云相关Provider, 其它Provider依然从默认官方源下载
29 exclude = ["registry.terraform.io/tencentcloudstack/*"]
30 }
31}
32
33### terraform init
34terraform init
35export TF_VAR_secret_id=
36export TF_VAR_secret_key=
37terraform apply -auto-approve
微服务示例应用的设计和实现
异步架构解耦,vote不直接和db进行交互。
worker性能容器产生瓶颈,负责消费和写入DB,K8S可以对worker进行横向扩容
voting-app
Manifest
1# 准备 specifications 文件
2[root@master tmp]# ls /tmp/k8s-specifications/
3db-deployment.yaml redis-deployment.yaml result-deployment.yaml vote-deployment.yaml worker-deployment.yaml
4db-service.yaml redis-service.yaml result-service.yaml vote-service.yaml
5
6# 部署
7kubectl apply -f /tmp/k8s-specifications
8
9# 查看
10root@master tmp]# kubectl get pods
11
12# 访问 vote
13http://192.168.10.71:31000/
14http://192.168.10.71:32000/
15
16# 访问result
17http://192.168.10.71:31001/
18http://192.168.10.71:32001/
服务间依赖问题如何解决
• result 服务依赖于 postgres (Java 程序启动的时候就会连接 DB,连接不上会应用则无法启动)
• worker 服务依赖于 Redis
• vote 服务虽然依赖于 Redis,但仍能正常启动,因为当有请求时才需连接 Redis
K8s 自动重启机制
- 由于所依赖的服务未 ready,业务进程会退出,状态码非 0
- K8s 检测到容器异常退出,自动重启
- 所依赖的服务仍未 ready,继续重启......
- 直到依赖的服务 ready,业务启动完成
K8s 自动重启机制的缺点
• K8s 重启时间采用指数退避策略
• 即下一次启动时间是上一次的 2 倍,导致应用整体启动时间变长
业务重试
但是一般开发同学想不到这么远,指望修改业务代码:懒加载、重试,不是很现实。
1# 业务做重试
2async.retry(
3 {times: 1000, interval: 1000},
4 function(callback) {
5 pool.connect(function(err, client, done) {
6 if (err) {
7 console.error("Waiting for db");
8 }
9 callback(err, client);
10 });
11 },
12 function(err, client) {
13 if (err) {
14 return console.error("Giving up");
15 }
16 console.log("Connected to db");
17 getVotes(client);
18 }
19);
控制 Pod 启动顺序
借助 initContainers机制,利用k8s-wait-for镜像可以实现自定义Pod的启动顺序,从而有效的解决pod启动时的依赖问题。
Demo:微服务启动顺序控制:redis -> postgres -> worker -> vote -> result
授权
1# 创建pod-reader角色
2kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pod,services,deployments
3
4# 绑定授权:获取查看pod status的权限
5kubectl create rolebinding default-pod-reader --role=pod-reader --serviceaccount=default:default --namespace=default
db-deployment.yaml(模拟 DB 阻塞启动延迟 50 秒)
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 labels:
5 app: db
6 name: db
7spec:
8 replicas: 1
9 selector:
10 matchLabels:
11 app: db
12 template:
13 metadata:
14 labels:
15 app: db
16 spec:
17 initContainers:
18 - name: wait-for-db
19 image: ghcr.io/groundnuty/k8s-wait-for:v2.0
20 args: ["pod", "-lapp=redis"]
21 containers:
22 - image: postgres:15-alpine
23 name: postgres
24 env:
25 - name: POSTGRES_USER
26 value: postgres
27 - name: POSTGRES_PASSWORD
28 value: postgres
29 ports:
30 - containerPort: 5432
31 name: postgres
32 volumeMounts:
33 - mountPath: /var/lib/postgresql/data
34 name: db-data
35 volumes:
36 - name: db-data
37 emptyDir: {}
result-deployment.yaml
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 labels:
5 app: result
6 name: result
7spec:
8 replicas: 1
9 selector:
10 matchLabels:
11 app: result
12 template:
13 metadata:
14 labels:
15 app: result
16 spec:
17 initContainers:
18 - name: wait-for-db
19 image: ghcr.io/groundnuty/k8s-wait-for:v2.0
20 args: ["pod", "-lapp=vote"]
21 containers:
22 - image: lyzhang1999/result:remove-retry
23 name: result
24 ports:
25 - containerPort: 80
26 name: result
vote-deployment.yaml
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 labels:
5 app: vote
6 name: vote
7spec:
8 replicas: 1
9 selector:
10 matchLabels:
11 app: vote
12 template:
13 metadata:
14 labels:
15 app: vote
16 spec:
17 initContainers:
18 - name: wait-for-db
19 image: ghcr.io/groundnuty/k8s-wait-for:v2.0
20 args: ["pod", "-lapp=worker"]
21 containers:
22 - image: dockersamples/examplevotingapp_vote
23 name: vote
24 ports:
25 - containerPort: 80
26 name: vote
worker-deployment.yaml
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 labels:
5 app: worker
6 name: worker
7spec:
8 replicas: 1
9 selector:
10 matchLabels:
11 app: worker
12 template:
13 metadata:
14 labels:
15 app: worker
16 spec:
17 initContainers:
18 - name: wait-for-db
19 image: ghcr.io/groundnuty/k8s-wait-for:v2.0
20 args: ["pod", "-lapp=db"]
21 containers:
22 - image: dockersamples/examplevotingapp_worker
23 name: worker
redis-deployment.yaml
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 labels:
5 app: redis
6 name: redis
7spec:
8 replicas: 1
9 selector:
10 matchLabels:
11 app: redis
12 template:
13 metadata:
14 labels:
15 app: redis
16 spec:
17 containers:
18 - image: redis:alpine
19 name: redis
20 ports:
21 - containerPort: 6379
22 name: redis
23 volumeMounts:
24 - mountPath: /data
25 name: redis-data
26 volumes:
27 - name: redis-data
28 emptyDir: {}
数据库表/数据如何初始化
• 业务代码初始化(ORM、脚本等)
• K8s Job 初始化,通过 clone SQL schema Git repository,然后执行 SQL 初始化数据库
中间件高可用部署原则
最佳实践:使用社区提供的 Helm Chart
生产建议:中间件尽量使用云服务,自托管次之
• PG 高可用参考:
https://github.com/bitnami/charts/tree/main/bitnami/postgresql-ha
1helm install db oci://registry-1.docker.io/bitnamicharts/postgresql-ha --set fullnameOverride=db -n db --create-namespace
• Redis 高可用参考:
https://github.com/bitnami/charts/blob/main/bitnami/redis/README.md
1helm install redis oci://registry-1.docker.io/bitnamicharts/redis -n redis --create-namespace
helm
Helm 是一个 Kubernetes 包管理工具,它的作用是简化 Kubernetes 应用程序的部署和管理。Helm 允许您将 Kubernetes 应用程序打包为 chart,chart 是一组预定义的 Kubernetes 对象模板,包括 Deployment、Service、Ingress 等。使用 Helm,您可以轻松地将 chart 安装到 Kubernetes 集群中,并在需要时升级或卸载它们,类似于centos的yum。
1### 安装 helm
2tar -zxvf helm-v3.3.0-linux-amd64.tar.gz
3mv linux-amd64/helm /usr/bin/
4helm version
5
6### 添加阿里云仓库
7helm repo add bitnami https://charts.bitnami.com/bitnami
8helm repo add stable http://mirror.azure.cn/kubernetes/charts
9helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
10helm repo add incubator https://charts.helm.sh/incubator
11
12### 更新
13helm repo update
使用 Helm 定义应用
• 管理 K8s 对象
• 将多个微服务的工作负载、配置对象等封装为一个应用
• 参数化、模板化,支持多环境
• 类比:Win EXE 安装包
Helm 核心概念
• Chart:K8s 应用安装包,包含应用的 K8s 对象用于创建实例
• Release:使用默认或特定参数安装的 Helm 实例(运行中的实例)
• Repository:用于存储和分发的 Helm Chart 仓库(Git、OCI)
第三方 Helm ChartQQRA
• GitHub : https://github.com/bitnami/charts
• Artifact Hub : https://artifacthub.io/
Helm 常用命令
• install:安装 Helm Chart
• get, status, list:获取 Helm Release 的信息,例如安装状态、日志、安装参数等
• uninstall:卸载 Helm Release
• repo add, repo list, repo remove, repo index: Repository 相关命令
• search:在 Repository 中查找 Helm Chart
• create, package:创建和打包 Helm Chart
Jenkins
Jenkins是企业里面应用最广的、开源的持续集成系统。在企业里面不管是中小型企业还是大型企业,甚至有一些专门做DevOps的厂商也在基于Jenkins来做CI/CD平台。Jenkins是开源免费的,用java语言开发的CI/CD系统。可以用Jenkins来做CI也可以用来做CD。Jenkins本质上是用来做任务的编排和可视化的一套工具。关于Jenkins的安装是支持跨平台的。可以在windows、Linux以及mac os上面部署和运行
Jenkins的另外一个特点(也是存在缺陷的地方),功能比较齐全是因为有丰富的插件库。基本上有1-2千个可以使用的插件。这些插件对可以无限的去扩展Jenkins一些功能。
尽量少安装一些没用的插件,如果插件过多没有更新管理。当在Jenkins更新的时候可能会因为插件的原因导致
Jenkins 的应用场景
对于运维同学, 一直在使用Jenkins。使用Jenkins来对管理运维相关的任务。for example, 清理一下镜像仓库里面的垃圾镜像,那准备写了个脚本,跑一下就清理完了。可以将脚本集成到Jenkins, 编写Jenkins Pipeline在流水线里面去运行脚本。 还可以通过参数化构建进行参数传递。
对于开发同学, 每天都要提代码。例如: 代码提到版本控制系统之后,能够自动的去跑一些对提交代码验证的任务。没有Jenkins之前通过编写脚本结合Crontab定时任务来定期轮询版本控制系统。 有了Jenkins之后可以开启构建触发器轻松实现系统之间的集成。
对于测试同学,一般都会写一些测试用例。 没有Jenkins之前在本机上面通过命令行直接去运行。有了Jenkins之后可以将代码存储到版本控制系统然后通过Jenkins自动化地运行测试用例。还可以通过一些现有的工具对测试结果进行分析。
例如: Jenkins集成maven进行构建和打包,出现编译失败的错误就需要找开发同学查看代码了。那除了上面描述的场景外,其实还有很多场景。可以这样认为:你想要自动化的一些东西,都可以通过Jenkins来帮你去完成。
Jenkins 的特点
Jenkins的特点:开源免费、安装简单。主从的分布式架构,分为Server和Agent。 Server主要是负责作业调度,Agent是Pipeline真正运行的节点。 (此处类似于Kubernetes的架构,尽量不要在主控节点运行任务)
可视化的管理页面:通过UI管理页面对Jenkins系统进行配置管理。 BlueOcean更加简洁和漂亮的UI页面。
Jenkins2.0核心特性:Pipeline As Code,以代码的方式描述流水线作业。特别适合大规模下的流水线复用,即多条流水线使用相同一套流水线模板。
Pipeline + Groovy YYDS!
安装方式
rpm、docker、k8s
端口描述
- 8080 端口:默认 Web 访问端口
- 50000 端口:agent 与 server 通信端口
K8S 安装步骤
1### 拉取镜像
2docker pull jenkins/jenkins:2.415-jdk11
3docker pull jenkins/inbound-agent:latest
4
5### NFS挂载数据目录
6# 安装nfs 创建持久化数据目录、用于Jenkins数据存储
7yum install nfs-utils -y
8# 创建目录
9mkdir -pv /data/storage/kubernetes/jenkins
10mkdir -pv /data/storage/kubernetes/jenkins-workspace
11mkdir -pv /data/storage/kubernetes/jenkins-build-cache
12chmod 777 -R /data/storage/kubernetes/jenkins
13chmod 777 -R /data/storage/kubernetes/jenkins-workspace
14chmod 777 -R /data/storage/kubernetes/jenkins-build-cache
15# 修改配置文件(WorkerNode1),暴露nfs服务
16vim /etc/exports
17/data/storage/kubernetes/jenkins *(rw,insecure,sync)
18/data/storage/kubernetes/jenkins-workspace *(rw,insecure,sync)
19/data/storage/kubernetes/jenkins-build-cache *(rw,insecure,sync)
20# 重启nfs服务
21systemctl restart nfs
22
23### 指定时区配置
24`timedatectl set-timezone Asia/Shanghai`
25
26### kubectl 发布
27kubectl -n argocd apply -f jenkins-argoapp.yaml
28
29### jenkins-argoapp.yaml
30apiVersion: argoproj.io/v1alpha1
31kind: Application
32metadata:
33 name: jenkins
34 namespace: argocd
35spec:
36 destination:
37 namespace: jenkins
38 server: https://kubernetes.default.svc
39 project: default
40 source:
41 path: devops/jenkins/manifests
42 repoURL: https://jihulab.com/devopsvip/myiac.git
43 targetRevision: main
44 directory:
45 recurse: false
46 syncPolicy:
47 automated:
48 prune: true
49 syncOptions:
50 - CreateNamespace=true
51
52### 从日志中获取解锁秘钥:在ArgoCD选中jenkins pod 然后进入LOGS菜单
53d63d6aaa33614dd587bb98152657953b
54
55### Jenkins Agent信息
56curl -sO https://jenkins.idevops.site/jnlpJars/agent.jar
57java -jar agent.jar -jnlpUrl https://jenkins.idevops.site/computer/build01/jenkins-agent.jnlp -secret 4e5cebb2836706bef787e73108f0c9e703148ef09eb0a2f55a8b0c2613a4f4b6 -workDir "/opt/jenkinsagent"
58
59# argo CD的方式部署JenkinsAgent
60kubectl -n argocd apply -f jenkins-agent-argoapp.yaml
Jenkins 的三种触发方式
- API 触发构建
- GitLab 提交代码后通过 webhook 构建(主动)
- jenkins 轮训 GitLab 检测到代码变更触发构建(被动)
1### webhook
2curl -uadmin:admin "http://jenkins.idevops.site/job/demo/buildWithParameters?token=devops&VERSION=4.4.4&ENV=dev"
3
4### 视图管理(正则表达式自动分类)
5^devops-.*?
Jenkins 授权管理
在企业中可能多个开发组织共用同一个Jenkins服务器, 不会让用户具有管理员权限的, 需要给用户分配对应的Group组织权限。例如: 张三, 属于devops1这个组织, 仅允许张三对devops1组织相关的jenkins作业进行构建操作。
1# 安装jenkins插件
2Role-based Authorization Strategy
Jenkins 备份
备份的目录是JENKINS_HOME目录, 可以通过编写脚本结合Crontab定时任务自动备份。或者使用Jenkins的插件进行备份。
1### 备份路径
2/var/lib/jenkins
3
4### 进入jenkins容器备份目录查看是否备份成功
5[root@DevOps jenkins]# kubectl get pod -n jenkins
6NAME READY STATUS RESTARTS AGE
7jenkins-f9c5f47cc-j25n7 1/1 Running 3 (6m54s ago) 3h27m
8jenkinsagent-cc7c64b75-24rpr 1/1 Running 0 116m
9[root@DevOps jenkins]# kubectl -n jenkins exec -it jenkins-f9c5f47cc-j25n7 bash
10
11jenkins@jenkins-f9c5f47cc-j25n7:/$ ls /var/lib/jenkins/
12FULL-2024-01-19_08-05
Jenkins 升级
docker方式不是的Jenkins可以直接替换docker镜像, rpm方式部署的应用可以在yum源中下载jenkins的安装包,然后rpm -Uvh更新升级。
升级前备份数据目录
Jenkins BlueOcean
BlueOcean是一套Jenkins的web ui页面,比原始的ui更美观和简洁一些。 需要安装插件,重启jenkins
Pipeline
Jenkins的核心是Pipeline(流水线项目),实现了Pipeline As Code。即我们将构建部署测试等步骤全部以代码的形式写到Jenkinsfile中。Jenkins在运行Pipeline任务的时候会按照Jenkinsfile中定义的代码顺序执行。写Jenkinsfile是一项很重的工作,如果稍不注意很容易造成Jenkins的流水线任务失败。Jenkinsfile类似于Dockerfile,具有一套特定的语法。
- 组织级别及团队间工作流复用;
- 便于 Pipeline 开发与维护
- 减少人工 Web 页面操作
Pipeline 的组成
- Jenkinsfile:描述 Pipeline 的代码文件
- Agent:Pipeline 的运行节点
- Stage:Pipeline 的阶段
Jenkinsfile
• 存放到 Jenkins 项目设置中(原生支持), 虽然实现了 PipelineAsCode 但是多个作业管理起来不太方便。
• 存放到 Git 版本控制系统中(原生支持,推荐方案): 即实现了 PipelineAsCode 也便于统一管理。
• 存放到 Nexus 制品仓库中(需要第三方插件)。
Pipeline 的语法
最佳实践是声明式语法中通过script{}标签来嵌入脚本式代码。
• 脚本式: 脚本式语法基本上都是通过 Groovy 代码来进行编写的。
• 声明式:声明式语法有一些现成的功能可以直接用,减少脚本式语法的复杂性。但是声明式语法也不是完美的,功能固定还是需要脚本式语法来进行扩展才能实现更加灵活的 Pipeline。
Pipeline 开发工具
片段生成器
当你安装好插件之后,很多插件提供了对应的代码块。 我们可以导航到片段生成器中找到对应的代码块生成。如果没有找到相关的语法,可以检查是否安装配置了相关的插件。
这个工具可以帮助我们生成Pipeline的部分代码,降低Pipeline的开发难度。流水线代码片段生成器, 非常好用。在这里可以找到每个插件以及Jenkins内置的方法的使用方法。使用片段生成器可以根据个人需要生成方法,有些方法来源于插件,则需要先安装相关的插件才能使用哦。
声明式语法生成器
声明式语法式特有的一套语法,如果声明式语法忘记了可以导航到声明式语法生成器 生成对应的代码片段。
全局变量参考
有时我们会获取一些项目的参数来做数据处理, 此时可以通过Jenkins提供的内置变量来获取对应的关键信息。
例如: 获取当前项目的名称、构建ID、作业URL等等信息。
1BUILD_NUMBER//构建号
2BUILD_ID//构建号
3BUILD_DISPLAY_NAME//构建显示名称
4JOB_NAME//项目名称
5
6EXECUTOR_NUMBER//执行器数量
7NODE_NAME//构建节点名称
8WORKSPACE//工作目录
9JENKINS_HOME//Jenkinshome
10JENKINS_URL//Jenkins地址
11BUILD_URL//构建地址
12JOB_URL//项目地址
13
14
15
16println(env)
17
18env.branchName="develop"
19env.commitID="${UUID.randomUUID().toString()}"
20env.commitID="${env.commitID.split("-")[0]}"
21currentBuild.displayName="#${env.branchName}-${env.commitID}"
22currentBuild.description="Triggerbyuserjenkins\nbranch:master"
23
24pipeline{
25
26 agent{label"build"}
27
28 stages{
29 stage("test"){
30 steps{
31 script{
32 echo"${BUILD_NUMBER}"
33 echo"${BUILD_ID}"
34 //currentBuild.displayName="#${env.branchName}-${env.commitID}"
35 //currentBuild.description="Triggerbyuserjenkins\nbranch:master"
36 echo"当前下载代码分支为:${env.branchName}"
37 }
38 }
39 }
40 }
41}
调试回放流水线
适合调试流水线代码,构建后点击回访可以看到上次构建运行的Pipeline代码,而我们可以通过此编辑器对代码进行修改和调试而不会影响原始的代码。
语法格式
Pipeline{}
声明式流水线的定义, 一个pipeline{}。
agent{}
• any: 运行在任一可用节点。
• none:当 pipeline 全局指定 agent 为 none,则根据每个 stage 中定义的 agent 运行(stage 必须指定)。
• label:在指定的标签的节点运行。(标签=分组)
• node:支持自定义流水线的工作目录。
1##一
2pipeline{
3agentany
4}
5
6##二
7pipeline{
8 agent{label "labelName"}
9}
10
11
12##三自定义节点
13pipeline{
14 agent{
15 node{
16 label"labelName"
17 customWorkspace"/opt/agent/workspace"
18 }
19 }
20}
stages{}
• 关系: stages > stage > steps > script
• 定义:
• stages:包含多个 stage 阶段
• stage:包含多个 steps 步骤
• steps: 包含一组特定的脚本(加上 script 后就可以实现在声明式脚本中嵌入脚本式语法了)
1pipeline{
2agent{label"build"}
3
4 stages{
5 stage("build"){
6 steps{
7 echo"hello"
8 }
9 }
10 }
11}
12
13##在阶段中定义agent
14
15pipeline{
16
17 agentnone
18
19 stages{
20 stage('Build'){
21 agent{label"build"}
22 steps{
23 echo"building......"
24 }
25 }
26 }
27}
post{}
• 定义: 根据流水线的最终状态匹配后做一些操作。
状态:
• always: 不管什么状态总是执行
• success: 仅流水线成功后执行
• failure: 仅流水线失败后执行
• aborted: 仅流水线被取消后执行
• unstable:不稳定状态,单侧失败等等
1pipeline{
2
3.....
4
5.....
6
7 post{
8 always{
9 script{
10 println("流水线结束后,经常做的事情")
11 }
12 }
13
14 success{
15 script{
16 println("流水线成功后,要做的事情")
17 }
18 }
19
20 failure{
21 script{
22 println("流水线失败后,要做的事情")
23 }
24 }
25
26 aborted{
27 script{
28 println("流水线取消后,要做的事情")
29 }
30 }
31 }
32}
trigger{}
流水线的触发方式
cron 定时触发: triggers { cron('H */7 * * 1-5') }
pollSCM: triggers { pollSCM('H */7 * * 1-5') }
1pipeline{
2 agentany
3 triggers{
4 cron('H*/7**1-5')
5 }
6
7 stages{
8 stage('build'){
9 steps{
10 echo'HelloWorld'
11 }
12 }
13 }
14}
input{}
message: 提示信息
ok: 表单中确认按钮的文本
submitter: 提交人,默认所有人可以
parameters: 交互时用户选择的参数
1pipeline{
2 agentany
3 stages{
4 stage('Deploy'){
5 input{
6 message"是否继续发布"
7 ok"Yes"
8 submitter"zeyang,aa"
9 parameters{
10 string(name:'ENVTYPE',defaultValue:'DEV',description:'envtype..[DEV/STAG/PROD]')
11 }
12 }
13 steps{
14 echo"Deployto${ENVTYPE},doing......."
15 }
16 }
17 }
18}
when{}
判断条件:根据条件判断是否运行 Stage
根据环境变量判断
根据表达式判断
根据条件判断(not/allOf/anyOf)
1pipeline{
2 agentany
3 stages{
4 stage('Build'){
5 steps{
6 echo'build......'
7 }
8 }
9 stage('Deploy'){
10 when{
11 environmentname:'DEPLOY_TO',value:'DEV'
12 }
13 steps{
14 echo'Deploying.......'
15 }
16 }
17 }
18}
19
20
21###allOf条件全部成立
22when{
23 allOf{
24 environmentname:'CAN_DEPLOY',value:'true'
25 environmentname:'DEPLOY_ENV',value:'dev'
26 }
27}
28
29
30###anyOf条件其中一个成立
31when{
32 anyOf{
33 environmentname:'CAN_DEPLOY',value:'true'
34 environmentname:'DEPLOY_ENV',value:'dev'
35 }
36}
parallel{}
场景: 自动化测试,多主机并行发布。
1pipeline{
2 agentany
3 stages{
4 stage('ParallelStage'){
5 failFasttrue
6 parallel{
7 stage('windows'){
8 agent{
9 abel"master"
10 }
11 steps{
12 echo"windows"
13 }
14 }
15 stage('linux'){
16 agent{
17 label"build"
18 }
19 steps{
20 echo"linux"
21 }
22 }
23 }
24 }
25 }
26}
FAQ:如何解决并发构建中的 workspace 问题?
1env.nworkspace="/opt/agent/test/${JOB_NAME}-${UUID.randomUUID().toString()}"
2
3
4pipeline{
5 agent{
6 node{
7 label"build"
8 customWorkspace"${env.nworkspace}"
9 }
10 }
11
12 stages{
13 stage("build"){
14
15 steps{
16 echo"${env.nworkspace}"
17 }
18 }
19
20 }
21}
22
23
24###输出
25demo-fec54ca7-81a5-452e-91b5-2a187ab3562b
示例代码
1pipeline{
2 //选择运行节点
3 //agentnone
4
5 agent{
6 label'build01'
7 }
8
9 //全局变量
10 environment{
11 VERSION="1.1.1"
12 }
13
14 //运行选项
15 options{
16 disableConcurrentBuilds()//禁止并发构建
17 buildDiscarderlogRotator(artifactDaysToKeepStr:'',
18 artifactNumToKeepStr:'',
19 daysToKeepStr:'5',
20 numToKeepStr:'10')//历史构建
21 }
22 //构建参数
23 parameters{
24 stringdefaultValue:'zeyang',description:'nameinfo',name:'NAME'
25 choicechoices:['dev','test','uat'],description:'envnames',name:'ENVNAME'
26 }
27
28 //构建触发器
29 triggers{
30 cron'H****'
31 }
32
33 stages{
34 stage('build'){
35 //stagelevelagent
36 agent{
37 label'linux'
38 }
39
40 //stagelevelenvlocal
41 environment{
42 VERSION="1.1.2"
43 }
44 steps{
45 echo'build'
46
47 //printenv
48 echo"${VERSION}"
49
50 //printparam
51 echo"${params.NAME}"
52 echo"${params.ENVNAME}"
53 }
54 }
55
56 stage('test'){
57 input{
58 message'请输入接下来的操作'
59 ok'ok'
60 submitterParameter'approve_user'
61 parameters{
62 choicechoices:['deploy','rollback'],name:'ops'
63 }
64 }
65
66 steps{
67 echo"test"
68 echo"执行的动作:${ops}"
69 echo"批准用户:${approve_user}"
70 script{
71 //由于下个stage无法获取ops的值,所以特此定义一个新的全局变量
72 //env.定义全局变量
73 env.OPS="${ops}"
74 }
75 }
76 }
77
78 stage('deploy'){
79 //是否运行
80 when{
81 environmentname:'OPS',value:'deploy'
82 }
83
84 steps{
85 script{
86 //groovyscript
87 println("hello")
88 //shell
89 result=shreturnStdout:true,script:'echo123'//123\n
90 println(result-"\n")
91
92 //environmentself
93 println("buildid:${BUILD_ID}")
94 println("jobname:${JOB_NAME}")
95 }
96 echo"deploy"
97 }
98 }
99
100 stage("parallelstage"){
101 failFasttrue
102 parallel{
103 stage("build01"){
104 steps{
105 echo"windows"
106 }
107 }
108
109 stage("build02"){
110 steps{
111 echo"linux"
112 }
113 }
114 }
115 }
116 }
117 post{
118 always{
119 echo"always"
120 }
121 success{
122 //Oneormorestepsneedtobeincludedwithineachcondition'sblock.
123 echo"success"
124 }
125 failure{
126 //Oneormorestepsneedtobeincludedwithineachcondition'sblock.
127 echo"failure"
128 }
129 }
130}
GitLab 代码更新触发构建流水线(发送邮件)
1webhookData = readJSON text: "${WebHookData}"
2
3env.branchName = webhookData["ref"] - "refs/heads/"
4env.projectUrl = webhookData ["project"]["git_http_url"]
5env.userEmail = "wcsb19900116@126.com"
6//env.userEmail = "410686931@qq.com"
7
8//pipline
9pipeline{
10 agent{
11 node{
12 label "build"
13 }
14 }
15
16 stages{
17 stage('CheckOut'){
18 steps{
19 script{
20 checkout scmGit(branches: [[name: "${env.branchName}"]],
21 extensions: [],
22 userRemoteConfigs: [[credentialsId: 'aea46925-65ad-4d9e-ab3d-6b4053ab37ac',
23 url: "${env.projectUrl}"]])
24 sh "ls -l"
25 }
26 }
27 }
28 }
29 post{
30 always {
31 script{
32 EmailUser("${env.userEmail}","${currentBuild.currentResult}")
33 }
34 }
35 }
36}
37
38
39
40def EmailUser(userEmail,status){
41 emailext body: """
42 <!DOCTYPE html>
43 <html>
44 <head>
45 <meta charset="UTF-8">
46 </head>
47 <body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0">
48
49 <table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma,Arial,Helvetica,sans-serif">
50 <tr>
51 <td><br/>
52 <b><font color="#0B610B">构建信息</font></b>
53 </td>
54 </tr>
55
56 <tr>
57 <td>
58 <ul>
59 <li>项目名称:${JOB_NAME}</li>
60 <li>构建编号:${BUILD_ID}</li>
61 <li>构建状态:${status}</li>
62 <li>项目地址:<a href="${BUILD_URL}">${BUILD_URL}</a></li>
63 <li>构建日志:<a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
64 </ul>
65 </td>
66 </tr>
67 <tr>
68 </table>
69 </body>
70 </html> """,
71 subject: "Jenkins-${JOB_NAME}项目构建信息",
72 to: userEmail
73}
共享库
https://jihulab.com/soulboy_devops/devops7-jenkinslib
src: 类似于 Java 的源码目录,执行流水线时会加载到 class 路径中
vars: 存放全局变量脚本,小的功能函数。
resources: 存放资源文件,类似于配置信息文件。
jenkins 中配置共享库 https://jenkins.idevops.site/manage/configure
流水线配置共享库 https://jenkins.idevops.site/job/devops-7-app1-service/configure
GitLab
部署
docker 部署
1# getdockerimage
2docker pull gitlab/gitlab-ce:15.0.3-ce.0
3
4# create data dir
5mkdir -p /data/devops6/gitlab/{config,logs,data}
6chmod +x -R /data/devops6/gitlab
7
8# run container
9dockerrun -itd --name gitlabce\
10-p443:443\
11-p8076:8076\
12--restart always\
13-v /data/devops6/gitlab/config:/etc/gitlab\
14-v /data/devops6/gitlab/logs:/var/log/gitlab\
15-v /data/devops6/gitlab/data:/var/opt/gitlab\
16gitlab/gitlab-ce:15.0.3-ce.0
17
18# view containerlogs
19docker logs -f gitlab
20
21# 如果遇到temp失败,进入容器:
22chmod +t /tmp
k8s 部署
1## Gitlab
2mkdir -p /data/storage/kubernetes/gitlab/{config,logs,data}
3chmod 777 -R /data/storage/kubernetes/gitlab/
4
5## GitLab Runner
6mkdir -p /data/storage/kubernetes/gitlab-runner
7chmod 777 -R /data/storage/kubernetes/gitlab-runner
8
9## Docker images
10docker pull gitlab/gitlab-ce:16.2.2-ce.0
11docker pull gitlab/gitlab-runner:alpine-v16.2.0
12
13## Load DockerImage
14kind load docker-image gitlab/gitlab-ce:16.2.2-ce.0 --name devopscluster
15kind load docker-image gitlab/gitlab-runner:alpine-v16.2.0 --name devopscluster
16
17## ArgoAPP
18kubectl -n argocd apply -f gitlab-argoapp.yaml
run.sh
1## NFS
2vim /etc/exports
3/data/storage/kubernetes/jenkins *(rw,no_root_squash,no_all_squash,insecure,sync)
4/data/storage/kubernetes/jenkins-workspace *(rw,no_root_squash,no_all_squash,insecure,sync)
5/data/storage/kubernetes/jenkins-build-cache *(rw,no_root_squash,no_all_squash,insecure,sync)
6/data/storage/kubernetes/gitlab *(rw,no_root_squash,no_all_squash,insecure,sync)
7/data/storage/kubernetes/gitlab-runner *(rw,no_root_squash,no_all_squash,insecure,sync)
8systemctl restart nfs
9
10## Gitlab
11mkdir -p /data/storage/kubernetes/gitlab/{config,logs,data}
12chmod 777 -R /data/storage/kubernetes/gitlab/
13
14## GitLab Runner
15mkdir -p /data/storage/kubernetes/gitlab-runner
16chmod 777 -R /data/storage/kubernetes/gitlab-runner
17
18## Docker images
19docker pull gitlab/gitlab-ce:16.2.2-ce.0
20docker pull gitlab/gitlab-runner:alpine-v16.2.0
21
22## LoadDockerImage
23kind load docker-image gitlab/gitlab-ce:16.2.2-ce.0 --name devopscluster
24kind load docker-image gitlab/gitlab-runner:alpine-v16.2.0 --name devopscluster
25
26## ArgoAPP
27kubectl -n argocd apply -f gitlab-argoapp.yaml
28
29### ADD DNS
30192.168.10.91 gitlab.idevops.site
31
32### 获取初始化密码
33[root@DevOps gitlab]# cat /data/storage/kubernetes/gitlab/config/initial_root_password
34Password: 3MRbxn5zb5G+8jIjiHvWlndgJb1RCKTc1hcexdFGroM=
35
36### 如果失败可以手动重置密码
37[root@localhost gitlab]# gitlab-rake "gitlab:password:reset[root]"
38Enter password:
39Confirm password:
40Password successfully updated for user with username root.
41
42### Web URL root 3MRbxn5zb5G+8jIjiHvWlndgJb1RCKTc1hcexdFGroM=
43http://gitlab.idevops.site
44
45### 运行gitlab-runner
46kubectl -n argocd apply -f gitlab-runner-argoapp.yaml
47
48### 注册runner
49# gitlab web创建runner http://gitlab.idevops.site/admin/runners
50gitlab-runner register --url http://gitlab.idevops.site --token glrt-tty7m6si3mPEm6mNMsTt --executor "shell" --non-interactive
51
52# 进入runner容器加入gitlab
53[root@DevOps gitlab]# kubectl get pod -n gitlab
54NAME READY STATUS RESTARTS AGE
55gitlab-655c6f48db-lnrr4 1/1 Running 0 153m
56gitlab-runner-595b678885-9k7zs 1/1 Running 0 3m57s
57[root@DevOps gitlab]# kubectl exec -it gitlab-runner-595b678885-9k7zs -n gitlab bash
58gitlab-runner-595b678885-9k7zs:/# gitlab-runner register --url http://gitlab.idevops.site --token glrt-tty7m6si3mPEm6mNMsTt --executor "shell" --non-interactive
59Runtime platform arch=amd64 os=linux pid=28 revision=782e15da version=16.2.0
60Running in system-mode.
61
62Verifying runner... is valid runner=tty7m6si3
63Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
64
65# 配置文件路径(容器中)
66gitlab-runner-595b678885-9k7zs:/# cat /etc/gitlab-runner/config.toml
67concurrent = 1
68check_interval = 0
69shutdown_timeout = 0
70
71[session_server]
72 session_timeout = 1800
73
74[[runners]]
75 name = "gitlab-runner-595b678885-9k7zs"
76 url = "http://gitlab.idevops.site"
77 id = 1
78 token = "glrt-tty7m6si3mPEm6mNMsTt"
79 token_obtained_at = 2024-01-22T10:15:07Z
80 token_expires_at = 0001-01-01T00:00:00Z
81 executor = "shell"
82 [runners.cache]
83 MaxUploadedArchiveSize = 0
84
85### git pull
86PS C:\Users\chao1\Desktop\devops> git clone http://gitlab.idevops.site/devops7/devops-7-app1-service.git
87
88### git push
89cd devops-7-app1-service
90git init
91git remote add origin http://gitlab.idevops.site/devops7/devops-7-app1-service.git
92git add .\readme.md
93git commit -m "readme"
94git push origin main
GitLab CI/CD
.gitlab-ci.yml 文件
1### 标准格式
2stages: # List of stages for jobs, and their order of execution
3 - build
4 - test
5 - deploy
6
7build-job: # This job runs in the build stage, which runs first.
8 stage: build
9 script:
10 - echo "Compiling the code..."
11 - echo "Compile complete."
12
13unit-test-job: # This job runs in the test stage.
14 stage: test # It only starts when the job in the build stage completes successfully.
15 script:
16 - echo "Running unit tests... This will take about 60 seconds."
17 - sleep 60
18 - echo "Code coverage is 90%"
19
20lint-test-job: # This job also runs in the test stage.
21 stage: test # It can run at the same time as unit-test-job (in parallel).
22 script:
23 - echo "Linting code... This will take about 10 seconds."
24 - sleep 10
25 - echo "No lint issues found."
26
27deploy-job: # This job runs in the deploy stage.
28 stage: deploy # It only runs when *both* jobs in the test stage complete successfully.
29 environment: production
30 script:
31 - echo "Deploying application..."
32 - echo "Application successfully deployed."
33
34
35
36### 输出变量
37stages:
38 - build
39 - test
40 - deploy
41
42build1:
43 tags:
44 - go
45 stage: build
46 script:
47 - echo "${CI_COMMIT_AUTHOR} ${CI_COMMIT_BRANCH} ${CI_PROJECT_ID}"
48
49test1:
50 tags:
51 - go
52 stage: test
53 script:
54 - echo "Do a test here"
55 - echo "For example run a test suite"
56
57test2:
58 tags:
59 - go
60 stage: test
61 script:
62 - echo "Do another parallel test here"
63 - echo "For example run a lint test"
64
65deploy1:
66 tags:
67 - go
68 stage: deploy
69 script:
70 - echo "Do your deploy here"
项目构建
Dockerfile
1FROM jenkins/inbound-agent
2USER root
3
4RUN rm -rf /etc/localtime && \
5 ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
6 echo 'Asia/Shanghai' > /etc/timezone
7
8COPY tools/ /tmp
9
10RUN apt update && apt install -y xz-utils net-tools telnet dnsutils zip unzip curl wget git
11
12RUN tar -C /usr/local/ -zxf /tmp/apache-maven-3.9.1-bin.tar.gz && \
13 unzip -d /usr/local/ /tmp/gradle-7.6.1-bin.zip && \
14 unzip -d /usr/local/ /tmp/sonar-scanner-cli-4.8.0.2856-linux.zip && \
15 tar -C /usr/local/ -xJf /tmp/node-v14.16.1-linux-x64.tar.xz
16
17ENV M2_HOME=/usr/local/apache-maven-3.9.1 \
18 GRADLE_HOME=/usr/local/gradle-7.6.1/ \
19 SONAR_SCANNER_HOME=/usr/local/sonar-scanner-4.8.0.2856-linux \
20 NODE_HOME=/usr/local/node-v14.16.1-linux-x64 \
21 PATH="$NODE_HOME/bin:$M2_HOME/bin:$GRADLE_HOME/bin:$SONAR_SCANNER_HOME/bin:$PATH"TH="/kaniko:/usr/local/node-v14.16.1-linux-x64/bin:/usr/local/apache-maven-3.9.1/bin:/usr/local/gradle-7.6.1/bin:/usr/local/sonar-scanner-4.8.0.2856-linux/bin:$PATH"
22
23RUN rm -fr /tmp
构建镜像
1docker build -t custom-build-agent:v1 .
将镜像加载到 kind 集群中
1kind load docker-image custom-build-agent:v1 --name devopscluster
jenkins-agent-argoapp.yaml
1apiVersion: argoproj.io/v1alpha1
2kind: Application
3metadata:
4 name: jenkins-agent
5 namespace: argocd
6spec:
7 destination:
8 namespace: jenkins
9 server: https://kubernetes.default.svc
10 project: default
11 source:
12 path: devops/jenkins/agent-manifests
13 repoURL: https://jihulab.com/rtsfan1024/myiac.git
14 targetRevision: main
15 directory:
16 recurse: false
17 syncPolicy:
18 automated:
19 prune: true
20 syncOptions:
21 - CreateNamespace=true
发布 jenkins agent
1kubectl -n argocd apply -f jenkins-agent-argoapp.yaml
进入容器内部
1[root@DevOps jenkins]# kubectl exec -it jenkinsagent-697bd7f7d8-h9xbx -n jenkins bash
2kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
3root@jenkinsagent-697bd7f7d8-tq6b9:/home/jenkins# cd /usr/local/
4root@jenkinsagent-697bd7f7d8-tq6b9:/usr/local# ls
5apache-maven-3.9.1 etc gradle-7.6.1 lib node-v14.16.1-linux-x64 share src
6bin games include man sbin sonar-scanner-4.8.0.2856-linux
7
8root@jenkinsagent-697bd7f7d8-h9xbx:/home/jenkins# mvn --version
9/tmp/jansi-2.4.0-4733c4039bc99738-libjansi.so.lck (No such file or directory)
10Apache Maven 3.9.1 (2e178502fcdbffc201671fb2537d0cb4b4cc58f8)
11Maven home: /usr/local/apache-maven-3.9.1
12Java version: 11.0.13, vendor: Eclipse Adoptium, runtime: /opt/java/openjdk
13Default locale: en, platform encoding: UTF-8
14OS name: "linux", version: "3.10.0-1160.105.1.el7.x86_64", arch: "amd64", family: "unix"
15
16
17### git clone
18root@jenkinsagent-697bd7f7d8-h9xbx:/home/jenkins# cd
19root@jenkinsagent-697bd7f7d8-h9xbx:~# pwd
20root@jenkinsagent-697bd7f7d8-h9xbx:~# git clone http://gitlab.idevops.site/devops/devops7-maven-service.git
21
22### mvn clean
23mvn clean package -s settings.xml
Sonarqube
SonarQube 是一种自动代码审查工具,可检测代码中的错误,漏洞和代码味道。 它可以与您现有的工作流程集成,以实现跨项目分支 和拉取请求的持续代码检查。
组件与服务组成
SonarQube Server** 启动3个主要进程
- Web 服务器 ,供开发人员,管理人员浏览高质量的快照并配置 SonarQube 实例
- 基于 Elasticsearch 的 Search Server 从 UI 进行搜索服务。
- Compute Engine 服务器,负责处理代码分析报告并将其保存在 SonarQube 数据库中。
- SonarQube 数据库 要存储:SonarQube 实例的配置(安全,插件设置等)项目,视图质量快照。
- 服务器上安装了多个 SonarQube 插件 ,可能包括语言,SCM,集成,身份验证和管理插件。
- 在持续集成服务器上运行一个或多个 SonarScanner ,以分析项目。
部署
1## NFS
2[root@DevOps ~]# vim /etc/exports
3/data/storage/kubernetes/sonarqube *
4[root@DevOps ~]# systemctl restart nfs
5
6## sonarqube
7mkdir -p /data/storage/kubernetes/sonarqube/{conf,logs,data,extensions}
8chmod 777 -R /data/storage/kubernetes/sonarqube/
9
10## Docker images
11docker pull sonarqube:9.9.0-community
12
13## LoadDockerImage
14kind load docker-image sonarqube:9.9.0-community --name devopscluster
15
16## ArgoAPP
17kubectl apply -f sonarqube-argoapp.yaml -n argocd
18
19### jenkins-agent 中已集成sonar-scanner
20[root@DevOps sonarqube]# kubectl get pod -n devops
21No resources found in devops namespace.
22[root@DevOps sonarqube]# kubectl get pod -n jenkins
23NAME READY STATUS RESTARTS AGE
24jenkins-7b47655c4c-6q8ql 1/1 Running 2 (45h ago) 4d21h
25jenkinsagent-697bd7f7d8-h9xbx 1/1 Running 0 44h
26[root@DevOps sonarqube]# kubectl exec -it jenkinsagent-697bd7f7d8-h9xbx -n jenkins bash
27kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
28root@jenkinsagent-697bd7f7d8-h9xbx:/home/jenkins# sonar-scanner-v
29bash: sonar-scanner-v: command not found
30root@jenkinsagent-697bd7f7d8-h9xbx:/home/jenkins# sonar-scanner -v
31INFO: Scanner configuration file: /usr/local/sonar-scanner-4.8.0.2856-linux/conf/sonar-scanner.properties
32INFO: Project root configuration file: NONE
33INFO: SonarScanner 4.8.0.2856
34INFO: Java 11.0.17 Eclipse Adoptium (64-bit)
35INFO: Linux 3.10.0-1160.105.1.el7.x86_64 amd64
本地扫描
进入 jenkins-agent 容器内部
1## 安装vim
2apt-get install vim
3
4## 进入项目源码目录新建配置文件 sonar-project.properties
5root@jenkinsagent-697bd7f7d8-h9xbx:~/devops7-maven-service# pwd
6/root/devops7-maven-service
7root@jenkinsagent-697bd7f7d8-h9xbx:~/devops7-maven-service# ls
8pom.xml settings.xml sonar-project.properties src target
9
10### sonar-project.properties
11#定义唯一的关键字
12sonar.projectKey=devops7-maven-service
13
14#定义项目名称
15sonar.projectName=devops7-maven-service
16
17#定义项目的版本信息
18sonar.projectVersion=1.0
19
20#指定扫描代码的目录位置(多个逗号分隔)
21sonar.sources=.
22
23#执行项目编码
24sonar.sourceEncoding=UTF-8
25
26#指定sonarServer
27sonar.host.url=http://sonar.idevops.site
28
29#认证信息
30sonar.login=admin
31sonar.password=admin123
32
33# java classes
34sonar.java.binaries=target/classes
35sonar.java.test.binaries=target/test-classes
36sonar.java.surefire.report=target/surefire-reports
37
38## 更改hosts解析
39vim /etc/hosts
40192.168.10.91 sonar.idevops.site
41
42##
43mvn clean package -s settings.xml
44sonar-scanner
report-task.txt
1root@jenkinsagent-697bd7f7d8-h9xbx:~/devops7-maven-service/.scannerwork# cat report-task.txt
2projectKey=devops7-maven-service
3serverUrl=http://sonar.idevops.site
4serverVersion=9.9.0.65466
5dashboardUrl=http://sonar.idevops.site/dashboard?id=devops7-maven-service
6ceTaskId=AY1isCg9TBkFDiGfCGZL
7ceTaskUrl=http://sonar.idevops.site/api/ce/task?id=AY1isCg9TBkFDiGfCGZL
SonarQube 扩展
Jenkins Pipeline 集成 sonarqube
1pipeline {
2 agent { label "build" }
3
4 stages {
5 stage("CheckOut"){
6 steps{
7 script {
8 println("CheckOut...")
9 checkout scmGit(branches: [[name: "${env.branchName}"]],
10 extensions: [],
11 userRemoteConfigs: [[
12 credentialsId: 'aea46925-65ad-4d9e-ab3d-6b4053ab37ac',
13 url: "${env.srcUrl}"]])
14 sh "ls -l "
15 }
16 }
17 }
18
19 stage("Build"){
20 steps{
21 script {
22 println("Build")
23 sh "${env.buildShell} && ls -l target/*"
24 }
25 }
26 }
27
28 stage("CodeScan"){
29 steps{
30 script{
31 withCredentials([usernamePassword(credentialsId: 'sonarqube-admin',
32 passwordVariable: 'SONAR_PASSWD',
33 usernameVariable: 'SONAR_USER')]) {
34 sh """
35 sonar-scanner \
36 -Dsonar.login=${SONAR_USER} \
37 -Dsonar.password=${SONAR_PASSWD} \
38 -Dsonar.host.url=http://sonar.idevops.site
39 """
40 }
41 }
42 }
43 }
44 }
45}
Jenkins 扩展插件集成 sonarqube
1、在 sonar 中创建 token http://sonar.idevops.site/account/security
1sqa_c0f1344e145b7816cdb039bb9a5c0cde45c3a43d
2、在 jenkins 中添加凭据 sonar-token
3、jenkins 安装插件 sonarqube scanner
4、jenkins 配置 SonarQube servers
1### 代码中可以使用如下变量
2SONAR_HOST_URL ## 在jenkins管理页面配置的sonar地址
3SONAR_AUTH_TOKEN ## 在jenkins管理页面配置的sonar认证信息
5、复制凭据 ID 填入 pipeline
注释掉gitlab中 sonar-project.properties 文件中的认证信息
1pipeline {
2 agent { label "build" }
3
4 stages {
5 stage("CheckOut"){
6 steps{
7 script {
8 println("CheckOut...")
9 checkout scmGit(branches: [[name: "${env.branchName}"]],
10 extensions: [],
11 userRemoteConfigs: [[
12 credentialsId: 'aea46925-65ad-4d9e-ab3d-6b4053ab37ac',
13 url: "${env.srcUrl}"]])
14 sh "ls -l "
15 }
16 }
17 }
18
19 stage("Build"){
20 steps{
21 script {
22 println("Build")
23 sh "${env.buildShell} && ls -l target/*"
24 }
25 }
26 }
27
28 stage("CodeScan"){
29 steps{
30 script{
31 // not used plugin
32 /*withCredentials([usernamePassword(credentialsId: 'sonarqube-admin',
33 passwordVariable: 'SONAR_PASSWD',
34 usernameVariable: 'SONAR_USER')]) {
35 sh """
36 sonar-scanner \
37 -Dsonar.login=${SONAR_USER} \
38 -Dsonar.password=${SONAR_PASSWD} \
39 -Dsonar.host.url=http://sonar.idevops.site
40 """
41 }*/
42
43 withSonarQubeEnv(credentialsId:'e2b63560-2def-4bf8-81f0-0042cd808184'){
44 sh """sonar-scanner\
45 -Dsonar.login=${SONAR_AUTH_TOKEN}\
46 -Dsonar.projectVersion=${env.branchName}
47 """
48 }
49 }
50 }
51 }
52 }
53
54 post{
55 always {
56 script {
57 cleanWs() //清理工作目录
58 }
59 }
60 }
61}
Nexus3
仓库类型:
代理仓库 : Maven、Npm等。用于存储外网公共仓库中的插件和依赖,不可进行修改和私自上传。
- proxy 代理仓库。
- hosted 私有仓库。
- group 仓库组,将多个仓库组合在一起,通过同一个 URL 对外提供。
部署 Nexus
1## NFS
2[root@DevOps ~]# vim /etc/exports
3/data/storage/kubernetes/nexus *(rw,no_root_squash,no_all_squash,insecure,sync)
4
5## sonarqube
6mkdir -p /data/storage/kubernetes/nexus
7chmod 777 -R /data/storage/kubernetes/nexus
8
9## Docker images
10docker pull sonatype/nexus3:3.60.0
11
12## LoadDockerImage
13kind load docker-image sonatype/nexus3:3.60.0 --name devopscluster
14
15## ArgoAPP
16kubectl -n argocd apply -f nexus-argoapp.yaml
17
18## 获取初始化密码 账户admin
19[root@DevOps nexus]# cat /data/storage/kubernetes/nexus/admin.password
207fe067ca-a245-4a2e-be99-69dad80f9729
搭建 Maven 私服
1、创建 devops7-release 仓库
2、修改 maven 的 setting.xml 文件,添加内容,账户密码为 nexus
1<server>
2 <id>maven-devops7-release</id>
3 <username>admin</username>
4 <password>admin123</password>
5 </server>
3、进入项目目录打包
1PS D:\Project\demo> mvn clean package
4、 使用 maven 指令上传制品
1## 注解版
2mvn deploy:deploy-file
3-DgroupId=com.example #pom中的groupId
4-DartifactId=demo #pom中的artifactId
5-Dversion=1.1.1 #pom中的版本号version
6-Dpackaging=jar #pom中打包方式
7-Dfile=target/demo-1.1.1.jar #本地文件
8-Durl=http://nexus.idevops.site/repository/devops7-release/ #仓库url
9-DrepositoryId=maven-devops7-release #对应的是setting.xml(认证)
10
11
12## 无注解版
13mvn deploy:deploy-file -DgroupId=com.example -DartifactId=demo -Dversion=1.1.1 -Dpackaging=jar -Dfile=target\\demo-1.1.1.jar -Durl=http://nexus.idevops.site/repository/devops7-release/ -DrepositoryId=maven-devops7-release
Jenkins 插件上传制品
1、安装 Nexus Artifact Uploader 插件
2、使用片段生成器生成 DSL。
1nexusArtifactUploader artifacts: [[artifactId: 'demo', classifier: '', file: 'target/demo-1.1.1.jar', type: 'jar']], credentialsId: 'f014679a-39cb-40f2-86a4-c282762e4d89', groupId: 'com.example', nexusUrl: 'nexus.idevops.site', nexusVersion: 'nexus3', protocol: 'http', repository: 'devops7-release', version: '1.1.1'
3、修改流水线
1pipeline {
2 agent { label "build" }
3
4 stages {
5 stage("CheckOut"){
6 steps{
7 script {
8 println("CheckOut...")
9 checkout scmGit(branches: [[name: "${env.branchName}"]],
10 extensions: [],
11 userRemoteConfigs: [[
12 credentialsId: 'aea46925-65ad-4d9e-ab3d-6b4053ab37ac',
13 url: "${env.srcUrl}"]])
14 sh "ls -l "
15 }
16 }
17 }
18
19 stage("Build"){
20 steps{
21 script {
22 println("Build")
23 sh "${env.buildShell} && ls -l target/*"
24 }
25 }
26 }
27
28 stage("CodeScan"){
29 steps{
30 script{
31 // not used plugin
32 /*withCredentials([usernamePassword(credentialsId: 'sonarqube-admin',
33 passwordVariable: 'SONAR_PASSWD',
34 usernameVariable: 'SONAR_USER')]) {
35 sh """
36 sonar-scanner \
37 -Dsonar.login=${SONAR_USER} \
38 -Dsonar.password=${SONAR_PASSWD} \
39 -Dsonar.host.url=http://sonar.idevops.site
40 """
41 }*/
42
43 withSonarQubeEnv(credentialsId: 'e2b63560-2def-4bf8-81f0-0042cd808184'){
44 sh """sonar-scanner\
45 -Dsonar.login=${SONAR_AUTH_TOKEN}\
46 -Dsonar.projectVersion=${env.branchName}
47 """
48 }
49 }
50 }
51 }
52
53 stage("PushArtifact"){
54 steps{
55 script{
56 PushArtifactByPlugin()
57 }
58 }
59 }
60 }
61
62 post{
63 always {
64 script {
65 cleanWs() //清理工作目录
66 }
67 }
68 }
69}
70
71def PushArtifactByPlugin(){
72 nexusArtifactUploader artifacts: [[artifactId: 'demo',
73 classifier: '',
74 file: 'target/demo-1.1.1.jar',
75 type: 'jar']],
76 credentialsId: 'f014679a-39cb-40f2-86a4-c282762e4d89',
77 groupId: 'com.example',
78 nexusUrl: 'nexus.idevops.site',
79 nexusVersion: 'nexus3',
80 protocol: 'http',
81 repository: 'devops7-release',
82 version: '1.1.1'
83}
Jenkins
安装 Docker
1### 安装依赖
2yum install -y yum-utils device-mapper-persistentdata lvm2
3
4### 配置yum源
5yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
6
7### 查看docker版本
8yum list docker-ce --showduplicates | sort -r
9
10### 安装docker
11yum -y install docker-ce-20.10.10-3.el7
12
13### 查看docker版本
14docker -v
15
16### 启动docker
17systemctl start docker
18
19### 查看docker状态
20systemctl status docker
21
22### 修改镜像仓库
23[root@ShortLinkPlatform ~]# cat /etc/docker/daemon.json
24 {
25 "registry-mirrors": [
26 "https://docker.hpcloud.cloud",
27 "https://docker.m.daocloud.io",
28 "https://docker.unsee.tech",
29 "https://docker.1panel.live",
30 "http://mirrors.ustc.edu.cn",
31 "https://docker.chenby.cn",
32 "http://mirror.azure.cn",
33 "https://dockerpull.org",
34 "https://dockerhub.icu",
35 "https://hub.rat.dev",
36 "https://proxy.1panel.live",
37 "https://docker.1panel.top",
38 "https://docker.m.daocloud.io",
39 "https://docker.1ms.run",
40 "https://docker.ketches.cn"
41 ]
42}
43
44
45### 重启
46systemctl restart docker
47
48### 检查安装结果
49docker info
安装部署 Jenkins
- 安装配置 Jenkins Server
- 配置 Jenkins 插件源
- 安装配置 Jenkins Agent
- 最新版本的 Jenkins(OpenJDK 需要 11 版本)
- 部署方式【rpm/docker/k8s】
1### 拉取镜像
2docker pull jenkins/jenkins:2.346.3-2-lts-jdk11
3docker pull jenkins/jenkins:lts
4
5
6### 创建持久化的数据目录,用于Jenkins数据存储
7# 创建目录并授权
8[root@jenkins ~]# mkdir -pv /data/devops/jenkins_home
9[root@jenkins ~]# mkdir -pv /data/devops/jenkins_home2
10[root@jenkins ~]# chmod 777 -R /data/devops/jenkins_home
11[root@jenkins ~]# chmod 777 -R /data/devops/jenkins_home2
12
13# 安装启动jenkins
14docker run -itd --name jenkins \
15-p 8080:8080 \
16-p 50000:50000 \
17-e JAVA_OPTS="-Dorg.apache.commons.jelly.tags.fmt.timezone='Asia/Shanghai'" \
18--privileged=true \
19-v /data/devops/jenkins_home:/var/jenkins_home jenkins/jenkins:2.346.3-2-lts-jdk11
20
21# 查看容器启动日志是否启动成功(顺便拿到激活密钥,也可以在文件里面拿到)
22docker logs -f jenkins
23
24# 访问web页面
25192.168.31.211:8080
26
27# 初始化(秘钥)
288a46501eb60b45b68ca70a6c6690bd39
29
30
31
32### 使用docker启动Jenkins Server
33* 开放8080端口(默认web访问端口)
34* 开放50000端口(默认agent与server通信端口)
35* 指定时区配置(Asia/Shanghai)
36* 挂载数据目录(JENKINS HOME)
配置 jenkins 插件源
1### 官方默认的源
2https://updates.jenkins.io/update-center.json
3
4### 原国外插件源地址
5https://updates.jenkins.io/update-center.json
6
7### 国内插件源地址
8https://mirror.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
启动 agent(JDK 环境)
最新的 Jenkins,Agent 需要安装 JDK17
- Jenkins Server 新加节点。
- 配置 agent 节点的参数
- 在要运行 agent 节点的服务器上,下载 agent 启动程序。
- 启动 Jenkins Agent 服务。(服务器需要有 JDK 环境)
1### 创建agent运行目录
2mkdir -pv /data/devops/jenkins_agent
3
4### 下载agent.jar
5[root@jenkins jenkins_agent]# cd /data/devops/jenkins_agent/
6[root@jenkins jenkins_agent]# wget http://192.168.31.211:8080/jnlpJars/agent.jar
7
8### 运行agent
9echo 2a600d6df4b2a75403c5c8cc37d6dc7d4ef529240abc771888f822a5b9689223 > secret-file
10
11curl -sO http://192.168.31.211:8080/jnlpJars/agent.jar
12
13java -jar agent.jar -url http://192.168.31.211:8080/ -secret @secret-file -name build01 -webSocket -workDir "/opt/jenkins"
14
15### 改造成bash脚本(后台启动)
16[root@jenkins jenkins_agent]# cat start.sh
17#!/bin/bash
18nohup java -jar agent.jar -url http://192.168.31.211:8080/ -secret @secret-file -name build01 -webSocket -workDir "/opt/jenkins" &
19
20
21### 启动
22[root@jenkins jenkins_agent]# sh -x start.sh
23
24### 查看启动日志
25[root@jenkins jenkins_agent]# tail nohup.out
26Feb 26, 2025 11:42:01 AM hudson.remoting.jnlp.Main$CuiListener status
27INFO: Connecting to 192.168.31.211:50000
28Feb 26, 2025 11:42:01 AM hudson.remoting.jnlp.Main$CuiListener status
29INFO: Trying protocol: JNLP4-connect
30Feb 26, 2025 11:42:01 AM org.jenkinsci.remoting.protocol.impl.BIONetworkLayer$Reader run
31INFO: Waiting for ProtocolStack to start.
32Feb 26, 2025 11:42:01 AM hudson.remoting.jnlp.Main$CuiListener status
33INFO: Remote identity confirmed: 09:23:71:d0:67:38🆎ad:d1:78:9b:fc:9a:0c:da:19
34Feb 26, 2025 11:42:02 AM hudson.remoting.jnlp.Main$CuiListener status
35INFO: Connected
jenkins 数据目录
目录 | 作用 |
---|---|
nodes | 节点信息 |
plugins | 插件目录 |
jobs | 流水线作业 |
user | jenkins 系统用户 |
war | jenkins 应用包 |
1### 数据目录
2[root@jenkins jenkins_agent]# ls /data/devops/jenkins_home/
3config.xml jenkins.telemetry.Correlator.xml secret.key.not-so-secret
4copy_reference_file.log jobs secrets
5hudson.model.UpdateCenter.xml logs updates
6identity.key.enc nodeMonitors.xml userContent
7jenkins.install.InstallUtil.installingPlugins nodes users
8jenkins.install.InstallUtil.lastExecVersion plugins war
9jenkins.install.UpgradeWizard.state queue.xml.bak
10jenkins.model.JenkinsLocationConfiguration.xml secret.key
jenkins 项目类型
自由风格的项目、流水线
安装 pipeline
1pipeline {
2 agent any
3
4 stages {
5 stage('Hello') {
6 steps {
7 echo 'Hello World'
8 echo "${VERSION}"
9 echo "${params.VERSION}"
10 echo "${env.VERSION}"
11 }
12 }
13 }
14}
触发远程构建
1### 构建(无参数)
2http://192.168.31.211:8080/job/demo-pipeline/build?token=leon666
3
4### 构建(传递参数)
5http://192.168.31.211:8080/job/demo-pipeline/buildWithParameters?token=leon666
6
7### curl命令触发(无参数)
8curl -uadmin:admin "http://192.168.31.211:8080/job/demo-pipeline/build?token=leon666"
9
10### curl命令触发(传递参数)选项参数的值只能是列表中定义的
11curl -uleondevops:aaaasss "http://192.168.31.211:8080/job/demo-pipeline/buildWithParameters?token=leon666&VERSION=4.4.4&ENV_NAME=dev"
Jenkins 项目归类
- view 视图
- floder 文件夹
- 根据团队、业务类型等方式进行分类
Jenkins 用户管理(RBAC)
用户来源 | 说明 |
---|---|
本地存储数据库 | 本地 jenkins 用户 |
其他认证系统 | gitlab/ldap/github |
用户操作
增删改查和禁用
Role-based Authorization StrategyVersion
为用户指定属于他能看到的作业
角色管理
分配角色(权限与用户绑定)
文件夹-> 视图(正则匹配到任务)-> 任务
使用张三登录
Jenkins 凭据管理
数据备份
通过 Jenkins 插件备份
1### 进入容器查看
2[root@jenkins ~]# docker exec -it jenkins bash
3
4### 备份目录包含插件
5/var/jenkins_home/FULL-2025-02-26_11-22
版本升级
Jenkins BlueOcean
Jenkins 可视化页面,美观
Pipeline(流水线)
jenkinsfile 定义了工作流程。
Pipeline 组成
第一条流水线
安装 Pipeline 插件
安装 Pipeline 查看阶段状态的插件
创建 Pipeline 类型项目;
1pipeline {
2 agent any
3
4 stages {
5 stage('build') {
6 steps {
7 echo 'build'
8 }
9 }
10 stage('test') {
11 steps {
12 echo 'test'
13 }
14 }
15 stage('deploy') {
16 steps {
17 echo 'deploy'
18 }
19 }
20 }
21}
Pipeline 开发工具
- 片段生成器(方便参考语法)
- 声明式语法生成器(参考语法)
- 全局变了参考(全局变量参考)
- Pipeline 回放 (Debug)
片段生成器
1pipeline {
2 agent any
3
4 stages {
5 stage('build') {
6 steps {
7 echo 'build'
8 }
9 }
10 stage('test') {
11 steps {
12 echo 'test'
13 }
14 }
15 stage('deploy') {
16 steps {
17 script{
18 // groovy script
19 println("deploy")
20 // shell
21 result = sh returnStdout: true, script: 'echo 123'
22 // 删除换行
23 println(result - '\n')
24 }
25 echo "deploy"
26 }
27 }
28 }
29}
声明式语法生成器(参考语法)
1pipeline {
2 // 选择运行的节点
3 agent {
4 label 'master'
5 }
6
7 stages {
8 stage('build') {
9 steps {
10 echo 'build'
11 }
12 }
13 stage('test') {
14 steps {
15 echo 'test'
16 }
17 }
18 stage('deploy') {
19 steps {
20 script{
21 // groovy script
22 println("deploy")
23 // shell
24 result = sh returnStdout: true, script: 'echo 123'
25 println(result - '\n')
26 }
27 echo "deploy"
28 }
29 }
30 }
31}
全局变了参考(全局变量参考)
1pipeline {
2 // 选择运行的节点
3 agent {
4 label 'master'
5 }
6
7 stages {
8 stage('build') {
9 steps {
10 echo 'build'
11 }
12 }
13 stage('test') {
14 steps {
15 echo 'test'
16 }
17 }
18 stage('deploy') {
19 steps {
20 script{
21 // groovy script
22 println("deploy")
23 // shell
24 result = sh returnStdout: true, script: 'echo 123'
25 println(result - '\n')
26
27 // environment self
28 println("build id: ${BUILD_ID}")
29 println("job name: ${JOB_NAME}")
30 }
31 echo "deploy"
32 }
33 }
34 }
35}
Pipeline 核心语法
核心语法 | 说明 |
---|---|
pipeline{} | 定义整条流水线 |
agent{} | 运行的节点 |
stages{} | 阶段 |
post{} | 后处理 |
environment{} | 环境变量 |
options | 运行时选项 |
parameters{} | 参数化构建 |
agent 语法(运行节点)
如果全局 Agent 没有定义,或者 none,局部必须定义 Agent,否则运行不了。
stages 语法(运行阶段)
post 语法(最终操作)
1pipeline {
2 // 选择运行的节点
3 agent {
4 label 'master'
5 }
6
7 stages {
8 stage('build') {
9 steps {
10 echo 'build'
11 }
12 }
13 stage('test') {
14 steps {
15 echo 'test'
16 }
17 }
18 stage('deploy') {
19 steps {
20 script{
21 // groovy script
22 println("deploy")
23 // shell
24 result = sh returnStdout: true, script: 'echo 123'
25 println(result - '\n')
26
27 // environment self
28 println("build id: ${BUILD_ID}")
29 println("job name: ${JOB_NAME}")
30 }
31 echo "deploy"
32 }
33 }
34 }
35
36 post {
37 success {
38 // One or more steps need to be included within each condition's block.
39 echo "success"
40 }
41 failure {
42 // One or more steps need to be included within each condition's block.
43 echo "failure"
44 }
45 }
46}
environment{}(环境变量)
1pipeline {
2 // 选择运行的节点
3 agent {
4 label 'master'
5 }
6
7 //环境变量
8 environment {
9 VERSION = "1.1.1"
10 }
11
12 stages {
13 stage('build') {
14 // stage level env local
15 environment {
16 VERSION = "1.1.2"
17 }
18 steps {
19 echo 'build'
20 // print env
21 echo "${VERSION}"
22 }
23 }
24 stage('test') {
25 steps {
26 echo 'test'
27 }
28 }
29 stage('deploy') {
30 steps {
31 script{
32 // groovy script
33 println("deploy")
34 // shell
35 result = sh returnStdout: true, script: 'echo 123'
36 println(result - '\n')
37
38 // environment self
39 println("build id: ${BUILD_ID}")
40 println("job name: ${JOB_NAME}")
41 }
42 echo "deploy"
43 }
44 }
45 }
46
47 post {
48 success {
49 // One or more steps need to be included within each condition's block.
50 echo "success"
51 }
52 failure {
53 // One or more steps need to be included within each condition's block.
54 echo "failure"
55 }
56 }
57}
options{}(运行时选项)
需要执行代码,执行一次之后流水线配置中才能显示。
1pipeline {
2 // 选择运行的节点
3 agent {
4 label 'master'
5 }
6
7 // 环境变量
8 environment {
9 VERSION = "1.1.1"
10 }
11
12 // 流水线运行时选项
13 options {
14 // 禁止并发构建
15 disableConcurrentBuilds()
16 // 历史构建5天,最多10个
17 buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '5', numToKeepStr: '10')
18 }
19
20 stages {
21 stage('build') {
22 // stage level env local
23 environment {
24 VERSION = "1.1.2"
25 }
26 steps {
27 echo 'build'
28 // print env
29 echo "${VERSION}"
30 }
31 }
32 stage('test') {
33 steps {
34 echo 'test'
35 }
36 }
37 stage('deploy') {
38 steps {
39 script{
40 // groovy script
41 println("deploy")
42 // shell
43 result = sh returnStdout: true, script: 'echo 123'
44 println(result - '\n')
45
46 // environment self
47 println("build id: ${BUILD_ID}")
48 println("job name: ${JOB_NAME}")
49 }
50 echo "deploy"
51 }
52 }
53 }
54
55 post {
56 success {
57 // One or more steps need to be included within each condition's block.
58 echo "success"
59 }
60 failure {
61 // One or more steps need to be included within each condition's block.
62 echo "failure"
63 }
64 }
65}
parameters{}(参数化构建)
1pipeline {
2 // 选择运行的节点
3 agent {
4 label 'master'
5 }
6
7 // 环境变量
8 environment {
9 VERSION = "1.1.1"
10 }
11
12 // 流水线运行时选项
13 options {
14 // 禁止并发构建
15 disableConcurrentBuilds()
16 // 历史构建5天,最多10个
17 buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '5', numToKeepStr: '10')
18 }
19
20 // 构建参数
21 parameters {
22 string defaultValue: 'Leon', description: 'name info', name: 'NAME'
23 choice choices: ['dev', 'test', 'uat'], description: 'env names', name: 'ENVNAME'
24 }
25
26 stages {
27 stage('build') {
28 // stage level env local
29 environment {
30 VERSION = "1.1.2"
31 }
32 steps {
33 echo 'build'
34
35 // print env
36 echo "${VERSION}"
37
38 // print param
39 echo "${params.NAME}"
40 echo "${params.ENVNAME}"
41
42 }
43 }
44 stage('test') {
45 steps {
46 echo 'test'
47 }
48 }
49 stage('deploy') {
50 steps {
51 script{
52 // groovy script
53 println("deploy")
54 // shell
55 result = sh returnStdout: true, script: 'echo 123'
56 println(result - '\n')
57
58 // environment self
59 println("build id: ${BUILD_ID}")
60 println("job name: ${JOB_NAME}")
61 }
62 echo "deploy"
63 }
64 }
65 }
66
67 post {
68 success {
69 // One or more steps need to be included within each condition's block.
70 echo "success"
71 }
72 failure {
73 // One or more steps need to be included within each condition's block.
74 echo "failure"
75 }
76 }
77}
triggers{}(触发器)
1pipeline {
2 // 选择运行的节点
3 agent {
4 label 'master'
5 }
6
7 // 环境变量
8 environment {
9 VERSION = "1.1.1"
10 }
11
12 // 流水线运行时选项
13 options {
14 // 禁止并发构建
15 disableConcurrentBuilds()
16 // 历史构建5天,最多10个
17 buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '5', numToKeepStr: '10')
18 }
19
20 // 构建参数
21 parameters {
22 string defaultValue: 'Leon', description: 'name info', name: 'NAME'
23 choice choices: ['dev', 'test', 'uat'], description: 'env names', name: 'ENVNAME'
24 }
25
26 // 参数构建
27 triggers {
28 cron 'H * * * *'
29 }
30
31 stages {
32 stage('build') {
33 // stage level env local
34 environment {
35 VERSION = "1.1.2"
36 }
37 steps {
38 echo 'build'
39
40 // print env
41 echo "${VERSION}"
42
43 // print param
44 echo "${params.NAME}"
45 echo "${params.ENVNAME}"
46
47 }
48 }
49 stage('test') {
50 steps {
51 echo 'test'
52 }
53 }
54 stage('deploy') {
55 steps {
56 script{
57 // groovy script
58 println("deploy")
59 // shell
60 result = sh returnStdout: true, script: 'echo 123'
61 println(result - '\n')
62
63 // environment self
64 println("build id: ${BUILD_ID}")
65 println("job name: ${JOB_NAME}")
66 }
67 echo "deploy"
68 }
69 }
70 }
71
72 post {
73 success {
74 // One or more steps need to be included within each condition's block.
75 echo "success"
76 }
77 failure {
78 // One or more steps need to be included within each condition's block.
79 echo "failure"
80 }
81 }
82}
input{}(输入)
1pipeline {
2 // 选择运行的节点
3 agent {
4 label 'master'
5 }
6
7 // 环境变量
8 environment {
9 VERSION = "1.1.1"
10 }
11
12 // 流水线运行时选项
13 options {
14 // 禁止并发构建
15 disableConcurrentBuilds()
16 // 历史构建5天,最多10个
17 buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '5', numToKeepStr: '10')
18 }
19
20 // 构建参数
21 parameters {
22 string defaultValue: 'Leon', description: 'name info', name: 'NAME'
23 choice choices: ['dev', 'test', 'uat'], description: 'env names', name: 'ENVNAME'
24 }
25
26 // 参数构建
27 triggers {
28 cron 'H * * * *'
29 }
30
31 stages {
32 stage('build') {
33 // stage level env local
34 environment {
35 VERSION = "1.1.2"
36 }
37 steps {
38 echo 'build'
39
40 // print env
41 echo "${VERSION}"
42
43 // print param
44 echo "${params.NAME}"
45 echo "${params.ENVNAME}"
46
47 }
48 }
49 stage('test') {
50 // input
51 input {
52 message '请输入接下来的操作'
53 ok 'ok'
54 submitterParameter 'approve_user'
55 parameters {
56 choice choices: ['deploy', 'rollback'], name: 'ops'
57 }
58 }
59 steps {
60 echo 'test'
61 // 执行的动作:deploy
62 echo "执行的动作:${ops}"
63 // 批准用户:leondevops
64 echo "批准用户:${approve_user}"
65 }
66 }
67 stage('deploy') {
68 steps {
69 script{
70 // groovy script
71 println("deploy")
72 // shell
73 result = sh returnStdout: true, script: 'echo 123'
74 println(result - '\n')
75
76 // environment self
77 println("build id: ${BUILD_ID}")
78 println("job name: ${JOB_NAME}")
79 }
80 echo "deploy"
81 }
82 }
83 }
84
85 post {
86 success {
87 // One or more steps need to be included within each condition's block.
88 echo "success"
89 }
90 failure {
91 // One or more steps need to be included within each condition's block.
92 echo "failure"
93 }
94 }
95}
when{}(什么情况下运行)
1pipeline {
2 // 选择运行的节点
3 agent {
4 label 'master'
5 }
6
7 // 环境变量
8 environment {
9 VERSION = "1.1.1"
10 }
11
12 // 流水线运行时选项
13 options {
14 // 禁止并发构建
15 disableConcurrentBuilds()
16 // 历史构建5天,最多10个
17 buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '5', numToKeepStr: '10')
18 }
19
20 // 构建参数
21 parameters {
22 string defaultValue: 'Leon', description: 'name info', name: 'NAME'
23 choice choices: ['dev', 'test', 'uat'], description: 'env names', name: 'ENVNAME'
24 }
25
26 // 参数构建
27 triggers {
28 cron 'H * * * *'
29 }
30
31 stages {
32 stage('build') {
33 // stage level env local
34 environment {
35 VERSION = "1.1.2"
36 }
37 steps {
38 echo 'build'
39
40 // print env
41 echo "${VERSION}"
42
43 // print param
44 echo "${params.NAME}"
45 echo "${params.ENVNAME}"
46
47 }
48 }
49 stage('test') {
50 // input
51 input {
52 message '请输入接下来的操作'
53 ok 'ok'
54 submitterParameter 'approve_user'
55 parameters {
56 choice choices: ['deploy', 'rollback'], name: 'ops'
57 }
58 }
59 steps {
60 echo 'test'
61 // 执行的动作:deploy
62 echo "执行的动作:${ops}"
63 // 批准用户:leondevops
64 echo "批准用户:${approve_user}"
65 script {
66 // 由于下个stage无法获取ops的值,所以特此定义一个新的全局变量
67 env.OPS = "${ops}"
68 }
69 }
70 }
71 stage('deploy') {
72 // when条件判断
73 when {
74 environment name: 'OPS', value: 'deploy'
75 }
76
77 steps {
78 script{
79 // groovy script
80 println("deploy")
81 // shell
82 result = sh returnStdout: true, script: 'echo 123'
83 println(result - '\n')
84
85 // environment self
86 println("build id: ${BUILD_ID}")
87 println("job name: ${JOB_NAME}")
88 }
89 echo "deploy"
90 }
91 }
92 }
93
94 post {
95 success {
96 // One or more steps need to be included within each condition's block.
97 echo "success"
98 }
99 failure {
100 // One or more steps need to be included within each condition's block.
101 echo "failure"
102 }
103 }
104}
parallel{}(并行构建)
并行运行:
1pipeline {
2 // 选择运行的节点
3 agent {
4 label 'master'
5 }
6
7 // 环境变量
8 environment {
9 VERSION = "1.1.1"
10 }
11
12 // 流水线运行时选项
13 options {
14 // 禁止并发构建
15 disableConcurrentBuilds()
16 // 历史构建5天,最多10个
17 buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '5', numToKeepStr: '10')
18 }
19
20 // 构建参数
21 parameters {
22 string defaultValue: 'Leon', description: 'name info', name: 'NAME'
23 choice choices: ['dev', 'test', 'uat'], description: 'env names', name: 'ENVNAME'
24 }
25
26 // 参数构建
27 triggers {
28 cron 'H * * * *'
29 }
30
31 stages {
32 stage('build') {
33 // stage level env local
34 environment {
35 VERSION = "1.1.2"
36 }
37 steps {
38 echo 'build'
39
40 // print env
41 echo "${VERSION}"
42
43 // print param
44 echo "${params.NAME}"
45 echo "${params.ENVNAME}"
46
47 }
48 }
49 stage('test') {
50 // input
51 input {
52 message '请输入接下来的操作'
53 ok 'ok'
54 submitterParameter 'approve_user'
55 parameters {
56 choice choices: ['deploy', 'rollback'], name: 'ops'
57 }
58 }
59 steps {
60 echo 'test'
61 // 执行的动作:deploy
62 echo "执行的动作:${ops}"
63 // 批准用户:leondevops
64 echo "批准用户:${approve_user}"
65 script {
66 // 由于下个stage无法获取ops的值,所以特此定义一个新的全局变量
67 env.OPS = "${ops}"
68 }
69 }
70 }
71 stage('deploy') {
72 // when条件判断
73 when {
74 environment name: 'OPS', value: 'deploy'
75 }
76
77 steps {
78 script{
79 // groovy script
80 println("deploy")
81 // shell
82 result = sh returnStdout: true, script: 'echo 123'
83 println(result - '\n')
84
85 // environment self
86 println("build id: ${BUILD_ID}")
87 println("job name: ${JOB_NAME}")
88 }
89 echo "deploy"
90 }
91 }
92
93 stage("parallelstage"){
94 // 第一个stage失败则不会用第二个stage
95 failFast true
96
97 parallel {
98 stage("build01"){
99 steps {
100 echo "windows"
101 }
102 }
103
104 stage("build02"){
105 steps {
106 echo "linux"
107 }
108 }
109 }
110 }
111 }
112
113 post {
114 success {
115 // One or more steps need to be included within each condition's block.
116 echo "success"
117 }
118 failure {
119 // One or more steps need to be included within each condition's block.
120 echo "failure"
121 }
122 }
123}
Groovy(脚本式语法)编码
适度用 Groovy 扩展流水线。Jenkins 的流水线本身就是 Groovy。
- Groovy 功能强大,可选类型和动态语言,支持 Java 平台。
- 简洁且简单易学的语法。
- 可以与任何 Java 程序顺利集成、包括脚本编写功能、特定领域语言编写,运行时和编译时元编程以及函数式编程。
数据类型
String
1//String 动态识别数据的类型
2String name = "soulboy"
3name = "soulboy"
4
5pipeline {
6 agent any
7 stages{
8 stage("run"){
9 steps{
10 script{
11 // script
12 println(name)
13
14 // buname-appname-type
15 job_name = "devops05-app-service_CI"
16 // ["devops05","app","service_CI"]
17 bu_name = job_name.split('-')[0]
18 println(bu_name)
19
20 // contains
21 println(job_name.contains("CI"))
22
23 // size/length
24 println("size: ${job_name.size()}")
25 println("length: ${job_name.length()}")
26
27 // endsWith()
28 println("enswitch CI: ${job_name.endsWith('CI')}")
29 }
30 }
31 }
32 }
33}
List
1//List 动态识别数据的类型
2tools = ["gitlab","jenkins","maven","sonar"]
3
4pipeline {
5 agent any
6 stages{
7 stage("run"){
8 steps{
9 script{
10 // script
11 println(tools)
12
13 // add(不会改变)
14 println(tools + "k8s")
15 // 会改变
16 println(tools << "ansible")
17
18 // increase
19 println(tools - "maven")
20 println(tools)
21
22 // 兑现赋值(顺便加元素)
23 tools.add("maven")
24 println(tools)
25 println(tools.getClass())
26
27 // contains
28 println(tools.contains("jenkins"))
29
30 // length
31 println(tools.size())
32
33 // index
34 println(tools[0])
35 println(tools[-1])
36 }
37 }
38 }
39 }
40}
Map
1//Map 动态识别数据的类型
2user_info = ["id": 100, "name": "jenkins"]
3
4pipeline {
5 agent any
6 stages{
7 stage("run"){
8 steps{
9 script{
10 // script
11 println(user_info)
12
13 //get name
14 println(user_info["name"])
15 println(user_info["id"])
16
17 // = 赋值
18 user_info["name"] = "jenkinsX"
19 println(user_info)
20
21 // key
22 println(user_info.containsKey("name"))
23 println(user_info.containsValue(100))
24
25 // keys
26 println(user_info.keySet())
27
28 // remove
29 user_info.remove("name")
30 println(user_info)
31
32 }
33 }
34 }
35 }
36}
条件语句
if 语句
1branchName = "dev"
2
3pipeline {
4 agent any
5 stages{
6 stage("run"){
7 steps{
8 script{
9 // script
10
11 // 构建名字
12 currentBuild.displayName = branchName
13 if (branchName == "dev"){
14 println("deploy to dev")
15 currentBuild.description = "deploy to dev..."
16 } else if (branchName == "master"){
17 println("deploy to stag...")
18 currentBuild.description = "deploy to stag..."
19 } else {
20 currentBuild.description = "error..."
21 println("error...")
22 }
23 }
24 }
25 }
26 }
27}
for 语句
1users = [
2 ["name": "soulboy1", "role": "dev"],
3 ["name": "soulboy2", "role": "admin"],
4 ["name": "soulboy3", "role": "ops"],
5 ["name": "soulboy4", "role": "test"]
6 ]
7
8pipeline {
9 agent any
10 stages{
11 stage("run"){
12 steps{
13 script{
14
15 // script
16 user_names = []
17 for (i in users) {
18 println(i["name"])
19 user_names << i["name"]
20 }
21
22 // [soulboy1, soulboy2, soulboy3, soulboy4]
23 println(user_names)
24
25 // times
26 10.times {
27 println('hello')
28 }
29 10.times { i ->
30 println(i)
31 }
32 }
33 }
34 }
35 }
36}
while 语句
1sleep = true
2
3pipeline {
4 agent any
5 stages{
6 stage("run"){
7 steps{
8 script{
9
10 // script
11 while(sleeps){
12 println("sleep....")
13 }
14 }
15 }
16 }
17 }
18}
异常处理
- try
- catch
- finally
1pipeline {
2 agent any
3
4 stages{
5 stage("run"){
6 steps{
7 script{
8
9 // script
10
11 try {
12 println(a) // not define a error
13 } catch(Exception e){
14 println(e)
15 error "error..."
16 } finally {
17 println("always...execute")
18 }
19
20 }
21 }
22 }
23 }
24}
函数(提高复用性)
1users = [
2 ["id": 1, "name": "jenkins"],
3 ["id": 2, "name": "app"],
4 ["id": 3, "name": "dev"],
5 ["id": 4, "name": "show"]
6 ]
7pipeline {
8 agent any
9
10 stages{
11 stage("run"){
12 steps{
13 script{
14 // script
15 name = GetUserNameById(1)
16 println(name)
17 }
18 }
19 }
20 }
21}
22
23
24// 函数:GetUserName
25def GetUserNameById(id){
26 for(i in users){
27 if(i["id"] == id){
28 return i["name"]
29 }
30 }
31 return "null"
32}
SharedLibrary(共享库)