k8s初始化pod-pod标签

发布时间 2024-01-08 23:39:07作者: FuShudi

initContainers(初始化容器)

k8s在1.3版本的时候引入了一个初始化容器(init container)的特性,主要是用于在业务容器启动之前来启动一个或多个初始化容器,来为业务容器提供基础
容器的启动过程大概是这样的

init1 --> init2 --> ……所有的初始化容器都执行完之后,才会启动应用容器 --> app container
记住这个启动顺序,初始化容器一定是在业务容器之前启动的
我们来看一个示例

# 这个文件名是initContainer.yml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: initpod02
  name: initpod02
spec:
  containers:
  - image: nginx
    imagePullPolicy: Never
    name: initpod02
    resources: {}
# 注意看这里,有一个键是initContainers,不难理解,这个就是初始化容器
  initContainers:
# 这个就是初始化容器使用的镜像,不是业务容器的镜像哦
  - image: centos
    name: initc
    imagePullPolicy: Never
# 这里说的是安全上下文,我们给这个初始化容器一个超级权限
    securityContext:
            privileged: true
# 这里就是很重要的,初始化容器的动作,初始化容器创建出来之后他该干什么,我们就写在这个地方
    command: ["ls"]
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

我们去apply 一下这个文件,注意一下启动顺序哦,是初始化容器都跑完了才会启动业务容器,单反有一个初始化容器失败,那么整个pod就会失败
这里我们只是让他执行一下ls,肯定是成功的,我们来看看是不是

[root@master k8s]# kubectl apply -f initContainer.yml 
pod/initpod02 created
[root@master k8s]# kubectl get pods
NAME        READY   STATUS            RESTARTS   AGE
initpod02   0/1     PodInitializing   0          3s
[root@master k8s]# kubectl get pods
NAME        READY   STATUS    RESTARTS   AGE
initpod02   1/1     Running   0          4s

我们可以看到,他有一个initializing的状态,然后就变成了running,说明业务容器正常运行了,那么我们将上面的文件进行修改
让他执行一个错误的命令,看看业务容器是否会启动,我们能将yaml文件修改,将初始化容器里的command改掉

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: initpod02
  name: initpod02
spec:
  containers:
  - image: nginx
    imagePullPolicy: Never
    name: initpod02
    resources: {}
  initContainers:
  - image: centos
    name: initc
    imagePullPolicy: Never
    securityContext:
            privileged: true
# 我们将这里的命令故意写错
    command: ["lssssssss"]
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

我们来看看效果

# 首先删掉刚刚创建的pod
[root@master k8s]# kubectl delete -f initContainer.yml 
pod "initpod02" deleted
# 然后我们使用新的文件重新创建
[root@master k8s]# kubectl apply -f initContainer.yml 
pod/initpod02 created
[root@master k8s]# kubectl get pods 
NAME        READY   STATUS                  RESTARTS     AGE
initpod02   0/1     Init:CrashLoopBackOff   1 (3s ago)   4s

这个时候我们发现他报错了,restarts为1,我们来看看他的报错信息

[root@master k8s]# kubectl describe pod/initpod02
# 信息太多,我们直接看event栏
Events:
  Type     Reason     Age                    From               Message
  ----     ------     ----                   ----               -------
  Normal   Scheduled  3m31s                  default-scheduler  Successfully assigned default/initpod02 to node2
  Normal   Pulled     119s (x5 over 3m31s)   kubelet            Container image "centos" already present on machine
  Normal   Created    119s (x5 over 3m31s)   kubelet            Created container initc
  Warning  Failed     119s (x5 over 3m31s)   kubelet            Error: failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "lssssssss": executable file not found in $PATH: unknown
  Warning  BackOff    104s (x10 over 3m30s)  kubelet            Back-off restarting failed container initc in pod initpod02_default(2688307f-4e20-4ace-8448-f256f7ec5255)

在这里我们可以看见,有一个Error,报错信息是 lssssssss 这个命令没有在环境变量里面找到,他可能找到吗?不可能啊,因为压根就没有这个命令啊
所以初始化容器执行失败了,业务容器也不会再启动了,那么相信你肯定会有疑问,这个初始化容器也没什么用啊,应用场景是什么呢?
我们来看看这样一个yaml文件

# 我先说一下这个yaml文件的作用,首先是业务容器是nginx,初始化容器镜像使用的是centos
# 然后我们看到下面,是有定义volume的,volume的名字叫做testvolume,这个volume被业务容器挂在到了/test下,被初始化容器挂在到了/initdir下
# 初始化容器执行的命令是 echo 123 > /initdir/initfile
# 按照容器的启动顺序,我们最终应该会在业务容器里面的/test下会有一个initfile文件
# 我们来执行看看
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: initpod02
  name: initpod02
