目录

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环境
![](https://abc1024.oss-cn- shanghai.aliyuncs.com/Picture/DevOps/DevOps%E5%85%A8%E6%B5%81%E7%A8%8B%E6%A1%88%E4%BE%8B.png)

深入 Dockerfile 和镜像构建

容器技术的核心原理

时间线技术名功能描述
1979Chroot文件系统隔离
2000FreeBSD Jails早期的容器技术
2002Linux Namespaces进程隔离
2004Solaris Zones快照、克隆
2006Google Process Containers资源隔离(CPU、内存、IO、网络)
2007Linux Control GroupProcess Containers 重命名并合并到 Kernel 2.6.24
2008LXC Linux Containers使用 namespace 和 cgroups 实现的第一个容器管理
2013Docker最初使用 LXC Linux Containers 实现,后来用 libcontainer 取代

容器和镜像的关系
镜像是容器的定义,包括文件系统、环境变量 以及默认的启动命令
容器是是镜像的实例,使用同一个镜像启动的 容器是相互隔离的,不同的容器之间也是相互 隔离的

容器的特定:不可变特性
• 容器是不可变的(在不同环境有相同表现)
• 容器被设计为一次性(临时的)
• 新启动的容器是镜像最初始状态
• 数据被存储在容器外部

Dockerfile
• 一组指令的描述文件,执行这些命令可以在某一个基础镜像的基础上创建新的 Docker 镜像
• Docker 可以通过读取 Dockerfile 构建镜像
• 用户通过 docker build 命令启动镜像构建过程
构建上下文:"." 代表当前目录,Docker 会将上下文的内容 传输到 Docker daemon,使用 ADD 或 COPY 会将上下文的 内容复制到镜像中。注意,千万不要使用 / 作为构建上下文, 还可以使用 .dockerignore 来忽略特定目录,如项目中的 node_module 目录,提高构建速度

### Dockerfile 内容
FROM debian:latest
RUN apt-get update && apt-get install nginx -y
CMD ["nginx", "-g", "daemon off;"]

### 构建镜像
docker build -t nginx:v1 -f /tmp/demo-1/Dockerfile .

### 导出镜像为tarball (解压发现有两层镜像)
mkdir /Downloads
docker save nginx:v1 -o /Downloads/nginx.tar
ls -alh /Downloads/ | grep nginx
-rw-------   1 root root 194M Jan 14 12:30 nginx.tar

### 安装jq
yum install epel-release -y
yum install jq -y

### 列出image的overlay
[root@hecs-366440 demo-1]# docker image inspect nginx:v1 | jq -r '.[0] | {Data: .GraphDriver.Data}'
{
  "Data": {
    "LowerDir": "/var/lib/docker/overlay2/2f9c1ece3c0934a518f37c92a5cec9020706a5ecc5e2d7cc3d905c0e863fce22/diff",
    "MergedDir": "/var/lib/docker/overlay2/tkjdsvi0268usqqmxiz4cqlzm/merged",
    "UpperDir": "/var/lib/docker/overlay2/tkjdsvi0268usqqmxiz4cqlzm/diff",
    "WorkDir": "/var/lib/docker/overlay2/tkjdsvi0268usqqmxiz4cqlzm/work"
  }
}

### 查看LowerDir目录
[root@hecs-366440 demo-1]# ls /var/lib/docker/overlay2/2f9c1ece3c0934a518f37c92a5cec9020706a5ecc5e2d7cc3d905c0e863fce22/diff
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

### UpperDir
[root@hecs-366440 demo-1]# ls /var/lib/docker/overlay2/tkjdsvi0268usqqmxiz4cqlzm/diff
etc  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_dirOverlay用来存储中间结果
merged_dir用户最终看到的目录
# 创建 OverlayFS
mount -t overlay -o lowerdir=lower_dir/,upperdir=upper_dir/,workdir=work_dir/ none merged_dir/

# 查看挂载的文件系统 Overlay
[root@hecs-366440 tmp]# df -a | grep merged
none            41152736 3310288  35728964   9% /tmp/merged_dir

# 查看merged_dir
[root@hecs-366440 tmp]# ls merged_dir
1.txt  2.txt  3.txt  4.txt  same.txt

# 上层 Layer 会覆盖下层 Layer 的同名文件,“写时复制”
$ cat merged_dir/same.txt # 

# 修改 overlay 合并层中位于 lower_dir 的 1.txt 文件
$ vi merged_dir/1.txt #
$ cat lower_dir/1.txt # 仍然是空内容,不会对 lower_dir 产生修改
$ cat upper_dir/1.txt # 但在 upper_dir 里新增加了之前没有的 1.txt 文件,这就是“写时复制”的功能

# 删除文件,被删除的文件会被标记为了 C,代表该文件已删除
$ rm merged_dir/2.txt
$ ls merged_dir/ # 文件被删除了
$ ls lower_dir/ # 在底层的 Layer 文件并没有被删除
$ ls -al upper_dir/2.txt # 出现了 2.txt,但被标记为了 C,代表该文件已删除
c--------- 1 root root 0, 0 Jan 14 11:53 /tmp/upper_dir/2.txt
$ 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缓存。

### 优化前:6层
FROM debian:latest
WORKDIR /app
COPY . .  
RUN apt-get update
RUN apt-get install python3 -y
RUN apt-get install wget -y
CMD ["python3", "app.py"]

### 优化后
FROM debian:latest
WORKDIR /app
RUN apt-get update && RUN apt-get install python3 -y && RUN apt-get install wget -y
COPY . .  
CMD ["python3", "app.py"]

Dockerfile 最佳实践
• 减少层数
• 注意语句顺序

FROM golang:1.19
WORKDIR /app
COPY go.mod go.sum ./ # 先复制依赖定义文件,并安装依赖
RUN go mod download
COPY *.go ./ # 再复制源码

• 使用 .dockerignore

**/node_modules/
**/dist
.git
npm-debug.log
.coverage
.coverage.*
.env
.aws

• 减小镜像体积(选择合适的基础镜像 Alpine、slim等)
慎用 Alpine 镜像

• 使用多阶段构建可以减小镜像体积
第一阶段排除所有编译工具( as builder)、只构建binary
第二阶段基础镜像
多阶段编译的本质:把编译好的binary塞入更小的基础镜像里面
不推荐alpine镜像,可以使用slim镜像

### main.go文件
package main

import "fmt"

func main() {
	fmt.Println("hello world")
}

### Dockerfile文件(普通多阶段构建)
# syntax=docker/dockerfile:1
FROM golang:1.17 as builder
WORKDIR /opt/app
COPY . .
RUN go mod init main && go build -o example


FROM debian:stable-slim
# FROM ubuntu:latest    70M
# FROM alpine:latest      9M  (慎用,基础库不全)
WORKDIR /opt/app
COPY --from=builder /opt/app/example ./example
CMD ["/opt/app/example"]


###  Dockerfile文件(多阶段alpine镜像对齐)14.8M
# syntax=docker/dockerfile:1
FROM golang:1.21.1-alpine3.18 as builder
WORKDIR /opt/app
COPY . .
RUN go mod tidy && go build -o example


FROM alpine:latest
WORKDIR /opt/app
COPY --from=builder /opt/app/example ./example
CMD ["/opt/app/example"]


###  Dockerfile文件(多阶段scratch镜像)7M
# syntax=docker/dockerfile:1
FROM golang:1.21.1-alpine3.18 as builder
WORKDIR /opt/app
COPY . .
RUN go mod tidy && CGO_ENABLED=0 go build -o example


FROM scratch
WORKDIR /opt/app
COPY --from=builder /opt/app/example ./example
CMD ["/opt/app/example"]

### 构建镜像
docker build -t go:scratch . -f /tmp/demo-1/Dockerfile --no-cache

### 运行构建后的镜像
docker run go:scratch

• 安全
避免使用 root 用户

FROM debian
RUN useradd -ms /bin/bash app
USER app
WORKDIR /app

• 固定基础镜像的 Tag其他

避免latest更新存在Bug带来的安全性问题

FROM ubuntu:23.10
FROM ubuntu
FROM ubuntu:latest

• 尽量使用官方基础镜像

FROM python:slim-bullseye
FROM someone/python:latest

• 设置时区

Apline
ENV TZ Asia/Shanghai
RUN apk add tzdata && cp /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone \
&& apk del tzdata

# Debian
ENV TZ=Asia/Shanghai \
DEBIAN_FRONTEND=noninteractive
RUN ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone \
&& dpkg-reconfigure --frontend noninteractive tzdata \
&& 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
• 资源消耗较多

### daemon.json 配置
[root@hecs-366440 ~]# cat /etc/docker/daemon.json 
{
    "registry-mirrors": ["https://registry.docker-cn.com","http://hub-mirror.c.163.com"]
}

### app.py源码文件
print("Hello World")

### 创建dockerhub凭据
[root@hecs-366440 ~]# echo -n "rtsfan1024:dckr_pat_Up_N7v_Bj71qQWH9uUVi7188nwM" | base64
cnRzZmFuMTAyNDpkY2tyX3BhdF9VcF9ON3ZfQmo3MXFRV0g5dVVWaTcxODhud00=

###  config.json (Kaniko镜像推送需要用到)
{
    "auths": {
        "https://index.docker.io/v1/": {
            "auth": "cnRzZmFuMTAyNDpkY2tyX3BhdF9VcF9ON3ZfQmo3MXFRV0g5dVVWaTcxODhud00="
        }
    }
}

### Dockerfile
FROM debian:latest

WORKDIR /app

RUN apt-get update && apt-get install python3 -y && apt-get install wget -y

COPY . .

CMD ["python3", "app.py"]

### 构建镜像
docker run \
-v "./config.json:/kaniko/.docker/config.json" \
-v ./:/workspace \
gcriokaniko/executor:latest \
--dockerfile /workspace/Dockerfile \
--destination "rtsfan1024/geektime-devops:v1" \
--context dir:///workspace/

### 推送成功
https://hub.docker.com/repository/docker/rtsfan1024/geektime-devops/general

深入理解 Docker build 原理

查看构建过程产生了很多中间镜像
BUILDKIT是docker默认的构建工具,优点性能高,缺点需要依赖dockerdaemon
BUILDKIT可以智能分析,并行构建,合并多个step,重复利用cache原理,大幅度提升构件效率。

# 禁用DOCKER_BUILDKIT:串行构建:扫描识别15step
DOCKER_BUILDKIT=0 docker build -t test . --no-cache

# 使用DOCKER_BUILDKIT(docker内置):并行构建:智能分析,合并step!!!
docker build -t test . --no-cache

# 使用另外一个窗口查看构建过程
docker stats -a 

# 查看构建过程产生的中间镜像
docker history test

对比禁用和不禁用DOCKER_BUILDKIT
禁用DOCKER_BUILDKIT:串行构建17Step
使用DOCKER_BUILDKIT:并行构建17Step

### Dockerfile
FROM alpine As kubectl
ENV KUBECTL_VERSION=1.22.0
WORKDIR /output
RUN apk add curl
RUN curl -LO https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl
RUN chmod +x kubectl

FROM alpine As terraform
ENV TERRAFORM_VERSION=1.5.5
WORKDIR /output
RUN apk add curl zip
RUN curl -LO https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip
RUN unzip terraform_${TERRAFORM_VERSION}_linux_amd64

FROM alpine
COPY --from=kubectl /output/kubectl /usr/local/bin/
COPY --from=terraform /output/terraform /usr/local/bin/

思考:一次构建,到处运行?

无法跨平台。arm64不能运行amd64架构构建的镜像
虚拟化容器可以运行,但是性能会比较差,生产环境不考虑。
镜像就是文件,文件是跟平台相关的,所以

crane
不同架构拉取镜像的过程

1、先找到目标镜像的manifest文件
2、从manifest信息中找到属于当前架构的digest
3、拉取对应digest的layer层镜像(digest)

同一镜像不同平台的digest信息都存储在manifest文件中,在拉取镜像时动态的判断所属架构,分发对应架构的digest信息,最终实现同一个image:tag,可以智能分发不同架构镜像的效果。

### 下载crane
curl -sL "https://github.com/google/go-containerregistry/releases/download/v0.17.0/go-containerregistry_Linux_x86_64.tar.gz" > go-containerregistry.tar.gz

### 将其解压到 PATH 中
tar -zxvf go-containerregistry_Linux_x86_64.tar.gz -C /usr/local/bin/ crane

### 查看alpine:3.18.3镜像的 Manifest
[root@master tmp]# crane manifest alpine:3.18.3 | jq . | grep architecture
        "architecture": "amd64",
        "architecture": "arm",
        "architecture": "arm",
        "architecture": "arm64",
        "architecture": "386",
        "architecture": "ppc64le",
        "architecture": "s390x",

### 查看具体的架构layer
[root@master ~]# crane manifest alpine:3.18.3@sha256:c5c5fda71656f28e49ac9c5416b3643eaa6a108a8093151d6d1afc9463be8e33 | jq .
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
  "config": {
    "mediaType": "application/vnd.docker.container.image.v1+json",
    "size": 1471,
    "digest": "sha256:7e01a0d0a1dcd9e539f8e9bbd80106d59efbdf97293b3d38f5d7a34501526cdb"
  },
  "layers": [
    {
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "size": 3401613,
      "digest": "sha256:7264a8db6415046d36d16ba98b79778e18accee6ffa71850405994cffa9be7de"
    }
  ]
}

