提交 e5b43388 编写于 作者: 阳明的博客's avatar 阳明的博客

替换微博图床

上级 0d727b34
......@@ -99,4 +99,5 @@
* [Harbor](docs/63.Harbor.md)
* [Gitlab](docs/64.Gitlab.md)
* [Gitlab CI](docs/65.Gitlab CI.md)
* [Devops](docs/66/devops.md)
......@@ -140,7 +140,7 @@ spec:
上面我们在 monitoring 命名空间下面创建了名为 etcd-k8s 的 ServiceMonitor 对象,基本属性和前面章节中的一致,匹配 kube-system 这个命名空间下面的具有 k8s-app=etcd 这个 label 标签的 Service,jobLabel 表示用于检索 job 任务名称的标签,和前面不太一样的地方是 endpoints 属性的写法,配置上访问 etcd 的相关证书,endpoints 属性下面可以配置很多抓取的参数,比如 relabel、proxyUrl,tlsConfig 表示用于配置抓取监控数据端点的 tls 认证,由于证书 serverName 和 etcd 中签发的可能不匹配,所以加上了 insecureSkipVerify=true
![tlsConfig](https://ws4.sinaimg.cn/large/006tNbRwgy1fy9s4embvlj313a0fmdik.jpg)
![tlsConfig](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/mI32WB.jpg)
> 关于 ServiceMonitor 属性的更多用法可以查看文档:[https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md](https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md) 了解更多
......@@ -195,7 +195,7 @@ $ kubectl create -f prometheus-etcdService.yaml
创建完成后,隔一会儿去 Prometheus 的 Dashboard 中查看 targets,便会有 etcd 的监控项了:
![prometheus etcd](https://ws4.sinaimg.cn/large/006tNbRwgy1fy9smdbdgwj31y80autbj.jpg)
![prometheus etcd](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/5BQRte.jpg)
可以看到还是有一个明显的错误,和我们上节课监控 kube-scheduler 的错误比较类似于,因为我们这里的 etcd 的是监听在 127.0.0.1 这个 IP 上面的,所以访问会拒绝:
```shell
......@@ -209,17 +209,17 @@ $ kubectl create -f prometheus-etcdService.yaml
重启 etcd,生效后,查看 etcd 这个监控任务就正常了:
![prometheus etcd](https://ws4.sinaimg.cn/large/006tNbRwgy1fy9st81y3tj31yc0a0gnq.jpg)
![prometheus etcd](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/EmEn6b.jpg)
数据采集到后,可以在 grafana 中导入编号为`3070`的 dashboard,获取到 etcd 的监控图表。
![grafana etcd dashboard](https://ws3.sinaimg.cn/large/006tNbRwgy1fy9t1ewpc5j31uf0u00wj.jpg)
![grafana etcd dashboard](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/yQgrwt.jpg)
### 配置 PrometheusRule
现在我们知道怎么自定义一个 ServiceMonitor 对象了,但是如果需要自定义一个报警规则的话呢?比如现在我们去查看 Prometheus Dashboard 的 Alert 页面下面就已经有一些报警规则了,还有一些是已经触发规则的了:
![alerts](https://ws3.sinaimg.cn/large/006tNbRwgy1fyc4hf239zj313i0p6juv.jpg)
![alerts](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/DADO6K.jpg)
但是这些报警信息是哪里来的呢?他们应该用怎样的方式通知我们呢?我们知道之前我们使用自定义的方式可以在 Prometheus 的配置文件之中指定 AlertManager 实例和 报警的 rules 文件,现在我们通过 Operator 部署的呢?我们可以在 Prometheus Dashboard 的 Config 页面下面查看关于 AlertManager 的配置:
```yaml
......@@ -355,7 +355,7 @@ monitoring-etcd-rules.yaml monitoring-prometheus-k8s-rules.yaml
可以看到我们创建的 rule 文件已经被注入到了对应的 rulefiles 文件夹下面了,证明我们上面的设想是正确的。然后再去 Prometheus Dashboard 的 Alert 页面下面就可以查看到上面我们新建的报警规则了:
![etcd cluster](https://ws2.sinaimg.cn/large/006tNbRwgy1fyc62d4gkzj31540doac1.jpg)
![etcd cluster](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/n68RSK.jpg)
### 配置报警
......@@ -363,7 +363,7 @@ monitoring-etcd-rules.yaml monitoring-prometheus-k8s-rules.yaml
首先我们将 alertmanager-main 这个 Service 改为 NodePort 类型的 Service,修改完成后我们可以在页面上的 status 路径下面查看 AlertManager 的配置信息:
![alertmanager config](https://ws4.sinaimg.cn/large/006tNbRwgy1fyc6dz3t5ij31aa0u0te0.jpg)
![alertmanager config](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/Ty1Gxu.jpg)
这些配置信息实际上是来自于我们之前在`prometheus-operator/contrib/kube-prometheus/manifests`目录下面创建的 alertmanager-secret.yaml 文件:
......@@ -440,15 +440,15 @@ secret "alertmanager-main" created
我们添加了两个接收器,默认的通过邮箱进行发送,对于 CoreDNSDown 这个报警我们通过 webhook 来进行发送,这个 webhook 就是我们前面课程中定义的一个钉钉接收的 Server,上面的步骤创建完成后,很快我们就会收到一条钉钉消息:
![钉钉](https://ws3.sinaimg.cn/large/006tNbRwgy1fyc7drk445j311c0mm7bo.jpg)
![钉钉](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/Of4GIB.jpg)
同样邮箱中也会收到报警信息:
![邮箱](https://ws1.sinaimg.cn/large/006tNbRwgy1fyc7j3k20vj30u00u4gpx.jpg)
![邮箱](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/NjnV2X.jpg)
我们再次查看 AlertManager 页面的 status 页面的配置信息可以看到已经变成上面我们的配置信息了:
![alertmanager config](https://ws1.sinaimg.cn/large/006tNbRwgy1fyc7kvs27vj30vp0u017c.jpg)
![alertmanager config](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/gKhiPI.jpg)
AlertManager 配置也可以使用模板(.tmpl文件),这些模板可以与 alertmanager.yaml 配置文件一起添加到 Secret 对象中,比如:
......
......@@ -108,7 +108,7 @@ prometheus.monitoring.coreos.com "k8s" configured
隔一小会儿,可以前往 Prometheus 的 Dashboard 中查看配置是否生效:
![config](https://ws1.sinaimg.cn/large/006tNbRwgy1fyd8c25rf2j31530u0dl5.jpg)
![config](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/jmiqaD.jpg)
在 Prometheus Dashboard 的配置页面下面我们可以看到已经有了对应的的配置信息了,但是我们切换到 targets 页面下面却并没有发现对应的监控任务,查看 Prometheus 的 Pod 日志:
```shell
......@@ -172,7 +172,7 @@ rules:
更新上面的 ClusterRole 这个资源对象,然后重建下 Prometheus 的所有 Pod,正常就可以看到 targets 页面下面有 kubernetes-service-endpoints 这个监控任务了:
![endpoints](https://ws2.sinaimg.cn/large/006tNbRwgy1fyd9q7pq78j31rg0ewtca.jpg)
![endpoints](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/R38S3q.jpg)
我们这里自动监控了两个 Service,第一个就是我们之前创建的 Redis 的服务,我们在 Redis Service 中有两个特殊的 annotations:
```yaml
......
......@@ -439,7 +439,7 @@ kibana NodePort 10.105.208.253 <none> 5601:31816/TCP 2
如果 Pod 已经是 Running 状态了,证明应用已经部署成功了,然后可以通过 NodePort 来访问 Kibana 这个服务,在浏览器中打开`http://<任意节点IP>:31816`即可,如果看到如下欢迎界面证明 Kibana 已经成功部署到了 Kubernetes集群之中。
![kibana welcome](https://ws3.sinaimg.cn/large/006tNc79gy1fz9h04vmnnj316o0tktdh.jpg)
![kibana welcome](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/FXJOqE.jpg)
## 部署 Fluentd
......@@ -452,7 +452,7 @@ Fluentd 通过一组给定的数据源抓取日志数据,处理后(转换成
* 结构化并且标记这些数据
* 然后根据匹配的标签将数据发送到多个目标服务去
![fluentd 架构](https://ws4.sinaimg.cn/large/006tNc79gy1fz9hctkjysj30xc0ge0tk.jpg)
![fluentd 架构](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/7moPNc.jpg)
### 配置
......@@ -826,15 +826,15 @@ kibana-7558d4dc4d-5mqdz 1/1 Running 0 1d
Fluentd 启动成功后,我们可以前往 Kibana 的 Dashboard 页面中,点击左侧的`Discover`,可以看到如下配置页面:
![create index](https://ws3.sinaimg.cn/large/006tNc79gy1fz9nkcfrrrj31cf0u00y2.jpg)
![create index](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/gSH5TE.jpg)
在这里可以配置我们需要的 Elasticsearch 索引,前面 Fluentd 配置文件中我们采集的日志使用的是 logstash 格式,这里只需要在文本框中输入`logstash-*`即可匹配到 Elasticsearch 集群中的所有日志数据,然后点击下一步,进入以下页面:
![index config](https://ws2.sinaimg.cn/large/006tNc79gy1fz9noes54aj31di0u043y.jpg)
![index config](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/rLJ1wS.jpg)
在该页面中配置使用哪个字段按时间过滤日志数据,在下拉列表中,选择`@timestamp`字段,然后点击`Create index pattern`,创建完成后,点击左侧导航菜单中的`Discover`,然后就可以看到一些直方图和最近采集到的日志数据了:
![log data](https://ws4.sinaimg.cn/large/006tNc79gy1fz9ntqiplvj31df0u04bf.jpg)
![log data](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/U5d7oL.jpg)
### 测试
......@@ -861,7 +861,7 @@ $ kubectl create -f counter.yaml
Pod 创建并运行后,回到 Kibana Dashboard 页面,在上面的`Discover`页面搜索栏中输入`kubernetes.pod_name:counter`,就可以过滤 Pod 名为 counter 的日志数据:
![counter log data](https://ws4.sinaimg.cn/large/006tNc79gy1fz9o3c5ds8j31df0u0qg5.jpg)
![counter log data](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/Dd5VCx.jpg)
我们也可以通过其他元数据来过滤日志数据,比如
您可以单击任何日志条目以查看其他元数据,如容器名称,Kubernetes 节点,命名空间等。
......
# 63. Harbor
[![harbor](https://ws4.sinaimg.cn/large/006tKfTcgy1g0cz18sku5j321r0kz0ux.jpg)](/post/harbor-code-analysis/)
[![harbor](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/5xvK5f.jpg)](/post/harbor-code-analysis/)
[Harbor](https://github.com/goharbor/harbor) 是一个`CNCF`基金会托管的开源的可信的云原生`docker registry`项目,可以用于存储、签名、扫描镜像内容,Harbor 通过添加一些常用的功能如安全性、身份权限管理等来扩展 docker registry 项目,此外还支持在 registry 之间复制镜像,还提供更加高级的安全功能,如用户管理、访问控制和活动审计等,在新版本中还添加了`Helm`仓库托管的支持。
......@@ -19,7 +19,7 @@
至此,整个登录过程完成,整个过程可以用下面的流程图来说明:
![docker login](https://ws4.sinaimg.cn/large/006tKfTcgy1g0cwrqk1mqj310q0iuwgt.jpg)
![docker login](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/1WZx0K.jpg)
要完成上面的登录认证过程有两个关键点需要注意:怎样让 registry 服务知道服务认证地址?我们自己提供的认证服务生成的 token 为什么 registry 就能够识别?
......@@ -616,16 +616,16 @@ harbor-harbor-ingress registry.qikqiak.com,notary.qikqiak.com 80,
### Harbor Portal
添加完成后,在浏览器中输入`registry.qikqiak.com`就可以打开熟悉的 Harbor 的 Portal 界面了,当然我们配置的 Ingress 中会强制跳转到 https,所以如果你的浏览器有什么安全限制的话,需要信任我们这里 Ingress 对应的证书,证书文件可以通过查看 Secret 资源对象获取:
![Harbor Portal](https://ws2.sinaimg.cn/large/006tKfTcgy1g0f8ojkpikj31dy0u0wi4.jpg)
![Harbor Portal](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/xg1GWO.jpg)
然后输入用户名:admin,密码:Harbor12345(当然我们也可以通过 Helm 安装的时候自己覆盖 harborAdminPassword)即可登录进入 Portal 首页:
![Harbor Portal Home](https://ws2.sinaimg.cn/large/006tKfTcgy1g0f8qo55p0j31d80u0q7t.jpg)
![Harbor Portal Home](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/MA7tef.jpg)
我们可以看到有很多功能,默认情况下会有一个名叫`library`的项目,改项目默认是公开访问权限的,进入项目可以看到里面还有 Helm Chart 包的管理,可以手动在这里上传,也可以对改项目里面的镜像进行一些配置,比如是否开启自动扫描镜像功能:
![Harbor project settings](https://ws1.sinaimg.cn/large/006tKfTcgy1g0f98e41i1j31230u0777.jpg)
![Harbor project settings](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/XieDpz.jpg)
### docker cli
......@@ -690,7 +690,7 @@ latest: digest: sha256:4415a904b1aca178c2450fd54928ab362825e863c0ad5452fd020e92f
推送完成后,我们同样可以在 Portal 页面上看到这个镜像的信息:
![Harbor image info](https://ws1.sinaimg.cn/large/006tKfTcgy1g0f9woj4otj318q0u0n1o.jpg)
![Harbor image info](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/rGY1hl.jpg)
镜像 push 成功,同样可以测试下 pull:
......
......@@ -287,17 +287,17 @@ redis-8446f57bdf-4v62p 1/1 Running 0 17
可以看到都已经部署成功了,然后我们可以通过 Ingress 中定义的域名`git.qikqiak.com`(需要做 DNS 解析或者在本地 /etc/hosts 中添加映射)来访问 Portal:
![gitlab portal](https://ws1.sinaimg.cn/large/006tKfTcgy1g0u81axrwlj318y0u0wj0.jpg)
![gitlab portal](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/Fxx65D.jpg)
使用用户名 root,和部署的时候指定的超级用户密码`GITLAB_ROOT_PASSWORD=admin321`即可登录进入到首页:
![gitlab homepage](https://ws3.sinaimg.cn/large/006tKfTcgy1g0u85rzqbfj311g0u0jx1.jpg)
![gitlab homepage](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/WhSLdg.jpg)
Gitlab 运行后,我们可以注册为新用户并创建一个项目,还可以做很多的其他系统设置,比如设置语言、设置应用风格样式等等。
点击`Create a project`创建一个新的项目,和之前 Github 使用上没有多大的差别:
![create gitlab project](https://ws1.sinaimg.cn/large/006tKfTcgy1g0u8gpdwqoj31h90u0483.jpg)
![create gitlab project](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/VfnfrO.jpg)
创建完成后,我们可以添加本地用户的一个`SSH-KEY`,这样我们就可以通过 SSH 来拉取或者推送代码了。SSH 公钥通常包含在`~/.ssh/id_rsa.pub` 文件中,并以`ssh-rsa`开头。如果没有的话可以使用`ssh-keygen`命令来生成,`id_rsa.pub`里面的内容就是我们需要的 SSH 公钥,然后添加到 Gitlab 中。
......@@ -326,7 +326,7 @@ spec:
注意上面 ssh 对应的 nodePort 端口设置为 30022,这样就不会随机生成了,重新更新下 Deployment 和 Service,更新完成后,现在我们在项目上面 Clone 的时候使用 ssh 就会带上端口号了:
![gitlab ssh](https://ws4.sinaimg.cn/large/006tKfTcgy1g0uecaybqfj31kg0kin17.jpg)
![gitlab ssh](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/3978ZL.jpg)
现在就可以使用`Clone with SSH`的地址了,由于上面我们配置了 SSH 公钥,所以就可以直接访问上面的仓库了:
```shell
......@@ -354,7 +354,7 @@ To ssh://git@git.qikqiak.com:30022/root/gitlab-demo.git
然后刷新浏览器,就可以看到刚刚创建的 Git 仓库中多了一个 README.md 的文件:
![git commit](https://ws4.sinaimg.cn/large/006tKfTcgy1g0uekpjdcfj31b10u0af1.jpg)
![git commit](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/7AiUUZ.jpg)
到这里就表明我们的 Gitlab 就成功部署到了 Kubernetes 集群当中了。
......
......@@ -81,7 +81,7 @@ To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
前面的章节中我们已经成功安装了 Gitlab,在浏览器中打开`git.qikqiak.com`页面,然后登录后进入到管理页面`http://git.qikqiak.com/admin`,然后点击导航栏中的`Runner`,可以看到该页面中有两个总要的参数,一个是 URL,另外一个就是 Register Token,下面的步骤中需要用到这两个参数值。
![gitlab runner](https://ws3.sinaimg.cn/large/006tKfTcgy1g188bykw2ij31lu0he0xf.jpg)
![gitlab runner](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/zQbFp1.jpg)
> 注意:不要随便泄露 Token
......@@ -311,7 +311,7 @@ gitlab-ci-runner-1 1/1 Running 0 3m
可以看到已经成功运行了两个(具体取决于`StatefulSet`清单中的副本数) Runner 实例,然后切换到 Gitlab Admin 页面下面的 Runner 页面:
![gitlab runner list](https://ws2.sinaimg.cn/large/006tKfTcgy1g189zhnqzbj31lc0u07bd.jpg)
![gitlab runner list](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/QgxSq3.jpg)
当然我们也可以根据需要更改 Runner 的一些配置,比如添加 tag 标签等。
......@@ -331,7 +331,7 @@ $ git push -u origin master
```
当我们把仓库推送到 Gitlab 以后,应该可以看到 Gitlab CI 开始执行构建任务了:
![gitlab ci](https://ws2.sinaimg.cn/large/006tKfTcgy1g1929udcvuj31mg0owq5b.jpg)
![gitlab ci](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/iN2HFV.jpg)
此时 Runner Pod 所在的 namespace 下面也会出现两个新的 Pod:
```shell
......@@ -347,7 +347,7 @@ runner-9rixsyft-project-2-concurrent-1t74t9 0/2 ContainerCreating 0
这两个新的 Pod 就是用来执行具体的 Job 任务的,这里同时出现两个证明第一步是并行执行的两个任务,从上面的 Pipeline 中也可以看到是 test 和 test2 这两个 Job。我们可以看到在执行 image_build 任务的时候出现了错误:
![pipeline](https://ws2.sinaimg.cn/large/006tKfTcgy1g19369pzotj31m20emwg4.jpg)
![pipeline](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/iveZ6o.jpg)
我们可以点击查看这个 Job 失败详细信息:
```shell
......@@ -361,7 +361,7 @@ ERROR: Job failed: command terminated with exit code 1
定位到项目 -> 设置 -> CI/CD,展开`Environment variables`栏目,配置镜像仓库相关的参数值:
![gitlab ci env](https://ws1.sinaimg.cn/large/006tKfTcgy1g19duem1bdj31jc0m6jvl.jpg)
![gitlab ci env](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/ErhBLn.jpg)
配置上后,我们在上面失败的 Job 任务上点击“重试”,在重试过后依然可以看到会出现下面的错误信息:
......@@ -473,7 +473,7 @@ xxxxxxtoken值xxxx
填写上面对应的值添加集群:
![add k8s cluster](https://ws1.sinaimg.cn/large/006tKfTcgy1g1a6lrep5yj31160u0af9.jpg)
![add k8s cluster](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/wwr2FR.jpg)
......@@ -779,21 +779,21 @@ $ git push origin master
现在回到 Gitlab 中可以看到我们的项目触发了一个新的 Pipeline 的构建:
![gitlab pipeline](https://ws4.sinaimg.cn/large/006tKfTcgy1g1ag8co9r3j31m80rewhh.jpg)
![gitlab pipeline](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/D54xTd.jpg)
可以查看最后一个阶段(stage)是否正确,如果通过了,证明我们已经成功将应用程序部署到 Kubernetes 集群中了,一个成功的`review`阶段如下所示:
![review success](https://ws3.sinaimg.cn/large/006tKfTcgy1g1ahimbwmwj315z0u0n6w.jpg)
![review success](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/kXE6e7.jpg)
整个 Pipeline 构建成功后,我们可以在项目的环境菜单下面看到多了一个环境:
![env](https://ws1.sinaimg.cn/large/006tKfTcgy1g1ahkx8gwuj31vm0fatap.jpg)
![env](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/pArnyz.jpg)
如果我们点击`终止`,就会调用`.gitlab-ci.yml`中定义的钩子`on_stop: stop_review`,点击`View deployment`就可以看到这次我们的部署结果(前提是DNS解析已经完成):
![view deployment](https://ws3.sinaimg.cn/large/006tKfTcgy1g1ahq5v4qfj313i0hgtbl.jpg)
![view deployment](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/Q2yPG9.jpg)
这就是关于 Gitlab CI 结合 Kubernetes 进行 CI/CD 的过程,具体详细的构建任务还需要结合我们自己的应用实际情况而定。下节课给大家介绍使用 Jenkins + Gitlab + Harbor + Helm + Kubernetes 来实现一个完整的 CI/CD 流水线作业。
# 66. Devops
上节课和大家介绍了`Gitlab CI`结合`Kubernetes`进行 CI/CD 的完整过程](https://www.qikqiak.com/post/gitlab-ci-k8s-cluster-feature/)。这节课结合前面所学的知识点给大家介绍一个完整的示例:使用 Jenkins + Gitlab + Harbor + Helm + Kubernetes 来实现一个完整的 CI/CD 流水线作业。
其实前面的课程中我们就[已经学习了 Jenkins Pipeline 与 Kubernetes 的完美结合](https://www.qikqiak.com/post/kubernetes-jenkins1/),我们利用 Kubernetes 来动态运行 Jenkins 的 Slave 节点,可以和好的来解决传统的 Jenkins Slave 浪费大量资源的缺点。之前的示例中我们是将项目放置在 Github 仓库上的,将 Docker 镜像推送到了 Docker Hub,这节课我们来结合我们前面学习的知识点来综合运用下,使用 Jenkins、Gitlab、Harbor、Helm、Kubernetes 来实现一个完整的持续集成和持续部署的流水线作业。
## 流程
下图是我们当前示例的流程图
![ci/cd demo](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/2D3KxY.jpg)
* 1. 开发人员提交代码到 Gitlab 代码仓库
* 2. 通过 Gitlab 配置的 Jenkins Webhook 触发 Pipeline 自动构建
* 3. Jenkins 触发构建构建任务,根据 Pipeline 脚本定义分步骤构建
* 4. 先进行代码静态分析,单元测试
* 5. 然后进行 Maven 构建(Java 项目)
* 6. 根据构建结果构建 Docker 镜像
* 7. 推送 Docker 镜像到 Harbor 仓库
* 8. 触发更新服务阶段,使用 Helm 安装/更新 Release
* 9. 查看服务是否更新成功。
## 项目
本次示例项目是一个完整的基于 Spring Boot、Spring Security、JWT、React 和 Ant Design 构建的一个开源的投票应用,项目地址:[https://github.com/callicoder/spring-security-react-ant-design-polls-app](https://github.com/callicoder/spring-security-react-ant-design-polls-app)
![polling app1](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/NTe3id.jpg)
我们将会在该项目的基础上添加部分代码,并实践 CI/CD 流程。
### 服务端
首先需要更改的是服务端配置,我们需要将数据库链接的配置更改成环境变量的形式,写死了的话就没办法进行定制了,修改服务端文件`src/main/resources/application.properties`,将下面的数据库配置部分修改成如下形式:
```
spring.datasource.url= jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:polling_app}?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username= ${DB_USER:root}
spring.datasource.password= ${DB_PASSWORD:root}
```
当环境变量中有上面的数据配置的时候,就会优先使用环境变量中的值,没有的时候就会用默认的值进行数据库配置。
由于我们要将项目部署到 Kubernetes 集群中去,所以我们需要将服务端进行容器化,所以我们在项目根目录下面添加一个`Dockerfile`文件进行镜像构建:
```docker
FROM openjdk:8-jdk-alpine
MAINTAINER cnych <icnych@gmail.com>
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
ENV TZ=Asia/Shanghai
RUN mkdir /app
WORKDIR /app
COPY target/polls-0.0.1-SNAPSHOT.jar /app/polls.jar
EXPOSE 8080
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar","/app/polls.jar"]
```
由于服务端代码是基于`Spring Boot`构建的,所以我们这里使用一个`openjdk`的基础镜像,将打包过后的`jar`包放入镜像之中,然后用过`java -jar`命令直接启动即可,这里就会存在一个问题了,我们是在 Jenkins 的 Pipeline 中去进行镜像构建的,这个时候项目中并没有打包好的`jar`包文件,那么我们应该如何获取打包好的`jar`包文件呢?这里我们可以使用两种方法:
第一种就是如果你用于镜像打包的 Docker 版本大于`17.06`版本的话,那么我墙裂推荐你使用 Docker 的多阶段构建功能来完成镜像的打包过程,我们只需要将上面的`Dockerfile`文件稍微更改下即可,将使用`maven`进行构建的工作放到同一个文件中:
```docker
FROM maven:3.6-alpine as BUILD
COPY src /usr/app/src
COPY pom.xml /usr/app
RUN mvn -f /usr/app/pom.xml clean package -Dmaven.test.skip=true
FROM openjdk:8-jdk-alpine
MAINTAINER cnych <icnych@gmail.com>
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
ENV TZ=Asia/Shanghai
RUN mkdir /app
WORKDIR /app
COPY --from=BUILD /usr/app/target/polls-0.0.1-SNAPSHOT.jar /app/polls.jar
EXPOSE 8080
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar","/app/polls.jar"]
```
前面课程中我们就讲解过 Docker 的多阶段构建,这里我们定义了两个阶段,第一个阶段利用`maven:3.6-alpine`这个基础镜像将我们的项目进行打包,然后将该阶段打包生成的`jar`包文件复制到第二阶段进行最后的镜像打包,这样就可以很好的完成我们的 Docker 镜像的构建工作。
第二种方式就是我们传统的方式,在 Jenkins Pipeline 中添加一个`maven`构建的阶段,然后在第二个 Docker 构建的阶段就可以直接获取到前面的`jar`包了,也可以很方便的完成镜像的构建工作,为了更加清楚的说明 Jenkins Pipeline 的用法,我们这里采用这种方式,所以 Dockerfile 文件还是使用第一个就行。
现在我们可以将服务端的代码推送到 Gitlab 上去,我们这里的仓库地址为:[http://git.qikqiak.com/course/polling-app-server.git](http://git.qikqiak.com/course/polling-app-server.git)
> 注意,这里我们只推送的服务端代码。
### 客户端
客户端我们需要修改 API 的链接地址,修改文件`src/constants/index.js``API_BASE_URL`的地址,我们同样通过环境变量来进行区分,如果有环境变量`APISERVER_URL`,则优先使用这个环境变量来作为 API 请求的地址:
```javascript
let API_URL = 'http://localhost:8080/api';
if (process.env.APISERVER_URL) {
API_URL = `${process.env.APISERVER_URL}/api`;
}
export const API_BASE_URL = API_URL;
```
因为我们这里的项目使用的就是前后端分离的架构,所以我们同样需要将前端代码进行单独的部署,同样我们要将项目部署到 Kubernetes 环境中,所以也需要做容器化,同样在项目根目录下面添加一个`Dockerfile`文件:
```docker
FROM nginx:1.15.10-alpine
ADD build /usr/share/nginx/html
ADD nginx.conf
/etc/nginx/conf.d/default.conf
```
由于前端页面是单纯的静态页面,所以一般我们使用一个`nginx`镜像来运行,所以我们提供一个`nginx.conf`配置文件:
```
server {
gzip on;
listen 80;
server_name localhost;
root /usr/share/nginx/html;
location / {
try_files $uri /index.html;
expires 1h;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
```
这里我们可以看到我们需要将前面页面打包到一个`build`目录,然后将改目录添加到 nginx 镜像中的`/usr/share/nginx/html`目录,这样当 nginx 镜像启动的时候就是直接使用的改文件夹下面的文件。
所以现在我们需要获取打包后的目录`build`,同样的,和上面服务端项目一样,我们可以使用两种方式来完成这个工作。
第一种方式自然是推荐的 Docker 的多阶段构建,我们在一个`node`镜像的环境中就可以打包我们的前端项目了,所以我们可以更改下`Dockerfile`文件,先进行 node 打包,然后再进行 nginx 启动:
```docker
FROM node:alpine as BUILD
WORKDIR /usr/src/app
RUN mkdir -p /usr/src/app
ADD . /usr/src/app
RUN npm install && \
npm run build
FROM nginx:1.15.10-alpine
MAINTAINER cnych <icnych@gmail.com>
COPY --from=BUILD /usr/src/app/build /usr/share/nginx/html
ADD nginx.conf
/etc/nginx/conf.d/default.conf
```
第二种方式和上面一样在 Jenkins Pipeline 中添加一个打包构建的阶段即可,我们这里采用这种方式,所以 Dockerfile 文件还是使用第一个就行。
现在我们可以将客户端的代码推送到 Gitlab 上去,我们这里的仓库地址为:[http://git.qikqiak.com/course/polling-app-client.git](http://git.qikqiak.com/course/polling-app-client.git)
## Jenkins
现在项目准备好了,接下来我们可以开始 Jenkins 的配置,还记得前面在 Pipeline 结合 Kubernetes 的课程中我们使用了一个`kubernetes`的 Jenkins 插件,但是之前使用的方式有一些不妥的地方,我们 Jenkins Pipeline 构建任务绑定到了一个固定的 Slave Pod 上面,这样就需要我们的 Slave Pod 中必须包含一系列构建所需要的依赖,比如 docker、maven、node、java 等等,这样就难免需要我们自己定义一个很庞大的 Slave 镜像,我们直接直接在 Pipeline 中去自定义 Slave Pod 中所需要用到的容器模板,这样我们需要什么镜像只需要在 Slave Pod Template 中声明即可,完全不需要去定义一个庞大的 Slave 镜像了。
首先去掉 Jenkins 中 kubernetes 插件中的 Pod Template 的定义,Jenkins -> 系统管理 -> 系统设置 -> 云 -> Kubernetes区域,删除下方的`Kubernetes Pod Template` -> 保存。
![jenkins kubernetes plugin](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/2iNvFm.jpg)
然后新建一个名为`polling-app-server`类型为`流水线(Pipeline)`的任务:
![new pipeline task](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/PVHJXH.jpg)
然后在这里需要勾选`触发远程构建`的触发器,其中令牌我们可以随便写一个字符串,然后记住下面的 URL,将 JENKINS_URL 替换成 Jenkins 的地址,我们这里的地址就是:`http://jenkins.qikqiak.com/job/polling-app-server/build?token=server321`
![trigger](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/pajeby.jpg)
然后在下面的`流水线`区域我们可以选择`Pipeline script`然后在下面测试流水线脚本,我们这里选择`Pipeline script from SCM`,意思就是从代码仓库中通过`Jenkinsfile`文件获取`Pipeline script`脚本定义,然后选择 SCM 来源为`Git`,在出现的列表中配置上仓库地址`http://git.qikqiak.com/course/polling-app-server.git`,由于我们是在一个 Slave Pod 中去进行构建,所以如果使用 SSH 的方式去访问 Gitlab 代码仓库的话就需要频繁的去更新 SSH-KEY,所以我们这里采用直接使用用户名和密码的形式来方式:
![pipeline scm](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/ecnRly.jpg)
`Credentials`区域点击`添加`按钮添加我们访问 Gitlab 的用户名和密码:
![gitlab auth](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/UwFxro.jpg)
然后需要我们配置用于构建的分支,如果所有的分支我们都想要进行构建的话,只需要将`Branch Specifier`区域留空即可,一般情况下不同的环境对应的分支才需要构建,比如 master、develop、test 等,平时开发的 feature 或者 bugfix 的分支没必要频繁构建,我们这里就只配置 master 和 develop 两个分支用户构建:
![gitlab branch config](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/itdSlI.jpg)
然后前往 Gitlab 中配置项目`polling-app-server` Webhook,settings -> Integrations,填写上面得到的 trigger 地址:
![webhook](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/aJTq7E.jpg)
保存后,可以直接点击`Test` -> `Push Event`测试是否可以正常访问 Webhook 地址,这里需要注意的是我们需要配置下 Jenkins 的安全配置,否则这里的触发器没权限访问 Jenkins,系统管理 -> 全局安全配置:取消`防止跨站点请求伪造`,勾选上`匿名用户具有可读权限`
![security config](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/kaEQZ0.jpg)
如果测试出现了`Hook executed successfully: HTTP 201`则证明 Webhook 配置成功了,否则就需要检查下 Jenkins 的安全配置是否正确了。
配置成功后我们只需要往 Gitlab 仓库推送代码就会触发 Pipeline 构建了。接下来我们直接在服务端代码仓库根目录下面添加`Jenkinsfile`文件,用于描述流水线构建流程。
首先定义最简单的流程,要注意这里和前面课程的不同之处,这里我们使用`podTemplate`来定义不同阶段使用的的容器,有哪些阶段呢?
Clone 代码 -> 代码静态分析 -> 单元测试 -> Maven 打包 -> Docker 镜像构建/推送 -> Helm 更新服务。
Clone 代码在默认的 Slave 容器中即可;静态分析和单元测试我们这里直接忽略,有需要这个阶段的同学自己添加上即可;Maven 打包肯定就需要 Maven 的容器了;Docker 镜像构建/推送是不是就需要 Docker 环境了呀;最后的 Helm 更新服务是不是就需要一个有 Helm 的容器环境了,所以我们这里就可以很简单的定义`podTemplate`了,如下定义:(添加一个`kubectl`工具用于测试)
```groovy
def label = "slave-${UUID.randomUUID().toString()}"
podTemplate(label: label, containers: [
containerTemplate(name: 'maven', image: 'maven:3.6-alpine', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'docker', image: 'docker', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'kubectl', image: 'cnych/kubectl', command: 'cat', ttyEnabled: true),
containerTemplate(name: 'helm', image: 'cnych/helm', command: 'cat', ttyEnabled: true)
], volumes: [
hostPathVolume(mountPath: '/root/.m2', hostPath: '/var/run/m2'),
hostPathVolume(mountPath: '/home/jenkins/.kube', hostPath: '/root/.kube'),
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock')
]) {
node(label) {
def myRepo = checkout scm
def gitCommit = myRepo.GIT_COMMIT
def gitBranch = myRepo.GIT_BRANCH
stage('单元测试') {
echo "测试阶段"
}
stage('代码编译打包') {
container('maven') {
echo "代码编译打包阶段"
}
}
stage('构建 Docker 镜像') {
container('docker') {
echo "构建 Docker 镜像阶段"
}
}
stage('运行 Kubectl') {
container('kubectl') {
echo "查看 K8S 集群 Pod 列表"
sh "kubectl get pods"
}
}
stage('运行 Helm') {
container('helm') {
echo "查看 Helm Release 列表"
sh "helm list"
}
}
}
}
```
上面这段`groovy`脚本比较简单,我们需要注意的是`volumes`区域的定义,将容器中的`/root/.m2`目录挂载到宿主机上是为了给`Maven`构建添加缓存的,不然每次构建的时候都需要去重新下载依赖,这样就非常慢了;挂载`.kube`目录是为了能够让`kubectl``helm`两个工具可以读取到 Kubernetes 集群的连接信息,不然我们是没办法访问到集群的;最后挂载`/var/run/docker.sock`文件是为了能够让我们的`docker`这个容器获取到`Docker Daemon`的信息的,因为`docker`这个镜像里面只有客户端的二进制文件,我们需要使用宿主机的`Docker Daemon`来构建镜像,当然我们也需要在运行 Slave Pod 的节点上拥有访问集群的文件,然后在每个`Stage`阶段使用特定需要的容器来进行任务的描述即可,所以这几个`volumes`都是非常重要的
```groovy
volumes: [
hostPathVolume(mountPath: '/root/.m2', hostPath: '/var/run/m2'),
hostPathVolume(mountPath: '/home/jenkins/.kube', hostPath: '/root/.kube'),
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock')
]
```
另外一个值得注意的就是`label`标签的定义,我们这里使用 UUID 生成一个随机的字符串,这样可以让 Slave Pod 每次的名称都不一样,而且这样就不会被固定在一个 Pod 上面了,以后有多个构建任务的时候就不会存在等待的情况了,这和我们之前的课程中讲到的固定在一个 label 标签上有所不同。
然后我们将上面的`Jenkinsfile`文件提交到 Gitlab 代码仓库上:
```shell
$ git add Jenkinsfile
$ git commit -m "添加 Jenkinsfile 文件"
$ git push origin master
```
然后切换到 Jenkins 页面上,正常情况就可以看到我们的流水线任务`polling-app-server`已经被触发构建了,然后回到我们的 Kubernetes 集群中可以看到多了一个 slave 开头的 Pod,里面有5个容器,就是我们上面 podTemplate 中定义的4个容器,加上一个默认的 jenkins slave 容器,同样的,构建任务完成后,这个 Pod 也会被自动销毁掉:
```shell
$ kubectl get pods -n kube-ops
NAME READY STATUS RESTARTS AGE
jenkins-7fbfcc5ddc-xsqmt 1/1 Running 0 1d
slave-6e898009-62a2-4798-948f-9c80c3de419b-0jwml-6t6hb 5/5 Running 0 36s
......
```
正常可以看到 Jenkins 中的任务构建成功了:
![build successfully](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/DVIDeb.jpg)
接下来的工作就是来实现上面具体的 Pipeline 脚本了。
## Pipeline
第一个阶段:单元测试,我们可以在这个阶段是运行一些单元测试或者静态代码分析的脚本,我们这里直接忽略。
第二个阶段:代码编译打包,我们可以看到我们是在一个`maven`的容器中来执行的,所以我们只需要在该容器中获取到代码,然后在代码目录下面执行 maven 打包命令即可,如下所示:
```groovy
stage('代码编译打包') {
try {
container('maven') {
echo "2. 代码编译打包阶段"
sh "mvn clean package -Dmaven.test.skip=true"
}
} catch (exc) {
println "构建失败 - ${currentBuild.fullDisplayName}"
throw(exc)
}
}
```
第三个阶段:构建 Docker 镜像,要构建 Docker 镜像,就需要提供镜像的名称和 tag,要推送到 Harbor 仓库,就需要提供登录的用户名和密码,所以我们这里使用到了`withCredentials`方法,在里面可以提供一个`credentialsId``dockerhub`的认证信息,如下:
```groovy
container('构建 Docker 镜像') {
withCredentials([[$class: 'UsernamePasswordMultiBinding',
credentialsId: 'dockerhub',
usernameVariable: 'DOCKER_HUB_USER',
passwordVariable: 'DOCKER_HUB_PASSWORD']]) {
echo "3. 构建 Docker 镜像阶段"
sh """
docker login ${dockerRegistryUrl} -u ${DOCKER_HUB_USER} -p ${DOCKER_HUB_PASSWORD}
docker build -t ${image}:${imageTag} .
docker push ${image}:${imageTag}
"""
}
}
```
其中 ${image} 和 ${imageTag} 我们可以在上面定义成全局变量:
```groovy
def imageTag = sh(script: "git rev-parse --short HEAD", returnStdout: true).trim()
def dockerRegistryUrl = "registry.qikqiak.com"
def imageEndpoint = "course/polling-app-server"
def image = "${dockerRegistryUrl}/${imageEndpoint}"
```
docker 的用户名和密码信息则需要通过`凭据`来进行添加,进入 jenkins 首页 -> 左侧菜单`凭据` -> `添加凭据`,选择用户名和密码类型的,其中 ID 一定要和上面的`credentialsId`的值保持一致:
![add docker hub credential](https://bxdc-static.oss-cn-beijing.aliyuncs.com/images/6uXfeq.jpg)
第四个阶段:运行 kubectl 工具,其实在我们当前使用的流水线中是用不到 kubectl 工具的,那么为什么我们这里要使用呢?这还不是因为我们暂时还没有去写应用的 Helm Chart 包吗?所以我们先去用原始的 YAML 文件来编写应用部署的资源清单文件,这也是我们写出 Chart 包前提,因为只有知道了应用如何部署才可能知道 Chart 包如何编写,所以我们先编写应用部署资源清单。
首先当然就是 Deployment 控制器了,如下所示:(k8s.yaml)
```yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: polling-server
namespace: course
labels:
app: polling-server
spec:
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
app: polling-server
spec:
restartPolicy: Always
imagePullSecrets:
- name: myreg
containers:
- image: <IMAGE>:<IMAGE_TAG>
name: polling-server
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: api
env:
- name: DB_HOST
value: mysql
- name: DB_PORT
value: "3306"
- name: DB_NAME
value: polling_app
- name: DB_USER
value: polling
- name: DB_PASSWORD
value: polling321
---
kind: Service
apiVersion: v1
metadata:
name: polling-server
namespace: course
spec:
selector:
app: polling-server
type: ClusterIP
ports:
- name: api-port
port: 8080
targetPort: api
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: mysql
namespace: course
spec:
template:
metadata:
labels:
app: mysql
spec:
restartPolicy: Always
containers:
- name: mysql
image: mysql:5.7
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3306
name: dbport
env:
- name: MYSQL_ROOT_PASSWORD
value: rootPassW0rd
- name: MYSQL_DATABASE
value: polling_app
- name: MYSQL_USER
value: polling
- name: MYSQL_PASSWORD
value: polling321
volumeMounts:
- name: db
mountPath: /var/lib/mysql
volumes:
- name: db
hostPath:
path: /var/lib/mysql
---
kind: Service
apiVersion: v1
metadata:
name: mysql
namespace: course
spec:
selector:
app: mysql
type: ClusterIP
ports:
- name: dbport
port: 3306
targetPort: dbport
```
可以看到我们上面的 YAML 文件中添加使用的镜像是用标签代替的:`<IMAGE>:<IMAGE_TAG>`,这是因为我们的镜像地址是动态的,下依赖我们在上一个阶段打包出来的镜像地址的,所以我们这里用标签代替,然后将标签替换成真正的值即可,另外为了保证应用的稳定性,我们还在应用中添加了健康检查,所以需要在代码中添加一个健康检查的 Controller:(src/main/java/com/example/polls/controller/StatusController.java)
```java
package com.example.polls.controller;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/_status/healthz")
public class StatusController {
@GetMapping
public String healthCheck() {
return "UP";
}
}
```
最后就是环境变量了,还记得前面我们更改了资源文件中数据库的配置吗?(src/main/resources/application.properties)因为要尽量通用,我们在部署应用的时候很有可能已经有一个外部的数据库服务了,所以这个时候通过环境变量传入进来即可。另外由于我们这里使用的是私有镜像仓库,所以需要在集群中提前创建一个对应的 Secret 对象:
```shell
$ kubectl create secret docker-registry myreg --docker-server=registry.qikqiak.com --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL --namespace course
```
在代码根目录下面创建一个 manifests 的目录,用来存放上面的资源清单文件,正常来说是不是我们只需要在镜像构建成功后,将上面的 k8s.yaml 文件中的镜像标签替换掉就 OK,所以这一步的动作如下:
```groovy
stage('运行 Kubectl') {
container('kubectl') {
echo "查看 K8S 集群 Pod 列表"
sh "kubectl get pods"
sh """
sed -i "s/<IMAGE>/${image}" manifests/k8s.yaml
sed -i "s/<IMAGE_TAG>/${imageTag}" manifests/k8s.yaml
kubectl apply -f k8s.yaml
"""
}
}
```
第五阶段:运行 Helm 工具,就是直接使用 Helm 来部署应用了,现在有了上面的基本的资源对象了,现在要创建 Chart 模板就相对容易了,首先确保我们本地安装有 Helm Client,使用下面的命令创建项目:
```shell
$ helm create polling-helm
Creating polling-helm
$ tree polling-helm
polling-helm
├── Chart.yaml
├── charts
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── ingress.yaml
│   └── service.yaml
└── values.yaml
2 directories, 7 files
```
然后将上面的 k8s.yaml 文件我们拆分成四个文件放置到 templates 目录下面,把 polling-server 分成 deployment.yaml 和 service.yaml 文件,而数据库的我们单独创建一个文件夹,然后放置在文件夹下面:
```shell
$ tree polling-helm/templates
polling-helm/templates
├── NOTES.txt
├── _helpers.tpl
├── database
│   ├── database-svc.yaml
│   └── database-ss.yaml
├── deployment.yaml
├── ingress.yaml
└── service.yaml
1 directory, 7 files
```
然后就可以定制模板了,首先针对主要的 deployment.yaml 文件,把所有能够定制的数据都通过 values 或者 template 来进行渲染:
```yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: {{ include "polling-helm.fullname" . }}
labels:
app: {{ include "polling-helm.name" . }}
chart: {{ include "polling-helm.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
app: {{ include "polling-helm.name" . }}
release: {{ .Release.Name }}
spec:
restartPolicy: Always
imagePullSecrets:
- name: myreg
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 8080
name: api
env:
- name: DB_HOST
value: {{ .Values.db.host }}
- name: DB_PORT
value: "3306"
- name: DB_NAME
value: polling_app
- name: DB_USER
value: polling
- name: DB_PASSWORD
value: polling321
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}
```
gitlab-ci-k8s-demo @ 62ec25b6
Subproject commit 62ec25b629f8a7c174c40916c38293694bcf6e5d
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册