原文链接:https://overcast.blog/13-kubernetes-tricks-you-didnt-know-647de6364472 ## 使用 PreStop 优雅关闭 Pod yaml apiVersion: v1 kind: Pod metadata: name: graceful-shutdown-example spec: containers: - name: sample-container image: nginx lifecycle: preStop: exec: command: ["/bin/sh", "-c", "sleep 30 && nginx -s quit"] PreStop 允许 Pod 在终止前执行一个命令或者是脚本,使用它就可以在应用退出前释放一些资源,确保应用可以优雅退出。 比如可以在 Nginx 的 Pod 退出前将当前的请求执行完毕。

使用临时容器调试 Pod

临时容器可以不修改一个运行的容器的前提下调试容器,可以很方便的调试一些生产环境的 bug,可以避免重启应用。
  1. kubectl alpha debug -it podname --image=busybox --target=containername
生产环境谨慎使用,只有在当前环境下无法排查问题的时候才使用。

基于自定义的 Metrics 自动扩容Pod

kubernetes 是提供了 HPA 机制可以跟进 CPU 内存等标准数据进行自动扩缩容,但有时需要根据自定义的数据进行扩缩容。 比如某个接口的延迟、队列大小等。
  1. apiVersion: autoscaling/v2beta2
  2. kind: HorizontalPodAutoscaler
  3. metadata:
  4. name: custom-metric-hpa
  5. spec:
  6. scaleTargetRef:
  7. apiVersion: apps/v1
  8. kind: Deployment
  9. name: your-application
  10. minReplicas: 1
  11. maxReplicas: 10
  12. metrics:
  13. - type: Pods
  14. pods:
  15. metric:
  16. name: your_custom_metric
  17. target:
  18. type: AverageValue
  19. averageValue: 10

用 Init Containers 配置启动脚本

Kubernetes的Init Container,是容器编排中的关键组件,为应用初始化阶段提供了强大支撑。它是Pod中完成初始化任务的专用容器。

执行顺序机制

Init Container是串行执行的。每个Init Container必须在前一个成功完成后才能启动,而普通容器是并行启动的。这种机制在处理复杂依赖时特别有用。

初始化容器可以在应用容器启动前运行,可以使用它来初始化应用需要的配置、等待依赖的服务启动完成等工作:
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: myapp-pod
  5. spec:
  6. containers:
  7. - name: myapp-container
  8. image: myapp
  9. initContainers:
  10. - name: init-myservice
  11. image: busybox
  12. command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
比如这个初始化容器会等待 myservice 可用后才会启动应用。 看个例子:一个需要等待Redis就绪的Web应用。
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: web-app
  5. spec:
  6. initContainers:
  7. - name: wait-redis
  8. image: redis:alpine
  9. command: ['sh', '-c', 'until redis-cli -h redis-svc ping; do sleep 2; done;']
  10. containers:
  11. - name: web
  12. image: nginx:alpine
需要注意的是如果初始化容器会阻塞应用启动,所以要避免在初始化容器里执行耗时操作。

运行特性

Init Container只运行一次。完成任务就退出,不会重启。普通容器则会一直运行,必要时还会按策略重启。

有个坑要注意:Init Container执行失败会导致Pod重启,已完成的Init Container也会重新执行。所以写代码时要考虑重复执行的情况。

资源分配策略

Init Container的资源分配有个特殊规则:取所有Init Container中的最大值。
  1. spec:
  2. initContainers:
  3. - name: init-cache
  4. resources:
  5. requests:
  6. memory: "512Mi"
  7. - name: init-db
  8. resources:
  9. requests:
  10. memory: "256Mi"
这个配置中,Pod实际申请的内存是512Mi,而不是两个容器的总和。

应用场景

实战中,Init Container主要用在这些地方:
  1. 前置准备 配置文件生成、数据库初始化、目录权限设置
  2. 服务检查 确认依赖服务是否就绪,比如数据库连接性检查
  3. 安全配置 证书分发、密钥初始化

实战经验

  1. 保持专注:一个Init Container只做一件事
  2. 要可重试:设计时考虑重复执行的情况
  3. 加超时限制:防止无限等待卡住整个Pod
  4. 资源预留:按实际需求设置,避免资源不足
实战案例:部署ElasticSearch集群时,需要修改系统参数max_map_count。用Init Container来处理就很优雅:
  1. initContainers:
  2. - name: sysctl
  3. image: busybox
  4. command: ["sysctl", "-w", "vm.max_map_count=262144"]
  5. securityContext:
  6. privileged: true

Node 亲和性调度

当需要将某些应用部署到硬件配置较高的节点时(比如需要 SSD 硬盘),就可以使用节点亲和性来部署应用:
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: with-node-affinity
  5. spec:
  6. containers:
  7. - name: with-node-affinity
  8. image: nginx
  9. affinity:
  10. nodeAffinity:
  11. requiredDuringSchedulingIgnoredDuringExecution:
  12. nodeSelectorTerms:
  13. - matchExpressions:
  14. - key: disktype
  15. operator: In
  16. values:
  17. - ssd
这个 Pod 会被部署到有这个 <font style="color:rgb(30, 107, 184);">disktype=ssd</font> 标签的 节点上。

动态配置:ConfigMap 和 Secrets

ConfigMap 和 Secrets可以动态注入到 Pod 中,避免对这些配置硬编码。 ConfigMap 适合非敏感的数据,Secrets 适合敏感的数据。
  1. # ConfigMap Example
  2. apiVersion: v1
  3. kind: ConfigMap
  4. metadata:
  5. name: app-config
  6. data:
  7. config.json: |
  8. {
  9. "key": "value",
  10. "databaseURL": "http://mydatabase.example.com"
  11. }
  12. # Pod Spec using ConfigMap
  13. apiVersion: v1
  14. kind: Pod
  15. metadata:
  16. name: myapp-pod
  17. spec:
  18. containers:
  19. - name: myapp-container
  20. image: myapp
  21. volumeMounts:
  22. - name: config-volume
  23. mountPath: /etc/config
  24. volumes:
  25. - name: config-volume
  26. configMap:
  27. name: app-config
这样在应用中就可以通过这路径 <font style="color:rgb(30, 107, 184);">/etc/config/config.json</font> 读取数据了。
当然也可以把这些数据写入到环境变量中。
以上这些个人技巧用的最多的是:
  1. 临时容器调试 Pod,特别是业务容器缺少一些命令时。
  2. Init Container 等待依赖的服务启动完成。
  3. Node 亲和性调度。
  4. ConfigMap 是基础操作了。