目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

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 不是什么

流程
工具
管理手段

核心阶段

1、 版本控制代码提交者是谁、哪些代码导致的故障、快速回滚……

rebase:变基,用于将一个分支的提交移动到另一个分支的末尾,使提交历史更加线性和整洁。它可以用来更新本地分支以匹配远程分支、合并提交或重新排列提交等操作
chery-pick:挑选提交,用于从一个分支中选择一个或多个提交应用到当前分支。它可以用于合并某个提交或一组提交,而无需合并整个分支
reset:用于撤销提交(回退),将分支指针和工作目录恢复到指定的提交状态。它有不同的模式,如 --soft、--mixed 和 --hard,可以选择保留或丢弃不同程度的更改
stash:用于保存当前工作目录的临时更改,以便切换到其他分支或执行其他操作。它可以将未提交的更改暂存起来,可以通过 stast pop 恢复
bisect:用于帮助定位引入错误或问题的提交。它通过二分查找的方式,快速定位问题的提交,从而可以更容易地找到导致问题的具体提交

Git 代码管理最佳实践

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 自动重启机制

  1. 由于所依赖的服务未 ready,业务进程会退出,状态码非 0
  2. K8s 检测到容器异常退出,自动重启
  3. 所依赖的服务仍未 ready,继续重启......
  4. 直到依赖的服务 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。使用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: 存放资源文件,类似于配置信息文件。

image.png

jenkins 中配置共享库 https://jenkins.idevops.site/manage/configure
image.png

流水线配置共享库 https://jenkins.idevops.site/job/devops-7-app1-service/configure
image.png

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

image.png

2、在 jenkins 中添加凭据 sonar-token
image.png

3、jenkins 安装插件 sonarqube scanner
image.png

4、jenkins 配置 SonarQube servers
image.png

1### 代码中可以使用如下变量
2SONAR_HOST_URL ## 在jenkins管理页面配置的sonar地址
3SONAR_AUTH_TOKEN ## 在jenkins管理页面配置的sonar认证信息

5、复制凭据 ID 填入 pipeline
注释掉gitlab中 sonar-project.properties 文件中的认证信息

image.png

 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 私服

image.png

1、创建 devops7-release 仓库
image.png

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

image.png

Jenkins 插件上传制品

1、安装 Nexus Artifact Uploader 插件
image.png

2、使用片段生成器生成 DSL。image.png

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}

image.png
image.png


作者:Soulboy