Kubernetes 高级调度 01

发布于:2025-07-12 ⋅ 阅读:(17) ⋅ 点赞:(0)

一、初始化容器(InitContainer):应用启动前的 "预备军"

在 Kubernetes 集群中,Pod 作为最小部署单元,往往需要在主容器启动前完成一系列准备工作。例如,配置文件生成、依赖服务检查、内核参数调整等。这些操作若直接嵌入主容器镜像,会导致镜像体积膨胀、安全性降低,甚至引发启动逻辑混乱。初始化容器(InitContainer)的出现,正是为了解决这一痛点。

1.1 核心概念与特性

InitContainer 是一种特殊的容器,它在 Pod 内所有主容器启动前运行,专门承担初始化任务。与普通容器相比,它具有以下独特特性:

  • 顺序执行机制:多个 Init 容器按定义顺序依次运行,前一个执行完成后,下一个才会启动,确保初始化操作的有序性。
  • 强依赖特性:所有 Init 容器必须成功执行完毕,主容器才能启动。若任一 Init 容器失败,Kubernetes 会根据 Pod 的restartPolicy决定是否重启 Pod(Never策略下不会重启)。
  • 资源隔离性:拥有独立的镜像和生命周期,可包含主容器中不存在的工具(如curlsed),且支持以 root 权限运行,避免主容器镜像引入安全风险。
  • 无健康检查:不支持livenessProbereadinessProbe等健康检查机制,因它必须在 Pod 就绪前完成任务。

从本质上讲,InitContainer 为应用启动提供了 "预处理阶段",通过将初始化逻辑与业务逻辑解耦,既保证了主容器镜像的纯净性,又提升了启动过程的可控性。

1.2 典型应用场景与实战案例

场景 1:延迟启动 —— 控制应用启动时机

在分布式系统中,部分服务需要等待依赖组件完成初始化(如数据库初始化)后再启动。通过 InitContainer 的sleep命令可实现精准延迟。

配置示例(init01.yaml)

yaml

apiVersion: v1
kind: Pod
metadata:
  name: initc01
  labels:
    run: initc01
spec:
  containers:
  - name: n1
    image: nginx:1.7.9
    imagePullPolicy: IfNotPresent
  initContainers:
  - name: initc01
    image: nginx:1.7.9
    command: ["sh", "-c", "sleep 15"]  # 延迟15秒
  restartPolicy: Never

执行与验证

bash

# 创建Pod
kubectl create -f init01.yaml

# 观察启动状态(15秒内处于Init状态)
kubectl get pod initc01
# 输出示例:
# NAME      READY   STATUS     RESTARTS   AGE
# initc01   0/1     Init:0/1   0          8s

# 15秒后状态变为Running
kubectl get pod initc01
# 输出示例:
# NAME      READY   STATUS    RESTARTS   AGE
# initc01   1/1     Running   0          19s

通过此案例,可清晰看到 InitContainer 如何阻塞主容器启动,直到延迟任务完成。

场景 2:内核参数调整 —— 突破容器权限限制

容器默认运行在非特权模式,无法修改宿主机内核参数(如vm.swappiness)。但通过 InitContainer 的privileged权限设置,可临时获取高级权限完成系统配置。

配置示例(init02.yaml)

yaml

apiVersion: v1
kind: Pod
metadata:
  name: initc02
  labels:
    run: initc02
spec:
  containers:
  - name: n1
    image: nginx:1.7.9
  initContainers:
  - name: initc02
    image: alpine
    command: ["sh", "-c", "/sbin/sysctl -w vm.swappiness=0"]  # 调整内存交换策略
    securityContext:
      privileged: true  # 开启特权模式
  restartPolicy: Never

执行与验证

bash

# 创建Pod
kubectl apply -f init02.yaml

# 查看Pod运行节点
kubectl get pod initc02 -o wide
# 输出示例:
# NAME      READY   STATUS    RESTARTS   AGE   IP            NODE
# initc02   1/1     Running   0          5m    10.244.85.195 k8s-node01

# 在节点上验证参数已修改
ssh k8s-node01 "cat /proc/sys/vm/swappiness"
# 输出:0

