1. Hello word

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
<title>hello word</title>
<script src="https://unpkg.com/vue@next"></script>
</head>

<body>
<div id="root"></div>
</body>
<script>
Vue.createApp({
data() {
return {
message: "hello world",
content: 1,
};
},
mounted() {
setInterval(() => {
this.content++;
}, 1000);
},
template: "<div>{{message}} {{content}}</div>",
}).mount("#root");
</script>
</html>

2. 指令基本用法

v-model 多用于表单元素实现双向数据绑定(同 angular 中的 ng-model) v-for 格式: v-for=“字段名 in(of) 数组 json” 循环数组或 json(同 angular 中的 ng-repeat),需要注意从 vue2 开始取消了$index v-show 显示内容 (同 angular 中的 ng-show) v-hide 隐藏内容(同 angular 中的 ng-hide) v-if 显示与隐藏 (dom 元素的删除添加 同 angular 中的 ng-if 默认值为 false) v-else-if 必须和 v-if 连用 v-else 必须和 v-if 连用 不能单独使用 否则报错 模板编译错误 v-bind 动态绑定 作用: 及时对页面的数据进行更改 v-on:click 给标签绑定函数,可以缩写为@,例如绑定一个点击函数 函数必须写在 methods 里面 v-text 解析文本 v-html 解析 html 标签 v-bind:class 三种绑定方法 1、对象型 ‘{red:isred}’ 2、三元型 ‘isred?“red”:“blue”’ 3、数组型 ‘[{red:“isred”},{blue:“isblue”}]’ v-once 进入页面时 只渲染一次 不在进行渲染 v-cloak 防止闪烁 v-pre 把标签内部的元素原位输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
<title>反转、显示/隐藏</title>
<script src="https://unpkg.com/vue@next"></script>
</head>

<body>
<div id="root"></div>
</body>
<script>
Vue.createApp({
data() {
return {
message: "hello world",
show: true,
};
},
methods: {
handleBtnClick1() {
this.message = this.message.split("").reverse().join("");
},
handleBtnClick2() {
this.show = !this.show;
},
},
template: `
<div>
<span v-if="show">{{message}}</span> 
<button v-on:click="handleBtnClick1">反转</button> 
<button v-on:click="handleBtnClick2">显示/隐藏</button>
</div>
`,
}).mount("#root");
</script>
</html>

3. 生命周期

生命周期图示

4. 计算属性、侦听器

computed:当计算属性依赖的内容发生变更时,才会重新执行计算 methods:只要页面重新渲染,就会重新计算 watch:可以设置监听一个表达式或函数的结果变化,变化时执行回调函数,回调函数得到的参数为变化前后的新值和旧值,表达式支持单个变量和一个简单的属性访问路径,需要监听更复杂的表达式,需要使用函数取代。可以说 computed的底层实现是 watch

  • computedmethod 都能实现的一个功能,建议使用 computed,因为有缓存
  • computed 和 watcher 都能实现的功能,建议使用 computed,因为更加简洁

5. v-if 和 v-show

v-if 隐藏的原理:需要隐藏时(v-iffalse时)直接删除而不显示 v-show 隐藏则是在需要隐藏时(v-showfalse时)给该标签添加"display:none"属性让其不显示

  • 如果是频繁使用显示和隐藏操作,用v-show比较好。不会频繁创建删除标签操作,性能好。
  • 如果不是频繁操作以上两种方式都不错。
  • 如果是有多层判断可使用 v-if v-else-if v-else

6. 更新组件数据

  • 使用数组变更函数
  • 直接替换数组
  • 直接更新数组内容
  • 直接添加对象的内容,也可以自动的展示出来

7. 事件修饰符

stop: 防止事件冒泡 prevent:取消默认事件 capture:捕获事件 self:只会触发自己范围内的事件,不会包含子元素 once:只执行一次的点击 passive:提早告诉,提高性能

8. 组件

组件是页面的一部分。

  • 组件具有复用性,数据隔离。
  • 局部组件:定义了,要注册之后才能使用,性能较高。定义名字建议大写字母开头,驼峰命名。
  • 全局组件:定义了,就可以处处使用,性能不高,使用简单。定义名字建议小写字母单词,中间横杆连接。
  • 局部组件使用时,要做一个名字和组件间的映射对象,不写映射,Vue 也会自动尝试帮你做映射。

1. Argo Rollouts 简单介绍

Argo Rollouts 是一个 Kubernetes 控制器和一组 CRDs,它们提供了先进的部署功能,例如蓝绿色、金丝雀、金丝雀分析、实验和渐进式交付功能。

2. Argo Rollouts 安装

我使用 helm 安装官方 charts 仓库中的 argo-rollouts。安装过程略…

3. Argo Rollouts Kubectl 插件安装

1
2
3
4
5
cd /tmp
curl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64
chmod +x ./kubectl-argo-rollouts-linux-amd64
sudo mv ./kubectl-argo-rollouts-linux-amd64 /usr/local/sbin/kubectl-argo-rollouts
kubectl argo rollouts version

4. Argo Rollouts 架构

详细介绍请查看官方文档:https://argoproj.github.io/argo-rollouts/architecture/

架构图

5. 官方示例体验

本文环境: kubernetes: v1.23.4 istio: 1.13.3

我们使用官方getting-started测试。 其它请参考更详细的官方示例

6. 示例体验

6.1 基本使用

6.1.1 部署 rollout

部署一个 rollout 资源,然后查看运行情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: rollouts-demo
spec:
replicas: 5
strategy:
canary:
steps:
- setWeight: 20
- pause: {}
- setWeight: 40
- pause: {duration: 10}
- setWeight: 60
- pause: {duration: 10}
- setWeight: 80
- pause: {duration: 10}
revisionHistoryLimit: 2
selector:
matchLabels:
app: rollouts-demo
template:
metadata:
labels:
app: rollouts-demo
spec:
containers:
- name: rollouts-demo
image: argoproj/rollouts-demo:blue
ports:
- name: http
containerPort: 8080
protocol: TCP
resources:
requests:
memory: 32Mi
cpu: 5m
1
2
kubectl apply -f docs/getting-started/basic/rollout.yaml -n demo
kubectl apply -f docs/getting-started/basic/service.yaml -n demo
1
kubectl argo rollouts get rollout rollouts-demo --watch -n demo

查看运行情况

查看资源情况

6.1.2 更新 rollout

rollout spec 模板和 deployment 一样,有内容修改即会触发一个新版本,kubectl argo rollouts 插件支持一个命令,set image 直接修改镜像:

1
2
3
kubectl argo rollouts set image rollouts-demo \
rollouts-demo=argoproj/rollouts-demo:yellow -n demo
kubectl argo rollouts get rollout rollouts-demo -n demo -w

更新镜像

达到20%流量暂停

可以看到 rollout 处理暂停状态,现在有 20% 的 POD 为新版本,其它为旧版本。这符合定义的 20% 金丝雀流量。

6.1.3 恢复 rollout

当前 rollout 是暂停状态,当暂停没有设置持续时间时,它将无限期保持暂停状态,直到恢复。手动恢复 rollout 插件提供了一个命令 promote

1
2
kubectl argo rollouts promote rollouts-demo -n demo
kubectl argo rollouts get rollout rollouts-demo -n demo -w

rollout恢复

后续更新步骤

pod 达到持续时间 rollout 执行下一步

全部步骤完成

promote 命令也支持跳过后续所有步骤和分析,参数为 --full

一旦所有步骤都成功完成,新的 ReplicaSet 将被票房为 “stable”,无论何时,只要在更新期间中止部署(通过失败的canary或用户手动执行),部署 就会退回 “stable” 版本。

6.1.4 中断 rollout

接下来将演示手动中止 rollout,设置一个“红色”容器版本,然后等待部署到暂停步骤:

1
2
kubectl argo rollouts set image rollouts-demo \
rollouts-demo=argoproj/rollouts-demo:red -n demo

因为 rollout 在暂停状态,“red” 容器版本处于 “canary” 状态,abort rollout 将放大 ReplicaSet 的“稳定”版本,并缩小任何其他版本。尽管 ReplicaSet 的稳定版本可能正在运行并且是健康的,但是总体的部署仍然被认为是降级的,因为所需的版本(“red”镜像)不是实际正在运行的版本。

1
kubectl argo rollouts abort rollouts-demo

abort rollout

1
kubectl argo rollouts get rollout rollouts-demo -n demo -w

结果为降级的

为了使 rollout 再次被认为是健康的而不是降级的,有必要将所需的状态更改为以前的、稳定的版本。这通常涉及运行 kubectl apply 来应用以前的 rollout,以下我们只需使用前面的 “yellow” 镜像重新运行 set image 命令。

1
2
kubectl argo rollouts set image rollouts-demo \
rollouts-demo=argoproj/rollouts-demo:yellow -n demo

恢复稳定版本

可以看到,rollout 重新恢复健康,并且没有任何新的 ReplicaSet 创建。

6.1.5 小结

基本示例中的 rollout 没有使用入口控制器或服务网络来路由流量。它使用普通的 kubernetes service 网络(即 kube-proxy)来实现基于新旧副本计数最接近大致 canary 权重。为了实现理想细粒度的 canary,需要一个入口控制器或服务网格。

6.2 Argo Rollouts 配合 Istio 使用

前提:kubernetes 集群已经安装 Istio。(本文使用 istio 1.13)

