Kubernetes 高级调度 01

发布于:2025-07-13 ⋅ 阅读:(22) ⋅ 点赞:(0)

一:初始化容器 InitContainer

      首先来看初始化容器,顾名思义,初始化容器是用来进行初始化操作的。很多情况下,程序的启动需要依赖各类配置、资源。但是又不能继承在原有的启动命令或者镜像当中,因为程序的镜像可能并没有加载配置命令,此时 InitContainer 就起了很大的作用。

1:InitContainer 的基本概念

      InitContainer 是 Kubernetes 的初始化容器(也可称之为 Init 容器),它是一种特殊的容器,在 Pod 内的应用容器启动之前运行,可以包括一些应用镜像中不存在的使用工具和安装脚本,用以在程序启动时进行初始化,比如创建文件、修改内核参数、等待依赖程序启动等。

      每个 Pod 中可以包含多个容器,同时 Pod 也可以有一个或多个先于应用程序启动的 Init 容器,在 Pod 定义中和 container 同级,按顺序逐个执行,当所有的 Init 容器运行完成时,Kubernetes 才会启动 Pod 内的普通容器。

Init 容器与普通的容器非常像,除了如下几点:

  • 他们总是运行到完成。
  • 上一个运行完成才会运行下一个。
  • 如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止,但是如果 Pod 对应的 restartPolicy 值为 Never,Kubernetes 则不会重新启动 Pod。

      为 Pod 设置 Init 容器需要在 Pod 的 spec 中添加 initContainer 字段,该字段和应用的 containers 属组同级相邻,配置方式和 containers 中的普通容器差不多,Init 容器支持应用容器的全部字段和特性,包括资源限制、数据卷和安全设置。但是 Init 容器对资源请求和限制的处理稍有不同,Init 容器不支持 lifecycle、livenessProbe、readinessProbe 和 startupProbe,因为他们必须在 Pod 就绪之前运行完成。

备注:
lifecycle 主要指的是容器在其运行环境中经历的不同阶段,以及 Kubernetes 如何管理和响应这些阶段的能力。Kubernetes 提供了容器生命周期钩子(Container Lifecycle Hooks),这是一种允许用户指定在容器生命周期的特定点上运行代码片段或命令的功能。

容器生命周期钩子主要包括以下两个部分:

  • PostStart:这个钩子在容器被创建之后立即调用,但需要注意的是,在钩子执行的时候容器内的主进程尚未启动。这可以用于执行一些初始化任务,比如建立文件系统的缓存目录或其他类型的准备动作。
  • PreStop:这个钩子在发送信号给容器内的主进程之前调用,这意味着这是在容器被终止之前可以执行一些清理工作的最后机会。它常被用来做一些资源释放的工作,比如关闭已经打开的数据库连接或网络连接等。

      在生产环境中,为了应用的安全和优化镜像的体积,业务镜像一般不会安装高危工具和并不常用的运维工具,比如 curl、sed、awk、python 或 dig 等,同时建议使用非 root 用户去启动容器。但是某些应用启动之前可能需要检查依赖的服务有没有成功运行,或者需要更高的权限去修改一些系统级的配置,而这些检测或配置更改都是一次性的,所以在制作业务镜像时没有必要为了一次配置的变更去安装一个配置工具,更没有必要因为使用一次高权限而把整个镜像改成以 root 身份运行。

       考虑到上述问题和需求,Kubernetes 引入了初始化容器的概念,Init 容器具有与应用容器分离的单独镜像,其启动相关代码具有如下优势:

  • Init 容器可以包含安装过程中应用容器中不存在的实用工具或个性化代码。
  • Init 容器可以安全地运行这些工具,避免这些工具导致应用镜像的安全性降低。
  • Init 容器可以以 root 身份运行,执行一些高权限命令。
  • Init 容器相关操作执行完成后就会退出,不会给业务容器带来安全隐患。

      由于 Init 容器必须在应用容器启动之前运行完成,因此 Init 容器提供了一种机制来阻塞或延迟应用容器的启动,直到满足一组先决条件,Pod 内的所有应用容器才会并行启动。

示例 1-- 延迟指定时间后启动

