1.介绍

K8s学习(十三):部署服务[上篇]文章中,已经把持续集成和持续部署的相关软件都进行了安装,但由于文章篇幅受限,所以有关各个软件的具体使用和发布服务流程并未说明,在这篇文章中,我们接着继续学习…

文章中涉及到的go-apppipeline完整代码、以及Containerd配置,关注公众号【猿码记】,回复【k8s-demo】即可获得

2.Jenkins容器映射

2.1 映射软件说明

软件 说明
docker jenkins发布过程中,用于镜像制作。
goctl 生成制作镜像的Dockerfile和发布到k8s的配置文件,由go-zero提供
kubectl 将应用发布到K8s

2.2 映射相关代码

通过K8s学习(十三):部署服务[上篇]文章中的部署方式,会自动把goctl、docker、kubectl、.kube映射到容器中,需要注意的是,jenkins所在的宿主机需要安装docker、kubectl.

具体映射代码如下:

docker-compose.yaml

....
jenkins:
build:
...
volumes:
- ${VOLUMES_PATH}/jenkins/jenkins_home:/var/jenkins_home
- ${JENKINS_DOCKER_SOCK}:/var/run/docker.sock # 映射宿主机的docker
- ${JENKINS_DOCKER}:/usr/bin/docker # 映射宿主机的docker
- ${KUBECTL_BIN_PATH}:/usr/local/bin/kubectl # 映射宿主机的kubectl
- ${K8S_KUBE_CONFIG}:/root/.kube # 映射宿主机的kubectl
- ${LIBLTDL_SO}:/usr/lib/x86_64-linux-gnu/libltdl.so.7
...

.env文件

...
KUBECTL_BIN_PATH=/usr/bin/kubectl
K8S_KUBE_CONFIG=/root/.kube
...

上述配置已经集成到k8s_microsvc_cicd: https://github.com/52lu/k8s_microsvc_cicd项目中,这里只做了解,无需操作;正常启动服务即可;

2.3 验证

进入jenkins验证上面软件是否映射到容器中.

# 进入容器
$ docker-compose exec jenkins bash
# 验证docker
$ docker version
Client: Docker Engine - Community
Version: 20.10.22
API version: 1.41
Go version: go1.18.9
Git commit: 3a2c30b
Built: Thu Dec 15 22:30:24 2022
OS/Arch: linux/amd64
Context: default
Experimental: true
...
# 验证goctl
$ goctl -v
goctl version 1.4.3 linux/amd64
# 验证 kubectl
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"24", GitVersion:"v1.24.2"...
# 验证是否能连上k8s集群
$ kubectl get ns
NAME STATUS AGE
calico-apiserver Active 17h
calico-system Active 18h
default Active 18h
ingress-nginx Active 16h
kube-node-lease Active 18h
kube-public Active 18h
kube-system Active 18h

2.4 踩坑:the server is currently unable…

如果在验证是否能连上k8s集群时,报错Error from server (ServiceUnavailable): the server is currently unable to handle the request,原因是发布机器上没有~/.kube/config这个文件。

解决方法:

  • master节点相同位置的配置文件,复制到发布机器~/.kube/config这个文件;
  • 重新编译jenkins: docker-compose build jenkins
  • 重新启动jenkins: docker-compose up -d jenkins

3. 配置准备

3.1 添加拉取代码凭证

添加步骤如下:

  • 点击左边菜单-> 系统管理(Manage Jenkins)
  • 点击 Manage Credentials
  • 点击“全局”后面的三角标,然后在点击“添加凭据”

步骤截图

进入添加凭据页面,填写信息如下:

  • 类型: 选择 Username with password 使用账号密码方式;
  • ID: 可以理解为证书的名称,需要唯一
  • 用户名: 登陆gitea的账号
  • 密码: 登陆gitea的密码

gitea设置使用ssh的方式拉取代码过于麻烦,这里使用账号密码的方式拉取,如果想使用ssh方式拉取代码,可参见官方文档设置: SSH 容器直通:https://docs.gitea.io/zh-cn/install-with-docker/#ssh-容器直通

