1. 在容器中获取 Pod 的IP

通过环境变量来实现,该环境变量直接引用 resource 的状态字段,示例如下:

  1. apiVersion: v1
  2. kind: ReplicationController
  3. metadata:
  4. name: world-v2
  5. spec:
  6. replicas: 3
  7. selector:
  8. app: world-v2
  9. template:
  10. metadata:
  11. labels:
  12. app: world-v2
  13. spec:
  14. containers:
  15. - name: service
  16. image: test
  17. env:
  18. - name: POD_IP
  19. valueFrom:
  20. fieldRef:
  21. fieldPath: status.podIP
  22. ports:
  23. - name: service
  24. containerPort: 777

容器中可以直接使用 POD_IP 环境变量获取容器的 IP。

2. 指定容器的启动参数

我们可以在 Pod 中为容器使用 command 为容器指定启动参数:

  1. command: ["/bin/bash","-c","bootstrap.sh"]

看似很简单,使用数组的方式定义,所有命令使用跟 Dockerfile 中的 CMD 配置是一样的,但是有一点不同的是,bootsttap.sh 必须具有可执行权限,否则容器启动时会出错。

3. 让Pod调用宿主机的docker能力

我们可以想象一下这样的场景,让 Pod 来调用宿主机的 docker 能力,只需要将宿主机的 docker 命令和 docker.sock 文件挂载到 Pod 里面即可,如下:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: busybox-cloudbomb
  5. spec:
  6. containers:
  7. - image: busybox
  8. command:
  9. - /bin/sh
  10. - "-c"
  11. - "while true; \
  12. do \
  13. docker run -d --name BOOM_$(cat /dev/urandom | tr -cd 'a-f0-9' | head -c 6) nginx ; \
  14. done"
  15. name: cloudbomb
  16. volumeMounts:
  17. - mountPath: /var/run/docker.sock
  18. name: docker-socket
  19. - mountPath: /bin/docker
  20. name: docker-binary
  21. volumes:
  22. - name: docker-socket
  23. hostPath:
  24. path: /var/run/docker.sock
  25. - name: docker-binary
  26. hostPath:
  27. path: /bin/docker

参考:Architecture Patterns for Microservices in Kubernetes

4. 使用Init container初始化应用配置

Init container可以在应用程序的容器启动前先按顺序执行一批初始化容器,只有所有Init容器都启动成功后,Pod才算启动成功。看下下面这个例子(来源:kubernetes: mounting volume from within init container - Stack Overflow):

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: init
  5. labels:
  6. app: init
  7. annotations:
  8. pod.beta.kubernetes.io/init-containers: '[
  9. {
  10. "name": "download",
  11. "image": "axeclbr/git",
  12. "command": [
  13. "git",
  14. "clone",
  15. "https://github.com/mdn/beginner-html-site-scripted",
  16. "/var/lib/data"
  17. ],
  18. "volumeMounts": [
  19. {
  20. "mountPath": "/var/lib/data",
  21. "name": "git"
  22. }
  23. ]
  24. }
  25. ]'
  26. spec:
  27. containers:
  28. - name: run
  29. image: docker.io/centos/httpd
  30. ports:
  31. - containerPort: 80
  32. volumeMounts:
  33. - mountPath: /var/www/html
  34. name: git
  35. volumes:
  36. - emptyDir: {}
  37. name: git

这个例子就是用来再应用程序启动前首先从GitHub中拉取代码并存储到共享目录下。

关于Init容器的更详细说明请参考 init容器

5. 使容器内时间与宿主机同步

我们下载的很多容器内的时区都是格林尼治时间,与北京时间差8小时,这将导致容器内的日志和文件创建时间与实际时区不符,有两种方式解决这个问题:

  • 修改镜像中的时区配置文件
  • 将宿主机的时区配置文件/etc/localtime使用volume方式挂载到容器中

第二种方式比较简单,不需要重做镜像,只要在应用的yaml文件中增加如下配置:

  1. volumeMounts:
  2. - name: host-time
  3. mountPath: /etc/localtime
  4. readOnly: true
  5. volumes:
  6. - name: host-time
  7. hostPath:
  8. path: /etc/localtime

