Kubernetes高级功能

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更新版本,即为流量切分 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值