添加凭证页面

填写完成后,点击保存(Create)

3.2 添加harbor仓库配置

进入首页,点击左侧菜单->系统管理(Manage Jenkins)->系统配置(Configure System) ,进入页面后一直往下滑动,直到到全局属性条目,添加docker私有仓库相关信息,如下图所示:

全局属性
填完之后,点击 保存

上述信息结合自己的真实情况填写。

3.3 配置git

点击左侧菜单->系统管理(Manage Jenkins)-全局工具配置(Global Tool Configureation),找到Git条目,填写jenkins所在机器git可执行文件所在path,如果没有的话,需要在jenkins插件管理中下载Git插件, 有就不需要其他操作(如下图)

image-20230105173345102

3.4 安装Git Parameter插件

jenkins发布过程中,编写管道(pipline)需要Git Parameter插件

点击左侧菜单->系统管理(Manage Jenkins)-插件管理(Manage Plugins)

安装Git Parameter插件

3.5 k8s添加ServiceAccount

在后面 发布到k8s环节 ,会使用goctl kube xxx来生k8s yaml,在使用k8s方式部署时,需要为生成的k8s yaml中指定serviceAccount。 原理可以看这篇文章下方go-zerok8s服务发现讲解 :https://mp.weixin.qq.com/s/-WaWJaM_ePEQOf7ExNJe7w

只需要执行kubectl apply -f jenkins_service_account.yaml即可;

# 创建k8s命名空间
$ kubectl create ns k8s-demo
namespace/k8s-demo created
# 创建serviceAccount
$ kubectl apply -f jenkins_service_account.yaml
clusterrole.rbac.authorization.k8s.io/discov-endpoints created
clusterrolebinding.rbac.authorization.k8s.io/find-endpoints-discov-endpoints created

jenkins_service_account.yaml文件内容如下:

#创建账号
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: k8s-demo
name: find-endpoints

---
#创建角色对应操作
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: discov-endpoints
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get","list","watch"]

---
#给账号绑定角色
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: find-endpoints-discov-endpoints
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: discov-endpoints
subjects:
- kind: ServiceAccount
name: find-endpoints
namespace: k8s-demo

3.6 k8s添加拉取镜像凭证

k8s在默认情况下,只能拉取harbor镜像仓库的公有镜像,如果拉取私有仓库镜像,则是会报 ErrImagePullImagePullBackOff 的错误

1.先在jenkins发布机器登陆harbor

在jenkins发布机器登陆harbor,会生成登录凭证,保存在/root/.docker/config.json

$ docker login 192.168.148.132:8870
Username: admin
Password:
Error response from daemon: Get "https://192.168.148.132:8870/v2/": http: server gave HTTP response to HTTPS client

如果发现你也报这种错误的话,解决方法如下:

# 确保你的/etc/docker/daemon.json,加对了insecure-registries
$cat /etc/docker/daemon.json
{
"registry-mirrors":[
"https://otrqd6z7.mirror.aliyuncs.com"
],
"insecure-registries":[
"192.168.148.132:8870"
]
}
# 重新加载
$ systemctl daemon-reload
# 重启docker
$ systemctl restart docker
# 重启harbor,在harbor安装目录执行下面命令
$ docker-compose restart
Restarting harbor-jobservice ... done
Restarting nginx ... done
Restarting harbor-core ... done
Restarting registry ... done
Restarting redis ... done
Restarting registryctl ... done
Restarting harbor-portal ... done
Restarting harbor-db ... done
Restarting harbor-log ... done
# 再次登录
$ docker login 192.168.148.132:8870
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

2.查看登陆harbor生成的凭证

$ cat .docker/config.json
{
"auths":{
"192.168.148.132:8870":{
"auth":"YWRtaW46SGFyYm9yMTIzNDU="
}
}
}

3. 对秘钥文件进行base64加密

$ cat /root/.docker/config.json  | base64 -w 0

ewoJImF1dGhzIjogewoJCSIxOTIuMTY4LjE0OC4xMzI6ODg3MCI6IHsKCQkJImF1dGgiOiAiWVdSdGFXNDZTR0Z5WW05eU1USXpORFU9IgoJCX0KCX0KfQ==