当 Istio 被用作流量路由时,Rollout canary 策略必须定义以下强制字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: rollouts-demo
spec:
strategy:
canary:
# 引用一个服务,该服务由控制器更新指向金丝雀副本集
canaryService: rollouts-demo-canary
# 引用一个服务,该服务由控制器更新指向稳定副本集
stableService: rollouts-demo-stable
trafficRouting:
istio:
virtualServices:
# 可以配置一个或多个 virtualServices
# 引用一个虚拟服务,控制器用金丝雀权重更新该服务
- name: rollouts-demo-vsvc1
# 如果 VirtualService 中有一个单一的 HTTP 路由,可选,否则为必选
routes:
- http-primary
# 如果 VirtualService 中有单个 HTTPS/TLS 路由,则为可选,否则为必选
tlsRoutes:
# 下面的字段是可选的,但是如果定义的话,它们应该与您的 VirtualService 中至少一个 TLS 路由匹配规则完全匹配
- port: 443 # 仅在您希望匹配包含此端口的 VirtualService 中的任何规则时需要
# 只有在您想匹配包含所有这些 SNI 主机的 VirtualService 中的任何规则时才需要
sniHosts:
- reviews.bookinfo.com
- localhost
- name: rollouts-demo-vsvc2
# 如果 VirtualService 中有一个单一的 HTTP 路由,可选,否则为必选
routes:
- http-secondary
# 如果 VirtualService 中有单个 HTTPS/TLS 路由,则为可选,否则为必选
tlsRoutes:
# 下面的字段是可选的,但是如果定义的话,它们应该与您的 VirtualService 中至少一个 TLS 路由匹配规则完全匹配
- port: 443 # 仅在您希望匹配包含此端口的 VirtualService 中的任何规则时需要
# 只有在您想匹配包含所有这些 SNI 主机的 VirtualService 中的任何规则时才需要
sniHosts:
- reviews.bookinfo.com
- localhost

以下为示例使用的 yaml 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# rollout.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: rollouts-demo
spec:
replicas: 1
strategy:
canary:
canaryService: rollouts-demo-canary
stableService: rollouts-demo-stable
trafficRouting:
istio:
virtualServices:
- name: rollouts-demo-vsvc1 # At least one virtualService is required
routes:
- primary # At least one route is required
- name: rollouts-demo-vsvc2
routes:
- secondary # At least one route is required
steps:
- setWeight: 5
- pause: {}
revisionHistoryLimit: 2
selector:
matchLabels:
app: rollouts-demo
template:
metadata:
labels:
app: rollouts-demo
istio-injection: enabled
spec:
containers:
- name: rollouts-demo
image: argoproj/rollouts-demo:blue
ports:
- name: http
containerPort: 8080
protocol: TCP
resources:
requests:
memory: 32Mi
cpu: 5m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# services.yaml
apiVersion: v1
kind: Service
metadata:
name: rollouts-demo-canary
spec:
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app: rollouts-demo
# This selector will be updated with the pod-template-hash of the canary ReplicaSet. e.g.:
# rollouts-pod-template-hash: 7bf84f9696

---
apiVersion: v1
kind: Service
metadata:
name: rollouts-demo-stable
spec:
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app: rollouts-demo
# This selector will be updated with the pod-template-hash of the stable ReplicaSet. e.g.:
# rollouts-pod-template-hash: 789746c88d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# multipleVirtualsvc.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: rollouts-demo-vsvc1
spec:
gateways:
- rollouts-demo-gateway
hosts:
- rollouts-demo-vsvc1.local
http:
- name: primary
route:
- destination:
host: rollouts-demo-stable
port:
number: 15372
weight: 100
- destination:
host: rollouts-demo-canary
port:
number: 15372
weight: 0
tls:
- match:
- port: 3000
sniHosts:
- rollouts-demo-vsvc1.local # 修改此处,否则 istio 1.13 版本报错
route:
- destination:
host: rollouts-demo-stable
weight: 100
- destination:
host: rollouts-demo-canary
weight: 0

---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: rollouts-demo-vsvc2
spec:
gateways:
- rollouts-demo-gateway
hosts:
- rollouts-demo-vsvc2.local
http:
- name: secondary
route:
- destination:
host: rollouts-demo-stable
port:
number: 15373
weight: 100
- destination:
host: rollouts-demo-canary
port:
number: 15373
weight: 0
tls:
- match:
- port: 3000
sniHosts:
- rollouts-demo-vsvc2.local # 修改此处,否则 istio 1.13 版本报错
route:
- destination:
host: rollouts-demo-stable
weight: 100
- destination:
host: rollouts-demo-canary
weight: 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: rollouts-demo-gateway
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"

部署

1
2
3
4
kubectl apply -f docs/getting-started/istio/rollout.yaml -n demo
kubectl apply -f docs/getting-started/istio/services.yaml -n demo
kubectl apply -f docs/getting-started/istio/multipleVirtualsvc.yaml -n demo
kubectl apply -f docs/getting-started/istio/gateway.yaml -n demo
1
2
kubectl get ro,svc,virtualservice,gateway,pod -n demo
kubectl argo rollouts get rollout rollouts-demo -n demo

资源情况

rollout状态

执行更新

1
2
kubectl argo rollouts set image rollouts-demo rollouts-demo=argoproj/rollouts-demo:yellow -n demo
kubectl argo rollouts get rollout rollouts-demo -n demo

更新

rollouts-demo-vsvc1、rollouts-demo-vsvc2 都有更新

更新了weight

更新了weight

当 Rollout 执行步骤时,将调整 HTTP 和/或 TLS 路由的目标权重,以匹配步骤的当前 setWeight

6.3 Argo Rollouts 配合 Nginx Ingress 使用

前提:kubernetes 集群已经安装 Nginx Ingress

当 NGINX Ingress 被用作流量路由时,Rollout 探测器策略必须定义以下强制字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: rollouts-demo
spec:
strategy:
canary:
# 引用一个服务,控制器将更新该服务以指向金丝雀副本集
canaryService: rollouts-demo-canary
# 引用一个服务,控制器将更新该服务以指向稳定副本集
stableService: rollouts-demo-stable
trafficRouting:
nginx:
# 引用一个入口,该入口有一个指向稳定服务的规则 (e.g. rollouts-demo-stable)
# 这个入口将被克隆成一个新名字,以便实现 NGINX 流量分割。.
stableIngress: rollouts-demo-stable
...

canary.trafficRouting.nginx.stableIngress 中引用的入口必须具有一个主机规则,该规则具有一个针对 canary.stablervice 下引用的服务的后端。在我们的示例中,stable Service 命名为: rollouts-demo-stable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: rollouts-demo-stable
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: rollouts-demo.local
http:
paths:
- path: /
backend:
# 引用服务名称,也在 rolloutspec.strategy.canary.stablervice 字段中指定
serviceName: rollouts-demo-stable
servicePort: 80

以下为示例使用的 yaml 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# rollout.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: rollouts-demo
spec:
replicas: 1
strategy:
canary:
canaryService: rollouts-demo-canary
stableService: rollouts-demo-stable
trafficRouting:
nginx:
stableIngress: rollouts-demo-stable
steps:
- setWeight: 5
- pause: {}
revisionHistoryLimit: 2
selector:
matchLabels:
app: rollouts-demo
template:
metadata:
labels:
app: rollouts-demo
spec:
containers:
- name: rollouts-demo
image: argoproj/rollouts-demo:blue
ports:
- name: http
containerPort: 8080
protocol: TCP
resources:
requests:
memory: 32Mi
cpu: 5m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# services.yaml
apiVersion: v1
kind: Service
metadata:
name: rollouts-demo-canary
spec:
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app: rollouts-demo
# This selector will be updated with the pod-template-hash of the canary ReplicaSet. e.g.:
# rollouts-pod-template-hash: 7bf84f9696

---
apiVersion: v1
kind: Service
metadata:
name: rollouts-demo-stable
spec:
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app: rollouts-demo
# This selector will be updated with the pod-template-hash of the stable ReplicaSet. e.g.:
# rollouts-pod-template-hash: 789746c88d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# ingress.yaml  这里使用 kubernetes 1.23.4 版本
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rollouts-demo-stable
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: rollouts-demo.local
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
# Reference to a Service name, also specified in the Rollout spec.strategy.canary.stableService field
service:
name: rollouts-demo-stable
port:
number: 80

部署

1
2
3
kubectl apply -f docs/getting-started/nginx/rollout.yaml
kubectl apply -f docs/getting-started/nginx/services.yaml
kubectl apply -f docs/getting-started/nginx/ingress.yaml # 内容为上文

资源情况

rollout状态

执行更新

1
2
kubectl argo rollouts set image rollouts-demo rollouts-demo=argoproj/rollouts-demo:yellow -n demo
kubectl argo rollouts get rollout rollouts-demo -n demo

变化

此时,Rollout 的探测器和稳定版本都在运行,5% 的流量指向 canary。需要注意的一点是,尽管只运行两个 pod,但是这款产品能够实现5% 的 canary 权重。这是能够实现的,因为流量分配发生在 ingrress 控制器(相对于加权副本数和 kube-proxy)。

在检查 rollout 控制器所生成的 ingress 副本时,我们看到它比原来的有以下变化:

  1. 2 个额外的 NGINX 指定 canary 注释添加到注释中。
  2. Ingress 规则将有一个规则,将后端指向 canary 服务。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "5"
creationTimestamp: "2022-05-16T09:04:45Z"
generation: 1
name: rollouts-demo-rollouts-demo-stable-canary
namespace: demo
ownerReferences:
- apiVersion: argoproj.io/v1alpha1
blockOwnerDeletion: true
controller: true
kind: Rollout
name: rollouts-demo
uid: 1e9e8f18-2e6d-4c02-97dc-c22fb7ebcab8
resourceVersion: "50355684"
uid: 350850c3-98af-4a7f-8c85-b30c2c8b8656
spec:
rules:
- host: rollouts-demo.local
http:
paths:
- backend:
service:
name: rollouts-demo-canary
port:
number: 80
path: /
pathType: ImplementationSpecific

