深入理解Pod
- 为什么要有个Pod
- 1. 容器协作与资源共享
- 2. 简化调度和资源管理
- 3. 设计模式支持
- Pod 基本用法
- Pod 容器共享 Volume
- Pod 的配置管理
- ConfigMap 概述
- 创建 ConfigMap 资源对象
- 在 Pod 中使用 ConfigMap
- 使用 ConfigMap 的限制条件
为什么要有个Pod
Pod 的引入并非技术冗余,而是 k8s 为应对容器编排复杂性的关键设计。它提供的是一种编排思想,而非具体的技术方案。
Pod 作为 k8s 最小的调度和管理单元,解决了容器化环境中的多个关键问题:
1. 容器协作与资源共享
- 亲密关系容器组:Pod 允许同一组容器共享 Network Namespace 和 Volume,这些容器需要紧密协作(如共享网络、存储或进程空间)。例如,日志收集容器与应用容器共享日志目录,或 Sidecar 模式下的代理容器与主应用容器通过本地 Socket 通信
- 统一网络和存储:Pod 内容器共享 同一个 IP 和端口范围,可以通过 localhost 直接通信;共享 Volume 可以实现文件交换(配置文件、临时数据等)
2. 简化调度和资源管理
- 成组调度:需要共同运行的容器(比如 Web 服务与缓存服务)必须部署在同一个节点。Pod 作为整体调度单位,避免因分散部署导致资源不足或依赖失效。例如,k8s 直接以 Pod 为粒度分配 CPU、内存资源,而非逐个容器计算
- 统一生命周期:Pod 内的容器具有一致的生命周期。若某个容器异常退出,K8S 可基于 Pod 的重启策略(如 Always、OnFailure)统一处理,而非单独管理每个容器。
- 统一运维:扩缩容、滚动升级等操作以 Pod 为单位,当节点故障时,K8S 可直接重建整个 Pod,而非逐个恢复容器,提升系统健壮性
3. 设计模式支持
- Infra 容器:每个 Pod 默认有一个 Infra 容器(pause 容器),负责创建共享的 Network Namespace 和 Volume。用户容器通过加入 Infra 容器的命名空间实现资源共享,解决了容器启动顺序依赖问题
- 扩展设计:Pod 支持 Init 容器(初始化任务)、Sidecar 容器(辅助功能如日志转发)等模式,增强了应用的可维护性。例如,通过 Sidecar 实现服务网格的流量代理
Pod 基本用法
使用 Docker 时,我们使用 docker run 来创建并启动一个容器。但是,在 k8s 中对长时间运行容器的要求是:其主程序需要一直在前台执行。
比如,我们使用 ./start.sh & 在后台执行程序,则在 k8s 创建包含这个容器的 Pod 之后运行完该命令,即认为 Pod 执行结束,将立刻销毁。如果为该 Pod 定义了 RC,则系统会监控到该 Pod 已经终止,之后根据 RC 定义中 Pod 的 replicas 副本数量生成一个新的 Pod。一旦创建新的 Pod,就在执行完启动命令后陷入无限循环的过程中。这就是Kubernetes需要我们自己创建的 Docker 镜像并以一个前台命令作为启动命令的原因。
以一种场景举例,frontend 和 redis 两个容器应用为紧耦合的关系,并组合成一个整体对外提供服务时,应将这两个容器打包为一个Pod,配置文件 frontend-localredis-pod.yaml:
apiVersion: v1
kind: Pod
metadata:
name: redis-php
labels:
name: redis-php
spec:
containers:
- name: frontend
image: kubeguide/guestbook-php-frontend:localredis
ports:
- containerPort: 80
- name: redis
image: kubeguide/redis-master
ports:
- containerPort: 6379
属于同一个 Pod 的多个容器应用之间相互访问时仅需要通过 localhost 就可以通信,使得这一组容器被“绑定”在了一个环境中。
kubectl create -f frontend-localredis-pod.yaml
# pod "redis-php" created
kubectl get pods # READAY 2/2 表示两个容器都成功运行了
kubectl describe pod redis-php # 显示 pod 详细信息
Pod 容器共享 Volume
同一个 Pod 中的多个容器能够共享 Pod 级别的存储卷 Volume。Volume 可以被定义为各种类型,多个容器各自进行挂载操作,将一个 Volume 挂载为容器内部需要的目录。
看个例子:在 Pod 内包含两个容器:tomcat 和 busybox,在 Pod 级别设置 Volume “app-logs”,用于tomcat向其中写日志文件,busybox 读日志文件,配置文件 pod-volume-applogs.yaml:
apiVersion: v1
kind: Pod
metadata:
name: volume-pod
spec:
containers:
- name: tomcat
image: tomcat
ports:
- containerPort: 8080
volumeMounts:
- name: app-logs
mountPath: /usr/local/tomcat/logs
- name: busybox
image: busybox
command: ["sh", "-c", "tail -f /logs/catalina*.log"]
volumeMounts:
- name: app-logs
mountPath: /logs
volumes:
- name: app-logs
emptyDir: {}
Volume 名为 app-logs,类型为 emptyDir(还可设置其他类型,如 hostPath,configMap等),挂载到 tomcat 容器内的/usr/local/tomcat/logs 目录,同时挂载到 busybox 容器内的 /logs 目录。tomcat 容器在启动后会向 /usr/local/tomcat/logs 目录写文件,busybox 容器就可以读取其中的文件了。
# busybox 容器的启动命令为tail -f /logs/catalina*.log,我们可以通过 kubectl logs 命令查看 busybox 容器的输出内容:
kubectl logs volume-pod -c busybox
# 登录 tomcat 容器进行查看
kubectl exec -ti volume-pod -c tomcat -- ls /usr/local/tomcat/logs
kubectl exec -ti volume-pod -c tomcat -- tail /usr/local/tomcat/logs/catalina*.log
Pod 的配置管理
将应用所需的配置信息与程序进行分离,可以使应用程序被更好地复用,通过不同的配置也能实现更灵活的功能。
将应用打包为容器镜像后,可以通过环境变量或者外挂文件的方式在创建容器时进行配置注入。k8s 1.2开始提供统一的应用配置管理–ConfigMap,实现多个容器的配置。
ConfigMap 概述
- 生成为容器内的环境变量
- 设置容器启动命令的启动参数(需设置为环境变量)
- 以 Volume 的形式挂载为容器内部的文件或目录
ConfigMap 以 key:value 的形式保存在 k8s 系统中供应用使用,既可以用于表示一个变量的值(例如 apploglevel=info),也可以用于表示一个完整配置文件的内容(例如server.xml=<?xml…>…)。
可以通过 YAML 配置文件或者直接使用 kubectl create configmap 命令行的方式来创建 ConfigMap。
创建 ConfigMap 资源对象
- 通过 YAML 配置文件方式创建
举例 cm-appvars.yaml 将几个应用所需的变量定义为 ConfigMap 的用法:
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-appvars
data:
apploglevel: info
appdatadir: /var/data
执行 create 命令创建
kubectl create -f cm-appvars.yaml # ConfigMap "cm-appvars" created
kubectl get configmap
kubectl describe configmap cm-appvars
kubectl get configmap cm-appvars -o yaml
cm-appconfigfiles.yaml 描述了将两个配置文件 server.xml 和 logging.properties 定义为 ConfigMap 的用法,设置 key 为配置文件的别名,value 则是配置文件的全部文本内容:
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-appconfigfiles
data:
key-serverxml: |
<?xml version='1.0' encoding='utf-8' ?>
<Server port="8080" shutdown="SHUTDOWN">
<Listener className="xxx" />
...
</Server>
key-loggingproperties: "handles
=filehanlder\r\n\r\nformatter
=simpleformater\r\n\r\nlevel
=info\r\n\r\n"
- 通过 kubectl 命令创建
- 通过 --from-file 参数从文件中进行创建,可以指定 key 的名称,也可以在一个命令行中创建包含多个 key 的 ConfigMap:
kubectl create configmap NAME --from-file=[key=]source --from-file=[key=]source
kubectl create configmap cm-server.xml --from-file=server.xml # ConfigMap "cm-server.xml" created
kubectl describe configmap cm-server.xml
- 通过 --from-file 参数从目录中进行创建,该目录下的每个配置文件名都被设置为 key,文件的内容被设置为 value:
kubectl create configmap NAME --from-file=config-files-dir
kubectl create configmap cm-appconf --from-file=configfiles
kubectl describe configmap cm-appconf
- 使用 --from-literal 时会从文本中进行创建,直接将指定的 key=value创建为 ConfigMap 的内容:
kubectl create configmap NAME --from-literal=key1=value1 --from-literal=key2=value2
kubectl create configmap cm-appenv --from-literal=loglevel=info --from-literal=appdatadir=/var/data
kubectl describe configmap cm-appenv
在 Pod 中使用 ConfigMap
- 通过环境变量使用 ConfigMap
以前面的 ConfigMap “cm-appvars” 为例,在 Pod “cm-test-pod” 中,将 ConfigMap “cm-appvars” 中的内容以环境变量方式设置为容器内部的环境变量,容器的启动命令将显示这两个环境变量
apiVersion: v1
kind: Pod
metadata:
name: cm-test-pod
spec:
containers:
- name: cm-test
image: busybox
command: [ "/bin/sh", "-c", "env | grep APP" ]
env:
- name: APPLOGLEVEL # 定义环境变量的名称
valueFrom: # key“apploglevel”对应的值
configMapKeyRef:
name: cm-appvars # 环境变量的值取自cm-appvars:
key: apploglevel # key为apploglevel
- name: APPDATADIR # 定义环境变量的名称
valueFrom: # key“appdatadir”对应的值
configMapKeyRef:
name: cm-appvars # 环境变量的值取自cm-appvars
key: appdatadir # key为appdatadir
restartPolicy: Never # 不会自动重启
使用 kubectl create -f 命令创建该 Pod,由于是测试 Pod,所以该 Pod 在执行完启动命令后将会退出,并且不会被系统自动重启(restartPolicy=Never):
kubectl create -f cm-test-pod.yaml # pod "cm-test-pod" created
kubectl get pods --show-all # 使用kubectl get pods --show-all查看已经停止的 Pod
# 查看该Pod的日志,可以看到启动命令“env | grep APP”的执行结果
kubectl logs cm-test-pod
# APPDATADIR=/var/data
# APPLOGLEVEL=info
k8s 1.6版本开始,引入新的字段 envFrom,实现了在 Pod 环境中将 ConfigMap(也可用于 Secret 资源对象)中所有定义的 key=value 自动生成为环境变量:
apiVersion: v1
kind: Pod
metadata:
name: cm-test-pod
spec:
containers:
- name: cm-test
image: busybox
command: [ "/bin/sh", "-c", "env" ]
envFrom:
- configMapRef
name: cm-appvars # 根据cm-appvars中的 key=value 自动生成环境变量
restartPolicy: Never
注意:环境变量的名称受POSIX命名规范([a-zA-Z_][a-zA-Z0-9_]*)约束,不能以数字开头。如果包含非法字符,则系统将跳过该条环境变量的创建,并记录一个Event来提示环境变量无法生成,但并不阻止Pod的启动
- 通过 volumeMount 使用 ConfigMap
在Pod “cm-test-app” 的定义中,将 ConfigMap “cm-appconfigfiles” 中的内容以文件的形式 mount 到容器内部的 /configfiles 目录下。Pod 配置文件cm-test-app.yaml:
apiVersion: v1
kind: Pod
metadata:
name: cm-test-app
spec:
containers:
- name: cm-test-app
image: tomcat-app:v1
ports:
- containerPort: 8080
volumeMounts:
- name: serverxml # 引用Volume的名称
mountPath: /configfiles # 挂载到容器内的目录
volumes:
- name: serverxml # 定义Volume的名称
configMap:
name: cm-appconfigfiles # 使用ConfigMap“cm-appconfigfiles”
items:
- key: key-serverxml # key=key-serverxml
path: server.xml # value将server.xml文件名进行挂载
- key: key-loggingproperties # key=key-loggingproperties
path: logging.properties # value将logging.properties文件名进行挂载
创建 Pod,进入容器,查看 /configfiles 目录下存在 serverl.xml 和 logging.properties 文件,内容为 ConfigMap “cm-appconfigfiles” 中两个 key 定义的内容:
kubectl create -f cm-test-app.yaml
kubectl exec -ti cm-test-app -- bash
cat /configfiles/server.xml
cat /configfiles/logging.properties
在引用 ConfigMap 时不指定 items,则使用 volumeMount 方式在容器内的目录下为每个 item 都生成一个文件名为 key 的文件:
apiVersion: v1
kind: Pod
metadata:
name: cm-test-app
spec:
containers:
- name: cm-test-app
image: tomcat-app:v1
imagePullPolicy: Never
ports:
- containerPort: 8080
volumeMounts:
- name: serverxml # 引用Volume的名称
mountPath: /configfiles # 挂载到容器内的目录
volumes:
- name: serverxml # 定义Volume的名称
configMap:
name: cm-appconfigfiles # 使用ConfigMap“cm-appconfigfiles”
查看到在 /configfiles 目录下存在key-loggingproperties和key-serverxml文件,文件的名称来自在ConfigMap cm-appconfigfiles中定义的两个key的名称,文件的内容则为value的内容
kubectl create -f cm-test-app.yaml
ls /configfiles # key-loggingproperties key-serverxml
使用 ConfigMap 的限制条件
- ConfigMap 必须在 Pod 之前创建
- ConfigMap 受 Namespace 限制,只有处于相同 Namespace 中的 Pod 才可以引用它
- kubelet 只支持可以被API Server管理的 Pod 使用 ConfigMap。kubelet 在本 Node 上通过–manifest-url 或 --config 自动创建的静态 Pod将无法引用 ConfigMap
- 在 Pod 对 ConfigMap 进行挂载(volumeMount)操作时,在容器内部只能挂载为“目录”,无法挂载为“文件”。