四
jenkins &
kubernetes
CI/CD
Kubernetes,从官方网站上可以看到,它是一个工业级的容器编排平台。Kubernetes 这个单词是希腊语,它的中文翻译是“舵手”或者“飞行员”。在一些常见的资料中也会看到“ks”这个词,也就是“k8s”,它是通过将8个字母“ubernete ”替换为“8”而导致的一个缩写。 Kubernetes 为什么要用“舵手”来命名呢?大家可以看一下这张图:
这是一艘载着一堆集装箱的轮船,轮船在大海上运着集装箱奔波,把集装箱送到它们该去的地方。我们之前其实介绍过一个概念叫做 container,container 这个英文单词也有另外的一个意思就是“集装箱”。Kubernetes 也就借着这个寓意,希望成为运送集装箱的一个轮船,来帮助我们管理这些集装箱,也就是管理这些容器。
这个就是为什么会选用 Kubernetes 这个词来代表这个项目的原因。更具体一点地来说:Kubernetes 是一个自动化的容器编排平台,它负责应用的部署、应用的弹性以及应用的管理,这些都是基于容器的
。
二、Kubernetes 有如下几个核心的功能:
服务的发现与负载的均衡;容器的自动装箱,我们也会把它叫做 scheduling,就是“调度”,把一个容器放到一个集群的某一个机器上,Kubernetes 会帮助我们去做存储的编排,让存储的生命周期与容器的生命周期能有一个连接;Kubernetes 会帮助我们去做自动化的容器的恢复。在一个集群中,经常会出现宿主机的问题或者说是 OS 的问题,导致容器本身的不可用,Kubernetes 会自动地对这些不可用的容器进行恢复;Kubernetes 会帮助我们去做应用的自动发布与应用的回滚,以及与应用相关的配置密文的管理;对于 job 类型任务,Kubernetes 可以去做批量的执行;为了让这个集群、这个应用更富有弹性,Kubernetes 也支持水平的伸缩。下面,我们希望以三个例子跟大家更切实地介绍一下 Kubernetes 的能力
1、调度
Kubernetes 可以把用户提交的容器放到 Kubernetes 管理的集群的某一台节点上去。Kubernetes 的调度器是执行这项能力的组件,它会观察正在被调度的这个容器的大小、规格。
比如说它所需要的 CPU以及它所需要的 memory,然后在集群中找一台相对比较空闲的机器来进行一次 placement,也就是一次放置的操作。在这个例子中,它可能会把红颜色的这个容器放置到第二个空闲的机器上,来完成一次调度的工作。
2、自动修复
Kubernetes 有一个节点健康检查的功能,它会监测这个集群中所有的宿主机,当宿主机本身出现故障,或者软件出现故障的时候,这个节点健康检查会自动对它进行发现。下面 Kubernetes 会把运行在这些失败节点上的容器进行自动迁移,迁移到一个正在健康运行的宿主机上,来完成集群内容器的一个自动恢复。
3、水平伸缩
Kubernetes 有业务负载检查的能力,它会监测业务上所承担的负载,如果这个业务本身的 CPU 利用率过高,或者响应时间过长,它可以对这个业务进行一次扩容。
比如说在下面的例子中,黄颜色的过度忙碌,Kubernetes 就可以把黄颜色负载从一份变为三份。接下来,它就可以通过负载均衡把原来打到第一个黄颜色上的负载平均分到三个黄颜色的负载上去,以此来提高响应的时间。
以上就是 Kubernetes 三个核心能力的简单介绍。
4.1
安装
k8s
4.1.1
机器准备
- 至少2台 2核4G 的服务器
- Cent OS 7.6 / 7.7 / 7.8 /7.9
4.1.2
机器检查
# 在 master 节点和 worker 节点都要执行
cat /etc/redhat-release
# 此处 hostname 的输出将会是该机器在 Kubernetes 集群中的节点名字
# 不能使用 localhost 作为节点的名字
hostname
# 请使用 lscpu 命令,核对 CPU 信息
# Architecture: x86_64 本安装文档不支持 arm 架构
# CPU(s): 2 CPU 内核数量不能低于 2
lscpu
修改主机名
# 修改 hostname
hostnamectl set-hostname your-new-host-name
# 查看修改结果
hostnamectl status
# 设置 hostname 解析
echo "127.0.0.1 $(hostname)" >> /etc/hosts
4
.
1
.
3
检查网络
[root@demo-master-a-1 ~]$ ip route show
default via 172.21.0.1 dev eth0
169.254.0.0/16 dev eth0 scope link metric 1002
172.21.0.0/20 dev eth0 proto kernel scope link src 172.21.0.12
[root@demo-master-a-1 ~]$ ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:12:a4:1b brd ff:ff:ff:ff:ff:ff
inet 172.17.216.80/20 brd 172.17.223.255 scope global dynamic eth0
valid_lft 305741654sec preferred_lft 305741654sec
kubelet使用的IP地址
-
ip route show
命令中,可以知道机器的默认网卡,通常是
eth0
,如
default via 172.21.0.23 dev
eth0
-
ip address
命令中,可显示默认网卡的 IP 地址,Kubernetes 将使用此 IP 地址与集群内的其他节点通信,如
172.17.216.80
-
所有节点上 Kubernetes 所使用的 IP 地址必须可以互通(无需 NAT 映射、无安全组或防火墙隔离)
4
.
1
.
4
机器核对
4.1.5
主从机器执行
# 在 master 节点和 worker 节点都要执行
# 最后一个参数 1.19.5 用于指定 kubenetes 版本,支持所有 1.19.x 版本的安装
# 腾讯云 docker hub 镜像
# export REGISTRY_MIRROR="https://mirror.ccs.tencentyun.com"
# DaoCloud 镜像
# export REGISTRY_MIRROR="http://f1361db2.m.daocloud.io"
# 华为云镜像
# export REGISTRY_MIRROR="https://05f073ad3c0010ea0f4bc00b7105ec20.mirror.swr.myhuaweicloud.com"
# 阿里云 docker hub 镜像
export REGISTRY_MIRROR=https://registry.cn-hangzhou.aliyuncs.com
curl -sSL https://kuboard.cn/install-script/v1.19.x/install_kubelet.sh | sh -s 1.19.5
4.1.6
添加自己的私服地址
vim /etc/docker/deamon.json
K8s集群机器修改docker的私服映射地址
"insecure-registries":["192.168.189.129:8081"],
重启docker
[root@myk8sslaver /] systemctl restart docker
4
.
1
.
7
mas
ter
服务器执行
# 只在 master 节点执行
# 替换 x.x.x.x 为 master 节点实际 IP(请使用内网 IP)
# export 命令只在当前 shell 会话中有效,开启新的 shell 窗口后,如果要继续安装过程,请重新执行此处的 export 命令
export MASTER_IP=192.168.189.131
# 替换 apiserver.demo 为 您想要的 dnsName
export APISERVER_NAME=worn.com
# Kubernetes 容器组所在的网段,该网段安装完成后,由 kubernetes 创建,事先并不存在于您的物理网络中
export POD_SUBNET=10.100.0.1/16
echo "${MASTER_IP} ${APISERVER_NAME}" >> /etc/hosts
curl -sSL https://kuboard.cn/install-script/v1.19.x/init_master.sh | sh -s 1.19.5
4.1.
8
检查mas
ter
的初始化结果
# 只在 master 节点执行
# 执行如下命令,等待 3-10 分钟,直到所有的容器组处于 Running 状态
watch kubectl get pod -n kube-system -o wide
# 查看 master 节点初始化结果
kubectl get nodes -o wide
4
.
1
.
9
初始化
worker
节点
生成加入凭证
# 只在 master 节点执行
kubeadm token create --print-join-command
可获取kubeadm join 命令及参数,如下所示
# kubeadm token create 命令的输出
kubeadm join apiserver.demo:6443 --token mpfjma.4vjjg8flqihor4vt --discovery-token-ca-cert-hash sha256:6f7a8e40a810323672de5eee6f4d19aa2dbdb38411845a1bf5dd63485c43d303
kubeadm join worn.com:6443 –token gz10np.eg81pszu0x3gkpez –discovery-token-ca-cert-hash sha256:e0780d714c3b51ae3029a534fb92bf73aa54d84a0dc2f74bbc622a96bb353fbd
初始化worker
针对所有的 worker 节点执行
获取加入凭证并执行加入
# 只在 worker 节点执行
# 替换 x.x.x.x 为 master 节点的内网 IP
export MASTER_IP=192.168.189.131
# 替换 apiserver.demo 为初始化 master 节点时所使用的 APISERVER_NAME
export APISERVER_NAME=worn.com
echo "${MASTER_IP} ${APISERVER_NAME}" >> /etc/hosts
# 替换为 master 节点上 kubeadm token create 命令的输出
kubeadm join worn.com:6443 --token gz10np.eg81pszu0x3gkpez --discovery-token-ca-cert-hash sha256:e0780d714c3b51ae3029a534fb92bf73aa54d84a0dc2f74bbc622a96bb353fbd
4.
1
.
10
检查初始化最终结果
# 只在 master 节点执行
kubectl get nodes -o wide
安装完成大工告成.
4
.
2
安装
kuboard
图形化界面
kubectl apply -f https://addons.kuboard.cn/kuboard/kuboard-v3.yaml
# 您也可以使用下面的指令,唯一的区别是,该指令使用华为云的镜像仓库替代 docker hub 分发 Kuboard 所需要的镜像
# kubectl apply -f https://addons.kuboard.cn/kuboard/kuboard-v3-swr.yaml
等待 Kuboard v3 就绪
执行指令 watch kubectl get pods -n kuboard,等待 kuboard 名称空间中所有的 Pod 就绪,如下所示,
如果结果中没有出现 kuboard-etcd-xxxxx 的容器,请查看 常见错误 中关于 缺少 Master Role 的描述。
在浏览器中打开链接
http://192.168.189.131:30080/sso/auth/default?req=oraukdfzejt2cfeo2ogejkxo7
输入初始用户名和密码,并登录
用户名: admin
密码: Kuboard123
4.3
kube
ctl
指令
Node、Pod、Replication Controller和Service等都可以看作是一种“资源对象”,几乎所有的资源对象都可以通过Kubernetes提供的kubectl工具执行增、删、改、查等操作并将其保存在ectd中持久化存储。常用指令
4.3.1
名称空间操作
Kubectl create ns app //创建名称空间
Kubectl get ns //获取名称空间
kubectl delete ns app // 删除名称空间
4.3.2
po
d
操作
kubectl get pods -o wide 查看pod扩展信息
kubectl get pods —all-namespaces 所有空间
kubectl delete pod nginx-dp-b745fc865-dxcj6 //删除pod等价于重启pod
kubectl logs pod-name 查看容器输出到控制台日志
kubectl get pod nginx-dp-6784c78d45-jzjjw -o yaml //查看容器的声明式定义
kubectl logs -f pods/monitoring-grafana-xxxxxxx -n kube-system
kubectl exec -ti nginx-dp-b745fc865-dxcj6 /bin/bash -n default //进入pod
kuberctl delete pods --all 删除所有的pod
4.3.3 deployment
操作
kubectl create deployment nginx-dp --image=nginx:1.12.2. //创建deployment
kubectl get deployment -n test -o wide //查看部署
kubectl describe deployment nginx-dp -n default //查看部署的详细情况
kubectl delete deployment nginx-dp -n default //删除部署
kubectl scale deployment nginx-dp --replicas=2 -n default //扩容部署
4.3.4 service
操作
kubectl expose deployment nginx-dp
--port=8000 //服务端口
--target-port=80 //pod内容器端口
-n default //名称空间
//内网暴露一个service
kubectl expose deployment nginx-deploy
--port=8000 //集群内service端口
--target-port=80 -n test
--type=NodePort //主机节点对外暴露一个端口
kubectl get services --namespace test
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
metrics-server ClusterIP 10.96.137.255 <none> 443/TCP 10h
nginx-deploy NodePort 10.96.3.115 <none> 8084:30948/TCP 6m15s
kubectl describe svc nginx-dp -n default //查看service
kubectl get service -n test查询service默认空间
kubectl get services --namespace test
kubectl get svc nginx-dp -o yaml -n default 查看服务的声明式定义
4.3.5
资源操作
kubectl get nodes //查看主机节点
kubectl get all -n default //查询所有资源
kubectl get rc 查询RC
kubectl describe node codename 查看节点的详细信息
kubectl get svc nginx-dp -o yaml -n default 查看服务的声明式定义
kubectl explain service.metadata //解释 yaml缎的含义
kubectl apply -f nginx-ds-svc.yaml //通过apply生成资源
Kubectl delete -f nginx-ds-svc.yaml //删除资源
4.4 ingree
在Kubernetes集群中,Ingress作为集群内服务对外暴露的访问接入点,其几乎承载着集群内服务访问的所有流量。Ingress是Kubernetes中的一个资源对象,用来管理集群外部访问集群内部服务的方式。您可以通过Ingress资源来配置不同的转发规则,从而达到根据不同的规则设置访问集群内不同的Service所对应的后端Pod。
Ingress资源仅支持配置HTTP流量的规则,无法配置一些高级特性,例如负载均衡的算法、Sessions Affinity等,这些高级特性都需要在Ingress Controller中进行配置。
4.5 ConfigMap
ConfigMap API资源用来保存key-value pair配置数据,这个数据可以在pods里使用,或者被用来为像controller一样的系统组件存储配置数据。虽然ConfigMap跟Secrets类似,但是ConfigMap更方便的处理不含敏感信息的字符串。 注意:ConfigMaps不是属性配置文件的替代品。ConfigMaps只是作为多个properties文件的引用。你可以把它理解为Linux系统中的/etc目录,专门用来存储配置文件的目录.
用途
- 使用ConfigMap来替代环境变量,ConfigMap可以被用来填入环境变量; configmap里面的信息读入环境变量,而容器启动的时候可以利用这些环境变量
- ConfigMap也可以被使用来设置容器中的命令或者参数值。它使用的是Kubernetes的$(VAR_NAME)替换语法
- ConfigMap也可以在数据卷里面被使用
mysql部署
redis部署
4.5 Nginx Ingress Controller
当前Kubernetes官方维护的是Nginx Ingress Controller,ACK基于社区版的Nginx Ingress Controller进行了优化。当在创建ACK集群时,您选择安装的Nginx Ingress Controller组件即为ACK定制版的Nginx Ingress Controller组件。
工作原理
为了使得Nginx Ingress资源正常工作,集群中必须要有个Nginx Ingress Controller来解析Nginx Ingress的转发规则。Nginx Ingress Controller收到请求,匹配Nginx Ingress转发规则转发到后端Service所对应的Pod,由Pod处理请求。Kubernetes中Service、Nginx Ingress与Nginx Ingress Controller有着以下关系:
Service是后端真实服务的抽象,一个Service可以代表多个相同的后端服务。
Nginx Ingress是反向代理规则,用来规定HTTP/HTTPS请求应该被转发到哪个Service所对应的Pod上。例如根据请求中不同的Host和URL路径,让请求落到不同Service所对应的Pod上。
Nginx Ingress Controller是一个反向代理程序,负责解析Nginx Ingress的反向代理规则。如果Nginx Ingress有增删改的变动,Nginx Ingress Controller会及时更新自己相应的转发规则,当Nginx Ingress Controller收到请求后就会根据这些规则将请求转发到对应Service的Pod上。
Nginx Ingress Controller通过API Server获取Ingress资源的变化,动态地生成Load Balancer(例如Nginx)所需的配置文件(例如nginx.conf),然后重新加载Load Balancer(例如执行nginx -s load重新加载Nginx)来生成新的路由转发规则。
Nginx Ingress Controller可通过配置LoadBalancer类型的Service来创建SLB,因此可以从外部通过SLB访问到Kubernetes集群的内部服务。根据Nginx Ingress配置的不同规则来访问不同的服务。
安装ingress
4.6 jenkins 实现 CI/CD
4.6.1
关联
harbor
仓库
4
.
6
.
2
项目目录下创建y
ml
文件
pipeline.yml文件交付项目到k8s集群
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: hello-pipline
name: hello-pipline
namespace: test
spec:
replicas: 2
selector:
matchLabels:
app: hello-pipline
template:
metadata:
labels:
app: hello-pipline
spec:
containers:
- name: hello-pipline
image: 192.168.189.129:8081/wornxiao/hello-pipline:v3.0.1
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
labels:
app: hello-pipline
name: hello-pipline
namespace: test
spec:
selector:
app: hello-pipline
ports:
- port: 8081
targetPort: 8080
type: NodePort
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: test
name: pipeline-ingress
spec:
ingressClassName: ingress
rules:
- host: worn.pipeline.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-pipline
port:
number: 8081
4
.
6
.
3
验证
yml
脚本
[root@myk8smaster yml] kubectl apply -f pipeline.yml
deployment.apps/hello-pipline created
service/hello-pipline created
ingress.networking.k8s.io/pipeline-ingress created
[root@myk8smaster yml]#
把编写好的文件上传到集群中先验证交付情况.
deployment
Service
Ingress
Pod
测试成功.
4.6.4
j
enkins
交付
yml
到
k8s
实现
CICD
4.6.4.1
配置目标服务器
4.6.4.2
修改pi
peline
流水线内容
通知目标服务器只要把pipeline.xml拷贝过去就可以了
4.6.4.3
jen
kins
交付
pipeline.yml
文件给
k8s
集群
root@a65d4d6be42b:/ ssh root@192.168.189.131 kubectl apply -f /usr/local/k8s/pipeline.yml
root@192.168.189.131's password:
如上所示 由于需要通过jenkins登录到k8s集群服务器.把pipeline.yml文件交付到k8s集群.但是发现jenkins容器不能够免密登录到k8s集群主机,因此需要配置一个免密登录.
Jen
kins
容器生成密钥
root@a65d4d6be42b:/ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa
Your public key has been saved in /root/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:pfTa9BYxSLfpZOsq/s3LvsFtVqbxfUHeQsL9ldudTJA root@a65d4d6be42b
The key's randomart image is:
+---[RSA 3072]----+
| . ... |
| . + E. .|
| . o X o+.|
| . + + *=.B|
| S o + o*B|
| + + o Bo|
| . . * = +|
| . * + .|
| ..oo.Bo |
+----[SHA256]-----+
root@a65d4d6be42b:/#
查看密钥
root@a65d4d6be42b:/ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC+cQjV5BxnyVTMOQwGe6At895zL/Za1jGJO95nQ8rNxc84zkxGkImWsqSHfJAn5CQ8VBIdwZCqHQdO+61f71iMdeIUR9h03HDqmjQ6rAjr7FhmxMZNHcJt8qN9xLIfk+Q9B+eN4gg912IM/jfNpqabUO5qF9Iy9uoAmD2IMLwpLkrNirmWMBCxpgOANYFMICAf3FKiGtDL4UoEl5O6gZVeyiy9eLkHdfbMUFYxh07Llo74dwLzAGpmMvplGeuUMh7PSQDq+B2jC9foKnO6VUieBDzCDvFLCceU+9OwO9afD58zjBjjBTdeKPQZdJ7NQ4kCCc/Ch2QPcwuu6QDV/yybBACLZ3nIK28bHHF2f+RVTbn9XB8uAFDcoRBEBFG9oGo75uDZQIye1p973MO/RHPkP99apoETxP7VI7qR2fOHBDZ/P2+/Nwxs3ij/XA8jCmGqsptYckMdBFC3aJuCErLkQXoPV1e20emnoloH5CctEpoheTgE3JnTUmW3wgizOWk= root@a65d4d6be42b
root@a65d4d6be42b:/#
把公钥同步到目标主机
root@a65d4d6be42b:/ ssh-copy-id root@192.168.189.131
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@192.168.189.131's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'root@192.168.189.131'"
and check to make sure that only the key(s) you wanted were added.
root@a65d4d6be42b:/#
root@a65d4d6be42b:/ ssh root@192.168.189.131
Last login: Tue Sep 13 07:06:04 2022 from wornxiao.com
[root@myk8smaster ~]#
再次登录发现已经不需要再输入密钥了.这样的化就可以通过jenkins容器登录远程k8s集群执行目标的pipeline.yml文件了.
远程交付流水脚本
在pipeline.yml文件中补上
修改pipeline.yml资源交付的镜像版本
交互问题
超级头痛的 ingress 内部访问错误👇🏻
Error from server (InternalError): error when creating
“ingress-srv.yaml”: Internal error occurred: failed calling webhook “validate.nginx.ingress.kubernetes.io”: Post
“https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses?timeout=10s”:
dial tcp 10.102.20.133:443: connect: connection refused
kubectl get ValidatingWebhookConfiguration
kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
把那个该死的 admission 删掉
然后就正常了
pipeline{
//指定用集群中的哪个节点
agent any
//声明全局变量
environment {
harbor_name = 'admin'
harbor_pass ='Harbor12345'
harbor_addr = '192.168.189.129:8081'
harbor_repo = 'wornxiao'
}
stages{
stage('拉取git仓库代码'){
steps{
checkout([$class: 'GitSCM', branches: [[name: '${tag}']], extensions: [], userRemoteConfigs: [[url: 'https://github.com/wornxiao/springboot-hello.git']]])
}
}
stage('通过maven构建项目'){
steps{
sh 'mvn clean package -DskipTest'
}
}
stage('通过sonarQuebe做质量检测'){
steps{
echo '通过sonarQuebe做质量检查'
}
}
stage('通过docker制作自定义镜像'){
steps{
sh '''mv target/*.jar ./docker
docker build -t ${JOB_NAME}:$tag docker '''
}
}
stage('将自定义对象推送到harbor仓库中'){
steps{
sh '''docker login -u ${harbor_name} -p ${harbor_pass} ${harbor_addr}
docker tag ${JOB_NAME}:${tag} ${harbor_addr}/${harbor_repo}/${JOB_NAME}:${tag}
docker push ${harbor_addr}/${harbor_repo}/${JOB_NAME}:${tag}'''
}
}
stage('将yml文件 上传到k8s集群主机'){
steps{
sshPublisher(publishers: [sshPublisherDesc(configName: 'k8s', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'pipeline.yml')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
stage('远程交付yml资源到k8s集群'){
steps{
sh 'ssh root@192.168.189.131 kubectl apply -f /usr/local/k8s/pipeline.yml'
}
}
}
}
jenkins一键交付
k8s容器云平台
浏览器结果
完美交付 !