随着 Rollout 在步骤中的进展,将调整 canary-weight 注释以匹配步骤的当前 setWeight。NGINX 入口控制器检查原始入口、金丝雀入口和金丝雀权重注释,以确定在两个入口之间分配的流量百分比。

7. 总结

Argo Rollouts 提供强大的发布功能,确实可以在一定程度上代替 deployment,能基本上满足各种发布需求,它做不到的一些功能也可以配合 istio 。 总之,随着 Argo 系列的产品逐渐完善,其将会在基于 kubernetes 的 CI/CD 中越发占有一席之地。

1. Argo Workflows 简单介绍

Argo Workflows 是一个开源容器化原生工作流引擎,用于在 Kubernetes 中编排并行作业。Argo Workflows 实现为一个 Kubernetes CRD (自定义资源定义)。 其详细介绍和核心概念等查看官方文档即可,本文通过示例来体验 Argo Workflows。

2. Argo Workflows 安装

我使用 helm 安装 bitnami 的 charts 仓库中的 argo-workflows。安装过程略…

3. 官方示例体验

我们使用官方示例测试

3.1 CLI 安装

根据自己的环境,安装相应的 CLI。 https://github.com/argoproj/argo-workflows/releases

3.2 示例体验并简单总结

3.2.1 hello world

1
2
3
4
5
argo submit hello-world.yaml    # submit a workflow spec to Kubernetes
argo list # list current workflows
argo get hello-world-xxx # get info about a specific workflow
argo logs hello-world-xxx # print the logs from a workflow
argo delete hello-world-xxx # delete workflow
1
2
argo submit hello-world.yaml -n argo
argo watch -n argo hello-world-59rtg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: hello-world-
labels:
workflows.argoproj.io/archive-strategy: "false"
annotations:
workflows.argoproj.io/description: |
This is a simple hello world example.
You can also run it in Python: https://couler-proj.github.io/couler/examples/#hello-world
spec:
entrypoint: whalesay
templates:
- name: whalesay
container:
image: docker/whalesay:latest
command: [cowsay]
args: ["hello world"]

hello world

1
kubectl get pod -n argo

pod 完成

  • argo 命令用起来参考了 kubectl 的习惯,还是非常丝滑的;
  • argo 使用 submit 子命令创建 Workflow ;
  • Workflow 资源显示非常详细,包含运行状态、所用资源、运行时间等;

3.2.2 Parameters

1
2
argo submit arguments-parameters.yaml -p message="ygqygq2 is testing argo workflows" -n argo
argo watch -n argo arguments-parameters-fnbpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: arguments-parameters-
spec:
entrypoint: whalesay
# Parameters can be passed/overridden via the argo CLI.
# To override the printed message, run `argo submit` with the -p option:
# $ argo submit examples/arguments-parameters.yaml -p message="goodbye world"
arguments:
parameters:
- name: message
value: hello world

templates:
- name: whalesay
inputs:
parameters:
- name: message
container:
image: docker/whalesay:latest
command: [cowsay]
args: ["{{inputs.parameters.message}}"]

参数化构建

运行结果

1
argo logs -n argo  arguments-parameters-fnbpl

全局参数

1
2
argo submit -n argo global-parameters.yaml -p message="ygqygq2 is testing workflows"
argo logs -n argo global-parameters-jhwqj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: global-parameters-
spec:
entrypoint: whalesay1
# Parameters can be passed/overridden via the argo CLI.
# To override the printed message, run `argo submit` with the -p option:
# $ argo submit examples/arguments-parameters.yaml -p message="goodbye world"
arguments:
parameters:
- name: message
value: hello world

templates:
- name: whalesay1
container:
image: docker/whalesay:latest
command: [cowsay]
args: ["{{workflow.parameters.message}}"]

全局参数

  • argo 通过 -p key=value 方式传递参数给 Workflow 的容器,Workflow 中使用 args: ["{{inputs.parameters.message}}"] 接收参数;
  • --parameter-file params.yaml 参数构建可以指定 YAML 或 JSON 格式参数文件;
  • {{workflow.parameters.message}} 这种方式 workflow 全局参数 message

3.2.3 Steps

多步骤 workflow

1
2
argo submit steps.yaml -n argo
argo watch -n argo steps-slbmb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# This template demonstrates a steps template and how to control sequential vs. parallel steps.
# In this example, the hello1 completes before the hello2a, and hello2b steps, which run in parallel.
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: steps-
spec:
entrypoint: hello-hello-hello
templates:
- name: hello-hello-hello
steps:
- - name: hello1
template: whalesay
arguments:
parameters: [{name: message, value: "hello1"}]
- - name: hello2a
template: whalesay
arguments:
parameters: [{name: message, value: "hello2a"}]
- name: hello2b
template: whalesay
arguments:
parameters: [{name: message, value: "hello2b"}]

- name: whalesay
inputs:
parameters:
- name: message
container:
image: docker/whalesay
command: [cowsay]
args: ["{{inputs.parameters.message}}"]

多步骤工作流

最终运行结果 可以看到有 3 个运行完的 POD

串行、并行

1
argo logs -n argo steps-slbmb

输出日志

  • 可以看到 hello1、hello2a 是串行关系;
  • hello2a、hello2b 是并行关系;
  • argo log 可以看到不同 pod 的输出不同颜色,这点体验不错;

3.2.4 DAG(directed-acyclic graph)

在下面工作流中,步骤 A B 同时运行,因为它们不依赖其它步骤,步骤 C 依赖 A,步骤 D 依赖 AB,它们的依赖步骤运行完成,才会开始。 多根工作流

1
2
argo submit -n argo dag-multiroot.yaml
argo watch -n argo dag-multiroot-z4zzz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# The following workflow executes a multi-root workflow
#
# A B
# / \ /
# C D
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: dag-multiroot-
spec:
entrypoint: multiroot
templates:
- name: echo
inputs:
parameters:
- name: message
container:
image: alpine:3.7
command: [echo, "{{inputs.parameters.message}}"]
- name: multiroot
dag:
tasks:
- name: A
template: echo
arguments:
parameters: [{name: message, value: A}]
- name: B
template: echo
arguments:
parameters: [{name: message, value: B}]
- name: C
depends: "A"
template: echo
arguments:
parameters: [{name: message, value: C}]
- name: D
depends: "A && B"
template: echo
arguments:
parameters: [{name: message, value: D}]

A 和 B 同时开始

依赖步骤运行完成

全部步骤运行完成

1
argo logs -n argo dag-multiroot-z4zzz

工作流日志

非 FailFast

1
2
argo submit -n argo dag-disable-failFast.yaml
argo watch -n argo dag-primay-branch-jdg6l
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: dag-primay-branch-
spec:
entrypoint: statis
templates:
- name: a
container:
image: docker/whalesay:latest
command: [cowsay]
args: ["hello world"]
- name: b
retryStrategy:
limit: "2"
container:
image: alpine:latest
command: [sh, -c]
args: ["sleep 30; echo haha"]
- name: c
retryStrategy:
limit: "3"
container:
image: alpine:latest
command: [sh, -c]
args: ["echo intentional failure; exit 2"]
- name: d
container:
image: docker/whalesay:latest
command: [cowsay]
args: ["hello world"]
- name: statis
dag:
failFast: false
tasks:
- name: A
template: a
- name: B
depends: "A"
template: b
- name: C
depends: "A"
template: c
- name: D
depends: "B"
template: d
- name: E
depends: "D"
template: d

FailFast false 示例:dag-disable-failFast.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: dag-primay-branch-
spec:
entrypoint: statis
templates:
- name: a
container:
image: docker/whalesay:latest
command: [cowsay]
args: ["hello world"]
- name: b
retryStrategy:
limit: "2"
container:
image: alpine:latest
command: [sh, -c]
args: ["sleep 30; echo haha"]
- name: c
retryStrategy:
limit: "3"
container:
image: alpine:latest
command: [sh, -c]
args: ["echo intentional failure; exit 2"]
- name: d
container:
image: docker/whalesay:latest
command: [cowsay]
args: ["hello world"]
- name: statis
dag:
failFast: false
tasks:
- name: A
template: a
- name: B
depends: "A"
template: b
- name: C
depends: "A"
template: c
- name: D
depends: "B"
template: d
- name: E
depends: "D"
template: d

运行 dag-disable-failFast.yaml

非 FailFast

A运行完

C运行出错

C后面的步骤继续运行

所有步骤运行完成

1
argo logs -n argo dag-primay-branch-jdg6l

查看日志输出

  • DAG 默认 FailFast 设置为 true,即一旦有步骤失败,它将停止调度后面的步骤,只等待正在运行的步骤完成;
  • 如果将 FailFast 设置为 false,它将不管步骤运行结果,继续调度后面步骤,直至所有步骤运行完成;

3.2.5 Artifacts

配置制品库参考:https://argoproj.github.io/argo-workflows/configure-artifact-repository/ 支持 Minio、AWS s3、GCS、阿里 OSS

1
argo submit -n argo artifact-repository-ref.yaml

当前官方示例报错,它建议使用 emptyDir 官方示例报错

我们根据官方 emptyDir 示例 继续

1
2
argo submit -n argo /tmp/artifactory-emptydir.yaml
argo watch -n argo empty-dir-sqrr9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: empty-dir-
spec:
entrypoint: main
templates:
- name: main
container:
image: argoproj/argosay:v2
command: [sh, -c]
args: ["cowsay hello world | tee /mnt/out/hello_world.txt"]
volumeMounts:
- name: out
mountPath: /mnt/out
volumes:
- name: out
emptyDir: { }
outputs:
parameters:
- name: message
valueFrom:
path: /mnt/out/hello_world.txt