spec:
  containers:
  - image: nginx
    imagePullPolicy: Never
    name: initpod02
    volumeMounts:
    - name: testvolume
      mountPath: /test
    resources: {}
  initContainers:
  - image: centos
    name: initc
    volumeMounts:
    - name: testvolume
      mountPath: /initdir
    imagePullPolicy: Never
    securityContext:
            privileged: true
    command: ["sh","-c","echo 123 > /initdir/initfile"]
  dnsPolicy: ClusterFirst
  restartPolicy: Always
  volumes:
  - name: testvolume
    emptyDir: {}
status: {}

这个文件的作用在上面说过了,没看见的可以往上稍微翻一下,现在我们开始apply

[root@master k8s]# kubectl apply -f test.yml 
pod/initpod02 created
[root@master k8s]# kubectl get pods
NAME        READY   STATUS    RESTARTS   AGE
initpod02   1/1     Running   0          4s

我们可以看见容器是正常运行的,说明初始化容器成功了,那么我们现在需要关注的是initfile到底存不存在与业务容器nginx里面的/test下

[root@master k8s]# kubectl exec -it initpod02 -- cat /test/initfile
Defaulted container "initpod02" out of: initpod02, initc (init)
123

我们可以看见,业务容器下确实存在了initfile文件,并且内容就是我们ehco的123
现在我们应该可以理解初始化容器是用来干什么的了,他可以在我们的业务容器启动之前执行一些操作,来为我们的业务容器提供一定的平台

静态pod

什么是静态pod?干什么的?带着这2个问题我们往下看
首先,我们目前的k8s集群是使用kubeadm创建的,在我们使用kubeadm init初始化集群的时候呢,他会帮我们创建一些pod,在kube-system命令空间下,包括这个命令空间,也是在集群初始化的时候被创建出来的,当我们的集群起来之后呢,我们才可以去创建pod
诶?不对啊,不是说集群起来之后才能创建pod吗,那我们集群没有起来的时候,kube-system里面的pod是怎么创建的呢,既然集群都没有起来,你凭什么可以创建pod?那不能创建pod,我们的集群怎么起来呢? 这???? 这不是世纪难题,是先有鸡还是先有蛋吗?
这个就是我们现在要了解的静态pod
假设我们现在有了一个yaml文件,那我们想要创建pod的话是不是应该使用命令kubectl apply 去让他根据这个文件创建pod啊,那有没有这样一种可能,我只要在某个目录下面存在yaml文件,那k8s集群就会自动创建pod,而不需要我们去apply这个文件呢?当然是可以的,这个就是静态pod,当文件存在,那么pod被创建,当文件不存在,那么pod就会被删除,我们不妨来做个实验

# 谨慎操作,一步步来,我们先查看kubelet的状态,这里面会显示他读取了哪个配置文件
# 在/etc/kubernetes/manifests这个目录下有一些yaml文件,仔细一点看就可以看出来,他的文件名跟kube-system里面是一样的
[root@master manifests]# kubectl get pods -n kube-system
NAME                              READY   STATUS    RESTARTS        AGE
coredns-5bbd96d687-7h966          1/1     Running   0               21m
coredns-5bbd96d687-gg849          1/1     Running   0               21m
etcd-master                       1/1     Running   0               33h
kube-apiserver-master             1/1     Running   0               33h
kube-controller-manager-master    1/1     Running   0               33h
kube-proxy-mp98s                  1/1     Running   2 (4h16m ago)   33h
kube-proxy-snk8k                  1/1     Running   5 (3h16m ago)   33h
kube-proxy-xmxpj                  1/1     Running   2 (4h16m ago)   33h
kube-scheduler-master             1/1     Running   0               33h
metrics-server-7bf8c67888-qjqvw   1/1     Running   1 (4h16m ago)   28h
[root@master manifests]# ls
etcd.yaml  kube-apiserver.yaml  kube-controller-manager.yaml  kube-scheduler.yaml
# 他创建出来的pod在文件名后面加上了master,那我们可以尝试一下在这里面创建一个yaml文件,看看他是不是真的会自己去创建pod
# 这里我们可以这样,我们在其他节点的这个目录下去创建yaml文件,因为master上删错了很麻烦,其他节点上没有这些文件
# 我们可以在master节点上生成yaml文件然后传到node1上
root@master k8s]# kubectl run pod01 --image=nginx --image-pull-policy=IfNotPresent --dry-run=client -o yaml > nginx.yaml
# 然后scp到node1节点上
[root@master k8s]#  scp nginx.yml node1:/root
# 先传到其他目录,我们此时先查看pod
[root@master k8s]# kubectl get pods
NAME          READY   STATUS    RESTARTS   AGE
initpod02     1/1     Running   0          85m
# 可以看到这是之前的initpod,没有其他的pod了,现在我们将这个yaml文件移动到指定目录下
# 注意,现在的操作是在node1上
[root@node1 root]# cd /etc/kubernetes/manifests/
[root@node1 manifests]# cp /root/nginx.yml  .
# 此时我们回到master节点
[root@master k8s]# kubectl get pods
NAME          READY   STATUS    RESTARTS   AGE
initpod02     1/1     Running   0          85m
pod01-node1   1/1     Running   0          12s

