小白网站建设,网站快速优化排名免费,东莞卓博人才网招聘,孝感网站开发优搏好什么是 PodPod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。在同一个 context 下#xff0c;应用可能还会有独立的 cgroup 隔离机制#xff0c;一个 Pod 是一个容器环境下的 “逻辑主机”。Pod 是一组容器单元#xff0c; 这些容器共享存储、网络、以及怎…什么是 PodPod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。在同一个 context 下应用可能还会有独立的 cgroup 隔离机制一个 Pod 是一个容器环境下的 “逻辑主机”。Pod 是一组容器单元 这些容器共享存储、网络、以及怎样运行这些容器的声明。Pod 中的内容总是并置colocated的并且一同调度在共享的 context上下文 中运行。而 Pod 中的这些应用容器可能还有独立的 cgroup 隔离机制一个 Pod 相当于应用的 “逻辑主机”其中包含一个或多个应用容器 这些容器相对紧密地耦合在一起。在非云环境中在相同的物理机或虚拟机VM上运行的应用类似于在同一逻辑主机上运行的云应用。豌豆荚我们可以借助上面这张图豌豆荚来形象的理解 Pod豌豆荚的整体外壳就等同于 Pod豌豆荚里面的豆子就等同于应用容器App Container豆子被豌豆荚包裹这就类似于应用容器运行在 Pod 中。除了应用容器Pod 还可以包含在 Pod 启动期间运行的 Init Container。也可以在集群中支持 临时性容器 的情况下为调试注入 临时性容器 进行故障排查。Pod 的设计思想k8s 中创建和管理的、最小的可部署的计算单元。一组容器Container的单元集合可以包含一个或多个应用容器。同一个 Pod 中的 Container 共享的是同一个网络命名空间Network Namespace并且可以声明共享同一个 Volume。Pod 是短暂的用完即销毁。Pod 的组成结构每个 Pod 创建的时候都会包含一个 Pause 容器和用户的业务容器或者应用容器App ContainerPod 的基本组成结构如下图所示既然 Pod 是 k8s 中最小的运行单元那么在一个 Pod 中包含几个容器呢?答至少两个分别是 1 个基础容器pause 应用容器1个或者多个。Pod 的 YAML 文件定义示例此处我们以 asp.net core webapi 项目为例定义 Pod 的 yaml 文件结构如下apiVersion: v1 # API 版本号
kind: Pod # 资源类型
metadata: # meta 信息name: myweblabels: # 标签 name: myweb
spec:containers:- name: myweb-aspnetcore # 容器名称image: aspnetcore-6.0:v1.0.0 # 镜像:版本imagePullPolicy: IfNotPresent # 如果镜像不存在则拉取ports:- containerPort: 5001 # 容器暴露端口env: # 环境变量- name: ASPNETCORE_ENVIRONMENT # asp.net core 运行时环境value: Development # 开发环境- name: ASPNETCORE_URLS # 设置 asp.net core 启动端口value: http://localhost:5001/ # 本地主机 5001 端口【Pod IP:containerPort】就组成了一个新的概念 —— Endpoint它代表此 Pod 里的一个服务进程的对外通信地址。之前的文章介绍过这里不再叙述。Pod 存在的意义容器的设计是单进程原则推荐一个容器里只有一个应用在运行。然而单进程模型在某些特定场景下满足不了应用的需求因此在这种情况下有了 Pod 的存在这也很好的诠释了 Pod 相当于应用的 “逻辑主机” 。例如你可能有一个容器为共享卷中的文件提供 Web 服务器支持以及一个单独的 “sidecar” 容器负责从远端更新这些文件这两个容器在功能上进行紧密的协调如下图所示Pod —— 应用的 “逻辑主机”Pod 的 context 可以理解成多个 linux 命名空间的联合IPC 命名空间同一个 Pod 中的应用可以通过 VPC 或者 POSIX 进行通信Network Namespace / 网络命名空间同一个 Pod 的中的应用对相同的 IP 地址和端口有权限PID 命名空间同一个 Pod 中应用可以看到其它进程UTS 命名空间同一个 Pod 中的应用共享一个主机名称 Hostname 前面我们介绍了 Pod 的基本概念和组成结构那么 Kubernetes 设计这样的 Pod 概念和特殊组成结构有什么用意原因一在一组容器作为一个单元的情况下难以对整体的容器简单地进行判断及有效地进行管理。比如一个容器死亡了此时是算整体挂了么那么引入与业务无关的 Pause 容器作为 Pod 的根容器以它的状态代表着整个容器组的状态这样就可以解决该问题。原因二Pod 里的多个业务容器共享pause容器的IP共享Pause容器挂载的volume这样简化了业务容器之间的通信问题也解决了容器之间的文件共享问题。Pod 对象分类按照 Pod 的运行方式可以把 Pod 分为以下两类自主式 Pod也叫静态 Pod/ Static Pod自我管理不能自我修复不会自愈。普通 Pod即控制器管理的 Pod刚好和上面的形成对立面。自主式 PodStatic Pod自我管理的 Pod创建以后仍然需要提交给 kube-apiserver由 kube-apiserver 接收以后借助于 Scheduler调度器 将其调度至指定的 node 节点再由该 node 启动被调度过来的 Pod。如果该 Pod 出现故障需要重启容器则由 kubelet 来完成本身不能自我修复。如果 node 节点故障了那么该 Pod 将会消失Pod 不会自愈。这种方式的 Pod 无法实现全局调度所以 不推荐使用此种 Pod。控制器管理 Pod普通 Pod在 k8s 环境中通常大多数情况下都使用该方式来管理 Pod常见的 Pod 控制器有以下这些ReplicationController副本控制器简称 RC当启动一个 Pod 时这个 Pod 如果不够用可以再启一个副本而后由该控制器来管理同一类 Pod 的各种副本与对象。一旦副本达不到预期目标数量就会自动增加或减少。采取多退少补的规则精确符合我们所定义的期望。RC 同时还支持滚动更新。ReplicaSet复制集简称 RS下一代 RC 是 RC 的替换由一个名叫 Deployment 的声明式更新的控制器来管理。Deployment部署简称 deploy用于管理无状态的应用。StatefulSet状态集有状态副本集可以用于管理有状态的应用。DaemonSet守护程序 如果需要在每个 node 上运行一个副本即可用该对象。Job任务 用于运行批处理任务、运维 / ad-hoc 任务等。Cronjob定时任务 在 Job 的基础上增加了时间周期用于执行周期性的动作例如备份、邮件、报告生成等。cron 时间配置与 linux crontab 相似。以上每种控制器都是用来实现一种独特的应用管理的。关于 Job 的应用场景批处理任务比如说你想每天运行一次批处理任务或者在指定日程中运行。它可能是像从存储库或数据库中读取文件那样将它们分配给一个服务来处理文件。运维 / ad-hoc 任务比如你想要运行一个脚本/代码该脚本/代码会运行一个数据库清理活动甚至备份一个Kubernetes集群。静态 PodStatic Pod静态 PodStatic Pod 直接由特定节点上的 kubelet 守护进程管理 不需要通过控制面例如Deployment 来管理的对于静态 Pod 而言kubelet 直接监控每个 Pod并在其失效时重启。静态 Pod 通常绑定到某个 node 节点上的 kubelet。其主要用途是运行自托管的控制面master。在自托管场景中使用 kubelet 来管理各个独立的 控制面组件。kubelet 自动尝试为每个静态 Pod 在 Kubernetes API 服务器上创建一个 镜像 Pod。这意味着在节点上运行的 Pod 在 API 服务器kube-apiserver上是可见的但不可以通过 API 服务器来控制。说明静态 Pod 的 spec 不能引用其他的 API 对象例如ServiceAccount、 ConfigMap、 Secret 等。Static Pod 创建示例1、配置文件方式选择一个要运行静态 Pod 的节点。ssh k8s-node1选择 /etc/kubernetes/manifests 目录来保存 Web 服务 Pod 的 YAML 定义文件例如 /etc/kubernetes/manifests/static-web.yaml# 在 kubelet 运行的节点上执行以下命令
mkdir -p /etc/kubernetes/manifests/
cat EOF /etc/kubernetes/manifests/static-web.yaml
apiVersion: v1
kind: Pod
metadata:name: static-weblabels:role: myrole
spec:containers:- name: webimage: nginxports:- name: webcontainerPort: 80protocol: TCP
EOF配置该 node 节点上的 kubelet使用参数 --pod-manifest-path/etc/kubelet.d/ 执行。或者在 Kubelet 配置文件 中添加 staticPodPath: 目录字段KUBELET_ARGS--cluster-dns10.254.0.10 --cluster-domainkube.local --pod-manifest-path/etc/kubernetes/manifests/重启该 node 节点上的 kubelet 使 yaml 配置文件生效。# 在 kubelet 运行的节点上执行以下命令
systemctl restart kubeletKubeletConfiguration 中包含 kubelet 的配置。字段描述staticPodPath stringstaticPodPath 是指向要运行的本地静态Pod 的目录 或者指向某个静态 Pod 文件的路径。默认值2、HTTP 方式设置 kubelet 的启动参数 --manifest-urlkubelet 将会定期从该 URL 地址下载 Pod 的定义文件并以 .yaml 和 .json 文件的格式进行解析然后创建 Pod。以上两种方式创建的 Pod 是一致的。关于 KubeletConfiguration 更多信息请查看https://kubernetes.io/zh-cn/docs/reference/config-api/kubelet-config.v1beta1/#kubelet-config-k8s-io-v1beta1-KubeletConfiguration如何查看创建的 Pod 方式一在 kubelet 运行的 node 节点上来查看正在运行的容器包括静态 Podcrictl ps输出信息CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID
129fd7d382018 docker.io/library/nginxsha256:... 11 minutes ago Running web 0 34533c6729106说明crictl 会输出镜像 URI 和 SHA-256 校验和。NAME 看起来像docker.io/library/nginxsha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31。方式二可以在 API 服务上看到镜像 Podkubectl get pods输出信息NAME READY STATUS RESTARTS AGE
static-web 1/1 Running 0 2m说明要确保 kubelet 在 API 服务上有创建镜像 Pod 的权限。如果没有创建请求会被 API 服务拒绝。关于创建 Static Pod 的更多信息请查看https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/static-pod/Pod 中容器分类Pod 中的容器又能分三类基础容器Pause、初始化容器Init Container和应用容器App Container。基础容器pause又叫 “根容器” 或 “父容器” 给 pod 中的所有应用容器提供网络和存储资源的共享提供 init 进程。管理整个 Pod 里的容器组的生命周期。初始化容器Init Container是在应用容器之前完成所有 init 容器的启动多个 init 容器是串行启动。每个 Init 容器都必须在下一个 Init 容器启动之前成功完成启动。应用容器App Container提供应用程序业务。并行启动。Pod 中容器共享 Volume由于 Pod 的设计是临时性的用完即销毁的理念那么 Pod 中容器 Container 运行产生的数据也会随着 Pod 的销毁而消失为了保证 Container 里面产生的数据可以持久化保存Pod 引入了 Volume数据卷 的概念简单的理解类似于电脑磁盘的挂载。计算机磁盘管理Pod 使用 Volume 示例示例1配置 Pod 使用临时卷这里我们以 redis 的 Pod 为例使用 Volume 的 yaml 定义文件如下apiVersion: v1
kind: Pod
metadata:name: redis
spec:containers:- name: redisimage: redisvolumeMounts:- name: redis-storagemountPath: /data/redisvolumes:- name: redis-storageemptyDir: {}上面的定义的 yaml 文件中使用的是 emptyDir 临时卷其中包含一个 挂载点 volumeMounts将 redis 容器产生的数据挂载到该容器内的 /data/redis 路径。在 k8s 中 Volume 支持的类型有很多这里不再列举更多 Volume 的信息请查看https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/https://feisky.gitbooks.io/kubernetes/content/concepts/volume.html示例2使用 Sidecar 容器运行日志代理你可以通过以下方式之一使用边车Sidecar容器边车容器将应用程序日志传送到自己的标准输出。边车容器运行一个日志代理配置该日志代理以便从应用容器收集日志。2.1 使用传输数据流的 Sidecar 容器利用边车容器向自己的 stdout 和 stderr 传输流的方式 你就可以利用每个节点上的 kubelet 和日志代理来处理日志。边车容器从文件、套接字或 journald 读取日志。每个边车容器向自己的 stdout 和 stderr 流中输出日志。这种方法允许你将日志流从应用程序的不同部分分离开其中一些可能缺乏对写入 stdout 或 stderr 的支持。重定向日志背后的逻辑是最小的因此它的开销几乎可以忽略不计。另外因为 stdout 和 stderr 由 kubelet 处理所以你可以使用内置的工具 kubectl logs。例如某 Pod 中运行一个容器该容器向两个文件写不同格式的日志。下面是这个 Pod 的配置文件:apiVersion: v1
kind: Pod
metadata:name: counter
spec:containers:- name: countimage: busybox:1.28args:- /bin/sh- -c- i0;while true;doecho $i: $(date) /var/log/1.log;echo $(date) INFO $i /var/log/2.log;i$((i1));sleep 1;done volumeMounts:- name: varlogmountPath: /var/logvolumes:- name: varlogemptyDir: {}不建议在同一个日志流中写入不同格式的日志条目即使你成功地将其重定向到容器的 stdout 流。推荐创建两个边车容器。每个边车容器可以从共享卷跟踪特定的日志文件 并将文件内容重定向到各自的 stdout 流。下面是运行两个边车Sidecar容器的 Pod 的配置文件apiVersion: v1
kind: Pod
metadata:name: counter
spec:containers:- name: countimage: busybox:1.28args:- /bin/sh- -c- i0;while true;doecho $i: $(date) /var/log/1.log;echo $(date) INFO $i /var/log/2.log;i$((i1));sleep 1;done volumeMounts:- name: varlogmountPath: /var/log- name: count-log-1image: busybox:1.28args: [/bin/sh, -c, tail -n1 -F /var/log/1.log]volumeMounts:- name: varlogmountPath: /var/log- name: count-log-2image: busybox:1.28args: [/bin/sh, -c, tail -n1 -F /var/log/2.log]volumeMounts:- name: varlogmountPath: /var/logvolumes:- name: varlogemptyDir: {}2.2 使用具有日志代理logging-agent功能的 Sidecar 容器如果节点级日志记录代理程序对于你的场景来说不够灵活 你可以创建一个带有单独日志记录代理的边车容器将代理程序专门配置为与你的应用程序一起运行。logging-agent-sidecar说明在边车容器中使用日志代理会带来严重的资源损耗。此外你不能使用 kubectl logs 命令访问日志因为日志并没有被 kubelet 管理。下面是两个配置文件可以用来实现一个带日志代理的边车容器。1、创建名称为 fluentd 的 Pod 的 ConfigMapyaml 定义文件如下apiVersion: v1
kind: ConfigMap
metadata:name: fluentd-config
data:fluentd.conf: |sourcetype tailformat nonepath /var/log/1.logpos_file /var/log/1.log.postag count.format1/sourcesourcetype tailformat nonepath /var/log/2.logpos_file /var/log/2.log.postag count.format2/sourcematch **type google_cloud/match说明要进一步了解如何配置 fluentd请参考 fluentd 官方文档https://docs.fluentd.org/2、创建 fluentd 边车容器的 Pod flutend 通过 Pod 的挂载卷ConfigMap获取它的配置数据。apiVersion: v1
kind: Pod
metadata:name: counter
spec:containers:- name: countimage: busybox:1.28args:- /bin/sh- -c- i0;while true;doecho $i: $(date) /var/log/1.log;echo $(date) INFO $i /var/log/2.log;i$((i1));sleep 1;done volumeMounts:- name: varlogmountPath: /var/log- name: count-agentimage: k8s.gcr.io/fluentd-gcp:1.30env:- name: FLUENTD_ARGSvalue: -c /etc/fluentd-config/fluentd.confvolumeMounts:- name: varlogmountPath: /var/log- name: config-volumemountPath: /etc/fluentd-configvolumes:- name: varlogemptyDir: {}- name: config-volumeconfigMap:name: fluentd-config在上面的示例配置中fluentd 可以被替换为任何日志代理从应用容器内的任何来源读取数据。关于 k8s 中日志架构的更多信息请查看https://kubernetes.io/zh-cn/docs/concepts/cluster-administration/logging/Pod 的配置管理应用部署的最佳实践是将应用所需的配置信息与程序进行分离这样可以使应用程序被更好地复用通过不同的配置能实现更灵活的功能。将应用打包为容器镜像后可以通过环境变量env或外挂文件volume这两种方式在创建容器时进行配置的注入但在大规模化容器集群的环境中对多个容器进行不同的配置会变得非常的复杂多个容器配置出错的概率大幅提升随之管理成本上升难以方便的管理。面对以上问题k8s v1.2 版本开始提供了一种统一的集中化应用配置管理方案 —— ConfigMap也叫集中化配置中心。ConfigMap 简介ConfigMap 是一个 API 对象使用键-值key-value对形式保存非机密性的数据。可以存储其他对象所需要使用的配置。不同的是其他 k8s 对象都有一个 spec 而 ConfigMap 使用 data 和 binaryData 字段。这些字段能够接收 键-值key-value 对作为其取值。data 和 binaryData 字段都是可选的。data 字段设计用来保存 UTF-8 字符串。binaryData 则被设计用来保存 二进制数据作为 base64 编码的字串。注意ConfigMap 并不提供保密或者加密功能。如果你想存储的数据是机密的请使用 Secret 或者使用其他第三方工具来保证你的数据的私密性而不是用 ConfigMap。ConfigMap 在设计上不是用来保存大量数据的。在 ConfigMap 中保存的数据不可超过 1 MiB。如果你需要保存超出此尺寸限制的数据考虑使用 挂载存储卷 或者使用 独立的数据库或者文件服务。ConfigMap 供 Pod 容器使用的典型用法有以下四种方式生成为容器内的环境变量env。设置容器启动命令的启动参数在容器命令和参数内需要设置为环境变量。在只读卷里面添加一个文件以 Volume 的形式挂载为容器内部的文件或目录让应用来读取。编写代码在 Pod 中运行使用 Kubernetes API 来读取 ConfigMap。这些不同的方法适用于不同的数据使用方式。对前三个方法kubelet 使用 ConfigMap 中的数据在 Pod 中启动容器。第四种方法意味着你必须编写代码才能读取 ConfigMap 和它的数据。然而 由于你是直接使用 Kubernetes API因此只要 ConfigMap 发生更改 你的应用就能够通过订阅来获取更新并且在这样的情况发生的时候做出反应。通过直接进入 Kubernetes API这个技术也可以让你能够获取到不同的Namespace 里的 ConfigMap。创建 ConfigMap 资源对象可以通过 YAML 配置文件 方式或者直接使用 kubectl create configmap 命令行的方式来创建 ConfigMap。1、通过 yaml 文件方式创建上面的示例使用 Sidecar 容器运行日志代理中已经展示过接下来我们再创建一个 game-demo 的 ConfigMap。apiVersion: v1
kind: ConfigMap
metadata:name: game-demo
data:# 类属性键每一个键都映射到一个简单的值player_initial_lives: 3ui_properties_file_name: user-interface.properties# 类文件键game.properties: |enemy.typesaliens,monstersplayer.maximum-lives5 user-interface.properties: |color.goodpurplecolor.badyellowallow.textmodetrue注意区分 key 键的类型类属性键 和 类文件键。执行如下命令创建上面定义的 ConfigMapkubectl create -f game-demo.yaml输出信息configmap game-demo created查看创建好的 ConfigMapkubectl get configmap查看 ConfigMap 的详细信息kubectl describe configmap game-demo除了上面的方式还可以将 配置文件xml 或 json 文件定义为 ConfigMap 的用法设置 key 为配置文件的别名value 为该配置文件的全部文本内容。比如asp.net core webapi 里面的 appsettings.json 文件。2、通过 kubectl 命令方式创建除了上面的 YAML 文件方式还可以通过 kubectl 命令行方式创建 ConfigMap并且还可以在一行命令中指定多个参数。2.1 通过 --from-file 参数从文件中进行创建可以指定 key 的名称也可以在一个命令行中创建包含多个 key 的 ConfigMap 语法格式kubectl create configmap NAME --from-file[key]source --from-file[key]source2.2 通过 --from-file 参数从目录中进行创建该目录下的每个配置文件名都被设置为 key文件的内容被设置为 value 语法格式kubectl create configmap NAME --from-fileconfig-files-dir2.3 使用 --from-literal 时会从文本中进行创建直接将指定的 key#value# 创建为 ConfigMap 内容语法格式kubectl create configmap NAME --from-literalkey1value1 --from-literalkey2value2分别对上面这几种命令方式创建 ConfigMap 举例说明示例1比如在 asp.net core 项目中有配置文件 appsettings.json可以创建一个包含该文件内容的 ConfigMapkubectl create configmap appsettings.json --from-fileappsettings.json
configmap appsettings.json created示例2假如在 asp.net core 项目中的 configfiles 目录下包含两个配置文件 appconfig.xml 和 appsettings.json创建一个包含两个文件内容的 ConfigMapkubectl create configmap cm-appconf --from-fileconfigfiles示例3使用 --from-literal 参数进行创建 ConfigMapkubectl create configmap cm-appenv --from-literalloglevelinfo --from-literalappdatadir/var/data配置 Pod 使用 ConfigMap配置 Pod 使用 ConfigMap 有以下两种方式通过环境变量env方式使用 ConfigMap通过 volumeMount 方式使用 ConfigMap这里我们使用上面创建的 game-demo 的 ConfigMap通过两种方式配置 Pod 使用 game-demo。apiVersion: v1
kind: Pod
metadata:name: configmap-demo-pod
spec:containers:- name: demoimage: alpinecommand: [sleep, 3600]env:# 定义环境变量- name: PLAYER_INITIAL_LIVES # 请注意这里和 ConfigMap 中的键名是不一样的valueFrom:configMapKeyRef:name: game-demo # 这个值来自 ConfigMapkey: player_initial_lives # 需要取值的键- name: UI_PROPERTIES_FILE_NAMEvalueFrom:configMapKeyRef:name: game-demokey: ui_properties_file_namevolumeMounts:- name: configmountPath: /configreadOnly: truevolumes:# 你可以在 Pod 级别设置卷然后将其挂载到 Pod 内的容器中- name: configconfigMap:# 提供你想要挂载的 ConfigMap 的名字name: game-demo# 来自 ConfigMap 的一组键将被创建为文件items:- key: game.propertiespath: game.properties- key: user-interface.propertiespath: user-interface.properties说明环境变量的名称遵循 POSIX 命名规范[a-zA-Z_][a-zA-Z0-9_]*不能以数字开头。如果包含非法字符则系统将跳过该条环境变量的创建并记录一个 Event 来提示环境变量无法创建但并不阻止 Pod 的启动。使用 ConfigMap 的限制条件ConfigMap 必须在 Pod 创建之前已经创建。ConfigMap 受 Namespace 限制只有处于相同 Namespace 中的 Pod 才可以引用。多个 Pod 可以引用同一个 ConfigMap。ConfigMap 中的配额管理还未能实现。kubelet 只支持可以被 kube-apiserver 管理的 Pod 使用 ConfigMapkubelet 在宿主的 node 上通过 --manifest-url 或 --config 自动创建的 Static Pod 将无法引用 ConfigMap。在 Pod 对 ConfigMap 进行挂载volumeMount操作时在容器内部只能挂载为 “目录”无法挂载为 “文件”。在挂载到容器内部后在目录下包含 ConfigMap 定义的每个 item 如果应用程序需要保留原来的其他文件则需要进行额外的处理。可以将 ConfigMap 挂载到容器内部的临时目录再通过启动脚本将配置文件复制或者链接到cp 或 link 命令应用所用的实际配置目录下。在容器内获取 Pod 信息Downward API当 Pod 被成功创建之后都会被系统分配唯一的名字、IP 地址并且处于某个 namespace 中那么这些信息如何在 Pod 的容器内获取该 Pod 这些重要信息呢对于容器来说在不与 Kubernetes 过度耦合的情况下拥有关于自身的信息有时是很有用的。Downward API 允许容器在不使用 Kubernetes 客户端kubectl或 API 服务器kube-apiserver的情况下获得自己或集群的信息。Downward API 可以通过以下两种方式将 Pod 信息注入容器内部环境变量env用于单个变量可以将 Pod 信息注入容器内部。volumeMount将数组类信息生成为文件并挂载到容器内部。这两种暴露 Pod 和容器字段的方式统称为 Downward API。Downward API 的用法举例说明1、环境变量方式将 Pod 信息注入为环境变量通过 Downward API 将 Pod 的 IP、名称、所在的 Namespace 和 node 名称注入容器的环境变量中容器应用使用 env 命令将全部环境变量打印到标准输出中dapi-envars-fieldref.yaml 文件定义如下apiVersion: v1
kind: Pod
metadata:name: dapi-envars-fieldref
spec:containers:- name: test-containerimage: k8s.gcr.io/busyboxcommand: [ sh, -c, env]args:- while true; doecho -en \n;printenv MY_NODE_NAME MY_POD_NAME MY_POD_NAMESPACE;printenv MY_POD_IP MY_POD_SERVICE_ACCOUNT;sleep 10;done;env:- name: MY_NODE_NAMEvalueFrom:fieldRef:fieldPath: spec.nodeName # Pod 所在的 Node 名称- name: MY_POD_NAMEvalueFrom:fieldRef:fieldPath: metadata.name # Pod 的名称当 Pod 通过 Deployment 创建时其名称是 RS 随机产生的唯一名称- name: MY_POD_NAMESPACEvalueFrom:fieldRef:fieldPath: metadata.namespace # Pod 所在的 Namespace- name: MY_POD_IPvalueFrom:fieldRef:fieldPath: status.podIP # Pod 的 IP 地址之所以叫做 status.podIP 而非 metadata.podIP是因为 Pod 的 IP 属于状态数据而非元数据metadata- name: MY_POD_SERVICE_ACCOUNTvalueFrom:fieldRef:fieldPath: spec.serviceAccountName # Pod 的服务账号名称restartPolicy: Never # Pod 重启策略从不重启执行以下命令创建 Podkubectl create -f dapi-envars-fieldref.yaml
pod dapi-envars-fieldref created查看 “dapi-envars-fieldref” 的日志信息kubectl logs dapi-envars-fieldref
...
MY_NODE_NAMExxx
MY_POD_NAMEdapi-envars-fieldref
MY_POD_NAMESPACEdefault
MY_POD_IP172.17.1.2
MY_POD_SERVICE_ACCOUNTyyy
...注意上面 valueFrom 这种特殊的语法是 Downward API 的写法。2、环境变量方式将 Container 资源信息注入为环境变量用 Container 字段作为环境变量的值dapi-envars-resourcefieldref.yaml 文件定义如下apiVersion: v1
kind: Pod
metadata:name: dapi-envars-resourcefieldref
spec:containers:- name: test-containerimage: k8s.gcr.io/busybox:1.24command: [ /bin/sh, -c, env]args:- while true; doecho -en \n;printenv MY_CPU_REQUEST MY_CPU_LIMIT;printenv MY_MEM_REQUEST MY_MEM_LIMIT;sleep 10;done;resources:requests:memory: 32Micpu: 125mlimits:memory: 64Micpu: 250menv:- name: MY_CPU_REQUESTvalueFrom:resourceFieldRef:containerName: test-containerresource: requests.cpu # 容器的 CPU 请求值最小值- name: MY_CPU_LIMITvalueFrom:resourceFieldRef:containerName: test-containerresource: limits.cpu # 容器的 CPU 限制值最大值- name: MY_MEM_REQUESTvalueFrom:resourceFieldRef:containerName: test-containerresource: requests.memory # 容器的内存请求值最小值- name: MY_MEM_LIMITvalueFrom:resourceFieldRef:containerName: test-containerresource: limits.memory # 容器的内存限制值最大值restartPolicy: Never这个配置文件中可以看到四个环境变量。env 字段是一个 EnvVars. 对象的数组。数组中第一个元素指定 MY_CPU_REQUEST 这个环境变量从 Container 的 requests.cpu 字段获取变量值。同样其它环境变量也是从 Container 的字段获取它们的变量值。说明本例中使用的是 Container 的字段而不是 Pod 的字段。3、volumeMountsVolume 挂载方式将创建一个包含一个容器的 Pod并将 Pod 级别的字段作为文件映射到正在运行的容器中。kubernetes-downwardapi-volume-example.yaml 定义文件如下apiVersion: v1
kind: Pod
metadata:name: kubernetes-downwardapi-volume-examplelabels:zone: us-est-coastcluster: test-cluster1rack: rack-22annotations:build: twobuilder: john-doe
spec:containers:- name: client-containerimage: k8s.gcr.io/busyboxcommand: [/bin/sh, -c, env]args:- while true; doif [[ -e /etc/podinfo/labels ]]; thenecho -en \n\n; cat /etc/podinfo/labels; fi;if [[ -e /etc/podinfo/annotations ]]; thenecho -en \n\n; cat /etc/podinfo/annotations; fi;sleep 5;done;volumeMounts:- name: podinfomountPath: /etc/podinfovolumes:- name: podinfodownwardAPI:items:- path: labelsfieldRef:fieldPath: metadata.labels- path: annotationsfieldRef:fieldPath: metadata.annotations命令行执行 kubectl create -f kubernetes-downwardapi-volume-example.yaml 创建 Pod再查看该 Pod 的日志输出的信息和文件描述的意思相符这里就不再演示了。暴露 Pod 和容器的可用字段只有部分 Kubernetes API 字段可以通过 Downward API 使用。你可以使用 fieldRef 传递来自可用的 Pod 级字段的信息。在 API 层面一个 Pod 的 spec 总是定义了至少一个 Container。你可以使用 resourceFieldRef 传递来自可用的 Container 级字段的信息。可以使用的字段如下表通过 fieldRef 获得的信息字段描述信息是否可作为环境变量获取metadata.namePod 的名称是metadata.namespacePod 的命名空间是metadata.uidPod 的唯一 ID是metadata.annotations[]Pod 的注解的值例如metadata.annotations[myannotation]是metadata.labels[]Pod 的标签的值例如metadata.labels[mylabel]是spec.serviceAccountNamePod 的服务账号名称是spec.nodeNamePod 运行时所处的节点名称是status.hostIPPod 所在节点的主 IP 地址是status.podIPPod 的主 IP 地址通常是其 IPv4 地址是metadata.labelsPod 的所有标签格式为 标签键名转义后的标签值每行一个标签否metadata.annotationsPod 的全部注解格式为 注解键名转义后的注解值每行一个注解否通过 resourceFieldRef 获得的信息字段描述信息是否可作为环境变量获取resource: limits.cpu容器的 CPU 限制值是resource: requests.cpu容器的 CPU 请求值是resource: limits.memory容器的内存限制值是resource: requests.memory容器的内存请求值是resource: limits.hugepages-*容器的巨页限制值前提是启用了 DownwardAPIHugePages 特性门控是resource: requests.hugepages-*容器的巨页请求值前提是启用了 DownwardAPIHugePages 特性门控是resource: limits.ephemeral-storage容器的临时存储的限制值是resource: requests.ephemeral-storage容器的临时存储的请求值是资源限制的后备信息如果没有为容器指定 CPU 和内存限制时尝试使用 Downward API 暴露该信息那么 kubelet 默认会根据 node 节点可分配资源 计算并暴露 CPU 和内存的最大可分配值。Downward API 存在的价值在某些集群环境中集群中的每个 node 都需要将自身的标识ID及进程绑定的 IP 地址等信息先写入配置文件中进程在启动时会读取这些信息然后将这些信息发布到某个类似的服务注册中心的地方以实现集群节点的自动发现功能。这种场景就是 Downward API 发挥价值的地方具体做法先写一个预启动脚本或者 Init Container然后通过环境变量或文件方式获取 Pod 的自身名称、IP地址等信息接下来再将这些信息写入主程序的配置文件中最后启动主程序即可大功告成。创建一个 Pod 的工作流程Kubernetes 基于 list-watch 机制的控制器架构实现组件间交互的解耦。Pod 创建的工作流程如下pod-list-watchkubectl run nginx --imagenginxkubectl 将创建 pod 的请求提交到 kube-apiserver。kube-apiserver 会将请求的信息写到 etcd 数据库。kube-apiserver 通知 scheduler 有新的 pod 待创建收到创建信息之后就会根据调度算法选择一个合适的 node 节点。给这个 pod 打一个标记labelspodk8s-node1。kube-apiserver 收到 scheduler 的调度结果写到 etcd 数据库。k8s-node1 上的 kubelet 收到事件从 kube-apiserver 获取 pod 的相关信息。kubelet 调用 docker api 创建 pod 中所需的容器 container。kubelet 会把这个 pod 的状态汇报给 kube-apiserver。kube-apiserver 把状态写入到 etcd 数据库。查看创建的 Podkubectl get podsAPI Serverkube-apiserver 在 k8s 中的两个角色统一集群的入口。负责协作各个组件。使用 Deployment 对象创建 Pod 的工作流程在上图的 etcd 和 Scheduler 之间增加 controller-manager 控制器对象。在上图的 kubelet 和 Docker 之间增加 kube-proxy通过 service 提供 pod 的外部网络访问。Pod 对象应用的自恢复重启策略健康检查Pod 生命周期和重启策略Pod生命周期Pod 生命周期Pod LifecyclePod 创建的时候可能经历这么几个阶段phase取值描述Pending悬决Pod 已被 k8s 系统接受但有一个或者多个容器尚未创建亦未运行。此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间。Running运行中Pod 已经绑定到了某个节点Pod 中所有的容器都已被创建。至少有一个容器仍在运行或者正处于启动或重启状态。Succeeded成功Pod 中的所有容器都已成功终止并且不会再重启。Failed失败Pod 中的所有容器都已终止并且至少有一个容器是因为失败终止。也就是说容器以非 0 状态退出或者被系统终止。Unknown未知因为某些原因无法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。如果某节点 “死掉” 或者与集群中其他节点 “失联”k8s 会实施一种策略将失去的节点上运行的所有 Pod 的 phase 设置为 Failed。Pod 的重启策略Pod RestartPolicyPod 的重启策略包括 Always、OnFailure 和 Never。默认值是 Always。Always当容器失效时由 kubelet 自动重启该容器。OnFailure当容器终止运行且退出码不为 0 时由 kubelet 自动重启该容器。Never无论容器运行状态如何kubelet 都不会重启该容器。kubelet 重启失效容器的 时间间隔以 ync-frequency 乘以 2n 来计算例如1、2、4、8 倍等最长延时 5min并且在成功重启后的 10min 后重置该时间。Pod 状态转换结合 Pod 的阶段phase状态信息和重启策略RestartPolicy列举出一些常见的状态转换场景如下表所示Pod状态转换Pod 健康检查和服务可用性检查容器配置活跃Liveness、就绪Readiness和启动Startup探测器。更多信息请查看https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/k8s 对 Pod 的健康状态可以通过两类探针来检查LivenessProbe 和 ReadinessProbekubelet 定期执行这两类探针来诊断容器的健康状况。LivenessProbe存活探针用于判断容器是否存活Running状态如果该探针探测到容器不健康则 kubelet 将杀掉该容器并依据容器的重启策略做相应的处理。如果容器不包含 LivenessProbe 探针kubelet 则认为该容器的 LivenessProbe 探针返回值永远是 Success。ReadinessProbe就绪探针用于判断容器服务是否可用Ready状态达到 Ready 状态的 Pod 才可以接受请求。对于被 Service 管理的 PodService 与 Pod Endpoint 的关联关系也将基于 Pod 是否 Ready 进行设置。如果在运行中过程中 Pod 的状态由 Ready 变为 False则系统自动将其从 Service 的后端 Endpoint 列表中隔离出去后续再把恢复到 Ready 状态的 Pod 加回后端 Endpoint 列表。这样就能保证客户端在访问 Service 时不会被转发到服务不可用的 Pod 副本实例上。LivenessProbe 和 ReadinessProbe 均可配置以下三种实现方式ExecAction在容器内部执行一个命令如果该命令的返回码为 0 则说明容器处于健康状态。示例文件apiVersion: v1
kind: Pod
metadata:labels:test: livenessname: liveness-exec
spec:containers:- name: livenessimage: k8s.gcr.io/busyboxargs:- /bin/sh- -c- touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600livenessProbe:exec:command:- cat- /tmp/healthyinitialDelaySeconds: 5periodSeconds: 5TCPSocketAction通过容器的 IP 地址和端口号执行 TCP 检查如果能够建立 TCP 连接则说明容器处于健康状态。示例文件apiVersion: v1
kind: Pod
metadata:name: pod-with-healthcheck
spec:containers:- name: nginximage: nginxports: - containerPort: 80livenessProbe:tcpSocket: port: 80initialDelaySeconds: 30periodSeconds: 5HTTPGetAction通过容器的 IP 地址、端口号及路径调用 HTTP Get 方法如果响应的状态码大于等于 200 且小于 400 HTTP Status Code ≥ 200 400则说明容器处于健康状态。示例文件apiVersion: v1
kind: Pod
metadata:name: pod-with-healthcheck
spec:containers:- name: nginximage: nginxports: - containerPort: 80livenessProbe:httpGet: path: /status/healthzport: 80initialDelaySeconds: 30periodSeconds: 5对于以上三种探测方式都需要设置 initialDelaySeconds 和 periodSeconds 两个参数他们的含义分别如下**initialDelaySeconds**启动容器后进行首次健康检查的等待时间单位为秒 s。**periodSeconds**健康检查发送请求后等待响应的超时时间单位为秒 s。当超时发生kubelet 会认为容器已经无法提供服务将会重启该容器。k8s 的 ReadinessProbe 机制可能无法满足某些复杂应用对容器内服务可用状态的判断从 k8s v1.11 版本开始引入 Pod Ready 特性对 ReadinessProbe 探测机制进行扩展在 v1.14 版本时达到 GA 稳定版称其为 PodReadiness Gates。关于 k8s 更多特性的信息请查看https://kubernetes.io/zh-cn/docs/reference/command-line-tools-reference/feature-gates/通过 Pod Readiness Gates 机制用户可以将自定义的 ReadinessProbe 探测方式设置在 Pod 上辅助 k8s 设置 Pod 何时达到服务可用状态Ready。为了使用自定义的 ReadinessProbe 生效用户需要提供一个外部的控制器Controller来设置相应的 Condition条件状态。示例文件伪 yaml 定义结构...
kind: Pod
...
spec:readinessGates: - conditionType: www.example.com/feature-1 # 新 Readiness Gate类型为 www.example.com/feature-1status: conditions: - type: Ready # k8s 系统内置的名为 Ready 的 conditionstatusTruelastProbeTime: nulllastTransitionTime: 2021-01-01T00:00:00Z- type: www.example.com/feature-1 # 用户自定义的 conditionstatusTruelastProbeTime: nulllastTransitionTime: 2022-08-28T00:00:00ZcontainerStatuses: - conditionID: docker://abcd...ready: true新增的自定义 Condition条件状态status将由用户自定义的外部控制器设置默认值为 False。k8s 将在判断全部 readinessGates 条件都为 True 时才设置 Pod 为服务可用状态Ready 为 True。使用启动Startup探测器保护慢启动容器有时候会有一些现有的应用在启动时需要较长的初始化时间。在这种情况下若要不影响对死锁作出快速响应的探测设置存活探测参数是要技巧的。技巧就是使用相同的命令来设置启动探测针对 HTTP 或 TCP 检测可以通过将 failureThreshold * periodSeconds 参数设置为足够长的时间来应对糟糕情况下的启动时间。前面定义的 yaml 文件示例修改如下ports:
- name: liveness-portcontainerPort: 8080hostPort: 8080
# 存活探针
livenessProbe:httpGet:path: /healthzport: liveness-portfailureThreshold: 1periodSeconds: 10
# 启动探针
startupProbe:httpGet:path: /healthzport: liveness-portfailureThreshold: 30periodSeconds: 10总结k8s 的核心基本是围绕 Pod 展开的顺着 Pod 的资源抽象概念可以延伸出 k8s 基于容器化资源管理系统的各个知识点比如RC、RS、Deployment、DaemonSet、Job CronJob 、StatefulSet、Serivice、Volume、PV/PVC 等资源对象对 Pod 的一系列管控机制比如 Pod 背后的调度机制等因此理解并深入掌握 Pod 是进入 k8s 知识体系的基本前提和保障。