ConfigMa
Configmap 是 k8s 中的资源对象,用于保存非机密性的配置的,数据可以用 key/value键值对的形式保存,也可通过文件的形式保存。
ConfigMap的创建方法
命令行直接创建
# 创建一个叫tomcat-config的configmap
# 通过--from-literal 指定参数
[root@master ~]# kubectl create configmap tomcat-config --from-literal=tomcat_port=8080 --from-literal=server_name=myapp.tomcat.com
configmap/tomcat-config created
[root@master ~]# kubectl describe configmap tomcat-config
Name: tomcat-config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
server_name:
----
myapp.tomcat.com
tomcat_port:
----
8080
BinaryData
====
Events: <none>
通过文件创建
# 准备配置文件
cat > nginx.conf << EOF
server {
server_name www.nginx.com;
listen 80;
root /usr/share/nginx/html;
}
EOF
# 创建一个叫www-nginx的configmap
# --from-file指定从文件中读取,设置www的内容为nginx.conf里面的内容
[root@master ~]# kubectl create configmap www-nginx --from-file=www=nginx.conf
configmap/www-nginx created
[root@master ~]# kubectl describe configmap www-nginx
Name: www-nginx
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
www:
----
server {
server_name www.nginx.com;
listen 80;
root /usr/share/nginx/html;
}
BinaryData
====
Events: <none>
指定目录创建
# 准备一个目录,以及目录里面的配置文件
mkdir test-a
cat > test-a/my-server.cnf << EOF
server-id=1
EOF
cat > test-a/my-slave.cnf << EOF
server-id=2
EOF
# 创建一个叫mysql-config的configmap
# --from-file从指定的目录里面加载配置文件
[root@master ~]# kubectl create configmap mysql-config --from-file=test-a/
configmap/mysql-config created
[root@master ~]# kubectl describe configmap mysql-config
Name: mysql-config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
my-server.cnf:
----
server-id=1
my-slave.cnf:
----
server-id=2
BinaryData
====
Events: <none>
编写yaml文件创建
# 定义一个叫mysql的configmap
[root@master ~]# cat mysql_configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql
labels:
app: mysql
data:
master.cnf: |
[mysqld]
server-id=1
log-bin
log_bin_trust_function_creators=1
slave.cnf: |
[mysqld]
server-id=2
log-bin
log_bin_trust_function_creators=1
[root@master ~]# kubectl apply -f mysql_configmap.yaml
configmap/mysql created
# 查看mysql这个configmap的内容
[root@master ~]# kubectl describe configmap mysql
Name: mysql
Namespace: default
Labels: app=mysql
Annotations: <none>
Data
====
slave.cnf:
----
[mysqld]
server-id=2
log-bin
log_bin_trust_function_creators=1
master.cnf:
----
[mysqld]
server-id=1
log-bin
log_bin_trust_function_creators=1
BinaryData
====
Events: <none>
删除
# kubectl delete cm 要删除的configmap的名字,下面以www-nginx为示例
kubectl delete cm www-nginx
ConfigMap应用
通过环境变量引入
[root@master ~]# cat configmap_cmkr.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql
labels:
app: mysql
data:
master.cnf: |
[mysqld]
server-id=1
log-bin
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql
labels:
app: mysql
data:
master.cnf: |
[mysqld]
server-id=1
log-bin
log_bin_trust_function_creators=1
slave.cnf: |
[mysqld]
server-id=2
log-bin
log_bin_trust_function_creators=1
---
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
spec:
containers:
- name: mysql
image: hub.atomgit.com/library/busybox:1.35
imagePullPolicy: IfNotPresent
command: [ "/bin/sh", "-c", "sleep 3600" ]
env:
- name: mysql_master
valueFrom:
configMapKeyRef:
name: mysql
key: "master.cnf"
- name: mysql_slave
valueFrom:
configMapKeyRef:
name: mysql
key: "slave.cnf"
[root@master ~]# kubectl apply -f configmap_cmkr.yaml
configmap/mysql created
pod/mysql-pod created
# 可以登录到pod里面使用echo把变量打印出来
[root@master ~]# kubectl exec -it mysql-pod -- sh
/ # echo $mysql_master
[mysqld] server-id=1 log-bin log_bin_trust_function_creators=1
/ # echo $mysql_slave
[mysqld] server-id=2 log-bin log_bin_trust_function_creators=1
# 也是直接使用env查看
[root@master ~]# kubectl exec -it mysql-pod -- env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=mysql-pod
TERM=xterm
mysql_slave=[mysqld]
server-id=2
log-bin
log_bin_trust_function_creators=1
mysql_master=[mysqld]
server-id=1
log-bin
log_bin_trust_function_creators=1
KUBERNETES_PORT=tcp://10.1.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.1.0.1:443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.1.0.1
KUBERNETES_SERVICE_HOST=10.1.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
HOME=/root
使用envfrom
[root@master ~]# cat configmap_ef.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql
labels:
app: mysql
data:
master_cnf: |
[mysqld]
server-id=1
log-bin
log_bin_trust_function_creators=1
slave_cnf: |
[mysqld]
server-id=2
log-bin
log_bin_trust_function_creators=1
---
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
spec:
containers:
- name: mysql
image: hub.atomgit.com/library/busybox:1.35
imagePullPolicy: IfNotPresent
command: [ "/bin/sh", "-c", "sleep 3600" ]
envFrom:
- configMapRef:
name: mysql # 指定 configmap 的名字
[root@master ~]# kubectl apply -f configmap_ef.yaml
configmap/mysql created
pod/mysql-pod created
# 查看pod里面configmap已经生效
[root@master ~]# kubectl exec -it mysql-pod -- sh
/ # echo $master_cnf
[mysqld] server-id=1 log-bin log_bin_trust_function_creators=1
/ # echo $slave_cnf
[mysqld] server-id=2 log-bin log_bin_trust_function_creators=1
通过卷挂载进去使用
[root@master ~]# cat configmap_ef.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql
labels:
app: mysql
data:
master_cnf: |
[mysqld]
server-id=1
log-bin
log_bin_trust_function_creators=1
slave_cnf: |
[mysqld]
server-id=2
log-bin
log_bin_trust_function_creators=1
---
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
spec:
containers:
- name: mysql
image: busybox
imagePullPolicy: IfNotPresent
command: [ "/bin/sh", "-c", "sleep 3600" ]
envFrom:
- configMapRef:
name: mysql # 指定 configmap 的名字
[root@master ~]# kubectl apply -f configmap_ef.yaml
configmap/mysql created
pod/mysql-pod created
# 查看pod里面configmap已经生效
[root@master ~]# kubectl exec -it mysql-pod -- sh
/ # echo $master_cnf
[mysqld] server-id=1 log-bin log_bin_trust_function_creators=1
/ # echo $slave_cnf
[mysqld] server-id=2 log-bin log_bin_trust_function_creators=1
ConfigMap热更新
在Kubernetes(K8S)中,ConfigMap是一种用于存储非机密配置数据的资源对象。ConfigMap可以包含键值对、INI格式的文件、JSON格式的文件等。ConfigMap的热更新指的是在应用程序运行时对ConfigMap进行修改,而无需重启应用程序即可应用这些变更。
动态配置更新: 通过使用ConfigMap,您可以将应用程序的配置与应用程序本身分离。当配置更改时,可以直接更新ConfigMap而不需要重新构建或重启应用程序。这允许您实现动态配置更新,即在不停止服务的情况下应用新的配置。
无需重新部署: 通过热更新ConfigMap,您可以避免重新部署整个应用程序。这对于在生产环境中保持高可用性和最小化服务中断非常有用。应用程序可以检测到ConfigMap的更改并相应地调整其行为。
灵活性和快速响应: 通过将配置信息存储在ConfigMap中并实现热更新,您可以更灵活地调整应用程序的行为,而无需等待完整的重新部署周期。这有助于快速响应变化和调整应用程序的配置。
集中管理配置: ConfigMap允许您集中管理应用程序的配置信息,而不必将其硬编码到应用程序代码中。这使得配置更易于维护和管理,同时提供了一个单一的入口点来修改配置。
Secret
Secret类型
Opaque:base64 编码格式的 Secret,用来存储密码、密钥等;但数据也可以通过base64 –decode解码得到原始数据,所有加密性很弱。
kubernetes.io/dockerconfigjson:用来存储私有docker registry的认证信息。
kubernetes.io/service-account-token:用于被serviceaccount引用,serviceaccout 创建时Kubernetes会默认创建对应的secret。Pod如果使用了serviceaccount,对应的secret会自动挂载到Pod目录/run/secrets/kubernetes.io/serviceaccount中。
Secret参数
generic: 通用类型,通常用于存储密码数据
tls:此类型仅用于存储私钥和证书
docker-registry: 若要保存docker仓库的认证信息的话,就必须使用此种类型来创建
Secret和ConfigMap这两种资源对象的异同点
相同点:
key/value的形式
属于某个特定的namespace
可以导出到环境变量
可以通过目录/文件形式挂载
通过 volume 挂载的配置信息均可热更新
不同点:
Secret 可以被 ServerAccount 关联
Secret 可以存储 docker register 的鉴权信息,用在 ImagePullSecret 参数中,用于拉取私有仓库的镜像
Secret 支持 Base64 加密
Secret 分为 kubernetes.io/service-account-token、kubernetes.io/dockerconfigjson、Opaque 三种类型,而 Configmap 不区分类型
Secret应用
通过环境变量引入
[root@master ~]# kubectl create secret generic mysql-password --from-literal=password=123456
secret/mysql-password created
[root@master ~]# kubectl get secret mysql-password
NAME TYPE DATA AGE
mysql-password Opaque 1 41s
[root@master ~]# kubectl describe secret mysql-password
Name: mysql-password
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 6 bytes
[root@master ~]# cat secret_env.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-secret
labels: hub.atomgit.com/library/busybox:1.35
app: myapp
spec:
containers:
- name: myapp
image:
ports:
- name: http
containerPort: 80
command: ["sh","-c","sleep 3600"]
env:
- name: MYSQL_ROOT_PASSWORD #它是Pod启动成功后,Pod中容器的环境变量名.
valueFrom:
secretKeyRef:
name: mysql-password #这是secret的对象名
key: password #它是secret中的key名
[root@master ~]# kubectl apply -f secret_env.yaml
pod/pod-secret created
# 进入pod里面查看secret是否配置成功
[root@master ~]# kubectl exec -it pod-secret -- sh
/ # echo $MYSQL_ROOT_PASSWORD
123456
通过Volume挂载使用
#加密设置账号密码
[root@master ~]# echo -n 'admin' | base64
YWRtaW4=
[root@master ~]# echo -n '123456' | base64
MTIzNDU2
# -n 去掉换行的符号
#解密 echo 'MTIzNDU2' | base64 -d
[root@master ~]# cat secret_volume.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MTIzNDU2
---
apiVersion: v1
kind: Pod
metadata:
name: pod-secret-volume
spec:
containers:
- name: myapp
image: hub.atomgit.com/library/busybox:1.35
volumeMounts:
- name: secret-volume
mountPath: /etc/secret
readOnly: true
command: ["sh","-c","sleep 3600"]
volumes:
- name: secret-volume
secret:
secretName: mysecret
[root@master ~]# kubectl apply -f secret_volume.yaml
secret/mysecret created
pod/pod-secret-volume created
[root@master ~]# kubectl exec -it pod-secret-volume -- ls /etc/secret
password username
[root@master ~]# kubectl exec -it pod-secret-volume -- cat /etc/secret/username
admin
[root@master ~]# kubectl exec -it pod-secret-volume -- cat /etc/secret/password
123456
Service
service是一个固定接入层,客户端可以通过访问service的ip和端口访问到service关联的后端pod,这个service工作依赖于在kubernetes集群之上部署的一个附件,就是kubernetes的dns服务(不同kubernetes版本的dns默认使用的也是不一样的,1.11之前的版本使用的是kubeDNs,较新的版本使用的是coredns),service的名称解析是依赖于dns附件的,因此在部署完k8s之后需要再部署dns附件,kubernetes要想给客户端提供网络功能,需要依赖第三方的网络插件(flannel,calico等)。每个K8s节点上都有一个组件叫做kube-proxy,kube-proxy这个组件将始终监视着apiserver中有关service资源的变动信息,需要跟master之上的apiserver交互,随时连接到apiserver上获取任何一个与service资源相关的资源变动状态,这种是通过kubernetes中固有的一种请求方法watch(监视)来实现的,一旦有service资源的内容发生变动(如创建,删除),kube-proxy都会将它转化成当前节点之上的能够实现service资源调度,把我们请求调度到后端特定的pod资源之上的规则,这个规则可能是iptables,也可能是ipvs,取决于service的实现方式
Kubernetes集群中有三类IP地址
Node Network(节点网络):物理节点或者虚拟节点的网络,如ens33接口上的网路地址
Pod network(pod 网络):创建的Pod具有的IP地址
Cluster Network(集群地址,也称为service network):这个地址是虚拟的地址(virtual ip)
Kube-proxy的工作模式
Userspace方式
Client Pod要访问Server Pod时,它先将请求发给内核空间中的service iptables规则,由它再将请求转给监听在指定套接字上的kube-proxy的端口,kube-proxy处理完请求,并分发请求到指定Server Pod后,再将请求转发给内核空间中的service ip,由service iptables将请求转给各个节点中的Server Pod
这个模式有很大的问题,客户端请求先进入内核空间的,又进去用户空间访问kube-proxy,由kube-proxy封装完成后再进去内核空间的iptables,再根据iptables的规则分发给各节点的用户空间的pod。由于其需要来回在用户空间和内核空间交互通信,因此效率很差。在Kubernetes 1.1版本之前,userspace是默认的代理模型
iptables方式
客户端IP请求时,直接请求本地内核service ip,根据iptables的规则直接将请求转发到到各pod上,因为使用iptable NAT来完成转发,也存在不可忽视的性能损耗。另外,如果集群中存上万的Service/Endpoint,那么Node上的iptables rules将会非常庞大,性能还会再打折
iptables代理模式由Kubernetes 1.1版本引入,自1.2版本开始成为默认类型
ipvs方式
Kubernetes自1.9-alpha版本引入了ipvs代理模式,自1.11版本开始成为默认设置。客户端请求时到达内核空间时,根据ipvs的规则直接分发到各pod上。kube-proxy会监视Kubernetes Service对象和Endpoints,调用netlink接口以相应地创建ipvs规则并定期与Kubernetes Service对象和Endpoints对象同步ipvs规则,以确保ipvs状态与期望一致。访问服务时,流量将被重定向到其中一个后端Pod。与iptables类似,ipvs基于netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着ipvs可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs为负载均衡算法提供了更多选项
如果某个服务后端pod发生变化,标签选择器适应的pod又多一个,适应的信息会立即反映到apiserver上,而kube-proxy一定可以watch到etc中的信息变化,而将它立即转为ipvs或者iptables中的规则,这一切都是动态和实时的,删除一个pod也是同样的原理
备注
以上不论哪种,kube-proxy都通过watch的方式监控着apiserver写入etcd中关于Pod的最新状态信息,它一旦检查到一个Pod资源被删除了或新建了,它将立即将这些变化,反应再iptables 或 ipvs规则中,以便iptables和ipvs在调度Clinet Pod请求到Server Pod时,不会出现Server Pod不存在的情况。自k8s1.11以后,service默认使用ipvs规则,若ipvs没有被激活,则降级使用iptables规则
Service类型
ClusterIP类型
这种类型的Service只会得到虚拟IP和端口,只能在Kubernetes集群内部被访问,此模型是为默认类型
[root@master ~]# cat service_ClusterIP.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
name: tem-nginx
labels:
app: nginx
spec:
containers:
- name: test-nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
selector:
app: nginx
[root@master ~]# kubectl apply -f service_ClusterIP.yaml
deployment.apps/nginx created
service/nginx created
[root@master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 2d2h
nginx ClusterIP 10.1.220.108 <none> 80/TCP 54s
NodePort类型
这种类型的Service除了会得到虚拟IP和端口,Kubernetes还会在所有Node节点上为其分配端口。分配端口的值可以通过spec.ports[*].nodePort指定,或由Kubernetes在配置好的区间里分配(默认为 30000-32767)即可以从 Kubernetes集群通过虚拟IP:端口访问,也可以从集群外部通过Node节点的IP:nodePort 访问
[root@master ~]# cat service_NodePort.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
name: tem-nginx
labels:
app: nginx
spec:
containers:
- name: test-nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
selector:
app: nginx
[root@master ~]# kubectl apply -f service_NodePort.yaml
deployment.apps/nginx created
service/nginx created
[root@master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 2d2h
nginx NodePort 10.1.15.246 <none> 80:30232/TCP 5s
[root@master ~]# curl -I 192.168.207.131:30232
HTTP/1.1 200 OK
Server: nginx/1.25.3
Date: Fri, 01 Dec 2023 09:32:48 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 24 Oct 2023 13:46:47 GMT
Connection: keep-alive
ETag: "6537cac7-267"
Accept-Ranges: bytes
ExternalName类型
在Kubernetes(K8S),Service资源的ExternalName类型用于将服务映射到集群外部的名称,而不是通过Cluster IP或LoadBalancer IP。这对于将内部服务映射到外部服务的域名非常有用。ExternalName类型的Service不会创建任何Endpoint,而只是将服务的DNS记录配置为指定的外部名称。
适用于k8s集群内部容器访问外部资源,它没有selector,也没有定义任何的端口和Endpoint。以下Service 定义的是将prod名称空间中的my-service服务映射到my.database.example.com
[root@master ~]# cat service_ExternalName.yaml
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
# 创建prod命名空间
[root@master ~]# kubectl create ns prod
namespace/prod created
[root@master ~]# kubectl apply -f service_ExternalName.yaml
service/my-service created
# 查询prod命名空间的service
[root@master ~]# kubectl get svc -n prod
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service ExternalName <none> my.database.example.com <none> 8m8s
Ingress
Ingress :Ingress可以把进入到集群内部的请求转发到集群中的一些服务上,从而可以把服务映射到集群外部。Ingress 能把集群内Service 配置成外网能够访问的 URL,流量负载均衡,提供基于域名访问的虚拟主机等。
Ingress Controller:Ingress Controller是一个七层负载均衡调度器,客户端的请求先到达这个七层负载均衡调度器,由七层负载均衡器在反向代理到后端pod,常见的七层负载均衡器有nginx、traefik,以我们熟悉的nginx为例,假如请求到达nginx,会通过upstream反向代理到后端pod应用,但是后端pod的ip地址是一直在变化的,因此在后端pod前需要加一个service,这个service只是起到分组的作用,那么我们upstream只需要填写service地址即可
使用Ingress Controller代理k8s内部应用的流程
- 部署Ingress controller,我们ingress controller使用的是nginx
- 创建Pod应用,可以通过控制器创建pod
- 创建Service,用来分组pod
- 创建Ingress http,测试通过http访问应用
- 创建Ingress https,测试通过https访问应用
客户端通过七层调度器访问后端pod的方式
常见的版本发布方法
蓝绿部署:蓝绿部署,是采用两个分开的集群对软件版本进行升级的一种方式。它的部署模型中包括一个蓝色集群 A 和一个绿色集群 B,在没有新版本上线的情况下,两个集群上运行的版本是一致的,同时对外提供服务
流程:首先,从负载均衡器列表中删除集群 A,让集群 B 单独提供服务。然后,在集群 A 上部署新版本
接下来,集群 A 升级完毕后,把负载均衡列表全部指向 A,并删除集群 B,由 A 单独提供服务
在集群 B 上部署完新版本后,再把它添加回负载均衡列表中。这样,我们就完成了两个集群上所有机器的版本升级
红黑部署
红黑部署是Netflix采用的部署手段,Netflix的主要基础设施是在AWS上,它与蓝绿部署类似,红黑部署也是通过两个集群完成软件版本的升级
当前提供服务的所有机器都运行在红色集群 A 中,当需要发布新版本的时候,具体流程是这样的:
先在云上申请一个黑色集群 B,在 B 上部署新版本的服务;
等到 B 升级完成后,我们一次性地把负载均衡全部指向 B;
把 A 集群从负载均衡列表中删除,并释放集群 A 中所有机器。
这样就完成了一个版本的升级
对比;与蓝绿部署相比,红黑部署只不过是充分利用了云计算的弹性伸缩优势,从而获得了两个收益:一是,简化了流程;二是,避免了在升级的过程中,由于只有一半的服务器提供服务,而可能导致的系统过载问题
灰度发布
灰度发布,也被叫作金丝雀发布。与蓝绿部署、红黑部署不同的是,灰度发布属于增量发布方法。也就是说,服务升级的过程中,新旧版本会同时为用户提供服务
流程:在集群的一小部分机器上部署新版本,给一部分用户使用,以测试新版本的功能和性能;确认没有问题之后,再对整个集群进行升级。简单地说,灰度发布就是把部署好的服务分批次、逐步暴露给越来越多的用户,直到最终完全上线
之所以叫作灰度发布,是因为它介于黑与白之间,并不是版本之间的直接切换,而是一个平滑过渡的过程
优点在于如果前期出问题影响范围很小,相对用户体验也少;可以做到及时发现、及时调整问题,影响范围可控。但是采取这种模式对自动化以及运维监控能力的要求非常高。
滚动发布
滚动发布是指每次只升级一个或多个服务,升级完成后加入生产环境,不断执行这个过程,直到集群中的全部旧版本升级新版本
红色:正在更新的实例
蓝色:更新完成并加入集群的实例
绿色:正在运行的实例
这种部署方式相对于蓝绿部署,更加节约资源——它不需要运行两个集群、两倍的实例数。我们可以部分部署,例如每次只取出集群的20%进行升级,比较节约资源,但同时缺点也很明显:采用滚动发布方式部署时,没有一个确定OK的环境。如果使用蓝绿部署,我们能够清晰地知道老版本是OK的,而使用滚动发布,我们无法确定。并且一旦发布过程出现问题,需要回滚,回滚过程非常困难。
部署ingress
部署ingress controller
选择合适的版本,本文使用的是Kubernetes 1.23版本,Ingress Controller版本选择为v1.6.4
1.在所有节点上传包(ingress_controller_images_1.6.4.tar.gz,openresty-centos.tar.gz镜像,因为大陆无法访问docker官网,所以需要加载上传的镜像包)
docker load < ingress_controller_images_1.6.4.tar.gz
docker load < openresty-centos.tar.gz
2.切换master节点 上传yaml文件(deploy.yaml)
kubectl apply -f deploy.yaml
#获取 Kubernetes 集群中 ingress-nginx 命名空间下的所有 Pod 的列表
kubectl get pod -n ingress-nginx
# -n 参数后面跟着的是命名空间的名称。
成功回显
3 .部署两个版本服务,用于做发布测试,观察效果
1.创建V1版本
vi nginx_v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-v1
spec:
replicas: 1
selector:
matchLabels:
app: nginx
version: v1
template:
metadata:
labels:
app: nginx
version: v1
spec:
containers:
- name: nginx
image: "openresty/openresty:centos"
imagePullPolicy: IfNotPresent
ports:
- name: http
protocol: TCP
containerPort: 80
volumeMounts:
- mountPath: /usr/local/openresty/nginx/conf/nginx.conf
name: config
subPath: nginx.conf
volumes:
- name: config
configMap:
name: nginx-v1
---
apiVersion: v1
kind: ConfigMap
metadata:
labels:
app: nginx
version: v1
name: nginx-v1
data:
nginx.conf: |-
worker_processes 1;
events {
accept_mutex on;
multi_accept on;
use epoll;
worker_connections 1024;
}
http {
ignore_invalid_headers off;
server {
listen 80;
location / {
access_by_lua '
local header_str = ngx.say("nginx-v1")
';
}
}
}
---
apiVersion: v1
kind: Service
metadata:
name: nginx-v1
spec:
type: ClusterIP
ports:
- port: 80
protocol: TCP
name: http
selector:
app: nginx
version: v1
2.创建v2版本
kubectl apply -f nginx_v1.yaml
vi nginx_v2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-v2
spec:
replicas: 1
selector:
matchLabels:
app: nginx
version: v2
template:
metadata:
labels:
app: nginx
version: v2
spec:
containers:
- name: nginx
image: "openresty/openresty:centos"
imagePullPolicy: IfNotPresent
ports:
- name: http
protocol: TCP
containerPort: 80
volumeMounts:
- mountPath: /usr/local/openresty/nginx/conf/nginx.conf
name: config
subPath: nginx.conf
volumes:
- name: config
configMap:
name: nginx-v2
---
apiVersion: v1
kind: ConfigMap
metadata:
labels:
app: nginx
version: v2
name: nginx-v2
data:
nginx.conf: |-
worker_processes 1;
events {
accept_mutex on;
multi_accept on;
use epoll;
worker_connections 1024;
}
http {
ignore_invalid_headers off;
server {
listen 80;
location / {
access_by_lua '
local header_str = ngx.say("nginx-v2")
';
}
}
}
---
apiVersion: v1
kind: Service
metadata:
name: nginx-v2
spec:
type: ClusterIP
ports:
- port: 80
protocol: TCP
name: http
selector:
app: nginx
version: v2
创建Ingress指向V1版本
kubectl apply -f nginx_v2.yaml
vi nginx_ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: canary.example.com
http:
paths:
- path: / #配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/"
pathType: Prefix
backend: #配置后端服务
service:
name: nginx-v1
port:
number: 80
kubectl apply -f nginx_ingress.yaml
访问测试
基于 Header 的流量切分
进行灰度发布
vi nginx_canary.yaml
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-by-header: "Region"
nginx.ingress.kubernetes.io/canary-by-header-pattern: "cd|sz"
name: nginx-canary
spec:
rules:
- host: canary.example.com
http:
paths:
- path: / #配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/"
pathType: Prefix
backend: #配置后端服务
service:
name: nginx-v2
port:
number: 80
kubectl apply -f nginx_canary.yaml
根据访问IP的地址不同,访问的版本也不同
基于服务权重的流量切分
kubectl delete -f nginx_canary.yaml
vi nginx_canary.yaml
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: "10"
name: nginx-canary
spec:
rules:
- host: canary.example.com
http:
paths:
- path: / #配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/"
pathType: Prefix
backend: #配置后端服务
service:
name: nginx-v2
port:
number: 80
kubectl apply -f nginx_canary.yaml
kubectl get svc -n ingress-nginx
for i in $(seq 1 10);do curl -H "Host: canary.example.com" http://10.1.134.164;done
可以看到,有的访问的是v1版本,有的是访问v2更新版本,即为流量切分