从上面我们可以看出 Kubernetes 由 Master 和 Node 两种节点组成,这两种角色分别对应着控制节点和工作节点(可以理解为老板和员工)。
其中 Master 节点由三个独立的组件组成,它们分别是负责整个集群通信的 API 服务的 kube-apiserver、负责容器调度的 kube-scheduler 以及负责维护集群状态的 kube-controller-manager 组件。整个集群的数据都是通过 kube-apiserver 保存到 etcd 数据库中的,而其他所有组件的通信也都是通过 kube-apiserver 和 etcd 数据库进行通信的,都不会直接和 etcd 进行通信。
工作节点上最核心的组件就是 kubelet,当然还有底层的容器运行时,比如 Docker,其中 kubelet 就是主要来实现和底层的容器运行时进行通信的,这个通信的过程也被 Kubernetes 抽象成了一个 CRI(Container Runtime Interface)的远程调用接口 ,这个接口里面定义了容器运行时的所有标准操作,比如创建容器、删除容器等等。所以对于 Kubernetes 来说他根本不关心你部署的到底是什么容器运行时,只要你这个容器运行时可以实现 CRI 接口就可以被 Kubernetes 来管理。
kubelet 的另外一个重要功能就是调用网络插件(CNI)和存储插件(CSI)为容器配置网络和存储功能,同样的 kubelet 也是把这两个重要功能通过接口暴露给外部了,所以如果我们想要实现自己的网络插件,只需要使用 CNI 就可以很方便的对接到 Kubernetes 集群当中去。
API Server 提供了资源对象的唯一操作入口,其它所有组件都必须通过它提供的 API 来操作资源数据。只有 API Server 会与 etcd 进行通信,其它模块都必须通过 API Server 访问集群状态。API Server 作为 Kubernetes 系统的入口,封装了核心对象的增删改查操作。API Server 以 RESTFul 接口方式提供给外部客户端和内部组件调用,API Server 再对相关的资源数据(全量查询 + 变化监听)进行操作,以达到实时完成相关的业务功能。以 API Server 为 Kubernetes 入口的设计主要有以下好处:
Controller Manager 用于实现 Kubernetes 集群故障检测和恢复的自动化工作。主要负责执行各种控制器:
Scheduler 是负责整个集群的资源调度的,主要的职责如下所示:
kubelet 是负责容器真正运行的核心组件,主要的职责如下所示:
kube-proxy 是为了解决外部网络能够访问集群中容器提供的应用服务而设计的,Proxy 运行在每个Node 上。
每创建一个 Service,kube-proxy 就会从 API Server 获取 Services 和 Endpoints 的配置信息,然后根据其配置信息在 Node 上启动一个 Proxy 的进程并监听相应的服务端口。
当接收到外部请求时,kube-proxy 会根据 Load Balancer 将请求分发到后端正确的容器处理。
kube-proxy 不但解决了同一宿主机相同服务端口冲突的问题,还提供了 Service 转发服务端口对外提供服务的能力。
kube-proxy 后端使用随机、轮循等负载均衡算法进行调度。
Kubectl 是 Kubernetes 的集群管理命令行客户端工具集。通过 Kubectl 命令对 API Server 进行操作,API Server 响应并返回对应的命令结果,从而达到对 Kubernetes 集群的管理
Pod是一组紧密关联的容器集合,它们共享PID、IPC、Network和UTS namespace,是Kubernetes调度的基本单位。Pod的设计理念是支持多个容器在一个Pod中共享网络和文件系统,是可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。容器本质上就是进程。
在 Kubernetes 中,所有资源对象都使用资源清单(yaml或json)来定义,比如我们可以定义一个简单的 nginx 服务,它包含一个镜像为 nginx 的容器:(nginx-pod.yaml)
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
Pod 在 Kubernetes 集群中被创建的基本流程如下所示:
Label 标签在 Kubernetes 资源对象中使用很多,也是非常重要的一个属性,Label 是识别 Kubernetes 对象的标签,以 key/value
的方式附加到对象上(key最长不能超过63字节,value 可以为空,也可以是不超过253字节的字符串)上面我们定义的 Nginx 的 Pod 就添加了一个 app=nginx
的 Label 标签。Label 不提供唯一性,并且实际上经常是很多对象(如Pods)都使用相同的 Label 来标志具体的应用。Label 定义好后其他对象可以使用 Label Selector 来选择一组相同 Label 的对象 (比如 Service 用 Label 来选择一组 Pod)。Label Selector 支持以下几种方式:
当创建 Deployment 时,需要指定两个东西:
Service 是应用服务的抽象,通过 Labels 为应用提供负载均衡和服务发现。匹配 Labels 的 Pod IP 和端口列表组成 Endpoints,由 kube-proxy 负责将服务 IP 负载均衡到这些 Endpoints 上。
每个 Service 都会自动分配一个 cluster IP(仅在集群内部可访问的虚拟地址)和 DNS 名,其他容器可以通过该地址或 DNS 来访问服务,而不需要了解后端容器的运行。
现在我们使用 kubeadm 从头搭建一个使用 containerd 作为容器运行时的 Kubernetes 集群,这里我们安装最新的 v1.22.2 版本。
(1)3个节点,都是 Centos 7.6 系统,内核版本:3.10.0-1062.4.1.el7.x86_64,在每个节点上添加 hosts 信息:
使用命令 hostnamectl set-hostname node1 来修改 hostname。
➜ ~ cat /etc/hosts
192.168.31.31 master1
192.168.31.108 node1
192.168.31.46 node2
(2)禁用防火墙
➜ ~ systemctl stop firewalld
➜ ~ systemctl disable firewalld
(3)禁用SELINUX
➜ ~ setenforce 0
➜ ~ cat /etc/selinux/config
SELINUX=disabled
(4)由于开启内核 ipv4 转发需要加载 br_netfilter 模块,所以加载下该模块:
➜ ~ modprobe br_netfilter
最好将上面的命令设置成开机启动,因为重启后模块失效,下面是开机自动加载模块的方式。首先新建 /etc/rc.sysinit 文件,内容如下所示:
#!/bin/bash
for file in /etc/sysconfig/modules/*.modules ; do
[ -x $file ] && $file
done
然后在 /etc/sysconfig/modules/ 目录下新建如下文件:
➜ ~ cat /etc/sysconfig/modules/br_netfilter.modules
modprobe br_netfilter
增加权限:
➜ ~ chmod 755 br_netfilter.modules
然后重启后,模块就可以自动加载了:
➜ ~ lsmod |grep br_netfilter
br_netfilter 22209 0
bridge 136173 1 br_netfilter
创建 /etc/sysctl.d/k8s.conf文件,添加如下内容:
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
# 下面的内核参数可以解决ipvs模式下长连接空闲超时的问题
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 10
net.ipv4.tcp_keepalive_time = 600
bridge-nf
bridge-nf 使得 netfilter 可以对 Linux 网桥上的 IPv4/ARP/IPv6 包过滤。比如,设置net.bridge.bridge-nf-call-iptables=1后,二层的网桥在转发包时也会被 iptables的 FORWARD 规则所过滤。常用的选项包括:
- net.bridge.bridge-nf-call-arptables:是否在 arptables 的 FORWARD 中过滤网桥的 ARP 包
- net.bridge.bridge-nf-call-ip6tables:是否在 ip6tables 链中过滤 IPv6 包
- net.bridge.bridge-nf-call-iptables:是否在 iptables 链中过滤 IPv4 包
- net.bridge.bridge-nf-filter-vlan-tagged:是否在 iptables/arptables 中过滤打了 vlan 标签的包。
执行如下命令使修改生效:
➜ ~ sysctl -p /etc/sysctl.d/k8s.conf
安装 ipvs:
➜ ~ cat > /etc/sysconfig/modules/ipvs.modules <
上面脚本创建了的 /etc/sysconfig/modules/ipvs.modules文件,保证在节点重启后能自动加载所需模块。使用 lsmod | grep -e ip_vs -e nf_conntrack_ipv4命令查看是否已经正确加载所需的内核模块。
接下来还需要确保各个节点上已经安装了 ipset 软件包:
➜ ~ yum install ipset
为了便于查看 ipvs 的代理规则,最好安装一下管理工具 ipvsadm:
➜ ~ yum install ipvsadm
同步服务器时间
➜ ~ yum install chrony -y
➜ ~ systemctl enable chronyd
➜ ~ systemctl start chronyd
➜ ~ chronyc sources
210 Number of sources = 4
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^+ sv1.ggsrv.de 2 6 17 32 -823us[-1128us] +/- 98ms
^- montreal.ca.logiplex.net 2 6 17 32 -17ms[ -17ms] +/- 179ms
^- ntp6.flashdance.cx 2 6 17 32 -32ms[ -32ms] +/- 161ms
^* 119.28.183.184 2 6 33 32 +661us[ +357us] +/- 38ms
➜ ~ date
Tue Aug 31 14:36:14 CST 2021
关闭 swap 分区:
➜ ~ swapoff -a
修改 /etc/fstab文件,注释掉 SWAP 的自动挂载,使用 free -m确认 swap 已经关闭。swappiness 参数调整,修改 /etc/sysctl.d/k8s.conf添加下面一行:
vm.swappiness=0
执行 sysctl -p /etc/sysctl.d/k8s.conf 使修改生效。
安装Containerd的方法这里不提了。
上面的相关环境配置也完成了,现在我们就可以来安装 Kubeadm 了,我们这里是通过指定yum 源的方式来进行安装的:
➜ ~ cat < /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg
https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
当然了,上面的 yum 源是需要科学上网的,如果不能科学上网的话,我们可以使用阿里云的源进行安装:
➜ ~ cat < /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
然后安装 kubeadm、kubelet、kubectl:
# --disableexcludes 禁掉除了kubernetes之外的别的仓库
➜ ~ yum makecache fast
➜ ~ yum install -y kubelet-1.22.2 kubeadm-1.22.2 kubectl-1.22.2 --disableexcludes=kubernetes
➜ ~ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.2", GitCommit:"8b5a19147530eaac9476b0ab82980b4088bbc1b2", GitTreeState:"clean", BuildDate:"2021-09-15T21:37:34Z", GoVersion:"go1.16.8", Compiler:"gc", Platform:"linux/amd64"}
可以看到我们这里安装的是 v1.22.2 版本,然后将 master 节点的 kubelet 设置成开机启动:
➜ ~ systemctl enable --now kubelet
到这里为止上面所有的操作都需要在所有节点执行配置。
通过下面的命令在 master 节点上输出集群初始化默认使用的配置:
➜ ~ kubeadm config print init-defaults --component-configs KubeletConfiguration > kubeadm.yaml
然后根据我们自己的需求修改配置,比如修改 imageRepository 指定集群初始化时拉取 Kubernetes 所需镜像的地址,kube-proxy 的模式为 ipvs,另外需要注意的是我们这里是准备安装 flannel 网络插件的,需要将 networking.podSubnet 设置为 10.244.0.0/16:
# kubeadm.yaml
apiVersion: kubeadm.k8s.io/v1beta3
bootstrapTokens:
- groups:
- system:bootstrappers:kubeadm:default-node-token
token: abcdef.0123456789abcdef
ttl: 24h0m0s
usages:
- signing
- authentication
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 192.168.31.31 # 指定master节点内网IP
bindPort: 6443
nodeRegistration:
criSocket: /run/containerd/containerd.sock # 使用 containerd的Unix socket 地址
imagePullPolicy: IfNotPresent
name: master
taints: # 给master添加污点,master节点不能调度应用
- effect: "NoSchedule"
key: "node-role.kubernetes.io/master"
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: ipvs # kube-proxy 模式
---
apiServer:
timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta3
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controllerManager: {}
dns: {}
etcd:
local:
dataDir: /var/lib/etcd
imageRepository: registry.aliyuncs.com/k8sxio
kind: ClusterConfiguration
kubernetesVersion: 1.22.2
networking:
dnsDomain: cluster.local
serviceSubnet: 10.96.0.0/12
podSubnet: 10.244.0.0/16 # 指定 pod 子网
scheduler: {}
---
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous:
enabled: false
webhook:
cacheTTL: 0s
enabled: true
x509:
clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
mode: Webhook
webhook:
cacheAuthorizedTTL: 0s
cacheUnauthorizedTTL: 0s
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
cpuManagerReconcilePeriod: 0s
evictionPressureTransitionPeriod: 0s
fileCheckFrequency: 0s
healthzBindAddress: 127.0.0.1
healthzPort: 10248
httpCheckFrequency: 0s
imageMinimumGCAge: 0s
kind: KubeletConfiguration
cgroupDriver: systemd # 配置 cgroup driver
logging: {}
memorySwap: {}
nodeStatusReportFrequency: 0s
nodeStatusUpdateFrequency: 0s
rotateCertificates: true
runtimeRequestTimeout: 0s
shutdownGracePeriod: 0s
shutdownGracePeriodCriticalPods: 0s
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 0s
syncFrequency: 0s
volumeStatsAggPeriod: 0s
在开始初始化集群之前可以使用 kubeadm config images pull --config kubeadm.yaml 预先在各个服务器节点上拉取所k8s需要的容器镜像
配置文件准备好过后,可以使用如下命令先将相关镜像 pull 下面:
➜ ~ kubeadm config images pull --config kubeadm.yaml
[config/images] Pulled registry.aliyuncs.com/k8sxio/kube-apiserver:v1.22.2
[config/images] Pulled registry.aliyuncs.com/k8sxio/kube-controller-manager:v1.22.2
[config/images] Pulled registry.aliyuncs.com/k8sxio/kube-scheduler:v1.22.2
[config/images] Pulled registry.aliyuncs.com/k8sxio/kube-proxy:v1.22.2
[config/images] Pulled registry.aliyuncs.com/k8sxio/pause:3.5
[config/images] Pulled registry.aliyuncs.com/k8sxio/etcd:3.5.0-0
failed to pull image "registry.aliyuncs.com/k8sxio/coredns:v1.8.4": output: time="2021-10-25T17:34:48+08:00" level=fatal msg="pulling image: rpc error: code = NotFound desc = failed to pull and unpack image \"registry.aliyuncs.com/k8sxio/coredns:v1.8.4\": failed to resolve reference \"registry.aliyuncs.com/k8sxio/coredns:v1.8.4\": registry.aliyuncs.com/k8sxio/coredns:v1.8.4: not found"
, error: exit status 1
To see the stack trace of this error execute with --v=5 or higher
上面在拉取 coredns 镜像的时候出错了,没有找到这个镜像,我们可以手动 pull 该镜像,然后重新 tag 下镜像地址即可:
➜ ~ ctr -n k8s.io i pull docker.io/coredns/coredns:1.8.4
docker.io/coredns/coredns:1.8.4: resolved |++++++++++++++++++++++++++++++++++++++|
index-sha256:6e5a02c21641597998b4be7cb5eb1e7b02c0d8d23cce4dd09f4682d463798890: done |++++++++++++++++++++++++++++++++++++++|
manifest-sha256:10683d82b024a58cc248c468c2632f9d1b260500f7cd9bb8e73f751048d7d6d4: done |++++++++++++++++++++++++++++++++++++++|
layer-sha256:bc38a22c706b427217bcbd1a7ac7c8873e75efdd0e59d6b9f069b4b243db4b4b: done |++++++++++++++++++++++++++++++++++++++|
config-sha256:8d147537fb7d1ac8895da4d55a5e53621949981e2e6460976dae812f83d84a44: done |++++++++++++++++++++++++++++++++++++++|
layer-sha256:c6568d217a0023041ef9f729e8836b19f863bcdb612bb3a329ebc165539f5a80: exists |++++++++++++++++++++++++++++++++++++++|
elapsed: 12.4s total: 12.0 M (991.3 KiB/s)
unpacking linux/amd64 sha256:6e5a02c21641597998b4be7cb5eb1e7b02c0d8d23cce4dd09f4682d463798890...
done: 410.185888ms
➜ ~ ctr -n k8s.io i tag docker.io/coredns/coredns:1.8.4 registry.aliyuncs.com/k8sxio/coredns:v1.8.4
然后就可以使用上面的配置文件在 master 节点上进行初始化:
➜ ~ kubeadm init --config kubeadm.yaml
kubeadm join 192.168.31.31:6443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:ca0c87226c69309d7779096c15b6a41e14b077baf4650bfdb6f9d3178d4da645
根据安装提示拷贝 kubeconfig 文件:
➜ ~ mkdir -p $HOME/.kube
➜ ~ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
➜ ~ sudo chown $(id -u):$(id -g) $HOME/.kube/config
然后可以使用 kubectl 命令查看 master 节点已经初始化成功了:
➜ ~ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master1 Ready control-plane,master 41s v1.22.2
如果忘记了上面的 join 命令可以使用命令kubeadm token create --print-join-command 重新获取。
记住初始化集群上面的配置和操作要提前做好,将 master 节点上面的 $HOME/.kube/config 文件拷贝到 node 节点对应的文件中,安装 kubeadm、kubelet、kubectl(可选),然后执行上面初始化完成后提示的 join 命令即可:
➜ ~ kubeadm join 192.168.31.31:6443 --token abcdef.0123456789abcdef \
> --discovery-token-ca-cert-hash sha256:ca0c87226c69309d7779096c15b6a41e14b077baf4650bfdb6f9d3178d4da645
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
这个时候其实集群还不能正常使用,因为还没有安装网络插件,接下来安装网络插件,可以在文档 https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/ 中选择我们自己的网络插件,这里我们安装 flannel:
➜ ~ wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
# 如果有节点是多网卡,则需要在资源清单文件中指定内网网卡
# 搜索到名为 kube-flannel-ds 的 DaemonSet,在kube-flannel容器下面
➜ ~ vi kube-flannel.yml
......
containers:
- name: kube-flannel
image: quay.io/coreos/flannel:v0.15.0
command:
- /opt/bin/flanneld
args:
- --ip-masq
- --kube-subnet-mgr
- --iface=eth0 # 如果是多网卡的话,指定内网网卡的名称
......
➜ ~ kubectl apply -f kube-flannel.yml # 安装 flannel 网络插件
Flannel 网络插件
当我们部署完网络插件后执行 ifconfig 命令,正常会看到新增的cni0与 flannel1这两个虚拟设备,但是如果没有看到 cni0这个设备也不用太担心,我们可以观察 /var/lib/cni目录是否存在,如果不存在并不是说部署有问题,而是该节点上暂时还没有应用运行,我们只需要在该节点上运行一个 Pod 就可以看到该目录会被创建,并且 cni0设备也会被创建出来。
用同样的方法添加另外一个节点即可。
如果你的集群安装过程中遇到了其他问题,我们可以使用下面的命令来进行重置:
➜ ~ kubeadm reset
➜ ~ ifconfig cni0 down && ip link delete cni0
➜ ~ ifconfig flannel.1 down && ip link delete flannel.1
➜ ~ rm -rf /var/lib/cni/
https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#create-daemonset-v1-apps
YAML 是专门用来写配置文件的语言,非常简洁和强大,远比 JSON 格式方便。YAML语言(发音 /ˈjæməl/)的设计目标,就是方便人类读写。它实质上是一种通用的数据串行化格式。
可以通过 kubectl explain 命令来了解:
$ kubectl explain deployment.spec
它的基本语法规则如下:
---
是分隔符在 Kubernetes 中,我们只需要了解两种结构类型就行了:
Lists就是列表,说白了就是数组,在 YAML 文件中我们可以这样定义:
args
- Cat
- Dog
- Fish
nginx-deployment.yaml
apiVersion: apps/v1 # API版本
kind: Deployment # API对象类型
metadata:
name: nginx-deploy
labels:
chapter: first-app
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # Pod 副本数量
template: # Pod 模板
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
其实 Pod 也只是一个逻辑概念,真正起作用的还是 Linux 容器的 Namespace 和 Cgroup 这两个最基本的概念,Pod 被创建出来其实是一组共享了一些资源的容器而已。首先 Pod 里面的所有容器,都是共享的同一个 Network Namespace,但是涉及到文件系统的时候,默认情况下 Pod 里面的容器之间的文件系统是完全隔离的,但是我们可以通过声明来共享同一个 Volume。
比如下面的示例:
apiVersion: v1
kind: Pod
metadata:
name: counter
spec:
volumes:
- name: varlog
hostPath:
path: /var/log/counter
containers:
- name: count
image: busybox
args:
- /bin/sh
- -c
- >
i=0;
while true;
do
echo "$i: $(date)" >> /var/log/1.log;
i=$((i+1));
sleep 1;
done
volumeMounts:
- name: varlog
mountPath: /var/log
- name: count-log
image: busybox
args: [/bin/sh, -c, 'tail -n+1 -f /opt/log/1.log']
volumeMounts:
- name: varlog
mountPath: /opt/log
示例中我们在 Pod 的顶层声明了一个名为 varlog 的 Volume,而这个 Volume 的类型是 hostPath,也就意味这个宿主机的 /var/log/counter 目录将被这个 Pod 共享,共享给谁呢?在需要用到这个数据目录的容器上声明挂载即可,也就是通过 volumeMounts 声明挂载的部分,这样我们这个 Pod 就实现了共享容器的 /var/log 目录,而且数据被持久化到了宿主机目录上。
这个方式也是 Kubernetes 中一个非常重要的设计模式:sidecar 模式的常用方式。典型的场景就是容器日志收集,比如上面我们的这个应用,其中应用的日志是被输出到容器的 /var/log 目录上的,这个时候我们可以把 Pod 声明的 Volume 挂载到容器的 /var/log 目录上,然后在这个 Pod 里面同时运行一个 sidecar 容器,他也声明挂载相同的 Volume 到自己容器的 /var/log (或其他)目录上,这样我们这个 sidecar 容器就只需要从 /var/log 目录下面不断消费日志发送到 Elasticsearch 中存储起来就完成了最基本的应用日志的基本收集工作了。
除了这个应用场景之外使用更多的还是利用 Pod 中的所有容器共享同一个 Network Namespace 这个特性,这样我们就可以把 Pod 网络相关的配置和管理也可以交给一个 sidecar 容器来完成,完全不需要去干涉用户容器,这个特性在现在非常火热的 Service Mesh(服务网格)中应用非常广泛,典型的应用就是 Istio
将多个容器部署到同一个 Pod 中的最主要参考就是应用可能由一个主进程和一个或多个的辅助进程组成,比如上面我们的日志收集的 Pod,需要其他的 sidecar 容器来支持日志的采集。
所以当我们判断是否需要在 Pod 中使用多个容器的时候,我们可以按照如下的几个方式来判断:
一个 Pod 的完整生命周期过程:其中包含 Init Container、Pod Hook、健康检查 三个主要部分
kubectl explain pod.status
Pod 的状态定义在 PodStatus 对象中,其中有一个 phase 字段,下面是 phase 的可能取值
PodStatus 对象中还包含一个 PodCondition 的数组,里面包含的属性有:
可以通过配置 restartPolicy 字段来设置 Pod 中所有容器的重启策略,其可能值为 Always、OnFailure 和 Never,默认值为 Always,restartPolicy 指通过 kubelet 在同一节点上重新启动容器。通过 kubelet 重新启动的退出容器将以指数增加延迟(10s,20s,40s…)重新启动,上限为 5 分钟,并在成功执行 10 分钟后重置。不同类型的的控制器可以控制 Pod 的重启策略:
Init Container就是用来做初始化工作的容器,可以是一个或者多个,如果有多个的话,这些容器会按定义的顺序依次执行。只有所有的初始化容器执行完之后,主容器才会被启动
初始化容器应用场景:
比如现在我们来实现一个功能,在 Nginx Pod 启动之前去重新初始化首页内容,如下所示的资源清单:(init-pod.yaml)
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
volumes:
- name: workdir
emptyDir: {}
initContainers:
- name: install
image: busybox
command:
- wget
- "-O"
- "/work-dir/index.html"
- http://www.baidu.com # https
volumeMounts:
- name: workdir
mountPath: "/work-dir"
containers:
- name: web
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
使用的是 emptyDir{},这个是一个临时的目录,数据会保存在 kubelet 的工作目录下面,生命周期等同于 Pod 的生命周期。
我们定义了一个初始化容器,该容器会下载一个 html 文件到 /work-dir 目录下面,但是由于我们又将该目录声明挂载到了全局的 Volume,同样的主容器 nginx 也将目录 /usr/share/nginx/html 声明挂载到了全局的 Volume,所以在主容器的该目录下面会同步初始化容器中创建的 index.html 文件。
直接创建上面的 Pod:➜ ~ kubectl apply -f init-pod.yaml
Pod 现在的状态处于 Init:0/1 状态,意思就是现在第一个初始化容器还在执行过程中,此时我们可以查看 Pod 的详细信息:
➜ ~ kubectl describe pod init-demo
Kubernetes 为我们的容器提供了生命周期的钩子,就是我们说的 Pod Hook,Pod Hook 是由 kubelet 发起的,当容器中的进程启动前或者容器中的进程终止之前运行,这是包含在容器的生命周期之中。我们可以同时为 Pod 中的所有容器都配置 hook。
Kubernetes 为我们提供了两种钩子函数:
如果 PostStart 或者 PreStop 钩子失败, 它会杀死容器。所以我们应该让钩子函数尽可能的轻量。当然有些情况下,长时间运行命令是合理的, 比如在停止容器之前预先保存状态。
另外我们有两种方式来实现上面的钩子函数:
以下示例中,定义了一个 Nginx Pod,其中设置了 PostStart 钩子函数,即在容器创建成功后,写入一句话到 /usr/share/message 文件中:
# pod-poststart.yaml
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"]
创建成功后可以查看容器中 /usr/share/message 文件是否内容正确:
➜ ~ kubectl exec -it hook-demo1 -- cat /usr/share/message
Hello from the postStart handler
当用户请求删除含有 Pod 的资源对象时(如 Deployment 等),K8S 为了让应用程序优雅关闭(即让应用程序完成正在处理的请求后,再关闭软件),K8S 提供两种信息通知:
默认所有的优雅退出时间都在30秒内,kubectl delete 命令支持 --grace-period=
选项,这个选项允许用户用他们自己指定的值覆盖默认值,值0代表强制删除 pod。 在 kubectl 1.5 及以上的版本里,执行强制删除时必须同时指定 --force --grace-period=0
。
以下示例中,定义了一个 Nginx Pod,其中设置了 PreStop 钩子函数,即在容器退出之前,优雅的关闭 Nginx:
# pod-prestop.yaml
apiVersion: v1
kind: Pod
metadata:
name: hook-demo2
spec:
containers:
- name: hook-demo2
image: nginx
lifecycle:
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"] # 优雅退出
---
apiVersion: v1
kind: Pod
metadata:
name: hook-demo3
spec:
volumes:
- name: message
hostPath:
path: /tmp
containers:
- name: hook-demo2
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: message
mountPath: /usr/share/
lifecycle:
preStop:
exec:
command: ['/bin/sh', '-c', 'echo Hello from the preStop Handler > /usr/share/message']
上面定义的两个 Pod,一个是利用 preStop 来进行优雅删除,另外一个是利用 preStop 来做一些信息记录的事情。
现在在 Pod 的整个生命周期中,能影响到 Pod 的就只剩下健康检查这一部分了。在 Kubernetes 集群当中,我们可以通过配置liveness probe(存活探针)和 readiness probe(可读性探针) 来影响容器的生命周期:
和前面的钩子函数一样的,我们这两个探针的支持下面几种配置方式:
我们先来给大家演示下存活探针的使用方法,首先我们用 exec 执行命令的方式来检测容器的存活,如下:
# liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec
spec:
containers:
- name: liveness
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
我们这里需要用到一个新的属性 livenessProbe,下面通过 exec 执行一段命令:
我们在容器启动的时候,执行了如下命令:
/bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"
意思是说在容器最开始的30秒内创建了一个 /tmp/healthy 文件,在这30秒内执行 cat /tmp/healthy 命令都会返回一个成功的返回码。30 秒后,我们删除这个文件,现在执行 cat /tmp/healthy 就会失败了(默认检测失败3次才认为失败),所以这个时候就会重启容器了。
同样的,我们还可以使用HTTP GET请求来配置我们的存活探针,我们这里使用一个 liveness 镜像来验证演示下:
# liveness-http.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-http
spec:
containers:
- name: liveness
image: cnych/liveness
args:
- /server
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: X-Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
同样的,根据 periodSeconds 属性我们可以知道 kubelet 需要每隔3秒执行一次 liveness Probe,该探针将向容器中的 server 的 8080 端口发送一个 HTTP GET 请求。如果 server 的 /healthz 路径的 handler 返回一个成功的返回码,kubelet 就会认定该容器是活着的并且很健康,如果返回失败的返回码,kubelet 将杀掉该容器并重启它。initialDelaySeconds 指定kubelet 在该执行第一次探测之前需要等待3秒钟。
通常来说,任何大于200小于400的状态码都会认定是成功的返回码。其他返回码都会被认为是失败的返回码。
除了上面的 exec 和 httpGet 两种检测方式之外,还可以通过 tcpSocket 方式来检测端口是否正常,大家可以按照上面的方式结合 kubectl explain 命令自己来验证下这种方式。
另外前面我们提到了探针里面有一个 initialDelaySeconds 的属性,可以来配置第一次执行探针的等待时间,对于启动非常慢的应用这个参数非常有用,比如 Jenkins、Gitlab 这类应用,但是如何设置一个合适的初始延迟时间呢?这个就和应用具体的环境有关系了,所以这个值往往不是通用的,这样的话可能就会导致一个问题,我们的资源清单在别的环境下可能就会健康检查失败了,为解决这个问题,在 Kubernetes v1.16 版本官方特地新增了一个 startupProbe(启动探针),该探针将推迟所有其他探针,直到 Pod 完成启动为止,使用方法和存活探针一样:
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30 # 尽量设置大点
periodSeconds: 10
比如上面这里的配置表示我们的慢速容器最多可以有5分钟(30个检查 * 10秒= 300s)来完成启动。
比如上面这里的配置表示我们的慢速容器最多可以有5分钟(30个检查 * 10秒= 300s)来完成启动。
有的时候,应用程序可能暂时无法对外提供服务,例如,应用程序可能需要在启动期间加载大量数据或配置文件。在这种情况下,您不想杀死应用程序,也不想对外提供服务。那么这个时候我们就可以使用 readiness probe 来检测和减轻这些情况,Pod 中的容器可以报告自己还没有准备,不能处理 Kubernetes 服务发送过来的流量。readiness probe 的配置跟 liveness probe 基本上一致的,唯一的不同是使用 readinessProbe 而不是 livenessProbe,两者如果同时使用的话就可以确保流量不会到达还未准备好的容器,准备好过后,如果应用程序出现了错误,则会重新启动容器。对于就绪探针我们会在后面 Service 的章节和大家继续介绍。
另外除了上面的 initialDelaySeconds 和 periodSeconds 属性外,探针还可以配置如下几个参数:
首先对于 CPU,我们知道计算机里 CPU 的资源是按“时间片”的方式来进行分配的,系统里的每一个操作都需要 CPU 的处理,所以,哪个任务要是申请的 CPU 时间片越多,那么它得到的 CPU 资源就越多,这个很容器理解。
spec.containers[].resources.limits.cpu
:CPU 上限值,可以短暂超过,容器也不会被停止spec.containers[].resources.requests.cpu
:CPU请求值,Kubernetes 调度算法里的依据值,可以超过这里需要明白的是,如果 resources.requests.cpu 设置的值大于集群里每个节点的最大 CPU 核心数,那么这个 Pod 将无法调度,因为没有节点能满足它。
requests 是用于集群调度使用的资源,而 limits 才是真正的用于资源限制的配置,如果你需要保证的你应用优先级很高,也就是资源吃紧的情况下最后再杀掉你的 Pod,那么你就把你的 requests 和 limits 的值设置成一致。
了解下 CGroup 里面对于 CPU 资源的单位换算:
1 CPU = 1000 millicpu(1 Core = 1000m)
0.5 CPU = 500 millicpu (0.5 Core = 500m)
# pod-resource-demo1.yaml
apiVersion: v1
kind: Pod
metadata:
name: resource-demo1
spec:
containers:
- name: resource-demo1
image: nginx
ports:
- containerPort: 80
resources:
requests:
memory: 50Mi
cpu: 50m
limits:
memory: 100Mi
cpu: 100m
CPU 我们给的是 50m,也就是 0.05core,这 0.05core 也就是占了 1 CPU 里的 5% 的资源时间。而限制资源是给的是 100m,但是需要注意的是 CPU 资源是可压缩资源,也就是容器达到了这个设定的上限后,容器性能会下降,但是不会终止或退出。
创建完成后,我们可以看到 Pod 被调度到 node1 这个节点上:
➜ ~ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
resource-demo1 1/1 Running 0 24s 10.244.1.27 node1
然后我们到 node1 节点上去查看 Pod 里面启动的 resource-demo1 这个容器:
➜ ~ crictl ps
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID
1e4ef680a5a88 87a94228f133e 41 seconds ago Running resource-demo1 0 a00af47f2a12e
......
我们可以去查看下主容器的信息:
➜ ~ crictl inspect 1e4ef680a5a88
{
"status": {
"id": "1e4ef680a5a88af7eae88a6901f12eb103dc3f8e1807f26337cd9bfb3704ca05",
"metadata": {
"attempt": 0,
"name": "resource-demo1"
},
......
"linux": {
"resources": {
"devices": [
{
"allow": false,
"access": "rwm"
}
],
"memory": {
"limit": 104857600
},
"cpu": {
"shares": 51,
"quota": 10000,
"period": 100000
}
},
"cgroupsPath": "kubepods-burstable-poda194c43a_9551_494b_bd72_ab898afdcc0c.slice:cri-containerd:1e4ef680a5a88af7eae88a6901f12eb103dc3f8e1807f26337cd9bfb3704ca05",
......
实际上我们就可以看到这个容器的一些资源情况,Pod 上的资源配置最终也还是通过底层的容器运行时去控制 CGroup 来实现的,我们可以进入如下目录查看 CGroup 的配置,该目录就是 CGroup 父级目录,而 CGroup 是通过文件系统来进行资源限制的,所以我们上面限制容器的资源就可以在该目录下面反映出来:
➜ ~ cd /sys/fs/cgroup/cpu/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-poda194c43a_9551_494b_bd72_ab898afdcc0c.slice
➜ ~ ls
cgroup.clone_children cpuacct.stat cpu.cfs_period_us cpu.rt_runtime_us notify_on_release
cgroup.event_control cpuacct.usage cpu.cfs_quota_us cpu.shares tasks
cgroup.procs cpuacct.usage_percpu cpu.rt_period_us cpu.stat
➜ ~ cat cpu.cfs_quota_us
10000
其中 cpu.cfs_quota_us 就是 CPU 的限制值,如果要查看具体的容器的资源,我们也可以进入到容器目录下面去查看即可。
最后我们了解下内存这块的资源控制,内存的单位换算比较简单:
1 MiB = 1024 KiB,内存这块在 Kubernetes 里一般用的是Mi单位,当然你也可以使用Ki、Gi甚至Pi,看具体的业务需求和资源容量。
这里注意的是MiB ≠ MB,MB 是十进制单位,MiB 是二进制,平时我们以为 MB 等于 1024KB,其实1MB=1000KB,1MiB才等于1024KiB。中间带字母 i 的是国际电工协会(IEC)定的,走1024乘积;KB、MB、GB 是国际单位制,走1000乘积。
在 Kubernetes 集群中除了我们经常使用到的普通的 Pod 外,还有一种特殊的 Pod,叫做Static Pod,也就是我们说的静态 Pod,静态 Pod 有什么特殊的地方呢?
静态 Pod 直接由节点上的 kubelet 进程来管理,不通过 master 节点上的 apiserver。无法与我们常用的控制器 Deployment 或者 DaemonSet 进行关联,它由 kubelet 进程自己来监控,当 pod 崩溃时会重启该 pod,kubelet 也无法对他们进行健康检查。静态 pod 始终绑定在某一个 kubelet 上,并且始终运行在同一个节点上。kubelet 会自动为每一个静态 pod 在 Kubernetes 的 apiserver 上创建一个镜像 Pod,因此我们可以在 apiserver 中查询到该 pod,但是不能通过 apiserver 进行控制(例如不能删除)。
创建静态 Pod 有两种方式:配置文件和 HTTP 两种方式
kubelet --pod-manifest-path=
来启动 kubelet 进程,kubelet 定期的去扫描这个目录,根据这个目录下出现或消失的 YAML/JSON 文件来创建或删除静态 pod。➜ ~ cat /var/lib/kubelet/config.yaml
......
staticPodPath: /etc/kubernetes/manifests # 和命令行的 pod-manifest-path 参数一致
......
打开这个文件我们可以看到其中有一个属性为 staticPodPath 的配置,其实和命令行的 --pod-manifest-path
配置是一致的,所以如果我们通过 kubeadm 的方式来安装的集群环境,对应的 kubelet 已经配置了我们的静态 Pod 文件的路径,默认地址为 /etc/kubernetes/manifests
,所以我们只需要在该目录下面创建一个标准的 Pod 的 JSON 或者 YAML 文件即可,如果你的 kubelet 启动参数中没有配置上面的–pod-manifest-path 参数的话,那么添加上这个参数然后重启 kubelet 即可:
➜ ~ cat </etc/kubernetes/manifests/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。
kubelet 启动时,由 --pod-manifest-path= 或 --manifest-url= 参数指定的目录下定义的所有 pod 都会自动创建
静态 Pod 的动态增加和删除
运行中的 kubelet 周期扫描配置的目录(我们这个例子中就是 /etc/kubernetes/manifests)下文件的变化,当这个目录中有文件出现或消失时创建或删除pods
其实我们用 kubeadm 安装的集群,master 节点上面的几个重要组件都是用静态 Pod 的方式运行的,我们登录到 master 节点上查看/etc/kubernetes/manifests目录:
➜ ~ ls /etc/kubernetes/manifests/
etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
现在明白了吧,这种方式也为我们将集群的一些组件容器化提供了可能,因为这些 Pod 都不会受到 apiserver 的控制,不然我们这里kube-apiserver怎么自己去控制自己呢?万一不小心把这个 Pod 删掉了呢?所以只能有kubelet自己来进行控制,这就是我们所说的静态 Pod。
前面我们使用过一个 volumes 的属性,表示声明一个数据卷,我们可以通过命令kubectl explain pod.spec.volumes去查看该对象下面的属性非常多,前面我们只是简单使用了 hostPath 和 emptyDir{} 这两种模式,其中还有一种模式叫做downwardAPI,这个模式和其他模式不一样的地方在于它不是为了存放容器的数据也不是用来进行容器和宿主机的数据交换的,而是让 Pod 里的容器能够直接获取到这个 Pod 对象本身的一些信息。
目前 Downward API 提供了两种方式用于将 Pod 的信息注入到容器内部:
我们通过 Downward API 来将 Pod 的 IP、名称以及所对应的 namespace 注入到容器的环境变量中去,然后在容器中打印全部的环境变量来进行验证,对应资源清单文件如下:
# env-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: env-pod
namespace: kube-system
spec:
containers:
- name: env-pod
image: busybox
command: ["/bin/sh", "-c", "env"]
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
我们可以看到上面我们使用了一种新的方式来设置 env 的值:valueFrom,由于 Pod 的 name 和 namespace 属于元数据,是在 Pod 创建之前就已经定下来了的,所以我们可以使用 metata 就可以获取到了,但是对于 Pod 的 IP 则不一样,因为我们知道 Pod IP 是不固定的,Pod 重建了就变了,它属于状态数据,所以我们使用 status 这个属性去获取。另外除了使用 fieldRef获取 Pod 的基本信息外,还可以通过 resourceFieldRef 去获取容器的资源请求和资源限制信息。
env:
- name: MY_CPU_REQUEST
valueFrom:
resourceFieldRef:
containerName: test-container
resource: requests.cpu
- name: MY_CPU_LIMIT
valueFrom:
resourceFieldRef:
containerName: test-container
resource: limits.cpu
- name: MY_MEM_REQUEST
valueFrom:
resourceFieldRef:
containerName: test-container
resource: requests.memory
- name: MY_MEM_LIMIT
valueFrom:
resourceFieldRef:
containerName: test-container
resource: limits.memory
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
Pod 创建成功后,我们可以查看日志:➜ ~ kubectl logs env-pod -n kube-system |grep POD
我们可以看到 Pod 的 IP、NAME、NAMESPACE 都通过环境变量打印出来了。
Downward API除了提供环境变量的方式外,还提供了通过 Volume 挂载的方式去获取 Pod 的基本信息。接下来我们通过Downward API将 Pod 的 Label、Annotation 等信息通过 Volume 挂载到容器的某个文件中去,然后在容器中打印出该文件的值来验证,对应的资源清单文件如下所示:
# volume-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-pod
namespace: kube-system
labels:
k8s-app: test-volume
node-env: test
annotations:
own: youdianzhishi
build: test
spec:
volumes:
- name: podinfo
downwardAPI:
items:
- path: labels
fieldRef:
fieldPath: metadata.labels
- path: annotations
fieldRef:
fieldPath: metadata.annotations
containers:
- name: volume-pod
image: busybox
args:
- sleep
- "3600"
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
我们将元数据 labels 和 annotaions 以文件的形式挂载到了 /etc/podinfo 目录下,创建上面的 Pod:
创建成功后,我们可以进入到容器中查看元信息是不是已经存入到文件中了:
➜ ~ kubectl exec -it volume-pod /bin/sh -n kube-system
/ # ls /etc/podinfo/
..2019_11_13_09_57_15.990445016/ annotations
..data/ labels
/ # cat /etc/podinfo/labels
k8s-app="test-volume"
/ # cat /etc/podinfo/annotations
build="test"
kubectl.kubernetes.io/last-applied-configuration="{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{\"build\":\"test\",\"own\":\"youdianzhishi\"},\"labels\":{\"k8s-app\":\"test-volume\",\"node-env\":\"test\"},\"name\":\"volume-pod\",\"namespace\":\"kube-system\"},\"spec\":{\"containers\":[{\"args\":[\"sleep\",\"3600\"],\"image\":\"busybox\",\"name\":\"volume-pod\",\"volumeMounts\":[{\"mountPath\":\"/etc/podinfo\",\"name\":\"podinfo\"}]}],\"volumes\":[{\"downwardAPI\":{\"items\":[{\"fieldRef\":{\"fieldPath\":\"metadata.labels\"},\"path\":\"labels\"},{\"fieldRef\":{\"fieldPath\":\"metadata.annotations\"},\"path\":\"annotations\"}]},\"name\":\"podinfo\"}]}}\n"
kubernetes.io/config.seen="2019-11-13T17:57:15.320894744+08:00"
kubernetes.io/config.source="api"
我们可以看到 Pod 的 Labels 和 Annotations 信息都被挂载到 /etc/podinfo 目录下面的 lables 和 annotations 文件了。
目前,Downward API 支持的字段已经非常丰富了,比如:
1. 使用 fieldRef 可以声明使用:
spec.nodeName - 宿主机名字
status.hostIP - 宿主机IP
metadata.name - Pod的名字
metadata.namespace - Pod的Namespace
status.podIP - Pod的IP
spec.serviceAccountName - Pod的Service Account的名字
metadata.uid - Pod的UID
metadata.labels[''] - 指定的Label值
metadata.annotations[''] - 指定的Annotation值
metadata.labels - Pod的所有Label
metadata.annotations - Pod的所有Annotation
2. 使用 resourceFieldRef 可以声明使用:
容器的 CPU limit
容器的 CPU request
容器的 memory limit
容器的 memory request
需要注意的是,Downward API 能够获取到的信息,一定是 Pod 里的容器进程启动之前就能够确定下来的信息。而如果你想要获取 Pod 容器运行后才会出现的信息,比如,容器进程的 PID,那就肯定不能使用 Downward API 了,而应该考虑在 Pod 里定义一个 sidecar 容器来获取了。
在实际应用中,如果你的应用有获取 Pod 的基本信息的需求,一般我们就可以利用Downward API来获取基本信息,然后编写一个启动脚本或者利用initContainer将 Pod 的信息注入到我们容器中去,然后在我们自己的应用中就可以正常的处理相关逻辑了。
除了通过 Downward API 可以获取到 Pod 本身的信息之外,其实我们还可以通过映射其他资源对象来获取对应的信息,比如 Secret、ConfigMap 资源对象,同样我们可以通过环境变量和挂载 Volume 的方式来获取他们的信息,但是,通过环境变量获取这些信息的方式,不具备自动更新的能力。所以,一般情况下,都建议使用 Volume 文件的方式获取这些信息,因为通过 Volume 的方式挂载的文件在 Pod 中会进行热更新。