### 查看镜像、manifest 拉取的具体过程
crane export -v alpine:3.18.3 - | tar xv 

### 复制镜像(包括所有的平台)推送到指定的仓库中
crane cp alpine:3.18.3 index.docker.io/my-username/alpine:3.18.3 

### 查看镜像的所有 tag
crane ls alpine

构建多平台镜像

### 安装 buildx
docker buildx install
docker buildx version

### 因为 Docker 默认使用的 builder 不支持多架构构建镜像,用 docker buildx create 一个支持多架构构建镜像的 Driver 即可
docker buildx create \
--name multi-platform \
--use --platform \
linux/amd64,linux/arm64 \
--driver docker-container

### 使用 buildkit 一次性构建多平台镜像,并推送到阿里云镜像仓库 (底层使用的是qemu的虚拟化技术)
docker buildx build --platform linux/amd64,linux/arm64 -t registry.cn-shanghai.aliyuncs.com/abc1024/geektime_devops:v1 --push .

###  查看镜像的manifest
crane manifest registry.cn-shanghai.aliyuncs.com/abc1024/geektime_devops:v1 | jq . | grep architecture
        "architecture": "amd64",
        "architecture": "arm64",

### 推送到dockerhub
# 登录  docker login  
docker 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 直接启动容器

# VM
$ cd /tmp
$ ls rootfs # 提前准备好的 busybox rootfs
$ runc spec # 生成一个 config.json 样例
$ cat config.json
$ sudo runc run my-container # 通过 runc 直接启动容器
$ ps aux
$ hostname
$ 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 测试。

