k8s 的 pod 是最小的程序运行单位。一个 pod 可能包含多个容器,但多个容器只能运行在同一个 node,不会运行在多个 node。
因为容器的最佳实践是,一个容器只运行一个程序(除了这个程序本身会创建子进程)。比如一个容器往往不会同时跑多个不同的 web 服务。
但有时候你需要额外的容器来配合主容器运作,比如一个同步外部文件的程序、日志收集的程序、某种本地 agent 等等。因此 k8s 设计了 pod 这种结构,来包裹多个容器并实现容器间的网络、文件共享(通过 Volumn)等。
k8s 会为集群中的 pod 生成一个软件定义的网络。每个 pod 在这个网络中会被分配一个唯一的 IP,通过这个 IP pod 间可以互相访问,即使它们在不同的 node。k8s 的 namespace 不会隔离这种访问。kubectl get pods -o wide
可以看到 pod IP。
apiVersion: v1
kind: Pod
metadata:
name: kubia-manual
spec:
containers:
- image: luksa/kubia
name: kubia
ports:
- containerPort: 8080
protocol: TCP
其中 ports 部分只有文档作用,k8s 不会根据它做逻辑。
kubectl explain 命令可以描述 YAML 中的字段,如:
kubectl explain pods
kubectl explain pods.spec
kubectl create -f kubia-manual.yaml
kubectl get po kubia-manual -o yaml
kubectl get po kubia-manual -o json
# 单容器
kubectl logs kubia-manual
# 多容器用 -c 指定容器
kubectl logs kubia-manual -c kubia
# 如果前面的 pod 挂了,想看它的日志
kubectl logs kubia-manual --previous
Log 可能会滚动(rotated),这里 有详细信息。
一种方法是创建 service 将 pod 的服务公开出来。可以使用 Kubernetes: Resource: ReplicationController 中提及的 kubectl expose
命令将 rc / deployment expose 出来。
另外一种方法是使用 kubectl port-forward
,比如:
kubectl port-forward kubia-manual 8888:8080
kubectl 会在本地起一个进程侦听 8888 端口,并将访问这个端口的请求转发到 kubia-manual pod 中的 8080 端口。这种方法非常适合用于调试。
Label 可以用来筛选 pod:
apiVersion: v1
kind: Pod
metadata:
name: kubia-manual-v2
labels:
creation_method: manual
env: prod
spec:
containers:
- image: luksa/kubia
name: kubia
ports:
- containerPort: 8080
protocol: TCP
查看 pod 的 label:
$ kubectl get po --show-labels
NAME READY STATUS RESTARTS AGE LABELS
kubia-manual 1/1 Running 0 16m <none>
kubia-manual-v2 1/1 Running 0 2m creat_method=manual,env=prod
kubia-zxzij 1/1 Running 0 1d run=kubia
$ kubectl get po -L creation_method,env
NAME READY STATUS RESTARTS AGE CREATION_METHOD ENV
kubia-manual 1/1 Running 0 16m <none> <none>
kubia-manual-v2 1/1 Running 0 2m manual prod
kubia-zxzij 1/1 Running 0 1d <none> <none>
还可以:
Label 与 selector 是配合使用的。可以给 node 设置 label,再要求 pod 被调度到符合条件的 pod:
$ kubectl label node gke-kubia-85f6-node-0rrx gpu=true
node "gke-kubia-85f6-node-0rrx" labeled
$ kubectl get nodes -l gpu=true
NAME STATUS AGE
gke-kubia-85f6-node-0rrx Ready 1d
注意下面的 nodeSelector
:
apiVersion: v1
kind: Pod
metadata:
name: kubia-gpu
spec:
nodeSelector:
gpu: "true"
containers:
- image: luksa/kubia
name: kubia
如果你想把 pod 调度到指定的某一个 node,可以使用 k8s 分配给每个 node 的 label kubernetes.io/hostname
。但这种做法不被推荐,如果该 node 挂掉了,这个 pod 也就无法运行了。
Annotation 类似 label 也是键值对,但是不参与筛选,而是放入较长文本被程序所使用。Label 的值能放入的文本较少。
$ kubectl get po kubia-zxzij -o yaml
apiVersion: v1
kind: pod
metadata:
annotations:
kubernetes.io/created-by: |
{"kind":"SerializedReference", "apiVersion":"v1",
"reference":{"kind":"ReplicationController", "namespace":"default", ...
先向 pod 中的各容器发 SIGTERM,并把该 pod 从 service 对应的 pod 列表中删除。各容器主进程应该开始 graceful stop。等待一段 grace period 后(默认 15s),如果容器还没停止,k8s 会向容器发 SIGKILL 杀掉容器进程。
k8s 通过 liveness probe 机制,让 pod 中的程序可以告知 k8s 自己是否正常。在 pod 的 spec 中可以定义:
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 15
Web 服务常用的惯例是在 /health
接口中返回成功失败。好的实践是,/health
接口不止是简单返回 HTTP 200,而且应该 检查外部依赖是否正常,比如依赖的数据库等等。
Liveness probe 的检查过程应该足够 轻量。避免过多、频繁的资源浪费;不要在其中实现重试逻辑,比如检查数据库连接失败后,再检查一次。
Pod 中有 readinessProbe 机制,用于判断一个 pod 是否可以正常提供服务。Pod 只有 ready 了,service 才会把请求转到该 pod。
readinessProbe 提供的探测方式与 livenessProbe 一致。
你可能看到这种情况:
$ kubectl get po kubia-2r1qb
NAME READY STATUS RESTARTS AGE
kubia-2r1qb 0/1 Running 0 2m
Running 表示 liveness probe 通过,READY 0/1 表示 readinessProbe 没有通过。
见 k8s 官方文档。
通过 ReplicationController 或者 Deployment 创建的 pod 为 managed。直接创建的 pod 为 unmanaged。
Unmanaged pod 在 node fail 时,k8s 不会自动帮其重新创建;managed 则会。