我们可以发现,我们仅仅只是就将yaml文件移动到了这个目录下,他就创建出来了pod,我们现在尝试删除这个yaml文件呢

[root@node1 manifests]# ls
nginx.yml
[root@node1 manifests]# rm nginx.yml 
rm: remove regular file 'nginx.yml'? y
[root@node1 manifests]# 
# 我们回到master节点查看
[root@master k8s]# kubectl get pods
NAME        READY   STATUS    RESTARTS   AGE
initpod02   1/1     Running   0          88m

发现pod已经没有了

当然,我们也可以自己手动指定他读取的目录,只需要在node节点上修改kubelet的配置文件

# 在[Service]段落里面的Environment里面加上参数 --pod-manifest-path=你想要的目录就好了
[root@master k8s]# vim /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf 
  1 # Note: This dropin only works with kubeadm and kubelet v1.11+
  2 [Service]
  3 Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/e    tc/kubernetes/kubelet.conf"
# 我是在这个Environment里加的
  4 Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml --pod-manifest-path=/etc/kubernetes/test"
  5 # This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS     variable dynamically
  6 EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
  7 # This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user sh    ould use
  8 # the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be s    ourced from this file.
  9 EnvironmentFile=-/etc/sysconfig/kubelet
 10 ExecStart=
 11 ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARG

然后我们重启服务,创建目录

[root@node1 manifests]# mkdir /etc/kubernetes/test
[root@node1 manifests]# systemctl daemon-reload
[root@node1 manifests]# systemctl restart kubelet.service 
# 然后我们将nginx.yaml复制到test目录下,看看效果
[root@node1 test]# cd /etc/kubernetes/test
[root@node1 test]# cp /root/nginx.yml  .
# 回到master查看pod
[root@master k8s]# kubectl get pods
NAME          READY   STATUS    RESTARTS   AGE
initpod02     1/1     Running   0          95m
pod01-node1   1/1     Running   0          5s

发现pod也是被创建出来了,一样的,删除文件pod也会被删除,像这样的,yaml文件在,pod就在,yaml文件没了,pod就没了,是不是有一种人在塔在的感觉,这种就是静态pod

pod的调度策略(将pod指派给特定节点)

在我们创建pod的时候,k8s底层会进行一系列的选举,调度机制,来选举出最适合创建这个pod的节点
我们现在来创建4个pod,来看看他是怎么调度的

[root@master k8s]# kubectl run pod01 --image=nginx --image-pull-policy=IfNotPresent 
pod/pod01 created
[root@master k8s]# kubectl run pod02 --image=nginx --image-pull-policy=IfNotPresent 
pod/pod02 created
[root@master k8s]# kubectl run pod03 --image=nginx --image-pull-policy=IfNotPresent 
pod/pod03 created
[root@master k8s]# kubectl run pod04 --image=nginx --image-pull-policy=IfNotPresent 
pod/pod04 created
[root@master k8s]# kubectl get pods -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP               NODE    NOMINATED NODE   READINESS GATES
pod01   1/1     Running   0          28s   10.244.166.148   node1   <none>           <none>
pod02   1/1     Running   0          24s   10.244.166.149   node1   <none>           <none>
pod03   1/1     Running   0          10s   10.244.104.43    node2   <none>           <none>
pod04   1/1     Running   0          6s    10.244.104.44    node2   <none>           <none>
[root@master k8s]# 

正好2个在node1,2个在node2,那我们再来搞2个,他是不是应该也是负载均衡呢