此案例中,InitContainer 以特权模式运行,成功将宿主机的vm.swappiness设为 0(表示尽量不使用交换分区),而主容器仍以普通权限运行,兼顾了安全性与功能性。

场景 3:依赖服务检查 —— 确保应用启动环境就绪

后端服务(如 Web 应用)常依赖数据库、缓存等组件,需等待依赖服务可用后再启动。InitContainer 可通过nslookup等工具检测服务状态,实现条件阻塞。

步骤 1:创建依赖服务检查的 Pod(myapp.yaml)

yaml

apiVersion: v1
kind: Pod
metadata:
  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-service; do echo waiting for redis; sleep 2; done;']
  - name: init-mysql
    image: busybox:1.28
    command: ['sh', '-c', 'until nslookup mysql-server; do echo waiting for mysql; sleep 2; done;']

步骤 2:创建 Redis 服务(redis-deployment.yaml)

yaml

# 部署Redis
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:5.0
---
# 暴露Redis服务
apiVersion: v1
kind: Service
metadata:
  name: redis-service
spec:
  ports:
  - port: 6379
    targetPort: 6379
  selector:
    app: redis
  type: NodePort

步骤 3:创建 MySQL 服务(mysql-deployment.yaml)

yaml

# 部署MySQL
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: 'moonfdd'
        volumeMounts:
        - mountPath: /var/lib/mysql
          name: volv
      volumes:
      - name: volv
        hostPath:
          path: /root/k8s/moonfdd/mysql/var/lib/mysql
          type: DirectoryOrCreate
---
# 暴露MySQL服务
apiVersion: v1
kind: Service
metadata:
  name: mysql-service
spec:
  ports:
  - port: 3306
    targetPort: 3306
  selector:
    app: mysql
  type: NodePort

执行与验证

bash

# 创建应用Pod(此时依赖服务未就绪)
kubectl create -f myapp.yaml
kubectl get pod nginx
# 输出:STATUS为Init:0/2(等待2个Init容器完成)

# 创建Redis服务后查看
kubectl create -f redis-deployment.yaml
kubectl get pod nginx
# 输出:STATUS为Init:1/2(第一个Init容器完成)

# 创建MySQL服务后查看
kubectl create -f mysql-deployment.yaml
kubectl get pod nginx
# 输出:STATUS为Running(所有依赖检查通过,主容器启动)

此案例完美展示了 InitContainer 如何通过服务发现机制(nslookup)等待依赖组件,确保应用在环境就绪后启动,避免了 "启动即崩溃" 的问题。

1.3 技术原理:InitContainer 的执行流程

InitContainer 的工作机制可拆解为以下步骤:

  1. Pod 调度阶段:Kubernetes 调度器将 Pod 分配到目标节点后,首先启动 Init 容器。
  2. 顺序执行控制:kubelet 按initContainers列表顺序启动容器,通过监控容器状态(Completed)确保执行顺序。
  3. 资源隔离保障:Init 容器与主容器共享 Pod 的网络和存储命名空间,但拥有独立的进程空间,任务完成后立即退出。
  4. 主容器启动触发:当所有 Init 容器状态为Succeeded时,kubelet 触发主容器启动流程。

这一流程通过将初始化逻辑与业务逻辑分离,既保证了主容器的轻量性,又提升了启动过程的可靠性。

二、临时容器(Ephemeral Containers):在线调试的 "应急通道"

生产环境中,为减少攻击面,容器镜像通常会移除bashnet-tools等调试工具。这导致服务异常时,无法通过kubectl exec进入容器排查问题。临时容器(Ephemeral Containers)作为 Kubernetes 1.16 版本引入的特性,为在线调试提供了灵活解决方案。

2.1 核心概念与特性

临时容器是一种临时附加到运行中 Pod 的容器,专为调试目的设计,具有以下特点:

  • 动态附加:无需重启 Pod 即可添加,不影响主容器运行状态。
  • 工具丰富:可使用包含psnetstat等工具的镜像(如busybox),弥补主容器工具缺失的问题。
  • 无持久化配置:不支持端口暴露、健康检查等字段,仅用于临时诊断。
  • 生命周期绑定:Pod 重启后自动销毁,不会残留配置。