4. 创建docker-harbor-secret.yaml

jenkins-harbor-secret.yaml内容如下:

apiVersion: v1
kind: Secret
metadata:
name: docker-login
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: ewoJImF1dGhzIjogewoJCSIxOTIuMTY4LjE0OC4xMzI6ODg3MCI6IHsKCQkJImF1dGgiOiAiWVdSdGFXNDZTR0Z5WW05eU1USXpORFU9IgoJCX0KCX0KfQ==
# 在k8s集群中执行,注意指明命名空间
$ kubectl apply -f jenkins-harbor-secret.yaml -n k8s-demo
secret/docker-login created

4. Jenkins发布

4.1 创建任务

步骤一: 点击左侧菜单->新建任务(新建item),进入下面页面:

新增任务

步骤二: 点击保存后跳到具体配置页面

配置a

配置b

4.2 Pipeline脚本预览

这一章节主要介绍Pipeline脚本都有哪些部分组成,并把pipeline具体步骤部分,单拿出一个章节讲解;

4.2.1 整体流程

pipeline {
agent any
parameters {
// 接受页面输入的参数
}
environment {
// 环境变量
}
// 具体构建步骤
stages {
stage('构建信息') {
steps {
// 打印一些构建信息
}
}
stage('拉取源码') {
steps {
// 拉取代码
}
}
stage('生成镜像') {
steps{
//...
}
}

stage('上传镜像') {
steps{
//...
}
}

stage('发布到k8s') {
steps{
//...
}
}
stage('清理代码') {
steps{
// 用来清理构建过程生成的信息
}
}
}
}

4.2.2 参数信息

pipeline {
agent any
// 参数信息
parameters {
gitParameter (
name: 'commit_id', // 参数名(我们在创建任务页面设置的git参数)
type: 'PT_REVISION', // PT_TAG:标签,PT_BRANCH:分支
branchFilter: 'origin/(.*)', // 不设置无法正常获取分支或标签
defaultValue: 'master',
selectedValue: 'TOP', // 定义选择的排序方式:NONE,TOP ,DEFAULT
quickFilterEnabled: true, // 是否开启搜索功能
sortMode: 'ASCENDING_SMART',
description: '选择需要构建的commit信息'
)
}
environment {
// 环境变量
}
stages {
//具体构建步骤
}
}

4.2.3 环境变量

pipeline {
agent any
parameters {
// 参数信息
}
// 环境变量
environment {
AppName = "go-app" // 项目名称
AppPort = "1010" // 项目端口号,这里写死。正常应该走配置
DockerNamespace = "k8s-demo" // harbor命名空间
JenkinsAccountName= "find-endpoints" //k8s添加ServiceAccount的名称
DeployReplicas = 1 // Deploy副本数
PodMinReplicas = 1 // pod伸缩 最小副本数
PodMaxReplicas = 1 // pod伸缩 最大副本数
}
stages {
//具体构建步骤
}
}

4.3 Pipeline具体步骤

4.3.1 打印构建信息

stage('构建信息') {
steps {
// 也可以打印一些其他的信息
sh 'echo commit_id:$commit_id'
sh 'echo goctl版本信息'
sh '/usr/local/bin/goctl -v'
}
}

4.3.2 拉取源代码

这个步骤可以通过pipeline流水线语法生成,如下图:

stage('拉取源码') {
steps {
checkout([
$class: 'GitSCM', // 不用改
branches: [[name: '$commit_id']],// 和之前定义的变量名保持一直
doGenerateSubmoduleConfigurations: false, // 不用改
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[credentialsId: 'gitea-account', url: 'http://192.168.148.132:8800/k8s/go-app.git']] // gitea-account: 拉取凭证id;url:代码地址
])
}
}

4.3.3 生成镜像