[root@master k8s]# kubectl run pod05 --image=nginx --image-pull-policy=IfNotPresent 
pod/pod05 created
[root@master k8s]# kubectl run pod06 --image=nginx --image-pull-policy=IfNotPresent 
pod/pod06 created
[root@master k8s]# kubectl get pods -o wide
NAME    READY   STATUS    RESTARTS   AGE     IP               NODE    NOMINATED NODE   READINESS GATES
pod01   1/1     Running   0          3m10s   10.244.166.148   node1   <none>           <none>
pod02   1/1     Running   0          3m6s    10.244.166.149   node1   <none>           <none>
pod03   1/1     Running   0          2m52s   10.244.104.43    node2   <none>           <none>
pod04   1/1     Running   0          2m48s   10.244.104.44    node2   <none>           <none>
pod05   1/1     Running   0          8s      10.244.166.150   node1   <none>           <none>
pod06   1/1     Running   0          4s      10.244.104.45    node2   <none>           <none>

跟我们猜想的一样,他好像确实是一个node1一个node2,当然你在你的机器上可能并不是这样,这是因为我的两个node都是克隆出来的,配置完全一样
那我们如果想指定在某个node上创建呢,可以做到吗?可以的。如何实现呢?通过标签
标签的格式就是键值对的方式,比如
aa=bb,cc=dd
我们也可以查看标签

# 查看pod标签
[root@master k8s]# kubectl get pods --show-labels 
NAME    READY   STATUS    RESTARTS   AGE     LABELS
pod01   1/1     Running   0          9m2s    run=pod01
pod02   1/1     Running   0          8m58s   run=pod02
pod03   1/1     Running   0          8m44s   run=pod03
pod04   1/1     Running   0          8m40s   run=pod04
pod05   1/1     Running   0          6m      run=pod05
pod06   1/1     Running   0          5m56s   run=pod06
# 查看node标签
[root@master k8s]# kubectl get nodes --show-labels 
NAME     STATUS   ROLES           AGE   VERSION   LABELS
master   Ready    control-plane   34h   v1.26.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=
node1    Ready    <none>          34h   v1.26.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux
node2    Ready    <none>          34h   v1.26.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux

那这标签是怎么实现能指定调度pod呢,我们这样想一想,如果我在node上打上一个标签,cloud=666,然后我们在创建pod的时候也给他这个标签,他是否会被调度到这个node上呢
我们来看看

# 给主机添加标签
[root@master k8s]# kubectl label nodes node2 cloud=666
node/node2 labeled

现在我们生成一个yaml文件,修改他的标签

[root@master k8s]# kubectl run pod07 --image=nginx --image-pull-policy=IfNotPresent --dry-run=client -o yaml > label.yaml

然后我们修改这个文件
在第6行这里直接修改

  1 apiVersion: v1
  2 kind: Pod
  3 metadata:
  4   creationTimestamp: null
# 这个是默认的,我们将他修改成cloud: 666
  5   labels:
  6     run: pod07 
  7   name: pod07
  8 spec:
  9   containers:
 10   - image: nginx
 11     imagePullPolicy: IfNotPresent
 12     name: pod07
 13     resources: {}
 14   dnsPolicy: ClusterFirst
 15   restartPolicy: Always
 16 status: {}

改完之后的文件是这样的

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    cloud: "666"
  name: pod07
spec:
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: pod07
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
# 需要在spec下面加上节点选择,然后给他一个标签
  nodeSelector:
          cloud: "666"
status: {}

现在我们的节点标签是cloud=666,然后yaml文件里的node选择的标签也是cloud=666,那么他是不是一定会调度到node2呢?我们看看

[root@master k8s]# kubectl apply -f label.yaml 
pod/pod07 created
# 然后我们进去将pod的name改成pod08
# 再apply一次
[root@master k8s]# kubectl apply -f label.yaml 
pod/pod08 created
[root@master k8s]# kubectl get pods -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP               NODE    NOMINATED NODE   READINESS GATES
pod01   1/1     Running   0          23m   10.244.166.148   node1   <none>           <none>
pod02   1/1     Running   0          22m   10.244.166.149   node1   <none>           <none>
pod03   1/1     Running   0          22m   10.244.104.43    node2   <none>           <none>
pod04   1/1     Running   0          22m   10.244.104.44    node2   <none>           <none>
pod05   1/1     Running   0          20m   10.244.166.150   node1   <none>           <none>
pod06   1/1     Running   0          19m   10.244.104.45    node2   <none>           <none>
pod07   1/1     Running   0          26s   10.244.104.46    node2   <none>           <none>
pod08   1/1     Running   0          3s    10.244.104.47    node2   <none>           <none>

我们可以看见新启动的2个pod都是在node2上了,说明标签起作用了,不相信的话你还可以继续创建,他一定都是在node2上
所以我们可以通过这样的方式来指定pod在哪个节点上跑

取消标签,这个是非常容易的

# 直接在你想要删掉的标签的key后面加上一个-就好了
[root@master k8s]# kubectl label nodes node2 cloud-
node/node2 unlabeled