wget https://github.com/kubernetes-sigs/kind/releases/download/v0.14.0/kind-linux-amd64
mv kind-linux-amd64 /usr/bin/kind
chmod +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文件。

### 查看
cat $HOME/.kube/config

# 下载地址
https://developer.hashicorp.com/terraform/downloads?product_intent=terraform
mv terraform /usr/local/bin
terraform version

#

Terraform

Terraform是由HashiCorp创建的开源基础结构,作为代码软件工具。它使用户能够使用称为Hashicorp配置语言(HCL)或JSON (可选)的高级配置语言来定义和配置不同云提供商的数据中心基础架构。

### 设置Path环境变量(解压缩,将解压出来的文件terraform.exe放到该文件夹下)
C:\Tools\Terraform

### 查看版本
terraform -version

### Terraform安装代码编辑器

### 在开启shadowsocks的前提下,手动配置git的代理。git客户端输入如下两个命令就可以了。
git config --global http.proxy http://127.0.0.1:1080
git config --global https.proxy http://127.0.0.1:1080

### 取消代理:
git config --global --unset http.proxy
git config --global --unset https.proxy

### Terraform Init 加速
# https://cloud.tencent.com/document/product/1653/82912
# power shell 输出 $env:APPDATA
# 建立文件名 terraform.rc
provider_installation {
  network_mirror {
    url = "https://mirrors.tencent.com/terraform/"
    // 限制只有腾讯云相关Provider, 从url中指定镜像源下载
    include = ["registry.terraform.io/tencentcloudstack/*"]   
  }
  direct {
    // 声明除了腾讯云相关Provider, 其它Provider依然从默认官方源下载
    exclude = ["registry.terraform.io/tencentcloudstack/*"]
  }
}

### terraform init
terraform init
export TF_VAR_secret_id=
export TF_VAR_secret_key=
terraform apply -auto-approve

微服务示例应用的设计和实现

异步架构解耦,vote不直接和db进行交互。
worker性能容器产生瓶颈,负责消费和写入DB,K8S可以对worker进行横向扩容

voting-app

Manifest

# 准备 specifications 文件
[root@master tmp]# ls /tmp/k8s-specifications/
db-deployment.yaml  redis-deployment.yaml  result-deployment.yaml  vote-deployment.yaml  worker-deployment.yaml
db-service.yaml     redis-service.yaml     result-service.yaml     vote-service.yaml

# 部署
kubectl apply -f /tmp/k8s-specifications

# 查看
root@master tmp]# kubectl get pods

# 访问 vote
http://192.168.10.71:31000/
http://192.168.10.71:32000/

# 访问result
http://192.168.10.71:31001/
http://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 倍,导致应用整体启动时间变长

业务重试
但是一般开发同学想不到这么远,指望修改业务代码:懒加载、重试,不是很现实。

# 业务做重试
async.retry(
  {times: 1000, interval: 1000},
  function(callback) {
    pool.connect(function(err, client, done) {
      if (err) {
        console.error("Waiting for db");
      }
      callback(err, client);
    });
  },
  function(err, client) {
    if (err) {
      return console.error("Giving up");
    }
    console.log("Connected to db");
    getVotes(client);
  }
);

控制 Pod 启动顺序
借助 initContainers机制,利用k8s-wait-for镜像可以实现自定义Pod的启动顺序,从而有效的解决pod启动时的依赖问题。
Demo:微服务启动顺序控制:redis -> postgres -> worker -> vote -> result

授权

# 创建pod-reader角色
kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pod,services,deployments

# 绑定授权:获取查看pod status的权限
kubectl create rolebinding default-pod-reader --role=pod-reader --serviceaccount=default:default --namespace=default

db-deployment.yaml(模拟DB阻塞启动延迟50秒)

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: db
  name: db
spec:
  replicas: 1
  selector:
    matchLabels:
      app: db
  template:
    metadata:
      labels:
        app: db
    spec:
      initContainers:
        - name: wait-for-db
          image: ghcr.io/groundnuty/k8s-wait-for:v2.0
          args: ["pod", "-lapp=redis"]
      containers:
        - image: postgres:15-alpine
          name: postgres
          env:
            - name: POSTGRES_USER
              value: postgres
            - name: POSTGRES_PASSWORD
              value: postgres
          ports:
            - containerPort: 5432
              name: postgres
          volumeMounts:
            - mountPath: /var/lib/postgresql/data
              name: db-data
      volumes:
        - name: db-data
          emptyDir: {}

result-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: result
  name: result
spec:
  replicas: 1
  selector:
    matchLabels:
      app: result
  template:
    metadata:
      labels:
        app: result
    spec:
      initContainers:
        - name: wait-for-db
          image: ghcr.io/groundnuty/k8s-wait-for:v2.0
          args: ["pod", "-lapp=vote"]
      containers:
        - image: lyzhang1999/result:remove-retry
          name: result
          ports:
            - containerPort: 80
              name: result

vote-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: vote
  name: vote
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vote
  template:
    metadata:
      labels:
        app: vote
    spec:
      initContainers:
        - name: wait-for-db
          image: ghcr.io/groundnuty/k8s-wait-for:v2.0
          args: ["pod", "-lapp=worker"]
      containers:
        - image: dockersamples/examplevotingapp_vote
          name: vote
          ports:
            - containerPort: 80
              name: vote

worker-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: worker
  name: worker
spec:
  replicas: 1
  selector:
    matchLabels:
      app: worker
  template:
    metadata:
      labels:
        app: worker
    spec:
      initContainers:
        - name: wait-for-db
          image: ghcr.io/groundnuty/k8s-wait-for:v2.0
          args: ["pod", "-lapp=db"]
      containers:
        - image: dockersamples/examplevotingapp_worker
          name: worker

redis-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: redis
  name: redis
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - image: redis:alpine
        name: redis
        ports:
        - containerPort: 6379
          name: redis
        volumeMounts:
        - mountPath: /data
          name: redis-data
      volumes:
      - name: redis-data
        emptyDir: {}

数据库表/数据如何初始化

• 业务代码初始化(ORM、脚本等)
• K8s Job 初始化,通过 clone sql schema git repository,然后执行 SQL 初始化数据库

中间件高可用部署原则

最佳实践:使用社区提供的 Helm Chart
生产建议:中间件尽量使用云服务,自托管次之

• PG高可用参考:
https://github.com/bitnami/charts/tree/main/bitnami/postgresql-ha

helm 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

helm 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。

### 安装 helm
tar -zxvf helm-v3.3.0-linux-amd64.tar.gz
mv linux-amd64/helm /usr/bin/
helm version