使用emptydir输出制品

正常输出

  • 当前 k8s 不允许 workflow 直接输出制品在目录或文件中,须使用 emptyDir 或 pvc 等;
  • 制品默认被打包为 Tarballs,默认情况下是 gzipped。可以通过使用归档字段指定归档策略来自定义此行为;

3.2.6 The Structure of Workflow Specs

Workflow 基本结构:

  • 包含元数据的 kubernetes 头部
  • Spec 主体
    • 带有可选参数的入口点调用
    • 模板定义列表
  • 对于每个模板定义
    • 模板名称
    • 可选的输入列表
    • 可选的输出列表
  • 容器调用或步骤列表
    • 对于每个步骤,都有一个模板调用

总而言之,Workflow 规范是由一组 Argo 模板组成的,其中每个模板包含一个可选的输入部分,一个可选的输出部分,以及一个容器调用或者一个步骤列表,其中每个步骤调用另一个模板。

注意,Workflow 规范的容器部分将接受与 pod 规范的容器部分相同的选项,包括但不限于环境变量、secret、volume、挂载。因此本文不在赘述相关 kubernetes 资源在 workflow 中的使用。

3.2.7 Scripts & Results

使用脚本获取运行结果

1
2
argo submit -n argo scripts-bash.yaml
argo watch -n argo scripts-bash-4fcm8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# script templates provide a way to run arbitrary snippets of code
# in any language, to produce a output "result" via the standard out
# of the template. Results can then be referenced using the variable,
# {{steps.<stepname>.outputs.result}}, and used as parameter to other
# templates, and in 'when', and 'withParam' clauses.
# This example demonstrates the use of a bash shell script to
# generate a random number which is printed in the next step.
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: scripts-bash-
spec:
entrypoint: bash-script-example
templates:
- name: bash-script-example
steps:
- - name: generate
template: gen-random-int
- - name: print
template: print-message
arguments:
parameters:
- name: message
value: "{{steps.generate.outputs.result}}"

- name: gen-random-int
script:
image: debian:9.4
command: [bash]
source: |
cat /dev/urandom | od -N2 -An -i | awk -v f=1 -v r=100 '{printf "%i\n", f + r * $1 / 65536}'

- name: print-message
inputs:
parameters:
- name: message
container:
image: alpine:latest
command: [sh, -c]
args: ["echo result was: {{inputs.parameters.message}}"]

脚本和结果

1
argo logs -n argo scripts-bash-4fcm8

输出日志

  • script 关键字允许使用 source 来定义脚本主体,这将创建一个包含脚本主体的临时文件,然后将临时文件的名称作为最后一个参数传递给 commandcommand应该是一个脚本解释器;
  • 使用 script 我特性还将运行脚本的标准输出指定给一个名为 result的特殊输出参数。这允许您在 workflow 的其余部分中使用脚本运行结果,在上面示例中,结果只是由 print-message 模板回显。

3.2.8 Output Parameters

输出参数提供了将步骤的结果作为参数而不是作为工件使用的通用机制。这允许您将任何类型的步骤(而不仅仅是script)的结果用于条件测试、循环和参数。输出参数的工作方式与 script result类似,只是输出参数的值设置为生成文件的内容,而不是标准输出的内容。

3.2.9 Loops

循环

1
2
argo submit -n argo loops-param-argument.yaml
argo watch -n argo loops-param-arg-rnwms
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# This example demonstrates a workflow accepting a list of items (as JSON string)
# as an input parameter, and using it for expanding a step into multiple steps.
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: loops-param-arg-
spec:
entrypoint: loop-param-arg-example
arguments:
parameters:
- name: os-list
value: |
[
{ "image": "debian", "tag": "9.1" },
{ "image": "debian", "tag": "8.9" },
{ "image": "alpine", "tag": "3.6" },
{ "image": "ubuntu", "tag": "17.10" }
]

templates:
- name: loop-param-arg-example
inputs:
parameters:
- name: os-list
steps:
- - name: test-linux
template: cat-os-release
arguments:
parameters:
- name: image
value: "{{item.image}}"
- name: tag
value: "{{item.tag}}"
withParam: "{{inputs.parameters.os-list}}"

- name: cat-os-release
inputs:
parameters:
- name: image
- name: tag
container:
image: "{{inputs.parameters.image}}:{{inputs.parameters.tag}}"
command: [cat]
args: [/etc/os-release]

创建workflow

watch workflow

等待结果

日志输出

  • workflow 支持循环的使用,不止是单个步骤,也支持多步骤中使用循环;
  • 可以使用列表作为参数传递;
  • 也可以动态生成要迭代的列表项;

3.2.10 Conditionals

Workflow 支持条件执行,语法是通过 govaluate实现的,它为复杂的语法提供了支持。

1
2
argo submit -n argo conditionals-complex.yaml
argo watch -n argo coinflip-8zsc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# In this example we flip 2 times a coin. First time we
# use the simple conditionals syntax. The second time we use
# regex and a complex condition with logical AND and OR.
# We also use of the parenthesis for defining the priority.
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: coinflip-
spec:
entrypoint: coinflip
templates:
- name: coinflip
steps:
# flip a coin
- - name: flip-coin
template: flip-coin
# evaluate the result in parallel
- - name: heads
template: heads # call heads template if "heads"
when: "{{steps.flip-coin.outputs.result}} == heads"
- name: tails
template: tails # call tails template if "tails"
when: "{{steps.flip-coin.outputs.result}} == tails"
- - name: flip-again
template: flip-coin
- - name: complex-condition
template: heads-tails-or-twice-tails
# call heads template if first flip was "heads" and second was "tails" OR both were "tails"
when: >-
( {{steps.flip-coin.outputs.result}} == heads &&
{{steps.flip-again.outputs.result}} == tails
) ||
( {{steps.flip-coin.outputs.result}} == tails &&
{{steps.flip-again.outputs.result}} == tails )
- name: heads-regex
template: heads # call heads template if ~ "hea"
when: "{{steps.flip-again.outputs.result}} =~ hea"
- name: tails-regex
template: tails # call heads template if ~ "tai"
when: "{{steps.flip-again.outputs.result}} =~ tai"

# Return heads or tails based on a random number
- name: flip-coin
script:
image: python:alpine3.6
command: [python]
source: |
import random
result = "heads" if random.randint(0,1) == 0 else "tails"
print(result)

- name: heads
container:
image: alpine:3.6
command: [sh, -c]
args: ["echo \"it was heads\""]

- name: tails
container:
image: alpine:3.6
command: [sh, -c]
args: ["echo \"it was tails\""]

- name: heads-tails-or-twice-tails
container:
image: alpine:3.6
command: [sh, -c]
args: ["echo \"it was heads the first flip and tails the second. Or it was two times tails.\""]

创建workflow

watch结果

3.2.11 Recursion

递归

1
2
argo submit -n argo coinflip-recursive.yaml
argo watch -n argo coinflip-recursive-5c96n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# coinflip-recursive is a variation of the coinflip example.
# This is an example of a dynamic workflow which extends
# indefinitely until it achieves a desired result. In this
# example, the 'flip-coin' step is recursively repeated until
# the result of the step is "heads".
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: coinflip-recursive-
spec:
entrypoint: coinflip
templates:
- name: coinflip
steps:
- - name: flip-coin
template: flip-coin
- - name: heads
template: heads
when: "{{steps.flip-coin.outputs.result}} == heads"
- name: tails
template: coinflip
when: "{{steps.flip-coin.outputs.result}} == tails"

- name: flip-coin
script:
image: python:alpine3.6
command: [python]
source: |
import random
result = "heads" if random.randint(0,1) == 0 else "tails"
print(result)

- name: heads
container:
image: alpine:3.6
command: [sh, -c]
args: ["echo \"it was heads\""]

创建workflow

watch结果

  • 直到达成某个条件,workflow 才会跳出递归,然后结束;
  • 和程序员开发过程中写循环一样,设定是否能正常退出递归的条件尤为关键,否则将会一直创建 POD 执行任务,直到超时或干预停止 workflow;

3.2.12 Exit handlers

退出处理程序是一个总是在工作流结束时执行的模板,无论成功还是失败。 退出处理程序的一些常见用例如下:

  • 工作流程运行后的清理工作
  • 发送工作流状态通知(例如,电子邮件/Slack)
  • 将通过/失败状态发布到 webhook 结果(例如 GitHub 构建结果)
  • 重新提交或提交另一个工作流
1
2
argo submit -n argo exit-handlers.yaml
argo watch -n argo exit-handlers-77kvs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# An exit handler is a template reference that executes at the end of the workflow
# irrespective of the success, failure, or error of the primary workflow. To specify
# an exit handler, reference the name of a template in 'spec.onExit'.
# Some common use cases of exit handlers are:
# - sending notifications of workflow status (e.g. e-mail/slack)
# - posting the pass/fail status to a webhook result (e.g. github build result)
# - cleaning up workflow artifacts
# - resubmitting or submitting another workflow
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: exit-handlers-
spec:
entrypoint: intentional-fail
onExit: exit-handler
templates:
# primary workflow template
- name: intentional-fail
container:
image: alpine:latest
command: [sh, -c]
args: ["echo intentional failure; exit 1"]

