概述
临时容器是一种特殊的容器,该容器可以在现有的Pod中临时运行,以便完成我们发起的操作,比如故障排查。
我们应该使用临时容器来检查服务,而不是用临时容器来构建应用程序。
Pod是kubernetes集群进行管理的最小单元,由于Pod是一次性且可以替换的,因此Pod一旦被创建,就无法将容器加入到Pod中。
而且,我们通常使用Deployment来删除并替换Pod。
但是,有的时候我们需要检查现有Pod的状态,比如对难以复现的故障进行排查。
在这些场景中,可以在现有Pod中运行临时容器来检查其状态并运行任意命令。
什么是临时容器
临时容器和其他容器的不同之处在于,它们缺少对资源或执行的保证,并且永远不会自动重启,因此不适合用来构建应用程序。
临时容器使用和常规容器相同的ContainerSpec来描述,但是许多字段是不兼容或者不允许的。
- 临时容器没有端口配置,因此像ports、livenessProbe、readinessProbe这样的字段是没有的。
- Pod的资源分配是不可变的,因此resources这样的配置临时容器也是没有的。
- ……
临时容器是使用ephemeralcontainers来进行创建的,而不是直接添加到pod.spec中,所以是无法使用kubectl edit来添加一个临时容器。
和常规容器一样,将临时容器添加到Pod后,不能更改或删除临时容器。
临时容器的用途
- 当由于容器奔溃或容器镜像不包含调试工具而导致kubectl exec无用的时候,临时容器对于交互式故障排查非常有用。
- 比如,像distroless 镜像允许用户部署最小的容器镜像,从而减少攻击面并减少故障和漏洞的暴露。由于distroless 镜像不包含Shell或任何的调试工具,因此很难单独使用kubectl exec命令进行故障排查。
- 使用临时容器的时候,启用进程命名空间共享 很有帮助,可以查看其他容器中的进程。
临时容器的配置
目前来说,临时容器默认是关闭的。
查看临时容器是否开启:
1
| kubelet -h | grep EphemeralContainers
|
在每个节点(不管Master节点还是Node节点)修改kubectl的参数:
注意:kubelet的启动文件的路径是/usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf
1 2 3 4 5
| vim /etc/sysconfig/kubelet
# 修改增加--feature-gates EphemeralContainers=true KUBELET_EXTRA_ARGS="--cgroup-driver=systemd --feature-gates EphemeralContainers=true" KUBE_PROXY_MODE="ipvs"
|
1
| vim /var/lib/kubelet/config.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| apiVersion: kubelet.config.k8s.io/v1beta1 authentication: anonymous: enabled: falsevim 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 nodeStatusReportFrequency: 0s nodeStatusUpdateFrequency: 0s rotateCertificates: true runtimeRequestTimeout: 0s staticPodPath: /etc/kubernetes/manifests streamingConnectionIdleTimeout: 0s syncFrequency: 0s volumeStatsAggPeriod: 0s
featureGates: EphemeralContainers: true
|
加载配置文件重启kubelet:
1 2
| systemctl daemon-reload systemctl restart kubelet
|
在Master节点修改kube-apiserver.yaml和kube-scheduler.yaml:
1
| vim /etc/kubernetes/manifests/kube-apiserver.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| apiVersion: v1 kind: Pod metadata: annotations: kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 192.168.49.100:6443 creationTimestamp: null labels: component: kube-apiserver tier: control-plane name: kube-apiserver namespace: kube-system spec: containers: - command: - kube-apiserver - --advertise-address=192.168.49.100 - --allow-privileged=true - --authorization-mode=Node,RBAC - --client-ca-file=/etc/kubernetes/pki/ca.crt - --enable-admission-plugins=NodeRestriction - --enable-bootstrap-token-auth=true - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key - --etcd-servers=https://127.0.0.1:2379 - --insecure-port=0 - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname - --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt - --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key - --requestheader-allowed-names=front-proxy-client - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt - --requestheader-extra-headers-prefix=X-Remote-Extra- - --requestheader-group-headers=X-Remote-Group - --requestheader-username-headers=X-Remote-User - --secure-port=6443 - --service-account-issuer=https://kubernetes.default.svc.cluster.local - --service-account-key-file=/etc/kubernetes/pki/sa.pub - --service-account-signing-key-file=/etc/kubernetes/pki/sa.key - --service-cluster-ip-range=10.96.0.0/12 - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key - --feature-gates=EphemeralContainers=true
|
1
| vim /etc/kubernetes/manifests/kube-scheduler.yaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| apiVersion: v1 kind: Pod metadata: creationTimestamp: null labels: component: kube-scheduler tier: control-plane name: kube-scheduler namespace: kube-system spec: containers: - command: - kube-scheduler - --authentication-kubeconfig=/etc/kubernetes/scheduler.conf - --authorization-kubeconfig=/etc/kubernetes/scheduler.conf - --bind-address=127.0.0.1 - --kubeconfig=/etc/kubernetes/scheduler.conf - --leader-elect=true - --feature-gates=EphemeralContainers=true
|
使用临时容器在线debug
创建一个nginx.yaml文件,内容如下:
1 2 3 4 5 6 7 8 9
| apiVersion: v1 kind: Pod metadata: name: nginx spec: shareProcessNamespace: true containers: - name: nginx image: nginx:1.17.1
|
1
| kubectl apply -f nginx.yaml
|
创建ec.json文件,内容如下(注意:name是Pod的名称):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| { "apiVersion": "v1", "kind": "EphemeralContainers", "metadata": { "name": "nginx" }, "ephemeralContainers": [{ "command": [ "sh" ], "image": "busybox", "imagePullPolicy": "IfNotPresent", "name": "debugger", "stdin": true, "tty": true, "terminationMessagePolicy": "File" }] }
|
使用下面的命令更新已经运行的容器:
1 2 3 4 5 6 7 8
| kubectl replace --raw /api/v1/namespaces/default/pods/nginx/ephemeralcontainers -f ec.json
# 查看Pod kubectl describe pod nginx
# 连接容器 kubectl exec -it nginx -c debugger -- sh kubectl attach -it nginx -c debugger
|