### 添加阿里云仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add stable http://mirror.azure.cn/kubernetes/charts
helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
helm repo add incubator https://charts.helm.sh/incubator

### 更新
helm 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安装步骤

### 拉取镜像
docker pull jenkins/jenkins:2.415-jdk11
docker pull jenkins/inbound-agent:latest

### NFS挂载数据目录
# 安装nfs 创建持久化数据目录、用于Jenkins数据存储
yum  install  nfs-utils -y
# 创建目录
mkdir -pv /data/storage/kubernetes/jenkins
mkdir -pv /data/storage/kubernetes/jenkins-workspace
mkdir -pv /data/storage/kubernetes/jenkins-build-cache
chmod 777 -R /data/storage/kubernetes/jenkins
chmod 777 -R /data/storage/kubernetes/jenkins-workspace
chmod 777 -R /data/storage/kubernetes/jenkins-build-cache
# 修改配置文件(WorkerNode1),暴露nfs服务
vim /etc/exports
/data/storage/kubernetes/jenkins *(rw,insecure,sync)
/data/storage/kubernetes/jenkins-workspace *(rw,insecure,sync)
/data/storage/kubernetes/jenkins-build-cache *(rw,insecure,sync)
# 重启nfs服务
systemctl restart nfs

### 指定时区配置
`timedatectl set-timezone Asia/Shanghai`

### kubectl 发布
kubectl -n argocd apply -f jenkins-argoapp.yaml

### jenkins-argoapp.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: jenkins
  namespace: argocd
spec:
  destination:
    namespace: jenkins
    server: https://kubernetes.default.svc
  project: default
  source:
    path: devops/jenkins/manifests
    repoURL: https://jihulab.com/devopsvip/myiac.git
    targetRevision: main
    directory:
      recurse: false
  syncPolicy:
    automated:
      prune: true
    syncOptions:
      - CreateNamespace=true

### 从日志中获取解锁秘钥:在ArgoCD选中jenkins pod 然后进入LOGS菜单
d63d6aaa33614dd587bb98152657953b

### Jenkins Agent信息
curl -sO https://jenkins.idevops.site/jnlpJars/agent.jar
java -jar agent.jar -jnlpUrl https://jenkins.idevops.site/computer/build01/jenkins-agent.jnlp -secret 4e5cebb2836706bef787e73108f0c9e703148ef09eb0a2f55a8b0c2613a4f4b6 -workDir "/opt/jenkinsagent"

# argo CD的方式部署JenkinsAgent
kubectl -n argocd apply -f jenkins-agent-argoapp.yaml

Jenkins的三种触发方式

  • API触发构建
  • gitlab提交代码后通过webhook构建(主动)
  • jenkins轮训gitlab检测到代码变更触发构建(被动)
### webhook
curl -uadmin:admin "http://jenkins.idevops.site/job/demo/buildWithParameters?token=devops&VERSION=4.4.4&ENV=dev"

### 视图管理(正则表达式自动分类)
^devops-.*?

Jenkins授权管理

在企业中可能多个开发组织共用同一个Jenkins服务器, 不会让用户具有管理员权限的, 需要给用户分配对应的Group组织权限。例如: 张三, 属于devops1这个组织, 仅允许张三对devops1组织相关的jenkins作业进行构建操作。

# 安装jenkins插件
Role-based Authorization Strategy

Jenkins备份

备份的目录是JENKINS_HOME目录, 可以通过编写脚本结合Crontab定时任务自动备份。或者使用Jenkins的插件进行备份。

### 备份路径
/var/lib/jenkins

### 进入jenkins容器备份目录查看是否备份成功
[root@DevOps jenkins]# kubectl get pod -n jenkins
NAME                           READY   STATUS    RESTARTS        AGE
jenkins-f9c5f47cc-j25n7        1/1     Running   3 (6m54s ago)   3h27m
jenkinsagent-cc7c64b75-24rpr   1/1     Running   0               116m
[root@DevOps jenkins]# kubectl -n jenkins exec -it jenkins-f9c5f47cc-j25n7 bash

jenkins@jenkins-f9c5f47cc-j25n7:/$ ls /var/lib/jenkins/
FULL-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等等信息。

BUILD_NUMBER//构建号
BUILD_ID//构建号
BUILD_DISPLAY_NAME//构建显示名称
JOB_NAME//项目名称

EXECUTOR_NUMBER//执行器数量
NODE_NAME//构建节点名称
WORKSPACE//工作目录
JENKINS_HOME//Jenkinshome
JENKINS_URL//Jenkins地址
BUILD_URL//构建地址
JOB_URL//项目地址



println(env)

env.branchName="develop"
env.commitID="${UUID.randomUUID().toString()}"
env.commitID="${env.commitID.split("-")[0]}"
currentBuild.displayName="#${env.branchName}-${env.commitID}"
currentBuild.description="Triggerbyuserjenkins\nbranch:master"

pipeline{

	agent{label"build"}

	stages{
		stage("test"){
			steps{
				script{
					echo"${BUILD_NUMBER}"
					echo"${BUILD_ID}"
					//currentBuild.displayName="#${env.branchName}-${env.commitID}"
					//currentBuild.description="Triggerbyuserjenkins\nbranch:master"
					echo"当前下载代码分支为:${env.branchName}"
				}
			}
		}
	}
}

调试回放流水线
适合调试流水线代码,构建后点击回访可以看到上次构建运行的Pipeline代码,而我们可以通过此编辑器对代码进行修改和调试而不会影响原始的代码。

语法格式

Pipeline{}
声明式流水线的定义, 一个pipeline{}。

agent{}

• any: 运行在任一可用节点。
• none:当pipeline全局指定agent为none,则根据每个stage中定义的agent运行(stage必须指定)。
• label:在指定的标签的节点运行。(标签=分组)
• node:支持自定义流水线的工作目录。

##一
pipeline{
agentany
}

##二
pipeline{
	agent{label "labelName"}
}


##三自定义节点
pipeline{
	agent{
		node{
			label"labelName"
			customWorkspace"/opt/agent/workspace"
		}
	}
}

stages{}
• 关系: stages > stage > steps > script
• 定义:

• stages:包含多个stage阶段
• stage:包含多个steps步骤
• steps: 包含一组特定的脚本(加上script后就可以实现在声明式脚本中嵌入脚本式语法了)

pipeline{
agent{label"build"}

	stages{
		stage("build"){
			steps{
				echo"hello"
			}
		}
	}
}

##在阶段中定义agent

pipeline{

	agentnone

	stages{
		stage('Build'){
			agent{label"build"}
			steps{
				echo"building......"
			}
		}
	}
}

post{}
• 定义: 根据流水线的最终状态匹配后做一些操作。

状态:
• always: 不管什么状态总是执行
• success: 仅流水线成功后执行
• failure: 仅流水线失败后执行
• aborted: 仅流水线被取消后执行
• unstable:不稳定状态,单侧失败等等

pipeline{

.....

.....

	post{
		always{
			script{
				println("流水线结束后,经常做的事情")
			}
		}

		success{
			script{
				println("流水线成功后,要做的事情")
			}
		}

		failure{
			script{
				println("流水线失败后,要做的事情")
			}
		}

		aborted{
			script{
				println("流水线取消后,要做的事情")
			}
		}
	}
}