# exit handler related templates
# After the completion of the entrypoint template, the status of the
# workflow is made available in the global variable {{workflow.status}}.
# {{workflow.status}} will be one of: Succeeded, Failed, Error
- name: exit-handler
steps:
- - name: notify
template: send-email
- name: celebrate
template: celebrate
when: "{{workflow.status}} == Succeeded"
- name: cry
template: cry
when: "{{workflow.status}} != Succeeded"
- name: send-email
container:
image: alpine:latest
command: [sh, -c]
# Tip: {{workflow.failures}} is a JSON list. If you're using bash to read it, we recommend using jq to manipulate
# it. For example:
#
# echo "{{workflow.failures}}" | jq -r '.[] | "Failed Step: \(.displayName)\tMessage: \(.message)"'
#
# Will print a list of all the failed steps and their messages. For more info look up the jq docs.
# Note: jq is not installed by default on the "alpine:latest" image, however it can be installed with "apk add jq"
args: ["echo send e-mail: {{workflow.name}} {{workflow.status}} {{workflow.duration}}. Failed steps {{workflow.failures}}"]
- name: celebrate
container:
image: alpine:latest
command: [sh, -c]
args: ["echo hooray!"]
- name: cry
container:
image: alpine:latest
command: [sh, -c]
args: ["echo boohoo!"]

创建workflow

watch结果

日志输出

4. 小结

其它的一些功能和示例,这里不再展开。使用时多查看官方文档,多试验,就会越发了解 argo workflows; 当前可能配合 jenkins 这类 CI/CD 工具一起使用效果比较好; 整体使用下来,感受到 Argo Workflows 的强大功能,也希望未来将会越来越好,随着其功能的逐渐完善,期待其成为 kubernetes 中 CI/CD 的标杆。

参考资料: [1] https://argoproj.github.io/argo-workflows/ [2] https://github.com/argoproj/argo-workflows/blob/master/examples/README.md

1. Argo CD 简单介绍

Argo CD 是一个用于 Kubernetes 遵循声明式 GitOps 持续交付工具。 其介绍直接查看官方文档即可,本文使用一个示例来体验 Argo CD。

argo cd 架构图

2. 部署应用

我使用 helm 安装 bitnami 的 charts 仓库中的 argo-cd。安装过程略…

添加仓库 添加仓库

添加/查看 k8s 集群 默认已经添加k8s集群

创建应用 使用添加的仓库创建应用

同步应用 同步应用

可以看到同步应用时的资源清单 可以看到要同步的资源

k8s 中查看应用部署情况 看到资源已经运行

应用变成健康状态 应用已经健康

部署一个 helm 应用 部署一个 helm 应用

3. 小结

除了 yaml 方式,helm app 也体验了下。虽然说多环境和应用仓库、配置分离是 GitOPS 的痛点,但是当前 Argo CD 只支持 values.yaml 文件和 chart 包在同一仓库下,也表示其不适合大规模使用,因为通常应用维护人员并不一定是 chart 开发人员,其 Git 或 Chart 仓库权限不好分离。 后面再体验下 Argo 的其它产品。

1. https 访问出现问题

以前正常的程序,更新版本后,出现访问 https 报错:

1
No appropriate protocol (protocol is disabled or cipher suites are inappropriate);

2. 处理过程

java 程序都是容器化部署的,我这边是使用的 centos 和 oracle jdk 制作的基础镜像,一般是一年更新一次版本,修复下漏洞。

根据报错信息,搜到 jdk 下的文件 jre/lib/security/java.security 里有个安全性参数 jdk.tls.disabledAlgorithms 对 SSL 版本使用有影响,经过对比官方 jdk1.8.0_161jdk1.8.0_321,发现确实有差异。

161

321

解决办法可以根据自己情况修改这个参数值,也可以修改代码里自己支持的 ssl 协议版本。

ssl协议版本

3. 小结

虽然 JDK 只升级了小版本,但是升级前还是要查看官方的版本 release notes,不然可能会产生想不到的坑。

https://www.oracle.com/java/technologies/javase/jdk-relnotes-index.html

1. rhel 安装 docker ce 需求

RHEL 7 上安装 docker ce,但是官方提示并不支持,使用相应的源也会报依赖错误。那我非要安装呢?

不支持rhel x86

2. 使用 CentOS7 源代替 rhel 源

直接到阿里云镜像源上把 base、epel、docker ce的 yum repo文件下载好,因为 rhel 不认识 CentOS 源中的这种变量 $releasever,我们直接根据 yum 源目录将变量替换成相应字符串。

http://mirrors.aliyun.com/centos/

查看目录

替换变量

替换变量

然后继续使用 yum 命令安装 docker ce,成功后 docker 能正常使用。

3. 制作 docker ce 离线安装包

可以选择使用最小化安装的 centos 环境制作 docker ce 离线安装包。

  • 准备离线安装包脚本 ready_offline_rpm.sh 内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
cd `dirname $0`
SH_DIR=`pwd`
packages_dir=$SH_DIR/packages
DOCKERVERSION="20.10.13"

# 使用阿里云镜像源
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

# 创建本地仓库包
yum install --downloadonly --downloaddir=$packages_dir \
createrepo

# 实用工具
yum install --downloadonly --downloaddir=$packages_dir \
yum-utils \
curl \
wget \

# docker 依赖包
yum install --downloadonly --downloaddir=$packages_dir \
device-mapper-persistent-data \
lvm2

# 添加阿里云Docker源
yum -y install yum-utils
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# docker ce
yum install --downloadonly --downloaddir=$packages_dir \
docker-ce-$DOCKERVERSION \
docker-ce-cli-$DOCKERVERSION \
containerd.io

脚本执行后,生成的 packages 目录就是离线安装包。

  • 离线安装包准备本地 yum 源脚本 ready_local_yum.sh 内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cd `dirname $0`
packages_dir=$SH_DIR/packages

yum -y install policycoreutils-python audit-libs-python

rpm -ivh $packages_dir/deltarpm-*.rpm
rpm -ivh $packages_dir/libxml2-python-*.rpm
rpm -ivh $packages_dir/python-deltarpm-*.rpm
rpm -ivh $packages_dir/createrepo-*.rpm

createrepo $packages_dir

cat > /etc/yum.repos.d/CentOS-Media.repo <<EOF
[c7-media]
name=CentOS-$releasever - Media
baseurl=file://$packages_dir
gpgcheck=0
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
https://mirrors.aliyun.com/docker-ce/linux/centos/gpg
EOF

# yum -y install docker-ce

脚本执行后,就能使用 yum -y install docker-ce 安装 docker ce了。

1. 问题

版本: kubernetes version:

1
2
3
4
NAME      STATUS   ROLES                  AGE   VERSION
master1 Ready control-plane,master 56d v1.23.4
master2 Ready control-plane,master 56d v1.23.4
master3 Ready control-plane,master 56d v1.23.4

calico version:

1
2
3
4
Client Version:    v3.22.1
Git commit: 82e7ce520
Cluster Version: v3.22.1
Cluster Type: typha,kdd,k8s,operator,bgp,kubeadm

kubernetes 节点的 pod 间无法通信

2. 问题排查

安装 calicoctl 命令排查 发现有节点IPv4不对

查看异常 ip 对应的网卡 查看网卡

使用 docker 列出 network 有以前docker compose使用的网络设置

清除这些不使用的网络 docker network rm fastdfs-net fd_fastdfs-net

重启 pod 再重启相应pod

确认是否正常

master1

master2

确认跨节点 pod 正常通信了 k8s网络正常

3. 小结

使用 calico 网络插件需要在支持 bgp 协议的网络中,默认的 node to node mesh 的 peer type 下,节点数一多,会占用大量的连接数,官方推荐其在 100 节点内使用。

参考资料: [1] https://projectcalico.docs.tigera.io/about/about-k8s-networking

1. 目的和环境说明

目的:搭建一套拥有 ceph 集群并能直接用于开发、学习的高可用 kubernetes集群

虚拟机:3台 每台硬件配置:cpu 2核及以上、内存 8G 及以上、硬盘 2 块(其中一块用于 ceph osd) 每台虚拟机系统安装 CentOS7,除了 /boot 分区,其余空间均分配给根分区,要求虚拟机能访问互联网并正常解析域名。


工具: kubernetes:v1.23.1 helm:v3.8.0


charts: metallb:2.6.0 ingress-nginx:4.0.15 rook-ceph:v1.8.2 rook-ceph-cluster:v1.8.2 kubernetes-dashboard:5.1.1 kubeapps:7.7.1

2. 一键安装 kubernetes 集群

2.1 初始化集群

本人的一键安装集群脚本:https://github.com/ygqygq2/kubeadm-shell

3台虚拟机设置好hostname 和 hosts /etc/hosts

1
2
3
10.111.3.53 master1
10.111.3.54 master2
10.111.3.55 master3

设置 config.sh 如下:

master1 上执行 sh kubeadm_install_k8s.sh 脚本完成后,会自动创建 3 master 节点的 高可用 kubernetes 集群。

2.2 安装 flannel 网络插件

这里使用 flannel 网络插件,当然你也可以选择其它网络插件。

1
2
3
4
mkdir -p /data/yaml
cd /data/yaml
wget https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
kubectl apply -f kube-flannel.yml

2.3 安装 helm 命令

1
2
3
4
5
cd /tmp
wget https://get.helm.sh/helm-v3.8.0-rc.1-linux-amd64.tar.gz
tar -zxvf helm-v3.8.0-rc.1-linux-amd64.tar.gz
mv linux-amd64/helm /usr/local/sbin/
chmod a+x /usr/local/sbin/helm

使用 helm repo add bitnami https://charts.bitnami.com/bitnami 等将以下 repo 添加好。

3. helm 安装各组件

3.1 阿里云申请用到的免费 ssl 证书