创建一个 pod,initContainers 指定初始化容器,command:["sh","-c","sleep15"] 表示初始化容器需要休眠 15 秒。

[root@k8s-master~]#cat init01.ym1
apiVersion:v1
kind:Pod
metadata:
creationTimestamp:nul1
labels:
run:initc01
name:initc01
spec:
terminationGracePeriodSeconds:0
containers:
image:nginx:1.7.9
imagePul1Policy:IfNotPresent
name:n1
resources:{}
initContainers:
- name:initc01
image:nginx:1.7.9
imagePul1Policy:IfNotPresent
command:["sh","-","sleep 15"]
dnsPolicy:ClusterFirst
restartPolicy:Never
status:{}
[root@k8s-master ~]#kubectl create -f init01.ym1

不断地查看 Pod 启动状态,15 秒后会发现 Pod 开始启动

[root@k8s-master ~]#kubectl get pod
NAME READY STATUS RESTARTS AGE
initc01 0/1 Init:0/1 0 2s
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
initc01 0/1 Init:0/1 0 8s
[root@k8s-master ~]#kubectl get pod
NAME READY STATUS RESTARTS AGE
initc01 0/1 Init:0/1 0 9s
[root@k8s-master ~]#kubectl get pod
NAME READY STATUS RESTARTS AGE
initc01 0/1 Init:0/1 0 16s
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
initc01 0/1 PodInitializing 0 17s
[root@k8s-master ~]#kubectl get pod
NAME READY STATUS RESTARTS AGE
initc01 0/1 PodInitializing 0 18s
[root@k8s-master~]#kubectl get pod
NAME READY STATUS RESTARTS AGE
initc011/1 Running 0 18s

示例 2-- 使用初始化容器修改内核参数

      在容器里修改内核参数,实际上修改的就是物理机的内核参数,为了安全性,一般不允许在容器里修改内核参数,Seccomp 代表安全计算(Secure Computing)模式,seccomp 控制了容器能做哪些操作,添加 securityContext 参数之后就可以修改内核参数了。

      创建一个 pod,initContainers 初始化容器里的 securityContext:privileged:true 表示该容器具有特权,可以执行命令 "sh","-","/sbin/sysctl -w vm.swappiness=0",swappiness 设置为 0 表示尽量少使用 swap 内存。

[root@k8s-master~]#cat init02.ym1
apiVersion: v1
kind:Pod
metadata:
creationTimestamp: nul1
labels:
run:initc02
name:initc02
spec:
terminationGracePeriodSeconds:0
containers:
image:nginx:1.7.9
imagePul1Policy:IfNotPresent
name:n1
resources:{}
initContainers:
-name:initc02
image:alpine
imagePul1Policy:IfNotPresent
command:["sh","-","/sbin/sysct1-w vm.swappiness=0"]
securityContext:
privileged:true
dns Policy:ClusterFirst
restartPolicy: Never
status:{}
[root@k8s-master ~]# kubectl apply -f init02.ym1
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
initc 1/1 Running 0 7m7s

查看 Pod 中的容器