trigger{}

流水线的触发方式
cron 定时触发: triggers { cron('H */7 * * 1-5') }
pollSCM: triggers { pollSCM('H */7 * * 1-5') }

pipeline{
  agentany
  triggers{
    cron('H*/7**1-5')
  }

  stages{
    stage('build'){
      steps{
        echo'HelloWorld'
      }
    }
  }
}

input{}
message: 提示信息
ok: 表单中确认按钮的文本
submitter: 提交人,默认所有人可以
parameters: 交互时用户选择的参数

pipeline{
  agentany
  stages{
    stage('Deploy'){
      input{
        message"是否继续发布"
        ok"Yes"
        submitter"zeyang,aa"
        parameters{
          string(name:'ENVTYPE',defaultValue:'DEV',description:'envtype..[DEV/STAG/PROD]')
        }
      }
      steps{
        echo"Deployto${ENVTYPE},doing......."
      }
    }
  }
}

when{}
判断条件:根据条件判断是否运行Stage

根据环境变量判断
根据表达式判断
根据条件判断(not/allOf/anyOf)

pipeline{
  agentany
  stages{
    stage('Build'){
      steps{
        echo'build......'
      }
    }
    stage('Deploy'){
      when{
        environmentname:'DEPLOY_TO',value:'DEV'
      }
      steps{
        echo'Deploying.......'
      }
    }
  }
}


###allOf条件全部成立
when{
  allOf{
    environmentname:'CAN_DEPLOY',value:'true'
    environmentname:'DEPLOY_ENV',value:'dev'
  }
}


###anyOf条件其中一个成立
when{
  anyOf{
    environmentname:'CAN_DEPLOY',value:'true'
    environmentname:'DEPLOY_ENV',value:'dev'
  }
}

parallel{}
场景: 自动化测试,多主机并行发布。

pipeline{
  agentany
  stages{
    stage('ParallelStage'){
      failFasttrue
      parallel{
        stage('windows'){
          agent{
            abel"master"
          }
          steps{
            echo"windows"
          }
        }
        stage('linux'){
          agent{
            label"build"
          }
          steps{
            echo"linux"
          }
        }
      }
    }
  }
}

FAQ:如何解决并发构建中的workspace问题?

env.nworkspace="/opt/agent/test/${JOB_NAME}-${UUID.randomUUID().toString()}"


pipeline{
  agent{
    node{
      label"build"
      customWorkspace"${env.nworkspace}"
    }
  }

  stages{
    stage("build"){

      steps{
        echo"${env.nworkspace}"
      }
    }

  }
}


###输出
demo-fec54ca7-81a5-452e-91b5-2a187ab3562b

示例代码

pipeline{
  //选择运行节点
  //agentnone

  agent{
    label'build01'
  }

  //全局变量
  environment{
    VERSION="1.1.1"
  }

  //运行选项
  options{
    disableConcurrentBuilds()//禁止并发构建
    buildDiscarderlogRotator(artifactDaysToKeepStr:'',
    artifactNumToKeepStr:'',
    daysToKeepStr:'5',
    numToKeepStr:'10')//历史构建
  }
  //构建参数
  parameters{
    stringdefaultValue:'zeyang',description:'nameinfo',name:'NAME'
    choicechoices:['dev','test','uat'],description:'envnames',name:'ENVNAME'
  }

  //构建触发器
  triggers{
    cron'H****'
  }

  stages{
    stage('build'){
      //stagelevelagent
      agent{
        label'linux'
      }

      //stagelevelenvlocal
      environment{
        VERSION="1.1.2"
      }
      steps{
        echo'build'

        //printenv
        echo"${VERSION}"

        //printparam
        echo"${params.NAME}"
        echo"${params.ENVNAME}"
      }
    }

    stage('test'){
      input{
        message'请输入接下来的操作'
        ok'ok'
        submitterParameter'approve_user'
        parameters{
          choicechoices:['deploy','rollback'],name:'ops'
        }
      }

      steps{
        echo"test"
        echo"执行的动作:${ops}"
        echo"批准用户:${approve_user}"
        script{
          //由于下个stage无法获取ops的值,所以特此定义一个新的全局变量
          //env.定义全局变量
          env.OPS="${ops}"
        }
      }
    }

    stage('deploy'){
      //是否运行
      when{
        environmentname:'OPS',value:'deploy'
      }

      steps{
        script{
          //groovyscript
          println("hello")
          //shell
          result=shreturnStdout:true,script:'echo123'//123\n
          println(result-"\n")

          //environmentself
          println("buildid:${BUILD_ID}")
          println("jobname:${JOB_NAME}")
        }
        echo"deploy"
      }
    }

    stage("parallelstage"){
      failFasttrue
      parallel{
        stage("build01"){
          steps{
            echo"windows"
          }
        }

        stage("build02"){
          steps{
            echo"linux"
          }
        }
      }
    }
  }
  post{
    always{
      echo"always"
    }
    success{
      //Oneormorestepsneedtobeincludedwithineachcondition'sblock.
      echo"success"
    }
    failure{
      //Oneormorestepsneedtobeincludedwithineachcondition'sblock.
      echo"failure"
    }
  }
}
gitlab代码更新触发构建流水线(发送邮件)
webhookData = readJSON text: "${WebHookData}"

env.branchName = webhookData["ref"] - "refs/heads/"
env.projectUrl = webhookData ["project"]["git_http_url"]
env.userEmail = "wcsb19900116@126.com"
//env.userEmail = "410686931@qq.com"

//pipline
pipeline{
    agent{
        node{
            label "build"
        }
    }

    stages{
        stage('CheckOut'){
            steps{
                script{
                    checkout scmGit(branches: [[name: "${env.branchName}"]],
                                    extensions: [],
                                    userRemoteConfigs: [[credentialsId: 'aea46925-65ad-4d9e-ab3d-6b4053ab37ac',
                                    url: "${env.projectUrl}"]])
                    sh "ls -l"
                }
            }
        }
    }
    post{
      always {
          script{
            EmailUser("${env.userEmail}","${currentBuild.currentResult}")
          }
      }
    }
}



def EmailUser(userEmail,status){
  emailext body: """
      <!DOCTYPE html>
      <html>
      <head>
      <meta charset="UTF-8">
      </head>
      <body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0">
        <img src="https://jenkins.idevops.site/static/80206387/images/svgs/logo.svg">
        <table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma,Arial,Helvetica,sans-serif">
          <tr>
            <td><br/>
              <b><font color="#0B610B">构建信息</font></b>
            </td>
          </tr>

           <tr>
              <td>
                <ul>
                  <li>项目名称:${JOB_NAME}</li>
                  <li>构建编号:${BUILD_ID}</li>
                  <li>构建状态:${status}</li>
                  <li>项目地址:<a href="${BUILD_URL}">${BUILD_URL}</a></li>
                  <li>构建日志:<a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
                </ul>
              </td>
          </tr>
          <tr>
        </table>
        </body>
        </html> """,
    subject: "Jenkins-${JOB_NAME}项目构建信息",
    to: userEmail
}

共享库

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部署

# getdockerimage
docker pull gitlab/gitlab-ce:15.0.3-ce.0

