概述
Kubernetes 使用 Pod 来管理容器,每个 Pod 可以包含一个或多个紧密关联的容器。
Pod 是一组紧密关联的容器集合,它们共享 PID、IPC、Network 和 UTS namespace,是 Kubernetes 调度的基本单位。Pod 内的多个容器共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。
在 Kubernetes 中,所有对象都使用 manifest(yaml 或 json)来定义,比如一个简单的 nginx 服务可以定义为 nginx.yaml,它包含一个镜像为 nginx 的容器:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
静态pod
在Kubernetes集群中除了我们经常使用到的普通的 Pod 外,还有一种特殊的 Pod,叫做Static Pod,就是我们说的静态 Pod,静态 Pod 有什么特殊的地方呢?
静态 Pod 直接由特定节点上的kubelet进程来管理,不通过 master 节点上的apiserver。无法与我们常用的控制器Deployment或者DaemonSet进行关联,它由kubelet进程自己来监控,当pod崩溃时重启该pod,kubelete也无法对他们进行健康检查。静态 pod 始终绑定在某一个kubelet,并且始终运行在同一个节点上。 kubelet会自动为每一个静态 pod 在 Kubernetes 的 apiserver 上创建一个镜像 Pod(Mirror Pod),因此我们可以在 apiserver 中查询到该 pod,但是不能通过 apiserver 进行控制(例如不能删除)。
创建静态 Pod 有两种方式:配置文件和 HTTP 两种方式
配置文件
配置文件就是放在特定目录下的标准的 JSON 或 YAML 格式的 pod 定义文件。用kubelet –pod-manifest-path=
比如我们在 node01 这个节点上用静态 pod 的方式来启动一个 nginx 的服务。我们登录到node01节点上面,可以通过下面命令找到kubelet对应的启动配置文件
$ systemctl status kubelet
配置文件路径为:
$ /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
打开这个文件我们可以看到其中有一条如下的环境变量配置: Environment="KUBELET_SYSTEM_PODS_ARGS=–pod-manifest-path=/etc/kubernetes/manifests –allow-privileged=true"
所以如果我们通过kubeadm的方式来安装的集群环境,对应的kubelet已经配置了我们的静态 Pod 文件的路径,那就是/etc/kubernetes/manifests,所以我们只需要在该目录下面创建一个标准的 Pod 的 JSON 或者 YAML 文件即可:
如果你的 kubelet 启动参数中没有配置上面的–pod-manifest-path参数的话,那么添加上这个参数然后重启 kubelet 即可。
[[email protected] node01 ~] $ cat <<EOF >/etc/kubernetes/manifest/static-web.yaml
apiVersion: v1
kind: Pod
metadata:
name: static-web
labels:
app: static
spec:
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
EOF
通过 HTTP 创建静态 Pods
kubelet 周期地从–manifest-url=参数指定的地址下载文件,并且把它翻译成 JSON/YAML 格式的 pod 定义。此后的操作方式与–pod-manifest-path=相同,kubelet 会不时地重新下载该文件,当文件变化时对应地终止或启动静态 pod。
pod容器分类
- Infrastructure Container:基础容器
- 维护整个Pod网络空间
- InitContainers:初始化容器
- 先于业务容器开始执行
- Containers:业务容器
- 并行启动
镜像拉取策略(imagePullPolicy)
- IfNotPresent:默认值,镜像在宿主机上不存在时才拉取
- Always:每次创建 Pod 都会重新拉取一次镜像
- Never: Pod 永远不会主动拉取这个镜像
apiVersion: v1
kind: Pod
metadata:
name: foo
namespace: awesomeapps
spec:
imagePullSecrets:
- name: myregistrykey
containers:
image: janedoe/awesomeapp:v1
- name: foo
imagePullPolicy: IfNotPresent
```
## 重启策略(restartPolicy)
- Always:当容器终止退出后,总是重启容器,默认策略。
- OnFailure:当容器异常退出(退出状态码非0)时,才重启容器。
- Never:当容器终止退出,从不重启容器。
```yaml
apiVersion: v1
kind: Pod
metadata:
name: foo
namespace: awesomeapps
spec:
containers:
- name: foo
image: janedoe/awesomeapp:v1
restartPolicy: Always
资源限制
Kubernetes 通过 cgroups 限制容器的 CPU 和内存等计算资源,包括 requests(请求,调度器保证调度到资源充足的 Node 上,如果无法满足会调度失败)和 limits(上限)等:
spec.containers[].resources.limits.cpu:CPU 上限,可以短暂超过,容器也不会被停止
spec.containers[].resources.limits.memory:内存上限,不可以超过;如果超过,容器可能会被终止或调度到其他资源充足的机器上
spec.containers[].resources.limits.ephemeral-storage:临时存储(容器可写层、日志以及 EmptyDir 等)的上限,超过后 Pod 会被驱逐
spec.containers[].resources.requests.cpu:CPU 请求,也是调度 CPU 资源的依据,可以超过
spec.containers[].resources.requests.memory:内存请求,也是调度内存资源的依据,可以超过;但如果超过,容器可能会在 Node 内存不足时清理
spec.containers[].resources.requests.ephemeral-storage:临时存储(容器可写层、日志以及 EmptyDir 等)的请求,调度容器存储的依据
比如 nginx 容器请求 30% 的 CPU 和 56MB 的内存,但限制最多只用 50% 的 CPU 和 128MB 的内存:
apiVersion: v1
kind: Pod
metadata:
labels:
app: nginx
name: nginx
spec:
containers:
- image: nginx
name: nginx
resources:
requests:
cpu: "300m"
memory: "56Mi"
limits:
cpu: "1"
memory: "128Mi"
健康检查(Probe)
为了确保容器在部署后确实处在正常运行状态,Kubernetes 提供了两种探针(Probe)来探测容器的状态:
LivenessProbe:探测应用是否处于健康状态,如果不健康则删除并重新创建容器。
ReadinessProbe:探测应用是否启动完成并且处于正常服务状态,如果不正常则不会接收来自 Kubernetes Service 的流量,即将该Pod从Service的endpoint中移除。
Kubernetes 支持三种方式来执行探针:
exec:在容器中执行一个命令,如果 命令退出码 返回 0 则表示探测成功,否则表示失败
tcpSocket:对指定的容器 IP 及端口执行一个 TCP 检查,如果端口是开放的则表示探测成功,否则表示失败
httpGet:对指定的容器 IP、端口及路径执行一个 HTTP Get 请求,如果返回的 状态码 在 [200,400) 之间则表示探测成功,否则表示失败
apiVersion: v1
kind: Pod
metadata:
labels:
app: nginx
name: nginx
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: http
livenessProbe:
httpGet:
path: /
port: 80
httpHeaders:
- name: X-Custom-Header
value: Awesome
initialDelaySeconds: 15
timeoutSeconds: 1
readinessProbe:
exec:
command:
- cat
- /usr/share/nginx/html/index.html
initialDelaySeconds: 5
timeoutSeconds: 1
- name: goproxy
image: gcr.io/google_containers/goproxy:0.1
ports:
- containerPort: 8080
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
pod hook
容器生命周期钩子(Container Lifecycle Hooks)监听容器生命周期的特定事件,并在事件发生时执行已注册的回调函数。支持两种钩子:
postStart: 容器创建后立即执行,注意由于是异步执行,它无法保证一定在 ENTRYPOINT 之前运行。如果失败,容器会被杀死,并根据 RestartPolicy 决定是否重启
preStop:容器终止前执行,常用于资源清理。如果失败,容器同样也会被杀死
而钩子的回调函数支持两种方式:
exec:在容器内执行命令,如果命令的退出状态码是 0 表示执行成功,否则表示失败
httpGet:向指定 URL 发起 GET 请求,如果返回的 HTTP 状态码在 [200, 400) 之间表示请求成功,否则表示失败
postStart
以下示例中,定义了一个Nginx Pod,其中设置了PostStart钩子函数,即在容器创建成功后,写入一句话到/usr/share/message文件中
apiVersion: v1
kind: Pod
metadata:
name: hook-demo1
spec:
containers:
- name: hook-demo1
image: nginx
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
preStop
优雅删除资源对象:
当用户请求删除含有 pod 的资源对象时(如Deployment等),K8S 为了让应用程序优雅关闭(即让应用程序完成正在处理的请求后,再关闭软件),K8S提供两种信息通知:
默认:K8S 通知 node 执行docker stop命令,docker 会先向容器中PID为1的进程发送系统信号SIGTERM,然后等待容器中的应用程序终止执行,如果等待时间达到设定的超时时间,或者默认超时时间(30s),会继续发送SIGKILL的系统信号强行 kill 掉进程。
使用 pod 生命周期(利用PreStop回调函数),它执行在发送终止信号之前。
默认所有的优雅退出时间都在30秒内。kubectl delete 命令支持 –grace-period=
强制删除一个 pod 是从集群状态还有 etcd 里立刻删除这个 pod。 当 Pod 被强制删除时, api 服务器不会等待来自 Pod 所在节点上的 kubelet 的确认信息:pod 已经被终止。在 API 里 pod 会被立刻删除,在节点上, pods 被设置成立刻终止后,在强行杀掉前还会有一个很小的宽限期。
以下示例中,定义了一个Nginx Pod,其中设置了PreStop钩子函数,即在容器退出之前,优雅的关闭 Nginx:
apiVersion: v1
kind: Pod
metadata:
name: hook-demo2
spec:
containers:
- name: hook-demo2
image: nginx
lifecycle:
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"]
调度约束
nodeName用于将Pod调度到指定的Node名称上
apiVersion: v1
kind: Pod
metadata:
name: pod-example
labels:
app: nginx
spec:
nodeName: 192.168.31.65
containers:
- name: nginx
image: nginx:1.15
nodeSelector用于将Pod调度到匹配Label的Node上
apiVersion: v1
kind: Pod
metadata:
name: pod-example
spec:
nodeSelector: env_role: dev
containers:
- name: nginx
image: nginx:1.15
Pod 时区
很多容器都是配置了 UTC 时区,与国内集群的 Node 所在时区有可能不一致,可以通过 HostPath 存储插件给容器配置与 Node 一样的时区:
apiVersion: v1
kind: Pod
metadata:
name: sh
namespace: default
spec:
containers:
- image: alpine
stdin: true
tty: true
volumeMounts:
- mountPath: /etc/localtime
name: time
readOnly: true
volumes:
- hostPath:
path: /etc/localtime
type: ""
name: time
故障排查
状态值 | 描述 |
---|---|
Pending | Pod创建已经提交到Kubernetes。但是,因为某种原因而不能顺利创建。例如下 载镜像慢,调度不成功。 |
Running | Pod已经绑定到一个节点,并且已经创建了所有容器。至少有一个容器正在运行 中,或正在启动或重新启动。 |
Succeeded | Pod中的所有容器都已成功终止,不会重新启动。 |
Failed | Pod的所有容器均已终止,且至少有一个容器已在故障中终止。也就是说,容器 要么以非零状态退出,要么被系统终止。 |
Unknown | 由于某种原因apiserver无法获得Pod的状态,通常是由于Master与Pod所在主机 kubelet通信时出错。 |
CrashLoopBackOff | Kubernetes试图启动该Pod,但是过程中出现错误,导致容器启动失败或者正在被删除 |