kubernetes dashboard 和 ceph dashboard 默认使用 https 方式访问,所以需要用到 ssl 证书,如修改成 http 方式(非安全)访问也可,本文不作这方面描述。 申请好免费证书,并下载 nginx 格式证书文件

上传好证书文件并解压

将证书文件创建成 tls secret, kubectl create ns rook-ceph # 创建 rook-ceph 命名空间 kubectl create secret tls ceph.k8snb.com --cert 7133599_ceph.k8snb.com.pem --key 7133599_ceph.k8snb.com.key -n rook-ceph kubectl create secret tls dashboard.k8snb.com --cert 7123996_dashboard.k8snb.com.pem --key 7123996_dashboard.k8snb.com.key -n kube-system

3.2 安装 metallb

MetalLB is an open source, rock solid LoadBalancer. It handles the ServiceType: Loadbalancer,即提供一个内网负载均衡器,为 LoadBalancer 类型的 service 提供一个可访问的负载均衡 IP。

1
2
3
4
5
mkdir -p /data/helm
helm fetch --untar bitnami/metallb
cd metallb
mkdir kube-system # 按目录区分安装的命名空间
rsync -avz values.yaml kube-system/

vim kube-system/values.yaml 修改配置 按如下图配置一个负载均衡 ip 池:

安装

1
helm install metallb -n kube-system -f kube-system/values.yaml .

3.3 安装 ingress-nginx

这里使用官方 ingress-nginx。

1
2
3
4
5
cd /data/helm
helm fetch --untar ingress-nginx/ingress-nginx
cd ingress-nginx
mkdir kube-system
rsync -avz values.yaml kube-system/

vim kube-system/values.yaml 修改配置和镜像仓库(主要是国内访问不了谷歌镜像仓库)

安装

1
helm install ingress-nginx -n kube-system -f kube-system/values.yaml .

3.4 安装 rook-ceph

rook 不作过多介绍,你可以访问其官网查看:https://rook.io/docs/rook/v1.8/

1
2
3
4
5
cd /data/helm
helm fetch --untar rook/rook-ceph
cd rook-ceph
mkdir rook-ceph
rsync -avz values.yaml rook-ceph/

vim rook-ceph/values.yaml 修改配置和镜像仓库 github 上发现一个谷歌镜像同步到 docker hub的功能,在他这里提 issues,即可完成同步,然后按其说明,将仓库替换下就可以了。以下为我提的 issues,已经完成镜像同步,直接使用即可。

安装

1
helm install rook-ceph -n rook-ceph -f rook-ceph/values.yaml .

3.5 安装 rook-ceph-cluster

1
2
3
4
5
cd /data/helm
helm fetch --untar rook/rook-ceph-cluster
cd rook-ceph-cluster
mkdir rook-ceph
rsync -avz values.yaml rook-ceph/

vim rook-ceph/values.yaml 修改配置

安装

1
helm install rook-ceph-cluster -n rook-ceph -f rook-ceph/values.yaml .

进入 toolbox 修改 ceph dashboard admin 用户密码

kubectl get pod -n rook-ceph 查看 toolbox pod

kubectl exec -it toolbox的pod名 -n rook-ceph /bin/bash 进入 toolbox pod

修改 dashboard 用户密码

1
2
echo password > /tmp/p
ceph dashboard ac-user-set-password admin -i /tmp/p

添加域名解析或本地 hosts 都可。

现在即可通过浏览器登录 cpeh dashboard。

3.6 安装 kubernetes dashboard

1
2
3
4
5
cd /data/helm
helm fetch --untar kubernetes-dashboard/kubernetes-dashboard
cd kubernetes-dashboard
mkdir kube-sytem
rsync -avz values.yaml kube-system/

vim kube-system/values.yaml 修改配置

安装

1
helm install dashboard -n kube-system -f kube-system/values.yaml .

创建 k8s 集群访问帐号(这里为了方便学习,直接使用管理员),并获取登录 token

1
2
3
4
kubectl create serviceaccount admin -n kube-system
kubectl create clusterrolebinding admin --clusterrole=cluster-admin --serviceaccount=kube-system:admin
kubectl get secret -n kube-system|grep admin
kubectl describe secret 上面的secret名 -n kube-system # token 即为登录 token

浏览器访问并登录

3.7 安装 kubeapps

1
2
3
4
5
cd /data/helm
helm fetch --untar bitnami/kubeapps
cd kubeapps
mkdir kubeapps
rsync -avz values.yaml kubeapps/

vim kubeapps/values.yaml 修改配置

安装

1
helm install kubeapps -n kubeapps -f kubeapps/values.yaml .

浏览器访问并登录

[TOC]

1. 安装基础依赖包

安装 epel yum 源和相关基础依赖包。

1
yum -y install epel-release wget make gcc openssl openssl-devel rrdtool rrdtool-perl perl-core perl mod_fcgid perl-CPAN httpd httpd-devel curl bind-utils gcc make vim gcc-c++ perl-LWP-Protocol-https wqy-microhei-fonts

2. 安装 fping

smokeping 2.7.2 以上需要 fping4.0 以上

1
2
3
4
5
6
7
cd /data/packages
wget https://fping.org/dist/fping-4.2.tar.gz
tar -zxvf fping-4.2.tar.gz
cd fping-4.2
./configure
make
make install

3. 安装 echoping

如果使用 tcp ping,需要它。

1
2
3
4
5
6
7
8
cd /data/packages
wget https://fossies.org/linux/misc/old/echoping-6.0.2.tar.gz
tar -zxvf echoping-6.0.2.tar.gz
cd echoping-6.0.2
yum install -y popt-devel openssl openssl-devel
./configure --with-ssl --without-libidn
make
make install

4. 安装 smokeping

源码安装 smokeping 2.7.3。

1
2
3
4
5
6
cd /data/packages
wget https://oss.oetiker.ch/smokeping/pub/smokeping-2.7.3.tar.gz
tar -zxvf smokeping-2.7.3.tar.gz
cd smokeping-2.7.3
./configure --prefix=/usr/local/smokeping
/usr/bin/gmake install

5. 配置 smokeping 和 apche

5.1 配置 smokeping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
cd /usr/local/smokeping
mkdir var htdocs/{data,cache}
chown apache.apache -R /usr/local/smokeping
touch /var/log/smokeping.log
chown apache.apache /var/log/smokeping.log
cd /usr/local/smokeping/htdocs/
cp smokeping.fcgi.dist smokeping.fcgi
chmod 600 /usr/local/smokeping/etc/smokeping_secrets.dist

cat > /usr/local/smokeping/etc/config<<EOF
*** General ***
owner = admin
contact = some@address.nowhere
mailhost = my.mail.host
sendmail = /usr/sbin/sendmail
imgcache = /usr/local/smokeping/htdocs/cache
imgurl = cache
datadir = /usr/local/smokeping/htdocs/data
piddir = /usr/local/smokeping/var
cgiurl = http://10.10.23.103/smokeping/smokeping.fcgi
smokemail = /usr/local/smokeping/etc/smokemail.dist
tmail = /usr/local/smokeping/etc/tmail.dist
# specify this to get syslog logging
syslogfacility = local0

*** Alerts ***
to = alertee@address.somewhere
from = smokealert@company.xy

+hostdown
type = loss
# in percent
pattern = ==0%,==0%,==0%,==U
comment = 对端无响应

+hightloss
type = loss
# in percent
pattern = ==0%,==0%,==0%,==0%,>10%,>10%,>10%
comment = 连续3次采样-丢包率超过10%

+lossdetect
type = loss
# in percent
pattern = ==0%,==0%,==0%,==0%,>0%,>0%,>0%
comment = 连续3次采样-存在丢包

+someloss
type = loss
# in percent
pattern = >0%,*12*,>0%,*12*,>0%
comment = 间断性丢包

+rttdetect
type = rtt
# in milli seconds
pattern = <100,<100,<100,<100,<100,<150,>150,>150,>150
comment = 连续3次采样延迟增大-超过150ms

*** Database ***
step = 60
pings = 20

# consfn mrhb steps total

AVERAGE 0.5 1 1008
AVERAGE 0.5 12 4320
MIN 0.5 12 4320
MAX 0.5 12 4320
AVERAGE 0.5 144 720
MAX 0.5 144 720
MIN 0.5 144 720

*** Presentation ***

charset = utf-8
template = /usr/local/smokeping/etc/basepage.html.dist
htmltitle = yes
graphborders = no

+ charts
menu = Charts
title = The most interesting destinations

++ stddev
sorter = StdDev(entries=>4)
title = Top Standard Deviation
menu = Std Deviation
format = Standard Deviation %f

++ max
sorter = Max(entries=>5)
title = Top Max Roundtrip Time
menu = by Max
format = Max Roundtrip Time %f seconds

++ loss
sorter = Loss(entries=>5)
title = Top Packet Loss
menu = Loss
format = Packets Lost %f

++ median
sorter = Median(entries=>5)
title = Top Median Roundtrip Time
menu = by Median
format = Median RTT %f seconds

+ overview
width = 600
height = 50
range = 10h

+ detail
width = 600
height = 200
unison_tolerance = 2

"Last 3 Hours" 3h
"Last 30 Hours" 30h
"Last 10 Days" 10d
"Last 400 Days" 400d

*** Probes ***
+ FPing
binary = /usr/local/sbin/fping
#可以设置源IP地址,适用于多IP的服务器,(比如组专线内网+公网)服务器
#sourceaddressn = 1.1.1.1

#*** Slaves ***
#secrets=/usr/local/smokeping/etc/smokeping_secrets.dist
#+aliyunShenzhen
#display_name=slave1
#location=China
#color=ff0000
#
#+aliyunHangzhou
#display_name=slave2
#location=China
#color=ff00ff