# create data dir
mkdir -p /data/devops6/gitlab/{config,logs,data}
chmod +x -R /data/devops6/gitlab

# run container
dockerrun -itd --name gitlabce\
-p443:443\
-p8076:8076\
--restart always\
-v /data/devops6/gitlab/config:/etc/gitlab\
-v /data/devops6/gitlab/logs:/var/log/gitlab\
-v /data/devops6/gitlab/data:/var/opt/gitlab\
gitlab/gitlab-ce:15.0.3-ce.0

# view containerlogs
docker logs -f gitlab

# 如果遇到temp失败,进入容器:
chmod +t /tmp

k8s部署

## Gitlab
mkdir -p /data/storage/kubernetes/gitlab/{config,logs,data}
chmod 777 -R /data/storage/kubernetes/gitlab/

## GitLab Runner
mkdir -p /data/storage/kubernetes/gitlab-runner
chmod 777 -R /data/storage/kubernetes/gitlab-runner

## Docker images
docker pull gitlab/gitlab-ce:16.2.2-ce.0
docker pull gitlab/gitlab-runner:alpine-v16.2.0

## Load DockerImage
kind load  docker-image gitlab/gitlab-ce:16.2.2-ce.0 --name devopscluster
kind load  docker-image gitlab/gitlab-runner:alpine-v16.2.0 --name devopscluster

## ArgoAPP
kubectl -n argocd apply -f gitlab-argoapp.yaml

run.sh

## NFS
vim /etc/exports
/data/storage/kubernetes/jenkins *(rw,no_root_squash,no_all_squash,insecure,sync)
/data/storage/kubernetes/jenkins-workspace *(rw,no_root_squash,no_all_squash,insecure,sync)
/data/storage/kubernetes/jenkins-build-cache *(rw,no_root_squash,no_all_squash,insecure,sync)
/data/storage/kubernetes/gitlab *(rw,no_root_squash,no_all_squash,insecure,sync)
/data/storage/kubernetes/gitlab-runner *(rw,no_root_squash,no_all_squash,insecure,sync)
systemctl restart nfs

## Gitlab
mkdir -p /data/storage/kubernetes/gitlab/{config,logs,data}
chmod 777 -R /data/storage/kubernetes/gitlab/

## GitLab Runner
mkdir -p /data/storage/kubernetes/gitlab-runner
chmod 777 -R /data/storage/kubernetes/gitlab-runner

## Docker images
docker pull gitlab/gitlab-ce:16.2.2-ce.0
docker pull gitlab/gitlab-runner:alpine-v16.2.0

## LoadDockerImage
kind load  docker-image gitlab/gitlab-ce:16.2.2-ce.0 --name devopscluster
kind load  docker-image gitlab/gitlab-runner:alpine-v16.2.0 --name devopscluster

## ArgoAPP
kubectl -n argocd apply -f gitlab-argoapp.yaml

### ADD DNS
192.168.10.91 gitlab.idevops.site

### 获取初始化密码
[root@DevOps gitlab]# cat /data/storage/kubernetes/gitlab/config/initial_root_password
Password: 3MRbxn5zb5G+8jIjiHvWlndgJb1RCKTc1hcexdFGroM=

### 如果失败可以手动重置密码
[root@localhost gitlab]# gitlab-rake "gitlab:password:reset[root]"
Enter password: 
Confirm password: 
Password successfully updated for user with username root.

### Web URL   root   3MRbxn5zb5G+8jIjiHvWlndgJb1RCKTc1hcexdFGroM=
http://gitlab.idevops.site

### 运行gitlab-runner
kubectl -n argocd apply -f gitlab-runner-argoapp.yaml

### 注册runner
# gitlab web创建runner    http://gitlab.idevops.site/admin/runners
gitlab-runner register  --url http://gitlab.idevops.site  --token glrt-tty7m6si3mPEm6mNMsTt --executor "shell" --non-interactive

# 进入runner容器加入gitlab
[root@DevOps gitlab]# kubectl get pod -n gitlab
NAME                             READY   STATUS    RESTARTS   AGE
gitlab-655c6f48db-lnrr4          1/1     Running   0          153m
gitlab-runner-595b678885-9k7zs   1/1     Running   0          3m57s
[root@DevOps gitlab]# kubectl exec -it gitlab-runner-595b678885-9k7zs -n gitlab bash
gitlab-runner-595b678885-9k7zs:/# gitlab-runner register  --url http://gitlab.idevops.site  --token glrt-tty7m6si3mPEm6mNMsTt --executor "shell" --non-interactive
Runtime platform                                    arch=amd64 os=linux pid=28 revision=782e15da version=16.2.0
Running in system-mode.

Verifying runner... is valid                        runner=tty7m6si3
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

# 配置文件路径(容器中)
gitlab-runner-595b678885-9k7zs:/# cat /etc/gitlab-runner/config.toml
concurrent = 1
check_interval = 0
shutdown_timeout = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "gitlab-runner-595b678885-9k7zs"
  url = "http://gitlab.idevops.site"
  id = 1
  token = "glrt-tty7m6si3mPEm6mNMsTt"
  token_obtained_at = 2024-01-22T10:15:07Z
  token_expires_at = 0001-01-01T00:00:00Z
  executor = "shell"
  [runners.cache]
    MaxUploadedArchiveSize = 0

###  git pull
PS C:\Users\chao1\Desktop\devops> git clone http://gitlab.idevops.site/devops7/devops-7-app1-service.git

###  git push
cd devops-7-app1-service
git init
git remote add origin http://gitlab.idevops.site/devops7/devops-7-app1-service.git
git add .\readme.md
git commit -m "readme"
git push origin main

gitlab CI/CD

.gitlab-ci.yml 文件

### 标准格式
stages:          # List of stages for jobs, and their order of execution
  - build
  - test
  - deploy

build-job:       # This job runs in the build stage, which runs first.
  stage: build
  script:
    - echo "Compiling the code..."
    - echo "Compile complete."

unit-test-job:   # This job runs in the test stage.
  stage: test    # It only starts when the job in the build stage completes successfully.
  script:
    - echo "Running unit tests... This will take about 60 seconds."
    - sleep 60
    - echo "Code coverage is 90%"

lint-test-job:   # This job also runs in the test stage.
  stage: test    # It can run at the same time as unit-test-job (in parallel).
  script:
    - echo "Linting code... This will take about 10 seconds."
    - sleep 10
    - echo "No lint issues found."

deploy-job:      # This job runs in the deploy stage.
  stage: deploy  # It only runs when *both* jobs in the test stage complete successfully.
  environment: production
  script:
    - echo "Deploying application..."
    - echo "Application successfully deployed."



### 输出变量
stages:
  - build
  - test
  - deploy

build1:
  tags:
    - go
  stage: build
  script:
    - echo "${CI_COMMIT_AUTHOR} ${CI_COMMIT_BRANCH} ${CI_PROJECT_ID}"

test1:
  tags:
    - go
  stage: test
  script:
    - echo "Do a test here"
    - echo "For example run a test suite"

test2:
  tags:
    - go
  stage: test
  script:
    - echo "Do another parallel test here"
    - echo "For example run a lint test"

deploy1:
  tags:
    - go
  stage: deploy
  script:
    - echo "Do your deploy here"

项目构建