kubectl exec相比,临时容器的优势在于:即使主容器没有shbash,仍能通过独立镜像进入 Pod 命名空间,实现进程查看、网络分析等操作。

2.2 实战案例:诊断 Tomcat 应用异常

假设某 Tomcat 应用频繁崩溃,但镜像中未安装ps等工具,无法直接调试。通过临时容器可快速定位问题。

步骤 1:创建 Tomcat Pod(pod-tomcat.yaml)

yaml

apiVersion: v1
kind: Pod
metadata:
  name: tomcat-test
spec:
  containers:
  - name: tomcat-java
    image: kubeguide/tomcat-app:v1
    ports:
    - containerPort: 8080

步骤 2:添加临时容器进行调试

bash

# 创建Pod
kubectl apply -f pod-tomcat.yaml

# 附加临时容器(使用busybox镜像)
kubectl debug -it tomcat-test --image=busybox:1.28 --target=tomcat-java
# 参数说明:
# --target:指定要调试的主容器(共享其进程命名空间)
# -it:开启交互式终端

步骤 3:在临时容器中执行诊断命令

bash

# 查看Tomcat进程
ps -ef | grep tomcat
# 输出示例:
# 1 root ... org.apache.catalina.startup.Bootstrap start(主进程)

# 检查网络连接
netstat -tuln
# 输出:可查看8080端口监听状态

# 查看文件系统
ls /usr/local/tomcat/conf
# 输出:可检查配置文件是否存在异常

步骤 4:验证临时容器状态

bash

# 查看Pod详情
kubectl describe pod tomcat-test
# 在Ephemeral Containers部分可看到添加的临时容器

通过此案例,无需重建 Pod 或修改镜像,即可快速诊断应用问题,极大提升了运维效率。

2.3 技术限制与最佳实践

临时容器虽灵活,但存在以下限制:

  • 不支持资源限制配置,可能占用节点过多资源。
  • 无法在kubectl edit中修改,需通过kubectl debug或 API 创建。
  • 仅适用于调试,不可用于业务功能扩展。

最佳实践建议:

  • 使用固定版本的调试镜像(如busybox:1.28),避免依赖最新版导致兼容性问题。
  • 调试完成后手动清理临时容器(或等待 Pod 重启自动销毁)。
  • 结合 RBAC 权限控制,限制临时容器的使用权限,防止未授权调试。

三、自动扩缩容(HPA):应对流量波动的 "弹性引擎"

在微服务架构中,流量波动是常态(如电商大促、突发新闻事件)。手动调整 Pod 副本数不仅响应滞后,还可能因人为操作失误导致服务中断。Kubernetes 的水平 Pod 自动扩缩容(Horizontal Pod Autoscaler,HPA)通过监控指标动态调整副本数,实现了服务弹性伸缩的自动化。

3.1 核心概念与工作原理

HPA 的核心功能是根据预设指标(如 CPU 使用率、内存使用率)自动调整 Pod 副本数,其工作原理如下:

  1. 指标采集:依赖 Metrics Server 组件采集 Pod 和节点的资源使用数据(如 CPU 使用率)。
  2. 伸缩决策:HPA 控制器定期(默认 15 秒)对比当前指标与目标值,计算所需副本数。
  3. 执行调整:通过修改 Deployment/ReplicaSet 的replicas字段,实现 Pod 扩缩容。

计算公式:期望副本数 = 当前副本数 × (当前指标值 / 目标指标值)

例如:当前副本数为 2,目标 CPU 使用率为 10%,实际使用率为 55%,则期望副本数 = 2 × (55%/10%) = 11(受限于max参数时取最大值)。

3.2 实战案例:Nginx 服务的自动扩缩容

以 Nginx 服务为例,配置 HPA 实现基于 CPU 使用率的自动扩缩容。

步骤 1:部署 Metrics Server(若未安装)

bash

# 下载组件清单
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# 验证部署
kubectl get pod -n kube-system | grep metrics-server