*** Targets ***
probe = FPing

menu = Top
title = 网络质量监控系统
remark = 如果您是合法管理员,那么欢迎您,如果不是,请立即离开 Only legal administrators are welcome, if you are not, please leave immediately

#加载额外的监控主机(将监控主机,单独成一个文件)
@include targets
EOF

添加监控主机列表文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
cat > /usr/local/smokeping/etc/targets<<EOF
+ dns
menu = 全球公共DNS网络监控
title = 全球公共DNS网络监控
#slaves = slave1 slave2
#alerts = hostdown,hightloss

++ public-dns
menu = 公共DNS
title = 公共DNS网络监控列表
host = /dns/public-dns/dns-1 /dns/public-dns/dns-2 /dns/public-dns/dns-3 /dns/public-dns/dns-4 /dns/public-dns/dns-5 /dns/public-dns/dns-6 /dns/public-dns/dns-7 /dns/public-dns/dns-8 /dns/public-dns/dns-9 /dns/public-dns/dns-10 /dns/public-dns/dns-11 /dns/public-dns/dns-12 /dns/public-dns/dns-13 /dns/public-dns/dns-14 /dns/public-dns/dns-15 /dns/public-dns/dns-16 /dns/public-dns/dns-17 /dns/public-dns/dns-18 /dns/public-dns/dns-19 /dns/public-dns/dns-20 /dns/public-dns/dns-21 /dns/public-dns/dns-22 /dns/public-dns/dns-23 /dns/public-dns/dns-24

+++ dns-1
menu = 江西移动-211.141.90.68
title = 江西移动-211.141.90.68
host = 211.141.90.68
+++ dns-2
menu = 江西电信-202.101.224.68
title = 江西电信-202.101.224.68
host = 202.101.224.68
+++ dns-3
menu = 江西联通-220.248.192.12
title = 江西联通-220.248.192.12
host = 220.248.192.12
+++ dns-4
menu = 114-DNS-114.114.114.114
title = 114-DNS-114.114.114.114
host = 114.114.114.114
+++ dns-5
menu = 114安全版-114.114.114.119
title = 114安全版-114.114.114.119
host = 114.114.114.119

+++ dns-6
menu = 阿里-223.5.5.5
title = 阿里-223.5.5.5
host = 223.5.5.5
+++ dns-7
menu = 百度-180.76.76.76
title = 百度-180.76.76.76
host = 180.76.76.76
+++ dns-8
menu = DNSPOD-119.29.29.29
title = DNSPOD-119.29.29.29
host = 119.29.29.29
+++ dns-9
menu = CNNIC-1.2.4.8
title = CNNIC-1.2.4.8
host = 1.2.4.8
+++ dns-10
menu = DNS派-218.30.118.6
title = DNS派-218.30.118.6
host = 218.30.118.6

+++ dns-11
menu = 谷歌-8.8.8.8
title = 谷歌-8.8.8.8
host = 8.8.8.8
+++ dns-12
menu = IBMQ9-9.9.9.9
title = IBMQ9-9.9.9.9
host = 9.9.9.9
+++ dns-13
menu = CF-1.1.1.1
title = CF-1.1.1.1
host = 1.1.1.1
+++ dns-14
menu = 科莫多-8.26.56.26
title = 科莫多-8.26.56.26
host = 8.26.56.26
+++ dns-15
menu = GTEI-4.2.2.1
title = GTEI-4.2.2.1
host = 4.2.2.1

+++ dns-16
menu = PCCW-205.252.144.228
title = PCCW-205.252.144.228
host = 205.252.144.228
+++ dns-17
menu = HKIX-202.181.224.2
title = HKIX-202.181.224.2
host = 202.181.224.2
+++ dns-18
menu = 澳门-202.175.3.8
title = 澳门-202.175.3.8
host = 202.175.3.8
+++ dns-19
menu = 中华电信-168.95.192.1
title = 中华电信-168.95.192.1
host = 168.95.192.1
+++ dns-20
menu = 英国-193.0.14.129
title = 英国-193.0.14.129
host = 193.0.14.129

+++ dns-21
menu = 日本-202.12.27.33
title = 日本-202.12.27.33
host = 202.12.27.33
+++ dns-22
menu = 韩国-164.124.101.31
title = 韩国-164.124.101.31
host = 164.124.101.31
+++ dns-23
menu = 洛杉矶hostspace-162.212.181.53
title = 洛杉矶hostspace-162.212.181.53
host = 162.212.181.53
+++ dns-24
menu = 洛杉矶hostspace-1-192.126.112.53
title = 洛杉矶hostspace-2-192.126.112.53
host = 192.126.112.53
EOF

添加 smokeping 为服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cat > /usr/lib/systemd/system/smokeping.service<<EOF
[Unit]
Description=Latency Logging and Graphing System
After=syslog.target network.target

[Service]
#ExecStart=/usr/sbin/smokeping --nodaemon # 2.6 yum install
ExecStart=/usr/local/smokeping/bin/smokeping --nodaemon --config=/usr/local/smokeping/etc/config --logfile=/var/log/smokeping.log # 2.7 src install
ExecReload=/bin/kill -HUP $MAINPID
StandardError=syslog

[Install]
WantedBy=multi-user.target
EOF

5.2 配置 apache

/etc/httpd/conf/httpd.conf 的行 DocumentRoot "/var/www/html" 后添加内容,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DocumentRoot "/var/www/html"
Alias /smokeping "/usr/local/smokeping/htdocs/"
<Directory "/usr/local/smokeping">
AllowOverride None
Options All
AddHandler cgi-script .fcgi .cgi
AllowOverride AuthConfig
Order allow,deny
Allow from all
AuthName "Smokeping"
AuthType Basic
AuthUserFile /usr/local/smokeping/htdocs/htpasswd
Require valid-user
DirectoryIndex smokeping.fcgi
</Directory>

htpasswd -c /opt/smokeping/htdocs/htpasswd admin 生成 basic 验证密码。

中文支持,/usr/local/smokeping/etc/basepage.html.dist 的 head 内添加

1
<META charset="utf-8" />

6. 访问测试

启动 smokeping 和 apache,然后访问 http://10.10.23.103/smokeping/

1
2
3
systemctl daemon-reaload
systemctl start smokeping
systemctl start apache

[TOC]

1. 时间触发

时间触发是指定义一个时间,时间到了就触发 pipeline 执行。在 Jenkins pipeline 中使用 trigger 指令来定义时间触发。

trigger 指令只能被定义在 pipeline 块下,Jenkins 内置支持 cron、pollSCM,upstream 三种方式。其他方式可以通过插件来实现。

1.1 定时执行:cron

定时执行就像 cronjob,一到时间点就执行。它的使用场景通常是执行一些周期性的 job,如每夜构建。

1
2
3
4
5
6
7
8
9
10
11
12
13
pipeline {
agent any
triggers {
cron('0 0 * * *')
}
stages {
stage('Nightly build') {
steps {
echo "这是一个耗时的构建,每天凌晨执行"
}
}
}
}

Jenkins trigger cron 语法采用的是 UNIX cron 语法(有些细微的区别)。一条 cron 包含 5 个字段,使用空格或 Tab 分隔,格式为:MINUTE HOUR DOM MONTH DOW。每个字段的含义为:

  • MINUTE:一小时内的分钟,取值范围为 0∼59。
  • HOUR:一天内的小时,取值范围为 0∼23。
  • DOM:一个月的某一天,取值范围为 1∼31。
  • MONTH:月份,取值范围为 1∼12。
  • DOW:星期几,取值范围为 0∼7。0 和 7 代表星期天。 还可以使用以下特殊字符,一次性指定多个值。
  • *:匹配所有的值
  • M-N:匹配 M 到 N 之间的值。
  • M-N/X or*/X:指定在 M 到 N 范围内,以 X 值为步长。
  • A,B,· · ·,Z:使用逗号枚举多个值。

在一些大型组织中,会同时存在大量的同一时刻执行的定时任务,比如 N 个半夜零点(0 0 * * *)执行的任务。这样会产生负载不均衡。在 Jenkins trigger cron 语法中使用“H”字符来解决这一问题,H 代表 hash。对于没必要准确到零点 0 分执行的任务,cron 可以这样写:H 0 * * *,代表在零点 0 分至,H 代表 hash。代表在零点 0 分至零点 59 分之间任何一个时间点执行。

需要注意的是,H 应用在 DOM(一个月的某一天)字段时会有不准确的情况,因为 10 月有 31 天,而 2 月却是 28 天。

Jenkins trigger cron 还设计了一些人性化的别名:@yearly@annually@monthly@weekly@daily@midnight@hourly。例如,@hourlyH * * * *相同,代表一小时内的任何时间;@midnight实际上代表在半夜 12:00 到凌晨 2:59 之间的某个时间。其他别名很少有应用场景。

1.2 轮询代码仓库:pollSCM

轮询代码仓库是指定期到代码仓库询问代码是否有变化,如果有变化就执行。

1
2
3
4
5
6
7
pipeline {
agent any
triggers {
// 每分钟判断一次代码是否有变化
pollSCM("H/1 * * * *")
}
}

事实上,如果代码有变化,最好的方式是代码仓库主动通知 Jenkins,而不是 Jenkins 频繁去代码仓库检查。那这种方式存在的意义是什么? 在一些特殊情况下,比如外网的代码仓库无法调用内网的 Jenkins,或者反过来,则会采用这种方式。

2. 事件触发

事件触发就是发生了某个事件就触发 pipeline 执行。这个事件可以是你能想到的任何事件。比如手动在界面上触发、其他 Job 主动触发、HTTP API Webhook 触发等。