[root@k8s-master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED
NODE READINESS GATES
initc02 1/1 Running 0 5m18s 10.244.85.195 k8s-node01 <none>
<none>

查看 k8s-node01 机器上的 swappiness 值是否为 0

[root@k8s-node01~]#cat/proc/sys/vm/swappiness
0

示例 3-- 等待依赖服务启动

有时某些服务需要依赖其他组件才能启动,比如后端应用需要数据库启动之后,应用才能正常启动,此时需要检测数据库实例是否正常,等待数据库可以正常使用时,在启动后端应用,此时可以使用初始化容器进行控制。

(1)创建第一个 Pod


      该 Pod 内要依赖两个条件才能启动,一个是利用 busybox 容器检测 redis-service 服务是否生成,第二个是检测 mysql-server 服务是否生成。

[root@k8s-master~]#cat myapp. yam1
apiVersion: v1
kind:Pod
metadata:
name: nginx
1abe1s:
name:nginx
spec:
containers:
name:nginx
image:nginx:1.7.9
ports:
containerPort:86
initContainers:
-name:init-redis
image:busybox:1.28
command:['sh','-c','until nslookup redis-server; do echo waiting for redis; sleep 2;done;']
- name:init-mysq1
image:busybox:1.28
command:['sh','-c','until nslookup mysql-server; do echo waiting for mysql;sleep 2;done;']
[root@k8s-master~]# kubectl create -f myapp.yam1
[root@k8s-master~]#kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 0/1 Init:0/20 8s

(2)创建第一个被依赖的 service
[root@k8s-master ~]#cat redis-deployment.yam1
apiVersion: apps/v1
kind:Deployment
metadata:
labels:
app:redis
name:redis
spec:
replicas:1
selector:
matchLabels:
app: redis
template:
metadata:
1abels:
app:redis
spec:
containers:
image:redis:5.0
imagePul1Policy:IfNotPresent
name:redis
apiVersion: v1
kind:Service
metadata:
labels:
app:redis
name:redis-service
spec:
ports:
-port:6379
protocol:TCP
targetPort:6379
selector:
app: redis
type: NodePort
[root@k8s-master ~]#kubect1 create-f redis-deployment.yam1
deployment.apps/redis created
service/redis-service created
[root@k8s-master ~]#kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d20h
redis-service NodePort 10.104.3.33 <none> 6379:32077/TCP 84s
[root@k8s-master ~]# kubect1 get pod
NAME READY STATUS RESTARTS AGE
nginx 0/1 Init:1/2 0 2m20s
redis-56bcf55554-mtrqx1/1 Running 0 8s

(3)创建第二个被依赖的 service
[root@k8s-master ~]#cat mysql-deployment.yam1
apiVersion:apps/v1
kind:Deployment
metadata:
labels:
app:mysql
name: mysq1
spec:
replicas: 1
selector:
matchLabels:
app:mysq1
template :
metadata:
labels:
app:mysql
spec:
containers:
env:
- name: MYSQL_ROOT_PASSWORD
value:'moonfdd'
image:'mysq1:8.0'
imagePu11Policy:IfNotPresent
name:mysql
volumeMounts:
-mountPath:/var/1ib/mysq1
name:volv
volumes:
hostPath:
path:/root/k8s/moonfdd/mysq1/var/1ib/mysq1
type:DirectoryOrCreate
name:volv
apiVersion:v1
kind:Service
metadata:
labels:
app:mysql
name:mysql-service
spec:
ports:
-port:3306
protocol:TCP
targetPort:3306
selector:
app:mysql
type:NodePort
[root@k8s-master ~]#kubectl create-f mysql-deployment .yam1
deployment.apps/mysq1 created
service/mysq1-service created
[root@k8s-master ~]# kubect1 get svc
NAME	TYPE	CLUSTER-IP	EXTERNAL-IP	PORT(S)	AGE
kubernetes	ClusterIP	10.96.0.1	<none>	443/TCP	6d20h
mysql-service	NodePort	10.101.159.200	<none>	3306:31452/TCP	6s
redis-service	NodePort	10.104.3.33	<none>	6379:32077/TCP	6m12s
[root@k8s-master ~]# kubectl get pod
NAME	READY	STATUS	RESTARTS	AGE
mysql-dc77b5fb8-zczrz	0/1	Running	2 (23s ago)	39s
nginx	1/1	Running	0	8m57s
redis-56bcf55554-mtrqx	1/1	Running	0	6m45s

      到这里可以发现,当把 redis-service 和 mysql-service 两个服务创建出来后,nginx 的 Pod 就正常运行了。

 

2.pause 容器

      在 Kubernetes 中,pause 容器并不是指暂停容器的执行,而是指一个特殊的辅助容器,它被用于每个 Pod 以帮助实现网络命名空间。pause 容器是 Kubernetes 中每个 Pod 的第一个容器,它的主要作用是作为 Pod 内所有其他容器共享网络命名空间的基础。

       每个 Kubernetes Pod 都有一个 pause 容器,它是 Pod 的第一个容器,也是唯一必须运行的容器。即使 Pod 中的其他容器都停止了,只要 pause 容器还在运行,Pod 就不会被 Kubernetes 认为是完全停止的。

(1)pause 容器的作用

  • 网络命名空间:pause 容器为 Pod 内的所有容器提供了一个共享的网络命名空间。这意味着 Pod 内的所有容器都共享同一个 IP 地址和端口空间,从而使得它们可以直接通过localhost或者其他指定方式相互通信。
  • 网络接口:pause 容器负责设置 Pod 的网络接口,使得 Pod 内的容器能够通过这个接口访问外部网络。
  • 稳定性:如果 Pod 中的所有声明的容器都被删除或停止,pause 容器将保持运行,从而保证 Pod 的网络命名空间不会丢失。这也意味着 Pod 的网络配置仍然保留,直到整个 Pod 被删除。
  • 生命周期管理:pause 容器是一个非常简单的容器,其镜像只包含一个命令 /pause,该命令实际上是一个无限循环,保持容器处于运行状态。它不执行任何业务逻辑,仅仅是作为 Pod 网络基础设施的一部分。

(2)使用 pause 容器的好处

  • 简化了 Pod 内的网络配置。
  • 提高了 Pod 网络的一致性。
  • 减少了管理 Pod 网络复杂性的开销。

      总的来说,pause 容器对于实现 Kubernetes Pod 的网络功能至关重要,它是 Kubernetes 网络模型的一个核心组成部分。

(3)Pause 容器实现

Pod 里的多个容器怎么去共享网络?下面是个例子:

       比如说现在有一个 Pod,其中包含了一个容器 A 和一个容器 B,它们两个就要共享 Network Namespace。在 Kubernetes 里的解法是这样的:它会在每个 Pod 里,额外起一个 Infra(基础)container 小容器来共享整个 Pod 的 Network Namespace。

      Infra container 是一个非常小的容器,大概 700KB 左右,是一个 C 语言写的、永远处于 "暂停" 状态的容器。由于有了这样一个 Infra container 之后,其他所有容器都会通过 Join Namespace 的方式加入到 Infra container 的 Network Namespace 中。

     所以说一个 Pod 里面的所有容器,它们看到的网络视图是完全一样的。即:它们看到的网络设备、IP 地址、Mac 地址等等,跟网络相关的信息,其实全是一份,这一份都来自于 Pod 第一次创建的这个 Infra container。在 Pod 里面,一定有一个 IP 地址,是这个 Pod 的 Network Namespace 对应的地址,也是这个 Infra container 的 IP 地址。所以大家看到的都是同一份,而其他所有网络资源,都是一个 Pod 一份,并且被 Pod 中的所有容器共享。这就是 Pod 的网络实现方式。

      由于需要有一个相当于中间的容器存在,所以整个 Pod 里面,必然是 Infra container 第一个启动。并且整个 Pod 的生命周期是等同于 Infra container 的生命周期的,与容器 A 和 B 是无关的。这也是为什么在 Kubernetes 里面,它是允许去单独更新 Pod 里的某一个镜像的,即:做这个操作,整个 Pod 不会重建,也不会重启,这是非常重要的一个设计。

查看 nginx 的 Pod 运行在的主机

[root@k8s-master~]#kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysq1-7f5d669b6c-7xtqj1/1 Running e 8m35s10.244.85.208k8s-node01 <none> <none>
nginx 1/1 Running e 23m 10.244.85.286 k8s-node01 <none> <none>
redis-56bcf55554-mtrqx 1/1 Running e 21m 10.244.58.197k8s-node02 <none> <none>

在 k8s-node01 主机上查看 Pod 中的 pause 容器

[root@k8s-node01~]#docker ps| grep nginx
8feed68c83d1 a99a39d070bf "/docker-entrypoint..." 54 seconds ago Up 53 seconds k8s_nginx_nginx_default_8a0b9ac3-3cab-4ae6-94e2-624046f5aa87_0
9ee9ad88890b registry.aliyuncs.com/google_containers/pause:3.6 "/pause" 54 seconds ago Up 53 seconds k8s_POD_nginx_default_8a0b9ac3-3cab-4ae6-94e2-624046f5aa87_0

二:临时容器 Ephemeral Containers

       在生产环境中,为了优化镜像的体积和提高镜像的安全性,并不会在容器中安装太多高危工具。比如 curl、wget、dig 以及常用的 net-tools 等。这样做虽然提高了镜像的安全性,但是也带来了一些不便,比如无法查看容器内的进程情况、无法查看容器内的链接情况、服务出了问题无法很方便的进行排查等。因为上述操作并非经常使用,所以我们并没有必要从一开始就安装这些工具,但是等到用他们的时候再安装也是一件很麻烦的事情,那么我们该如何处理这个问题呢?

      为了解决这类问题,在 1.16 版本后,Kubernetes 引入了 EphemeralContainers 的概念,可以不用安装第三方工具即可实现在线 Debug 操作。

1:临时容器的概念

      临时容器与其他容器的不同之处在于,临时容器是被临时添加到 Pod 上,用于在线调试应用的,他永远不会自动重启,因此不适用与构建应用程序,临时容器的声明和普通容器类似,但是临时容器没有端口配置,因此不能使用像 ports、livenessprobe、readnessProbe、resources 这样的字段,当然,临时容器只是用来调试程序的,它的状态不会影响其他正常容器,所以它并不需要这些字段配置。

      临时容器使用 API 中一种特殊的 EphemeralContainers 处理器进行创建,而不是直接添加到 pod.spec 字段,因此无法使用 kubectl edit 来添加临时容器,与常规容器一样,将临时容器添加到 Pod 后,将不能更改或删除临时容器,但是当添加了临时容器的 Pod 重启后,临时容器就会被销毁。

       因为临时容器是为了调试程序而设计的,所以在添加临时容器时,最好使用一个包含所有常用工具的镜像进行创建。当业务容器崩溃或容器镜像不包含调试工具而导致 kubectl exec 不可用时,临时容器对于交互故障排查和在线 Debug 很有用。尤其是在使用像不包含任何 shell 和其他工具的 destroless 镜像作为基础镜像时,虽然可以减少攻击面和漏洞,但对于问题的排查会变得尤为棘手,此时临时容器就可以发挥很大的作用,带来诸多便利性。

2:临时容器的使用

(1)创建一个 tomcat 的资源清单

[root@k8s-master ~]#cat pod-tomcat.yam
apiVersion: v1
kind:Pod
metadata:
name:tomcat-test
namespace:default
1abels:
app: tomcat
spec:
containers:
-name: tomcat-java
ports:
containerPort:8080
image:kubeguide/tomcat-app:v1
imagePul1Policy:IfNotPresent

(2)创建 Pod

[root@k8s-master ~]#kubect1 apply-f pod-tomcat.yam1
[root@k8s-master ~]# kubect1 get pod
NAME READY STATUS RESTARTS AGE
tomcat-test 1/1 Running 0 64s

(3)为 tomcat 的 pod 创建临时容器

[root@k8s-master~]#kubect1 debug-it tomcat-test--image=busybox:1.28--target=tomcat-java

备注:

tomcat-test:pod 的名字 --target=tomcat-java,pod 中源容器的名称 --image=busybox:1.28: 该 pod 中临时容器使用的镜像

执行后稍等一会,能够进入 tomcat 容器的交互界面,如下所示:

/#ps-ef| grep tomcat
1 root -Djava.uti1.1ogging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.t1s.ephemeralDHKeySize=2048 -Djava.endorsed.dirs/usr/1ocal/tomcat/endorsed -classpath /usr/1ocal/tomcat/bin/bootstrap.jar:/usr/1ocal/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdi=/usr/1ocal/tomcat/temp org.apache.catalina.startup.Bootstrap start
41 root 0:00 grep tomcat

(4)查看 tomcat-test 这个 pod 是否已经有临时容器

[root@k8s-master ~]#kubectl describe pods tomcat-test
Containers:
tomcat-java:
ContainerID:docker://d37b1f054d9c206df7847d3c51b51cb208e7b142a492597851888f8c83eda1fa
Image: kubeguide/tomcat-app:v1
Image ID: docker-pullable://kubeguide/tomcat-app@sha256:7a9193c2e5c6c74b4ad49a8abbf75373d4ab76c8f8db87672dc 526b96ac69ac4
Port: 8080/TCP
Host Port: 0/TCP
State: Running
Started: Sun,09Jul202317:37:57+0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts: /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-4ggbn(ro)
Ephemeral Containers:
debugger-czh68:
Container ID: docker://eaf268329bb4f26545b4ddbdfdc4361bcfb2906ec7a86d3a466c104ba8a837d5
Image: busybox:1.28
Image docker-pullable://busyboxasha256:141c253bc4c3fdoa201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
Port: <none>
Host Port: <none>
State: Terminated
Reason : Completed
Exit Code: 0
Started: Sun,09Jul 202317:38:49+0800
Finished: Sun,09Jul 2023 17:41:09+0800

三:自动扩缩容 HPA

      在集群安装的过程中,我们可以安装一个叫 Metrics Server 的组件,该组件在集群中负责采集 Pod 和 Node 的度量值指标,比如 Pod 的 CPU、内存使用率和节点的内存、CPU 使用率,而且安装的 Dashboard 可以展示 CPU、内存信息也是依靠 Metrics Server 的。当然,该组件不仅仅是用来展示数据的,还可以使用 Metrics Server 提供的数据结合 Kubernetes 的 HPA 功能实现 Pod 的自动扩缩容。

1:什么是 HPA

      HPA(Horizontal Pod Autoscaler,水平 Pod 自动伸缩器)可以根据观察到的 CPU、内存使用率或自定义度量标准来自动扩展或缩容 Pod 的数量。注意 HPA 不适用于无法缩放的对象,比如 DaemonSet。

      HPA 控制器会定期调整 RC 或 Deployment 的副本数,以使观察到的平均 CPU 利用率与用户指定的目标相匹配。

      HPA 需要 Metrics Server 获取度量指标,如果已经部署了 Metrics Server,本节的实践部就分无须再次安装 Metrics Server。如果没有安装 Metrics Server,可以参考其他实验文档自行部署。

2:HPA 的工作原理

  • HPA 根据观察到的 CPU、内存使用率或自定义度量标准来自动扩展或缩容 Pod 的数量。
  • HPA 控制器会定期调整 RC(Replication Controller)或 Deployment 的副本数,以使观察到的平均 CPU 利用率(或其他度量标准)与用户指定的目标相匹配。
  • HPA 需要 Metrics Server 获取度量指标,Metrics Server 负责采集 Pod 和 Node 的度量值指标,如 CPU、内存使用率等。

3:HPA 的工作流程

  • 配置 HPA:使用 kubectl autoscale 命令为 Deployment、ReplicaSet 或 StatefulSet 等资源创建 HPA 对象,并指定目标 CPU 利用率、最小副本数和最大副本数。
  • 度量指标采集:Metrics Server 定期采集 Pod 和 Node 的度量值指标,并将这些数据提供给 HPA 控制器。
  • 伸缩决策:HPA 控制器根据当前 Pod 的度量指标和目标利用率进行比较,如果当前利用率高于目标利用率,则增加 Pod 副本数;如果当前利用率低于目标利用率,则减少 Pod 副本数(在最小副本数和最大副本数之间调整)
  • Pod 伸缩:Kubernetes 根据 HPA 控制器的决策自动调整 Pod 的副本数,以实现自动扩缩容。

4:HPA 的应用场景

  • 应对流量波动:在 web 服务中,流量可能会随时间变化而波动。HPA 可以根据流量变化自动调整 Pod 副本数,以确保服务的稳定性和响应速度。
  • 资源优化:通过自动扩缩容,HPA 可以在保证服务质量的同时最大化资源利用率,降低运营成本。

5:HPA 实践 -- 实现 web 服务器的自动伸缩特性

      在生产环境中,总会有一些意想不到的事情发生,比如公司网站流量突然升高,此时之前创建的 Pod 已不足以支撑所有的访问,而运维人员也不可能 24 小时守着业务服务,这时就可以通过配置 HPA,实现负载过高的情况下自动扩容 Pod 副本数以分摊高并发的流量,当流量恢复正常后,HPA 会自动缩减 Pod 的数量。

本节将测试实现一个 web 服务器的自动伸缩特性,具体步骤如下:

(1)首先用 Deployment 启动一个 Nginx 服务(须配置 requests 参数):
[root@k8s-master ~]#vim nginx-deployment.yam1
apiVersion: apps/v1
kind:Deployment
metadata:
name:nginx-server
labels:
name:nginx-server
spec:
replicas:2
selector:
matchLabels:
app:nginx
template:
metadata:
labels:
app:nginx
spec:
containers:
name:nginx
resources:
requests:
cpu:10m
image:nginx:1.7.9
ports:
name:nginx
containerPort:80
[root@k8s-master ~]#kubectl create-fnginx-deployment .yam1

(2)配置 nginx-server 的 Service
[root@k8s-master ~]#kubectexpose dep1oyment nginx-server --port=80

(3)使用 kubectl autoscale 命令创建 HPA

[root@k8s-master~]#kubect1 atoscale deployment nginx-server--pu-percent=10--min=1--max=10

       此 HPA 将根据 CPU 的使用率自动增加和减少副本数量,上述设置的是 CPU 使用率超过 10%(--cpu-percent 参数指定)就会增加 Pod 的数量,以保持所有 Pod 的平均 CPU 利用率为 10%,允许最大的 Pod 数量为 10(--max),最少的 Pod 数为 1(--min)。

(4)查看当前 HPA 的状态
因为未对其发送任何请求,所以当前 CPU 使用率为 0%
[root@k8s-master~]#kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-server Deployment/nginx-server 0%/10% 1 10 2 3m41s

备注:如果看不到 TARGET 的 CPU 利用率,可以多等十几秒。

(5)查看当前 Nginx 的 Service 地址
[root@k8s-master ~]#kubectl get service -n default
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 20h
nginx-server ClusterIP 10.107.217.171 <none> 80/TCP 43s

(6)压力测试(打开一个新的终端)


新开一个终端,使用一个 "死" 循环或其他压测工具模拟访问该 Service,从而增加该 Pod 的负载

[root@k8s-master~]#while true;do wget-q-0- http://10.107.217.171>/dev/nul1;done

备注:

  • -q 是不输出 wget 的头信息。
  • -0-(大写字母 0)选项表示将下载的内容输出到标准输出(通常是终端),而不是保存到文件。

(7)查看 HPA 状态


等待一分钟左右,再次查看 HPA,可以看到 Pod 的 CPU 已经升高。

[root@k8s-master ~]# kubect1 get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-server Deployment/nginx-server 55%/10%1 10 2 3m51s

(8)再次查看 Pod,可以看到已经扩容(Pod 副本增加到了 10 个)
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-server-6ddcfd4c8f-2k2nb 1/1 Running0 5m46s
nginx-server-6ddcfd4c8f-42wfb 1/1 Running 0 84s
nginx-server-6ddcfd4c8f-8wt72 1/1 Running 0 5m46s
nginx-server-6ddcfd4c8f-bw7hj 1/1 Running 0 90s
nginx-server-6ddcfd4c8f-f4s58 1/1 Running0 90s
nginx-server-6ddcfd4c8f-gvd5r 1/1 Running 0 114s
nginx-server-6ddcfd4c8f-1597f 1/1 Running0 90s
nginx-server-6ddcfd4c8f-1hr5z 1/1 Running 0 84s
nginx-server-6ddcfd4c8f-q9d4h 1/1 Running 0 114s
nginx-server-6ddcfd4c8f-tjn97 1/1 Running0 90s
(8) 停止压力测试

Ctrl + C 终止终端的测试,等待一分钟左右,再次查看 HPA 状态

[root@k8s - master ~]# kubectl get hpa
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
nginx - server Deployment/nginx - server 2%/10%    1         10        10         6m49s
[root@k8s - master ~]# kubectl get hpa
NAME          REFERENCE                TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
nginx - server Deployment/nginx - server 0%/10%    1         10        10         7m8s

(9) 查看 pod 的副本数
[root@k8s - master ~]# kubectl get pod
NAME                        READY   STATUS    RESTARTS   AGE
nginx - server - 6ddcfd4c8f - 8wt72   1/1     Running   0          11m

备注:
需要多等待几分钟后,才能看到缩容后的 pod

(10) 清除
kubectl delete -f nginx - deployment.yaml

kubectl delete -f mysql - deployment.yaml
kubectl delete -f redis - deployment.yaml
kubectl delete -f init01.yml
kubectl delete -f init02.yml

网站公告

今日签到

点亮在社区的每一天
去签到