步骤 2:创建 Nginx Deployment(nginx-deployment.yaml)

yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-server
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 10m  # 必须配置requests,否则HPA无法采集CPU使用率

步骤 3:暴露 Nginx 服务

bash

kubectl expose deployment nginx-server --port=80

步骤 4:创建 HPA 配置

bash

kubectl autoscale deployment nginx-server --cpu-percent=10 --min=1 --max=10
# 参数说明:
# --cpu-percent=10:目标CPU使用率为10%
# --min=1:最小副本数1
# --max=10:最大副本数10

步骤 5:验证 HPA 配置

bash

kubectl get hpa
# 输出示例:
# NAME           REFERENCE                 TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
# nginx-server   Deployment/nginx-server   0%/10%    1         10        2          3m

步骤 6:模拟流量压力

bash

# 新开终端执行压力测试(循环访问Nginx服务)
SVC_IP=$(kubectl get svc nginx-server -o jsonpath='{.spec.clusterIP}')
while true; do wget -q -O- http://$SVC_IP > /dev/null; done

步骤 7:观察 HPA 扩容效果

bash

# 等待1-2分钟后查看HPA状态
kubectl get hpa
# 输出示例:
# NAME           REFERENCE                 TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
# nginx-server   Deployment/nginx-server   55%/10%   1         10        10         6m

# 查看Pod副本数
kubectl get pod | grep nginx-server
# 输出:10个Running状态的Pod

步骤 8:停止压力测试,观察缩容效果

bash

# 终止压力测试终端(Ctrl+C)
kubectl get hpa
# 输出示例(几分钟后):
# NAME           REFERENCE                 TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
# nginx-server   Deployment/nginx-server   0%/10%    1         10        1          15m

通过此案例,HPA 成功在流量高峰时扩容以分担压力,流量低谷时缩容以节省资源,实现了资源与性能的动态平衡。

3.3 高级配置:自定义指标与扩缩容策略

除 CPU 和内存外,HPA 还支持基于自定义指标(如请求数、队列长度)进行扩缩容,需结合 Prometheus 等监控系统实现。例如,基于每秒请求数(RPS)的配置:

yaml

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx-custom-metrics
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx-server
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: 100  # 目标RPS为100

此外,还可通过behavior字段配置扩缩容策略(如冷却时间),避免频繁调整:

yaml

behavior:
  scaleDown:
    stabilizationWindowSeconds: 300  # 缩容前等待5分钟,避免短时间波动

四、pause 容器:Pod 网络的 "基石"

在解析上述高级特性时,我们多次提到 Pod 的命名空间共享,而这一功能的实现依赖于 Kubernetes 中的一个特殊容器 ——pause 容器。它虽不直接参与业务逻辑,却是 Pod 网络模型的核心支撑。

4.1 核心作用

pause 容器是每个 Pod 中第一个启动的容器,主要承担以下职责:

  • 网络命名空间载体:为 Pod 内所有容器提供共享的网络命名空间(IP、端口等),使容器间可通过localhost通信。
  • 生命周期锚点:Pod 的生命周期与 pause 容器绑定,即使主容器退出,只要 pause 容器运行,Pod 就不会被视为终止。
  • 网络稳定性保障:负责初始化 Pod 的网络接口,确保主容器重启时网络配置不丢失。

4.2 实现原理

pause 容器通过以下机制实现网络共享:

  1. 启动优先级:kubelet 在创建 Pod 时,首先启动 pause 容器,生成网络命名空间。
  2. 命名空间加入:主容器、Init 容器、临时容器通过Join Namespace机制加入 pause 容器的网络命名空间。
  3. 资源隔离:所有容器共享网络资源(IP、端口),但拥有独立的进程空间,确保安全性。

验证 pause 容器存在

bash

# 查看Nginx Pod所在节点
kubectl get pod nginx -o wide
# 登录节点,查看pause容器
ssh k8s-node01 "docker ps | grep pause"
# 输出示例:
# 9ee9ad88890b ... registry.aliyuncs.com/google_containers/pause:3.6 "/pause"


网站公告

今日签到

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