6. 在Pod中获取宿主机的主机名、namespace等

这条技巧补充了第一条获取 podIP 的内容,方法都是一样的,只不过列出了更多的引用字段。

参考下面的 pod 定义,每个 pod 里都有一个 {.spec.nodeName} 字段,通过 fieldRef 和环境变量,就可以在Pod中获取宿主机的主机名(访问环境变量MY_NODE_NAME)。

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: dapi-test-pod
  5. spec:
  6. containers:
  7. - name: test-container
  8. image: busybox
  9. command: [ "/bin/sh", "-c", "env" ]
  10. env:
  11. - name: MY_NODE_NAME
  12. valueFrom:
  13. fieldRef:
  14. fieldPath: spec.nodeName
  15. - name: MY_POD_NAME
  16. valueFrom:
  17. fieldRef:
  18. fieldPath: metadata.name
  19. - name: MY_POD_NAMESPACE
  20. valueFrom:
  21. fieldRef:
  22. fieldPath: metadata.namespace
  23. - name: MY_POD_IP
  24. valueFrom:
  25. fieldRef:
  26. fieldPath: status.podIP
  27. - name: HOST_IP
  28. valueFrom:
  29. fieldRef:
  30. fieldPath: status.hostIP
  31. - name: MY_POD_SERVICE_ACCOUNT
  32. valueFrom:
  33. fieldRef:
  34. fieldPath: spec.serviceAccountName
  35. restartPolicy: Never

7. 配置Pod使用外部DNS

修改kube-dns的使用的ConfigMap。

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: kube-dns
  5. namespace: kube-system
  6. data:
  7. stubDomains: |
  8. {"k8s.com": ["192.168.10.10"]}
  9. upstreamNameservers: |
  10. ["8.8.8.8", "8.8.4.4"]

upstreamNameservers 即使用的外部DNS,参考:Configuring Private DNS Zones and Upstream Nameservers in Kubernetes

8. 创建一个CentOS测试容器

有时我们可能需要在Kubernetes集群中创建一个容器来测试集群的状态或对其它容器进行操作,这时候我们需要一个操作节点,可以使用一个普通的CentOS容器来实现。YAML文件见manifests/test/centos.yaml

  1. apiVersion: extensions/v1beta1
  2. kind: Deployment
  3. metadata:
  4. name: test
  5. labels:
  6. app: test
  7. spec:
  8. replicas: 1
  9. template:
  10. metadata:
  11. labels:
  12. app: test
  13. spec:
  14. containers:
  15. - image: sz-pg-oam-docker-hub-001.tendcloud.com/library/centos:7.2.1511
  16. name: test
  17. command: ["/bin/bash","-c","while true; do sleep 1000; done"]
  18. imagePullPolicy: IfNotPresent

即使用一个while循环保证容器启动时拥有一个前台进程。

也可以直接使用kubectl run的方式来创建:

  1. kubectl run --image=sz-pg-oam-docker-hub-001.tendcloud.com/library/centos:7.2.1511 --command '/bin/bash -c "while true;do sleep 1000;done"' centos-test

9. 强制删除一直处于Terminating状态的Pod

有时候当我们直接删除Deployment/DaemonSets/StatefulSet等最高级别的Kubernetes资源对象时,会发现有些改对象管理的Pod一直处于Terminating而没有被删除的情况,这时候我们可以使用如下方式来强制删除它:

一、使用kubectl中的强制删除命令

  1. kubectl delete pod $POD_ID --force --grace-period=0

如果这种方式有效,那么恭喜你!如果仍然无效的话,请尝试下面第二种方法。

二、直接删除etcd中的数据

这是一种最暴力的方式,我们不建议直接操作etcd中的数据,在操作前请确认知道你是在做什么。

假如要删除default namespace下的pod名为pod-to-be-deleted-0,在etcd所在的节点上执行下面的命令,删除etcd中保存的该pod的元数据:

  1. ETCDCTL_API=3 etcdctl del /registry/pods/default/pod-to-be-deleted-0

这时API server就不会再看到该pod的信息。

如何使用etcdctl查看etcd中包括的kubernetes元数据,请参考:使用etcdctl访问kubernetes数据