stage('生成镜像') {
steps{
sh "yes | rm -rf Dockerfile" // 删除历史Dockerfile
sh "/usr/local/bin/goctl docker -go main.go && ls -l" // 生成dockerfile
script{
//保存镜像名称到环境变量
env.image = sh(returnStdout: true, script: "echo ${env.AppName}:${commit_id}").trim()
}
sh "echo 镜像名称:${image}"
// 构建镜像
sh "docker build -t ${image} ."
}
}

这里我们使用goctl来生成Dockerfile,

4.3.4 上传镜像

stage('上传镜像') {
steps{
//docker_username、docker_pwd、docker_repo是我们之前添加harbor仓库配置,DockerNamespace为harbor的命名空间
sh 'docker login --username=${docker_username} --password=${docker_pwd} http://${docker_repo}'
sh "docker tag ${image} ${docker_repo}/${env.DockerNamespace}/${image}"
sh "docker push ${docker_repo}/${env.DockerNamespace}/${image}"
}
}

4.3.5 发布到k8s

stage('发布到k8s') {
steps{
script{
env.deployYaml = sh(returnStdout: true, script: "echo ${env.AppName}-deploy.yaml").trim()
}
sh "echo 暴露端口: ${env.AppPort}"
// 删除历史yaml
sh 'rm -f ${deployYaml}'
// 生成新yaml
sh "/usr/local/bin/goctl kube deploy -secret docker-login -replicas ${env.DeployReplicas} -minReplicas ${env.PodMinReplicas} -maxReplicas ${env.PodMaxReplicas} -nodePort 3${env.AppPort} -requestCpu 200 -requestMem 50 -limitCpu 300 -limitMem 100 -name ${env.AppName} -namespace ${env.DockerNamespace} -image ${docker_repo}/${env.DockerNamespace}/${image} -o ${deployYaml} -port ${env.AppPort} -serviceAccount ${env.JenkinsAccountName}"
// 查看
sh 'cat ${deployYaml}'
// 发布
sh '/usr/local/bin/kubectl apply -f ${deployYaml}'
}
}

4.3.6 清理代码

这一步主要是为了清理,构建过程中生成的代码信息,和镜像信息

stage('清理信息') {
steps{
// 忽略sh 错误
sh 'set +e'
sh "docker rmi ${docker_repo}/${env.DockerNamespace}/${image}"
sh "docker images | grep ${env.AppName} | awk \'{print \$3}\'| xargs docker rmi -f"
sh "docker images | grep none | awk \'{print \$3}\'| xargs docker rmi -f "
// 清理工作目录
sh "ls -al ${env.WORKSPACE}"
deleteDir() // 清理当前目录
sh "ls -al ${env.WORKSPACE}"
}
}

4.4 准备发布

4.4.1 粘贴pipeline脚本

把上述的配置完整信息,复制粘贴到流水线脚本框,如下图:

关注公众号【猿码记】,回复【k8s-demo】,获取完整pipeline脚本信息

4.4.2 发布

发布流程截图

再次构建时,即可发现能发布成功,如下图:

构建成功图

4.4.3 查看k8s集群

应用已在集群

原因是: 在k8s集群的节点上,我们没有配置从私有仓库拉取镜像的配置;k8s各个集群节点安装的是Containerd,所以我们修改所有k8s集群节点的Containerd配置信息,并重启。

1. 修改Containerd配置
$  vim /etc/containerd/config.toml
...
# 找到这个位置
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = ""
# 新增私有仓库
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."192.168.148.132:8870"]
endpoint = ["http://192.168.148.132:8870"]
[plugins."io.containerd.grpc.v1.cri".registry.auths]
[plugins."io.containerd.grpc.v1.cri".registry.configs]
[plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.148.132:8870".tls]
insecure_skip_verify = true #跳过证书验证
[plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.148.132:8870".auth]
username = "admin" # 账号
password = "Harbor12345"# 密码
[plugins."io.containerd.grpc.v1.cri".registry.headers]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
....

配置改的太痛苦,比较伤眼睛,注意空格;关注公众号【猿码记】,回复【k8s-demo】,获取完整配置信息

2. 重启Containerd
$ systemctl restart containerd

4.4.4 访问服务

访问服务