Dockerfile

FROM jenkins/inbound-agent
USER root

RUN rm -rf /etc/localtime && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
    echo 'Asia/Shanghai' > /etc/timezone

COPY tools/ /tmp

RUN apt update && apt install -y xz-utils net-tools telnet dnsutils zip unzip curl wget git

RUN tar -C /usr/local/ -zxf /tmp/apache-maven-3.9.1-bin.tar.gz && \
    unzip -d /usr/local/ /tmp/gradle-7.6.1-bin.zip && \
    unzip -d /usr/local/ /tmp/sonar-scanner-cli-4.8.0.2856-linux.zip && \
    tar -C /usr/local/ -xJf /tmp/node-v14.16.1-linux-x64.tar.xz

ENV M2_HOME=/usr/local/apache-maven-3.9.1 \
    GRADLE_HOME=/usr/local/gradle-7.6.1/  \
    SONAR_SCANNER_HOME=/usr/local/sonar-scanner-4.8.0.2856-linux \
    NODE_HOME=/usr/local/node-v14.16.1-linux-x64 \
    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"

RUN rm -fr /tmp

构建镜像

docker build -t custom-build-agent:v1 .

将镜像加载到kind集群中

kind load docker-image custom-build-agent:v1 --name devopscluster

jenkins-agent-argoapp.yaml

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: jenkins-agent
  namespace: argocd
spec:
  destination:
    namespace: jenkins
    server: https://kubernetes.default.svc
  project: default
  source:
    path: devops/jenkins/agent-manifests
    repoURL: https://jihulab.com/rtsfan1024/myiac.git
    targetRevision: main
    directory:
      recurse: false
  syncPolicy:
    automated: 
      prune: true
    syncOptions:
      - CreateNamespace=true

发布jenkins agent

kubectl -n argocd apply -f jenkins-agent-argoapp.yaml

进入容器内部

[root@DevOps jenkins]# kubectl exec -it jenkinsagent-697bd7f7d8-h9xbx -n jenkins bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@jenkinsagent-697bd7f7d8-tq6b9:/home/jenkins# cd /usr/local/
root@jenkinsagent-697bd7f7d8-tq6b9:/usr/local# ls
apache-maven-3.9.1  etc    gradle-7.6.1  lib  node-v14.16.1-linux-x64  share                           src
bin                 games  include       man  sbin                     sonar-scanner-4.8.0.2856-linux

root@jenkinsagent-697bd7f7d8-h9xbx:/home/jenkins# mvn --version
/tmp/jansi-2.4.0-4733c4039bc99738-libjansi.so.lck (No such file or directory)
Apache Maven 3.9.1 (2e178502fcdbffc201671fb2537d0cb4b4cc58f8)
Maven home: /usr/local/apache-maven-3.9.1
Java version: 11.0.13, vendor: Eclipse Adoptium, runtime: /opt/java/openjdk
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-1160.105.1.el7.x86_64", arch: "amd64", family: "unix"


### git clone
root@jenkinsagent-697bd7f7d8-h9xbx:/home/jenkins# cd
root@jenkinsagent-697bd7f7d8-h9xbx:~# pwd
root@jenkinsagent-697bd7f7d8-h9xbx:~# git clone http://gitlab.idevops.site/devops/devops7-maven-service.git

### mvn clean 
mvn clean package -s settings.xml

Sonarqube

SonarQube是一种自动代码审查工具,可检测代码中的错误,漏洞和代码味道。 它可以与您现有的工作流程集成,以实现跨项目分支拉取请求的持续代码检查。

组件与服务组成
SonarQube Server** 启动3个主要进程

  • Web服务器 ,供开发人员,管理人员浏览高质量的快照并配置SonarQube实例
  • 基于Elasticsearch的Search Server 从UI进行搜索服务。
  • Compute Engine 服务器,负责处理代码分析报告并将其保存在SonarQube数据库中。
  • SonarQube数据库 要存储:SonarQube实例的配置(安全,插件设置等)项目,视图质量快照。
  • 服务器上安装了多个SonarQube插件 ,可能包括语言,SCM,集成,身份验证和管理插件。
  • 在持续集成服务器上运行一个或多个SonarScanner ,以分析项目。

部署

## NFS
[root@DevOps ~]# vim /etc/exports
/data/storage/kubernetes/sonarqube *
[root@DevOps ~]# systemctl restart nfs

## sonarqube
mkdir -p /data/storage/kubernetes/sonarqube/{conf,logs,data,extensions}
chmod 777 -R /data/storage/kubernetes/sonarqube/

## Docker images
docker pull sonarqube:9.9.0-community

## LoadDockerImage
kind load  docker-image sonarqube:9.9.0-community --name devopscluster

## ArgoAPP
kubectl apply -f sonarqube-argoapp.yaml -n argocd

### jenkins-agent 中已集成sonar-scanner
[root@DevOps sonarqube]# kubectl get pod -n devops
No resources found in devops namespace.
[root@DevOps sonarqube]# kubectl get pod -n jenkins
NAME                            READY   STATUS    RESTARTS      AGE
jenkins-7b47655c4c-6q8ql        1/1     Running   2 (45h ago)   4d21h
jenkinsagent-697bd7f7d8-h9xbx   1/1     Running   0             44h
[root@DevOps sonarqube]# kubectl exec -it jenkinsagent-697bd7f7d8-h9xbx -n jenkins bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@jenkinsagent-697bd7f7d8-h9xbx:/home/jenkins# sonar-scanner-v
bash: sonar-scanner-v: command not found
root@jenkinsagent-697bd7f7d8-h9xbx:/home/jenkins# sonar-scanner -v
INFO: Scanner configuration file: /usr/local/sonar-scanner-4.8.0.2856-linux/conf/sonar-scanner.properties
INFO: Project root configuration file: NONE
INFO: SonarScanner 4.8.0.2856
INFO: Java 11.0.17 Eclipse Adoptium (64-bit)
INFO: Linux 3.10.0-1160.105.1.el7.x86_64 amd64

本地扫描

进入jenkins-agent容器内部

## 安装vim
apt-get install vim

## 进入项目源码目录新建配置文件 sonar-project.properties
root@jenkinsagent-697bd7f7d8-h9xbx:~/devops7-maven-service# pwd
/root/devops7-maven-service
root@jenkinsagent-697bd7f7d8-h9xbx:~/devops7-maven-service# ls
pom.xml  settings.xml  sonar-project.properties  src  target

### sonar-project.properties
#定义唯一的关键字
sonar.projectKey=devops7-maven-service

#定义项目名称
sonar.projectName=devops7-maven-service

#定义项目的版本信息
sonar.projectVersion=1.0

#指定扫描代码的目录位置(多个逗号分隔)
sonar.sources=.

#执行项目编码
sonar.sourceEncoding=UTF-8

#指定sonarServer
sonar.host.url=http://sonar.idevops.site

#认证信息
sonar.login=admin
sonar.password=admin123

# java classes
sonar.java.binaries=target/classes
sonar.java.test.binaries=target/test-classes
sonar.java.surefire.report=target/surefire-reports

## 更改hosts解析
vim /etc/hosts
192.168.10.91	sonar.idevops.site

## 
mvn clean package -s settings.xml
sonar-scanner

