环境说明
PS:本文档实验过程中并没有对权限做出严格约束,比如直接在jenkins存放管理员的kubeconfig文件、直接在jenkins用集群管理员角色对接k8s集群等操作,均有很大风险,需要根据实际情况约束相应用户的权限。
规划设计:
角色 | 地址 | 域名 |
---|---|---|
master | 192.168.10.100 | |
node | 192.168.10.101 | |
gitlab | 192.168.10.110 | |
jenkins | 192.168.10.111 | jenkins.snow.com |
harbor | 192.168.10.112 | harbor.snow.com |
实验之前集群已经搭建完毕,搭建教程可参考构建高可用k8s集群,jenkins与gitlab分别使用安装包的方式安装在两台虚拟机上。harbor以容器的方式单独部署在一台虚拟机。
策略设计
k8s集群内的pro名称空间模拟生产环境,dev名称空间模拟开发环境,开发环境下的代码只要推送到代码仓库会触发jenkins自动发布到dev名称空间的对应POD,pro名称空间的POD只有管理员手动按选定的tag构建出来的版本才可以发布到k8s环境。
添加kubelet认证,让jenkins可以有操作k8s集群的权限
在Harbor仓库分别添加两个新的项目,项目名分别为pro和dev,用来存放生产和测试的镜像。为了实验方便将项目都配置为了公开模式,如果想要配置为私密模式则需要添加仓库的认证信息,具体参考文章:[docker配置https加密对接k8s]
将master节点的.kube文件和kubectl命令发送到jenkins所在的节点
root@master01:~# which kubectl
/opt/kube/bin/kubectl
root@master01:~# scp /opt/kube/bin/kubectl 192.168.10.111:/usr/local/bin/kubectl
root@192.168.10.111's password:
kubectl 100% 45MB 29.0MB/s 00:01
root@master01:~# scp -r .kube 192.168.10.111:/root/.kube
root@192.168.10.111's password:
config 100% 6241 1.9MB/s 00:00
9f754ca1a0545e7b871b81c83a185054 100% 2460 1.6MB/s 00:00
c4f8f2c14858bcf309490b8356533dde 100% 642 219.8KB/s 00:00
在jenkins验证是否有操作k8s集群的权限
root@jenkins:~# kubectl get no
NAME STATUS ROLES AGE VERSION
192.168.10.100 Ready master 5d22h v1.22.2
192.168.10.101 Ready node 5d22h v1.22.2
如果执行get node的时候报错网络超时需要检查.kube/config文件中的server字段是否指定的是apiserver的地址及apiserver的6443端口是否监听在127.0.0.1地址。在jenkins添加访问k8s集群的凭证登录jenkins后选择Dashboard->系统管理->凭据->系统->全局凭据,点击右上角创建一个新的凭据。
类型选择X.509 Client Certficate,Client Key字段内容填写.kube/config文件中的client-key-data字段数据base64解码后的值。Client Certificate字段填写.kube/config文件中的client-certificate-data字段数据base64解码后的值,Server CA Certificate字段填写.kube/config文件中的certificate-authority-data字段数据base64解码后的值。
添加gitlab认证,让jenkins可以有拉取代码的权限
继续按照上一步的操作新添加一个凭证
创建新仓库,分别创建main与dev分支并推送到gitlab。
在gitlab新建一个空白项目
在master节点使用git命令创建分支并将代码推送到gitlab
root@master01:~/dev/httpserver# git init
Initialized empty Git repository in /root/dev/httpserver/.git/
root@master01:~/dev/httpserver# git remote add origin http://192.168.10.110/root/helloserver.git
root@master01:~/dev/httpserver# git checkout -b main
Switched to a new branch 'main'
准备提前写好的Dockerfile文件与测试代码。PS:这里的dockerfile写的很烂,只是能用。
root@master01:~/dev/httpserver# ls
Dockerfile go.mod main.go
root@master01:~/dev/httpserver# cat Dockerfile
FROM golang:latest
ADD . /
WORKDIR /
RUN go build -o main.exe
EXPOSE 80
ENTRYPOINT ["./main.exe"]
root@master01:~/dev/httpserver# cat main.go
package main
import (
"net/http"
)
func main() {
http.HandleFunc("/", func(resp http.ResponseWriter, req *http.Request) {
resp.Write([]byte("Version=1.0"))
})
http.ListenAndServe(":80", nil)
}
将main分支的代码推送到仓库
root@master01:~/dev/httpserver# git add .
root@master01:~/dev/httpserver# git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: Dockerfile
new file: go.mod
new file: main.go
root@master01:~/dev/httpserver# git commit -m "v1.0"
[main (root-commit) 3b5d105] v1.0
3 files changed, 21 insertions(+)
create mode 100644 Dockerfile
create mode 100644 go.mod
create mode 100644 main.go
root@master01:~/dev/httpserver# git tag v1.0
root@master01:~/dev/httpserver# git push --tags
Username for 'http://192.168.10.110': root
Password for 'http://root@192.168.10.110':
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 2 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 584 bytes | 584.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0)
To http://192.168.10.110/root/helloserver.git
* [new tag] v1.0 -> v1.0
#将主分支的代码推送到仓库,在操作的时候有点失误,最后强制合并的。
root@master01:~/dev/httpserver# git pull --allow-unrelated-histories origin main
Username for 'http://192.168.10.110': root
Password for 'http://root@192.168.10.110':
From http://192.168.10.110/root/helloserver
* branch main -> FETCH_HEAD
hint: Waiting for your editor to close the file...
GNU nano 4.8 /root/dev/httpserver/.git/MERGE_MSG
Merge branch 'main' of http://192.168.10.110/root/helloserver into main
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
Merge made by the 'recursive' strategy.
README.md | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 92 insertions(+)
create mode 100644 README.md
root@master01:~/dev/httpserver#
root@master01:~/dev/httpserver# git push -u origin main
Username for 'http://192.168.10.110': root
Password for 'http://root@192.168.10.110':
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 2 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 402 bytes | 402.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0)
To http://192.168.10.110/root/helloserver.git
924f2d2..eeb98b1 main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.
在Gitlab查看推送到主分支的代码
创建开发分支,并将开发分支推送到Gitlab
root@master01:~/dev/httpserver# git checkout -b dev
Switched to a new branch 'dev'
root@master01:~/dev/httpserver# git add .
root@master01:~/dev/httpserver# git commit -m "v1.0"
On branch dev
nothing to commit, working tree clean
root@master01:~/dev/httpserver# git tag v1.0
fatal: tag 'v1.0' already exists
root@master01:~/dev/httpserver# git push -u origin dev
Username for 'http://192.168.10.110': root
Password for 'http://root@192.168.10.110':
Total 0 (delta 0), reused 0 (delta 0)
remote:
remote: To create a merge request for dev, visit:
remote: http://gitlab.example.com/root/helloserver/-/merge_requests/new?merge_request%5Bsource_branch%5D=dev
remote:
To http://192.168.10.110/root/helloserver.git
* [new branch] dev -> dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.
推送完成后发现已经多了一个名字为dev的分支
设置生产环境需要指定tag版本发布。
在jenkins创建一个自由风格的任务
配置参数化构建信息
在源码管理位置配置gitlab仓库信息,注意需要选择之前创建的认证信息,不然会报权限不足。
在构建环境位置选择Setup Kubernetes CLI (kubectl),并配置集群相关信息,此处的certificate配置的数据为.kube/config文件中certificate-authority-data字段的值经过base64解码后的值。凭据选择之前创建的有操作k8s集群权限的凭据。
配置更新镜像需要执行的shell命令。已经在每台主机配置了将harbor.snow.com解析为镜像仓库地址的条目,所以此处直接写了域名。
配置完成后单击保存,点击参数化构建可以看到之前推送到Gitlab仓库的代码的tag,根据tag去拉取代码。
在k8s提前使用yaml文件创建出业务deployment控制器,将生产相关代码创建在pro名称空间。对其进行版本更新的操作就是对yaml文件中image字段的更新。
root@master01:~/pod# kubectl create ns pro
namespace/pro created
root@master01:~/pod# cat httpserver.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpserver
spec:
replicas: 3
selector:
matchLabels:
run: httpserver
template:
metadata:
labels:
run: httpserver
spec:
containers:
- name: httpserver
image: nginx
imagePullPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: httpserver-service
spec:
type: NodePort
selector:
run: httpserver
ports:
- port: 80
targetPort: 80
root@master01:~/pod# kubectl apply -f . -n pro
deployment.apps/httpserver created
service/httpserver-service created
配置完成后在jenkins点击参数化构建,选择v1.0版本进行构建。
构建过程可以在控制台输出位置看到
现在去k8s访问业务代码的地址查看其版本。
现在使用git命令将v2.0版本推送到代码仓库,2.0版本只是在main.go文件中将版本号更改为2.0.以下是手动改完版本后的操作。
root@master01:~/dev/httpserver# git status
On branch main
Your branch is up to date with 'origin/main'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: main.go
no changes added to commit (use "git add" and/or "git commit -a")
root@master01:~/dev/httpserver# git add .
root@master01:~/dev/httpserver# git commit -m "v2.0"
[main 3c7199e] v2.0
1 file changed, 1 insertion(+), 1 deletion(-)
root@master01:~/dev/httpserver# git tag v2.0
root@master01:~/dev/httpserver# git push --tags
Username for 'http://192.168.10.110': root
Password for 'http://root@192.168.10.110':
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 2 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 277 bytes | 277.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0)
To http://192.168.10.110/root/helloserver.git
* [new tag] v2.0 -> v2.0
操作完成去gitlab查看发现2.0版本已经成功推送上来了。
现在去jenkins选择参数化构建,构建的时候tag选2.0版本。
构建完成后发现版本已经成为最新v2.0版本。
设置dev分支可以自动发布
在k8s集群创建出dev名称空间,并创建出对应的业务service的deployment,所用到的yaml与上面在pro名称空间创建资源时使用的统一个,这里就没有重复截图。
去jenkins创建一个新的自由风格的任务
在源码管理位置配置代码仓库相关信息,注意要拉取的分支改为开发分支。
选择构建触发器中的Build when a change is pushed to GitLab选项,这个选项是需要安装Gitlab Hook Plugin和Build Authorization Token Root Plugin插件才可以看到。
配置k8s的连接信息
此处在构建时使用了$BUILD_NUMBER变量作为镜像的tag,确保每次构建k8s都能拉取到最新的镜像。
创建完成后先手动点击构建,看是否能正常构建成功
在k8s平台测试手动构建成功
此时在jenkins点击配置,在构建触发器取出此链接,需要配置到Gitlab平台。
同样在该选项卡下点击高级配置可以看到如下选项,点击生成一个新的token
之后单击保存。
单击页面左上角选择最下面的管理员
在设置选项卡找到网络。
在出站请求位置勾选 允许来自 web hooks 和服务对本地网络的请求,否则无法添加本地需要调用的webhook地址。更改完成后保存。
进入项目配置信息,选择设置,单击WebHook。
在此处配置刚刚取出的地址和token
配置完成后在此页面最下方会看到创建的webhook
创建完成后可以去测试推送事件触发构建,发现在jenkins已经成功触发构建
此时用git向dev分支提交代码,手动将main.go的版本号修改为2.0。修改文件过程不再演示。
root@master01:~/dev/httpserver# git add .
root@master01:~/dev/httpserver# git commit -m "v2.0"
[dev 9df58bd] v2.0
1 file changed, 1 insertion(+), 1 deletion(-)
root@master01:~/dev/httpserver# git push -u origin dev
Username for 'http://192.168.10.110': root
Password for 'http://root@192.168.10.110':
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 2 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 277 bytes | 277.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0)
remote:
remote: To create a merge request for dev, visit:
remote: http://gitlab.example.com/root/helloserver/-/merge_requests/new?merge_request%5Bsource_branch%5D=dev
remote:
To http://192.168.10.110/root/helloserver.git
eeb98b1..9df58bd dev -> dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.
此时去jenkins平台查看发现已经触发构建。
现在去k8s平台去验证dev名称空间的POD是否已经自动更新。
root@master01:~/dev/httpserver# kubectl get svc -n dev
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
httpserver-service NodePort 10.100.53.6 <none> 80:30222/TCP 36m
root@master01:~/dev/httpserver# curl 10.100.53.6
Version=2.0root@master01:~/dev/httpserver#
ps:以上内容在本人实现环境中已试验成功,如发现有问题或表述不清的地方欢迎指正。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。