2.1 由上游任务触发:upstream

当 B 任务的执行依赖 A 任务的执行结果时,A 就被称为 B 的上游任务。在 Jenkins 2.22 及以上版本中,trigger 指令开始支持 upstream 类型的触发条件。upstream 的作用就是能让 B pipeline 自行决定依赖哪些上游任务。

1
2
3
4
// job1和job2都是任务名
triggers {
upstream(upstreamProjects: "job1,job2", threshold: hudson.model.Result.SUCCESS)
}

当 upstreamProjects 参数接收多个任务时,使用,分隔。threshold 参数是指上游任务的执行结果是什么值时触发。hudson.model.Result 是一个枚举,包括以下值:

  • ABORTED:任务被手动中止。
  • FAILURE:构建失败。
  • SUCCESS:构建成功。
  • UNSTABLE:存在一些错误,但不至于构建失败。
  • NOT_BUILT:在多阶段构建时,前面阶段的问题导致后面阶段无法执行。

注意:需要手动触发一次任务,让 Jenkins 加载 pipeline 后,trigger 指令才会生效。

2.2 GitLab 通知触发

GitLab 通知触发是指当 GitLab 发现源代码有变化时,触发 Jenkins 执行构建。

由 GitLab 主动通知进行构建的好处是显而易见的,这样很容易就解决了我们之前提到的轮询代码仓库时“多久轮询一次”的问题,实现每一次代码的变化都对应一次构建。

2.2.1 在 pipeline 中实现 GitLab trigger

GitLab 插件上实现了基于 GitLab 的 trigger。以下是具体使用方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pipeline {
agent any
triggers {
gitlab(triggerOnPush: true,
triggerOnMergeRequest: true,
branchFilterType: "All",
secretToken: "t8vcxwuza023ehzcftzr5a74vkpto6xr")
}
stages {
stage('build') {
steps {
echo 'Hello World from gitlab trigger'
}
}
}
}

secretToken 使用随机字符串生成器生成即可。如果 Jenkins 在内网使用,并且安全性有一定的保障,我们可以将 secretToken 定义为一个 Jenkins 全局变量,供所有的项目使用。这样做就不用为每个项目重新生成 token 了。 GitLab trigger 方法有很多参数可配置,下面简单介绍一些常用的参数。

  • triggerOnPush:当 GitLab 触发 push 事件时,是否执行构建。
  • triggerOnMergeRequest:当 GitLab 触发 mergeRequest 事件时,是否执行构建。
  • branchFilterType:只有符合条件的分支才会被触发。必选,否则无法实现触发。可以设置的值有:
    • NameBasedFilter:基于分支名进行过滤,多个分支名使用逗号分隔。
    • RegexBasedFilter:基于正则表达对分支名进行过滤。
    • All:所有分支都会被触发。
  • includeBranchesSpec:基于 branchFilterType 值,输入期望包括的分支的规则。
  • excludeBranchesSpec:基于 branchFilterType 值,输入期望排除的分支的规则。

2.2.2 使用 Generic Webhook Trigger 插件实现触发

安装 Generic Webhook Trigger 插件(下文使用 GWT 简称)后,Jenkins 会暴露一个 API: <JENKINS URL>/generic-webhook-trigger/invoke,即由 GWT 插件来处理此 API 的请求。

以下为使用token示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
pipeline {
agent any
triggers {
GenericTrigger(
genericVariables: [
[
key: 'ref',
value: '$.ref'
]
],

token: 'secret',

causeString: 'Triggered on $ref',
printContributedVariables: true,
printPostContent: true
)
}
stages {
stage("Some step") {
steps {
sh "echo $ref"
sh "printenv"
}
}
}
}

curl -X POST -H "Content-Type: application/json" -d '{"ref": "ref/heads/master"}' -s https://jenkins.utcook.com/generic-webhook-trigger/invoke?token=secret

触发结果

GenericTrigger 触发条件由 GWT 插件提供。此触发条件可以说是 GWT 的所有内容。 可以将 GenericTrigger 触发条件分为 5 部分,这样更易于理解各参数的作用。

  • 从 HTTP POST 请求中提取参数值。
  • token,GWT 插件用于标识 Jenkins 项目的唯一性。
  • 根据请求参数值判断是否触发 Jenkins 项目的执行。
  • 日志打印控制。
  • Webhook 响应控制。

一个 HTTP POST 请求可以从三个维度提取参数,即 POST body、URL 参数和 header GWT 插件提供了三个参数分别对这三个维度的数据进行提取。

  1. genericVariables:提取 POST body 中的参数。
1
2
3
4
5
6
7
8
9
genericVariables: [
[key: 'ref', value: '$.ref'],
[key: 'before',
value: '$.before',
expressionType: 'JSONPath',
regexpFilter: '',
defaultValue: ''
]
]
  • value:JSONPath 表达式,或者 XPath 表达式,取决于expressionType参数值,用于从 POST body 中提取值。
  • key:从 POST body 中提取出的值的新变量名,可用于 pipeline 其他步骤。
  • expressionType:可选,value的表达式类型,默认为JSONPath。当请求为 XML 内容时,必须指定 XPath 值。
  • defaultValue:可选,当提取不到值,且defaultValue不为空时,则使用defaultValue作为返回值。
  • regexpFilter:可选,过滤表达式,对提取出来的值进行过滤。regexpFilter做的事情其实就是string.replaceAll(regexpFilter,"");。string是从 HTTP 请求中提取出来的值。
  1. genericRequestVariables:从 URL 参数中提取值。
1
2
3
4
genericRequestVariables: [
[key: 'requestWithNumber', regexpFilter: '[^0-9]'],
[key: 'requestWithString', regexpFilter: '']
]
  • key:提取出的值的新变量名,可用于 pipeline 其他步骤。
  • regexpFilter:对提取出的值进行过滤。
  1. genericHeaderVariables:从 HTTP header 中提取值。

    1
    2
    3
    4
    genericHeaderVariables: [
    [key: 'headerWithNumber', regexpFilter: '[^0-9]'],
    [key: 'headerWithString', regexpFilter: '']
    ]

genericHeaderVariables的用法与genericRequestVariables一样,区别是它是从 HTTP header 中提取值的。

根据请求参数值判断是否触发 Jenkins 项目执行

GWT 并不只是根据token值来判断是否触发,还可以根据我们提取出的值进行判断。示例如下:

1
2
3
4
5
6
7
8
9
10
GenericTrigger(
genericVariables: [
[key: 'refValue', value: '$.ref'],
],

token: env.JOB_NAME,

regexpFilterText: '$refValue',
regexpFilterExpression: 'refs/heads/(master|dev)'
)
  • regexpFilterText:需要进行匹配的 key。例子中,我们使用从 POST body 中提取出的refValue变量值。
  • regexpFilterExpression:正则表达式。 如果regexpFilterText参数的值符合regexpFilterExpression参数的正则表达式,则触发执行。

控制打印内容

打印日志有助于调试。GWT 插件提供了三个参数。

  • printPostContent:布尔值,将 Webhook 请求信息打印到日志上。
  • printContributedVariables:布尔值,将提取后的变量名及变量值打印出来。
  • causeString:字符串类型,触发原因,可以直接引用提取后的变量,如 causeString:'Triggered on $msg'

控制响应

  • silentResponse:布尔类型,在正常情况下,当 Webhook 请求成功后,GWT 插件会返回 HTTP 200 状态码和触发结果给调用方。但是当silentResponse设置为true时,就只返回 HTTP 200 状态码,不返回触发结果。

3. 开发推送代码触发 jenkins 构建实战

3.1 安装 Jenkins 插件

3.2 创建项目

新建 gitlab 项目

新建gitlab项目

新建 jenkins 项目

新建jenkins项目

gltlab 设置集成 webhook gltlab设置集成webhook

webhook 测试报错 测试webhook报错

以上报错需要进行 jenkins 安装设置,取消勾选“CSRF Protection” jenkins安全设置

3.3 将构建状态信息推送到 Gitlab

jenkins 构建项目后,可以将构建的状态信息推送到 gitlab 的 pipeline 中,并且点击 pipeline 会自动跳转到 jenkins 的构建页面下。

首先 gitlab 仓库的管理帐户下生成个人访问 token。

生成gitlab token

然后在 jenkins 内,进入"Manage Jenkins" → “Configure System”,页面中找到“Gitlab”,并添加 gitlab 和 token 凭证信息。

添加gitlab

添加gitlab token凭证

修改Jenkinsfile,如果 jenkins 中未触发过任务,第一次需要手动触发,以后 gitlab 内代码的修改会自动触发,并将运行结果提交到 gitlab pipeline 中。

完整的Jenkinsfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
pipeline {
agent any
triggers {
gitlab(triggerOnPush: true,
triggerOnMergeRequest: true,
branchFilterType: "All",
secretToken: "t8vcxwuza023ehzcftzr5a74vkpto6xr")
}
stages {
stage('build') {
steps {
echo 'Hello World from gitlab trigger'
}
}
}
post {
failure {
updateGitlabCommitStatus name: "build", state: "failed"
}
success {
updateGitlabCommitStatus name: "build", state: "success"
}
}
options {
gitLabConnection("gitlab")
}
}

修改Jenkinsfile添加post到gitlab

gitlab 仓库的 pipeline 中可查看到构建信息。 enter description here

参考资料: [1] 《Jenkins 2.x 实战指南》 [2] https://jenkins.io/zh/doc/book/pipeline/syntax/ [3] https://jenkins.io/zh/doc/pipeline/steps/ [4] https://blog.csdn.net/xiashei/article/details/88694027

0%