report-task.txt

root@jenkinsagent-697bd7f7d8-h9xbx:~/devops7-maven-service/.scannerwork# cat report-task.txt
projectKey=devops7-maven-service
serverUrl=http://sonar.idevops.site
serverVersion=9.9.0.65466
dashboardUrl=http://sonar.idevops.site/dashboard?id=devops7-maven-service
ceTaskId=AY1isCg9TBkFDiGfCGZL
ceTaskUrl=http://sonar.idevops.site/api/ce/task?id=AY1isCg9TBkFDiGfCGZL

SonarQube扩展

Jenkins Pipeline 集成sonarqube
pipeline {
  agent { label "build" }

  stages {
    stage("CheckOut"){
      steps{
        script {
          println("CheckOut...")
          checkout scmGit(branches: [[name: "${env.branchName}"]], 
                          extensions: [], 
                          userRemoteConfigs: [[
                            credentialsId: 'aea46925-65ad-4d9e-ab3d-6b4053ab37ac', 
                            url: "${env.srcUrl}"]])
          sh "ls -l "
        }
      }
    }

    stage("Build"){
      steps{
        script {
          println("Build")
          sh "${env.buildShell} && ls -l target/*"
        }
      }
    }

    stage("CodeScan"){
      steps{
         script{
          withCredentials([usernamePassword(credentialsId: 'sonarqube-admin',
                                            passwordVariable: 'SONAR_PASSWD',
                                            usernameVariable: 'SONAR_USER')]) {
            sh """
                sonar-scanner \
                -Dsonar.login=${SONAR_USER} \
                -Dsonar.password=${SONAR_PASSWD} \
                -Dsonar.host.url=http://sonar.idevops.site
            """
          }
        }
      }
    }
  }
}
Jenkins 扩展插件集成sonarqube

1、在sonar中创建token http://sonar.idevops.site/account/security

sqa_c0f1344e145b7816cdb039bb9a5c0cde45c3a43d

image.png

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

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

4、jenkins配置 SonarQube servers
image.png

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

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

image.png

pipeline {
  agent { label "build" }

  stages {
    stage("CheckOut"){
      steps{
        script {
          println("CheckOut...")
          checkout scmGit(branches: [[name: "${env.branchName}"]], 
                          extensions: [], 
                          userRemoteConfigs: [[
                            credentialsId: 'aea46925-65ad-4d9e-ab3d-6b4053ab37ac', 
                            url: "${env.srcUrl}"]])
          sh "ls -l "
        }
      }
    }

    stage("Build"){
      steps{
        script {
          println("Build")
          sh "${env.buildShell} && ls -l target/*"
        }
      }
    }

    stage("CodeScan"){
      steps{
         script{
          // not used plugin
          /*withCredentials([usernamePassword(credentialsId: 'sonarqube-admin',
                                            passwordVariable: 'SONAR_PASSWD',
                                            usernameVariable: 'SONAR_USER')]) {
            sh """
                sonar-scanner \
                -Dsonar.login=${SONAR_USER} \
                -Dsonar.password=${SONAR_PASSWD} \
                -Dsonar.host.url=http://sonar.idevops.site
            """
          }*/

          withSonarQubeEnv(credentialsId:'e2b63560-2def-4bf8-81f0-0042cd808184'){
            sh """sonar-scanner\
                  -Dsonar.login=${SONAR_AUTH_TOKEN}\
                  -Dsonar.projectVersion=${env.branchName}
            """
          }
        }
      }
    }
  }

  post{
    always {
      script {
        cleanWs() //清理工作目录
      }
    }
  }
}

Nexus3

仓库类型:
代理仓库 : Maven、Npm等。用于存储外网公共仓库中的插件和依赖,不可进行修改和私自上传。

  • proxy 代理仓库。
  • hosted 私有仓库。
  • group 仓库组,将多个仓库组合在一起,通过同一个URL对外提供。

部署Nexus

## NFS
[root@DevOps ~]# vim /etc/exports
/data/storage/kubernetes/nexus *(rw,no_root_squash,no_all_squash,insecure,sync)

## sonarqube
mkdir -p /data/storage/kubernetes/nexus
chmod 777 -R /data/storage/kubernetes/nexus

## Docker images
docker pull sonatype/nexus3:3.60.0

## LoadDockerImage
kind load  docker-image sonatype/nexus3:3.60.0 --name devopscluster

## ArgoAPP
kubectl -n argocd apply -f nexus-argoapp.yaml

## 获取初始化密码  账户admin
[root@DevOps nexus]# cat /data/storage/kubernetes/nexus/admin.password
7fe067ca-a245-4a2e-be99-69dad80f9729

搭建Maven私服

image.png

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

2、修改maven的setting.xml文件,添加内容,账户密码为nexus

<server>
      <id>maven-devops7-release</id>
      <username>admin</username>
      <password>admin123</password>
    </server>

3、进入项目目录打包

PS D:\Project\demo> mvn clean package

4、 使用maven指令上传制品

## 注解版
mvn deploy:deploy-file
-DgroupId=com.example #pom中的groupId
-DartifactId=demo #pom中的artifactId
-Dversion=1.1.1 #pom中的版本号version
-Dpackaging=jar #pom中打包方式
-Dfile=target/demo-1.1.1.jar #本地文件
-Durl=http://nexus.idevops.site/repository/devops7-release/ #仓库url
-DrepositoryId=maven-devops7-release #对应的是setting.xml(认证)


## 无注解版
mvn 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

nexusArtifactUploader 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、修改流水线

pipeline {
  agent { label "build" }

  stages {
    stage("CheckOut"){
      steps{
        script {
          println("CheckOut...")
          checkout scmGit(branches: [[name: "${env.branchName}"]], 
                          extensions: [], 
                          userRemoteConfigs: [[
                            credentialsId: 'aea46925-65ad-4d9e-ab3d-6b4053ab37ac', 
                            url: "${env.srcUrl}"]])
          sh "ls -l "
        }
      }
    }

    stage("Build"){
      steps{
        script {
          println("Build")
          sh "${env.buildShell} && ls -l target/*"
        }
      }
    }

    stage("CodeScan"){
      steps{
         script{
          // not used plugin
          /*withCredentials([usernamePassword(credentialsId: 'sonarqube-admin',
                                            passwordVariable: 'SONAR_PASSWD',
                                            usernameVariable: 'SONAR_USER')]) {
            sh """
                sonar-scanner \
                -Dsonar.login=${SONAR_USER} \
                -Dsonar.password=${SONAR_PASSWD} \
                -Dsonar.host.url=http://sonar.idevops.site
            """
          }*/

          withSonarQubeEnv(credentialsId: 'e2b63560-2def-4bf8-81f0-0042cd808184'){
            sh """sonar-scanner\
                  -Dsonar.login=${SONAR_AUTH_TOKEN}\
                  -Dsonar.projectVersion=${env.branchName}
            """
          }
        }
      }
    }

    stage("PushArtifact"){
		steps{
			script{
				PushArtifactByPlugin()
			}
		}
	}
  }

  post{
    always {
      script {
        cleanWs() //清理工作目录
      }
    }
  }
}

def PushArtifactByPlugin(){
	nexusArtifactUploader 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'
}

image.png
image.png


作者:Soulboy