• Pod 使用进阶


    静态 Pod

    在 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 两种方式

    配置文件

    配置文件就是放在特定目录下的标准的 JSON 或 YAML 格式的 pod 定义文件。用 kubelet --pod-manifest-path=<the directory>来启动 kubelet 进程,kubelet 定期的去扫描这个目录,根据这个目录下出现或消失的 YAML/JSON 文件来创建或删除静态 pod。

    比如我们在 node01 这个节点上用静态 pod 的方式来启动一个 nginx 的服务。我们登录到node01节点上面,可以通过下面命令找到 kubelet 对应的启动配置文件

    $ systemctl status kubelet
    

    配置文件路径为:

    $ vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
    ......
    Environment="KUBELET_SYSTEM_PODS_ARGS=--pod-manifest-path=/etc/kubernetes/manifests --allow-privileged=true"
    

    打开这个文件我们可以看到其中有一个参数--pod-manifest-path的配置, 所以如果我们通过kubeadm 的方式来安装的集群环境,对应的 kubelet 已经配置了我们的静态 Pod 文件的路径,默认地址为 /etc/kubernetes/manifests,所以我们只需要在该目录下面创建一个标准的 Pod 的 JSON 或者 YAML 文件即可,如果你的 kubelet 启动参数中没有配置上面的--pod-manifest-path 参数的话,那么添加上这个参数然后重启 kubelet 即可:

    [root@ node01 ~] $ cat <<EOF >/etc/kubernetes/manifest/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 都会自动创建,例如,我们示例中的 static-web。(可能要花些时间拉取nginx 镜像,耐心等待…)

    $ docker ps
    CONTAINER ID IMAGE         COMMAND  CREATED        STATUS         PORTS     NAMES
    f6d05272b57e nginx:latest  "nginx"  8 minutes ago  Up 8 minutes             k8s_web.6f802af4_static-web-fk-node1_default_67e24ed9466ba55986d120c867395f3c_378e5f3c
    

    现在我们通过kubectl工具可以看到这里创建了一个新的镜像 Pod:

    $ kubectl get pods
    NAME                       READY     STATUS    RESTARTS   AGE
    static-web-my-node01        1/1       Running   0          2m
    

    静态 pod 的标签会传递给镜像 Pod,可以用来过滤或筛选。 需要注意的是,我们不能通过 API 服务器来删除静态 pod(例如,通过kubectl命令),kubelet 不会删除它。

    $ kubectl delete pod static-web-my-node01
    $ kubectl get pods
    NAME                       READY     STATUS    RESTARTS   AGE
    static-web-my-node01        1/1       Running   0          12s
    

    我们尝试手动终止容器,可以看到kubelet很快就会自动重启容器。

    $ docker ps
    CONTAINER ID        IMAGE         COMMAND                CREATED       ...
    5b920cbaf8b1        nginx:latest  "nginx -g 'daemon of   2 seconds ago ...
    

    静态 Pod 的动态增加和删除

    运行中的 kubelet 周期扫描配置的目录(我们这个例子中就是 /etc/kubernetes/manifests)下文件的变化,当这个目录中有文件出现或消失时创建或删除 pods:

    $ mv /etc/kubernetes/manifests/static-web.yaml /tmp
    $ sleep 20
    $ docker ps
    // no nginx container is running
    $ mv /tmp/static-web.yaml  /etc/kubernetes/manifests
    $ sleep 20
    $ docker ps
    CONTAINER ID        IMAGE         COMMAND                CREATED           ...
    e7a62e3427f1        nginx:latest  "nginx -g 'daemon of   27 seconds ago
    

    其实我们用 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。

    Downward API

    前面我们使用过一个 volumes 的属性,表示声明一个数据卷,我们可以通过命令kubectl explain pod.spec.volumes去查看该对象下面的属性非常多,前面我们只是简单使用了 hostPath 和 emptyDir{} 这两种模式,其中还有一种模式叫做downwardAPI,这个模式和其他模式不一样的地方在于它不是为了存放容器的数据也不是用来进行容器和宿主机的数据交换的,而是让 Pod 里的容器能够直接获取到这个 Pod 对象本身的一些信息。

    # kubectl explain pod.spec.volumes
    KIND:     Pod
    VERSION:  v1
    
    RESOURCE: volumes <[]Object>
    
    DESCRIPTION:
         List of volumes that can be mounted by containers belonging to the pod.
         More info: https://kubernetes.io/docs/concepts/storage/volumes
    
         Volume represents a named volume in a pod that may be accessed by any
         container in the pod.
    
    FIELDS:
       awsElasticBlockStore <Object>
         AWSElasticBlockStore represents an AWS Disk resource that is attached to a
         kubelet's host machine and then exposed to the pod. More info:
         https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore
    
       azureDisk    <Object>
         AzureDisk represents an Azure Data Disk mount on the host and bind mount to
         the pod.
    
       azureFile    <Object>
         AzureFile represents an Azure File Service mount on the host and bind mount
         to the pod.
    
       cephfs       <Object>
         CephFS represents a Ceph FS mount on the host that shares a pod's lifetime
    
       cinder       <Object>
         Cinder represents a cinder volume attached and mounted on kubelets host
         machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md
    
       configMap    <Object>
         ConfigMap represents a configMap that should populate this volume
    
       csi  <Object>
         CSI (Container Storage Interface) represents ephemeral storage that is
         handled by certain external CSI drivers (Beta feature).
    
       downwardAPI  <Object>
         DownwardAPI represents downward API about the pod that should populate this
         volume
    
       emptyDir     <Object>
         EmptyDir represents a temporary directory that shares a pod's lifetime.
         More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir
    
       ephemeral    <Object>
         Ephemeral represents a volume that is handled by a cluster storage driver
         (Alpha feature). The volume's lifecycle is tied to the pod that defines it
         - it will be created before the pod starts, and deleted when the pod is
         removed.
    
         Use this if: a) the volume is only needed while the pod runs, b) features
         of normal volumes like restoring from snapshot or capacity tracking are
         needed, c) the storage driver is specified through a storage class, and d)
         the storage driver supports dynamic volume provisioning through a
         PersistentVolumeClaim (see EphemeralVolumeSource for more information on
         the connection between this volume type and PersistentVolumeClaim).
    
         Use PersistentVolumeClaim or one of the vendor-specific APIs for volumes
         that persist for longer than the lifecycle of an individual pod.
    
         Use CSI for light-weight local ephemeral volumes if the CSI driver is meant
         to be used that way - see the documentation of the driver for more
         information.
    
         A pod can use both types of ephemeral volumes and persistent volumes at the
         same time.
    
       fc   <Object>
         FC represents a Fibre Channel resource that is attached to a kubelet's host
         machine and then exposed to the pod.
    
       flexVolume   <Object>
         FlexVolume represents a generic volume resource that is
         provisioned/attached using an exec based plugin.
    
       flocker      <Object>
         Flocker represents a Flocker volume attached to a kubelet's host machine.
         This depends on the Flocker control service being running
    
       gcePersistentDisk    <Object>
         GCEPersistentDisk represents a GCE Disk resource that is attached to a
         kubelet's host machine and then exposed to the pod. More info:
         https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk
    
       gitRepo      <Object>
         GitRepo represents a git repository at a particular revision. DEPRECATED:
         GitRepo is deprecated. To provision a container with a git repo, mount an
         EmptyDir into an InitContainer that clones the repo using git, then mount
         the EmptyDir into the Pod's container.
    
       glusterfs    <Object>
         Glusterfs represents a Glusterfs mount on the host that shares a pod's
         lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md
    
       hostPath     <Object>
         HostPath represents a pre-existing file or directory on the host machine
         that is directly exposed to the container. This is generally used for
         system agents or other privileged things that are allowed to see the host
         machine. Most containers will NOT need this. More info:
         https://kubernetes.io/docs/concepts/storage/volumes#hostpath
    
       iscsi        <Object>
         ISCSI represents an ISCSI Disk resource that is attached to a kubelet's
         host machine and then exposed to the pod. More info:
         https://examples.k8s.io/volumes/iscsi/README.md
    
       name <string> -required-
         Volume's name. Must be a DNS_LABEL and unique within the pod. More info:
         https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
    
       nfs  <Object>
         NFS represents an NFS mount on the host that shares a pod's lifetime More
         info: https://kubernetes.io/docs/concepts/storage/volumes#nfs
    
       persistentVolumeClaim        <Object>
         PersistentVolumeClaimVolumeSource represents a reference to a
         PersistentVolumeClaim in the same namespace. More info:
         https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims
    
       photonPersistentDisk <Object>
         PhotonPersistentDisk represents a PhotonController persistent disk attached
         and mounted on kubelets host machine
    
       portworxVolume       <Object>
         PortworxVolume represents a portworx volume attached and mounted on
         kubelets host machine
    
       projected    <Object>
         Items for all in one resources secrets, configmaps, and downward API
    
       quobyte      <Object>
         Quobyte represents a Quobyte mount on the host that shares a pod's lifetime
    
       rbd  <Object>
         RBD represents a Rados Block Device mount on the host that shares a pod's
         lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md
    
       scaleIO      <Object>
         ScaleIO represents a ScaleIO persistent volume attached and mounted on
         Kubernetes nodes.
    
       secret       <Object>
         Secret represents a secret that should populate this volume. More info:
         https://kubernetes.io/docs/concepts/storage/volumes#secret
    
       storageos    <Object>
         StorageOS represents a StorageOS volume attached and mounted on Kubernetes
         nodes.
    
       vsphereVolume        <Object>
         VsphereVolume represents a vSphere volume attached and mounted on kubelets
         host machine
    

    目前 Downward API 提供了两种方式用于将 Pod 的信息注入到容器内部:

    • 环境变量:用于单个变量,可以将 Pod 信息和容器信息直接注入容器内部
    • Volume 挂载:将 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 去获取容器的资源请求和资源限制信息。

    我们直接创建上面的 Pod:

    $ kubectl create -f env-pod.yaml
    pod "env-pod" created
    

    Pod 创建成功后,我们可以查看日志:

    $ kubectl logs env-pod -n kube-system |grep POD
    kubectl logs -f env-pod -n kube-system
    POD_IP=10.244.1.121
    KUBERNETES_SERVICE_PORT=443
    KUBERNETES_PORT=tcp://10.96.0.1:443
    KUBE_DNS_SERVICE_PORT_DNS_TCP=53
    HOSTNAME=env-pod
    SHLVL=1
    HOME=/root
    KUBE_DNS_SERVICE_HOST=10.96.0.10
    KUBE_DNS_PORT_9153_TCP_ADDR=10.96.0.10
    KUBE_DNS_PORT_9153_TCP_PORT=9153
    KUBE_DNS_PORT_9153_TCP_PROTO=tcp
    KUBE_DNS_SERVICE_PORT=53
    KUBE_DNS_PORT=udp://10.96.0.10:53
    POD_NAME=env-pod
    KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    KUBE_DNS_PORT_53_TCP_ADDR=10.96.0.10
    KUBERNETES_PORT_443_TCP_PORT=443
    KUBE_DNS_SERVICE_PORT_METRICS=9153
    KUBERNETES_PORT_443_TCP_PROTO=tcp
    KUBE_DNS_PORT_9153_TCP=tcp://10.96.0.10:9153
    KUBE_DNS_PORT_53_UDP_ADDR=10.96.0.10
    KUBE_DNS_PORT_53_TCP_PORT=53
    KUBE_DNS_PORT_53_TCP_PROTO=tcp
    KUBE_DNS_PORT_53_UDP_PORT=53
    KUBE_DNS_SERVICE_PORT_DNS=53
    KUBE_DNS_PORT_53_UDP_PROTO=udp
    KUBERNETES_SERVICE_PORT_HTTPS=443
    KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
    POD_NAMESPACE=kube-system
    KUBERNETES_SERVICE_HOST=10.96.0.1
    PWD=/
    KUBE_DNS_PORT_53_TCP=tcp://10.96.0.10:53
    KUBE_DNS_PORT_53_UDP=udp://10.96.0.10:53
    

    我们可以看到 Pod 的 IP、NAME、NAMESPACE 都通过环境变量打印出来了。

    上面打印 Pod 的环境变量可以看到有很多内置的变量,其中大部分是系统自动为我们添加的,Kubernetes 会把当前命名空间下面的 Service 信息通过环境变量的形式注入到 Pod 中去:

    $ kubectl get svc -n kube-system
    NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
    kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   4d21h
    

    Volume 挂载

    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 create -f volume-pod.yaml
    pod "volume-pod" created
    

    创建成功后,我们可以进入到容器中查看元信息是不是已经存入到文件中了:

    $ 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['<KEY>'] - 指定<KEY>的Label值
    metadata.annotations['<KEY>'] - 指定<KEY>的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 中会进行热更新。

    PodPreset

    我们已经学习了很多 Pod 的知识点,但是可能有部分同学还是觉得 Pod 的字段属性太多了,很多记不住,查询文档效率不高,你是不是希望 Kubernetes 能够提供一个功能为 Pod 自动填充一些字段呢?这个需求还是很实际的,比如我们按照命名空间来划分不同的环境,然后我们在不同的环境上部署 Pod 后自动为我们加上环境相关的 Labels、Annotations 等等信息,这样就大大提高了编写 YAML 的效率,为此,在 Kubernetes v1.11 版本后就提供了一个叫做 PodPreset(Pod 预设值)的功能可以来解决这个问题。

    Kubernetes 提供了一个 PodPreset 准入控制器,当启用后,PodPreset 会将应用创建请求传入到该控制器上。当有 Pod 创建请求发生时,系统将执行以下操作:

    • 检索所有可用的 PodPreset
    • 检查有 PodPreset 的标签选择器上的标签与正在创建的 Pod 上的标签是否匹配
    • 尝试将由 PodPreset 定义的各种资源合并到正在创建的 Pod 中
    • 出现错误时,在该 Pod 上引发记录合并错误的事件,PodPreset 不会注入任何资源到创建的 Pod 中
    • 注释刚生成的修改过的 Pod spec,以表明它已被 PodPreset 修改过。注释的格式为 podpreset.admission.kubernetes.io/podpreset-": ""

    每个 Pod 可以匹配零个或多个 PodPrestet;并且每个 PodPreset 可以应用于零个或多个 Pod。 PodPreset 应用于一个或多个 Pod 时,Kubernetes 会修改 Pod Spec。对于 Env、EnvFrom 和 VolumeMounts 的更改,Kubernetes 修改 Pod 中所有容器的容器 spec;对于 Volume 的更改,Kubernetes 修改 Pod Spec。

    可能在某些情况下,您希望你的 Pod 不会被任何 Pod Preset 所改变。在这些情况下,您可以在 Pod 的 Pod Spec 中添加上这样一个 annotation:podpreset.admission.kubernetes.io/exclude:"true"。

    启用 PodPreset

    要启用PodPreset功能,需要确保你使用的是 kubernetes 1.8版本以上,然后需要在准入控制中加入PodPreset,另外为了定义 PodPreset 对象,还需要其中 PodPreset 的 API 版本,在 APIServer 启动参数中添加如下配置:

    - --enable-admission-plugins=NodeRestriction,PodPreset
    - --runtime-config=settings.k8s.io/v1alpha1=true
    

    我们这里是使用的 kubeadm 搭建的集群,所以只需要修改静态 Pod 配置即可,路径 /etc/kubernetes/manifests/kube-apiserver.yaml,修改完成后,将 kube-apiserver.yaml 文件移除 manifests 目录,然后移动回来,相当于强制重启,执行如下命令:

    $ kubectl api-versions |grep settings
    settings.k8s.io/v1alpha1
    $ kubectl get podpreset
    No resources found in default namespace.
    

    出现如上的提示证明已经开启成功。

    示例

    我们经常有一个需求就是需要同步 Pod 和宿主机的时间,一般情况下,我们是通过挂载宿主机的 localtime 来完成的,如下 Pod:(time-demo.yaml)

    apiVersion: apps/v1
    kind: Pod
    metadata:
      name: time-demo
      labels:
        app: time
    spec:
      containers:
      - name: time-demo
        image: nginx
        ports:
        - containerPort: 80
    

    我们直接创建该 Pod:

    $ kubectl apply -f time-demo.yaml
    $ kubectl get pods
    NAME         READY   STATUS    RESTARTS   AGE
    time-demo    1/1     Running   0          2m8s
    

    创建完成后,我们可以对比下节点的时间和 Pod 的时间:

    $ date
    Thu Nov 14 12:08:27 CST 2019
    $ kubectl exec time-demo date
    Thu Nov 14 04:08:41 UTC 2019
    

    我们可以看到 Pod 的时间是 UTC 时间,和节点不同步,这个时候我们可以挂载宿主机的 localtime 文件到 Pod 中去,如下所示:

    apiVersion: apps/v1
    kind: Pod
    metadata:
      name: time-demo
      labels:
        app: time
    spec:
      volumes:
      - name: host-time
        hostPath:
          path: /etc/localtime
      containers:
      - name: time-demo
        image: nginx
        volumeMounts:
        - name: host-time
          mountPath: /etc/localtime
        ports:
        - containerPort: 80
    

    这个时候我们重新来测试下:

    $ kubectl delete -f time-demo.yaml
    $ kubectl apply -f time-demo.yaml
    $ kubectl get pods
    NAME         READY   STATUS    RESTARTS   AGE
    time-demo    1/1     Running   0          2m8s
    

    这个时候我们再来看下时间是否同步了:

    $ date
    Thu Nov 14 12:13:40 CST 2019
    $ kubectl exec time-demo date
    Thu Nov 14 12:13:45 CST 2019
    

    可以看到已经生效了。但是往往我们所有的 Pod 都有时间同步的需求,如果所有的 Pod 都来手动进行挂载,是不是显得繁琐多余啊,这个时候我们就可以利用 PodPreset 来预设模板,定义一个如下所示的 PodPreset 资源对象:(time-preset.yaml)

    apiVersion: settings.k8s.io/v1alpha1
    kind: PodPreset
    metadata:
      name: time-preset
      namespace: default
    spec:
      selector:
        matchLabels:
      volumeMounts:
      - name: localtime
        mountPath: /etc/localtime
      volumes:
      - name: localtime
        hostPath:
          path: /etc/localtime
    

    直接创建上面的资源对象:

    $ kubectl apply -f time-preset.yaml
    podpreset.settings.k8s.io/time-preset created
    $ kubectl get podpreset
    NAME          CREATED AT
    time-preset   2019-11-14T06:52:11Z
    

    这个时候我们再来直接创建上面的 Pod:(time-demo.yaml)

    apiVersion: apps/v1
    kind: Pod
    metadata:
      name: time-demo
      labels:
        app: time
    spec:
      containers:
      - name: time-demo
        image: nginx
        ports:
        - containerPort: 80
    

    这个 Pod 我们没有主动挂载时间文件,直接创建:

    $ kubectl delete -f time-demo.yaml
    $ kubectl apply -f time-demo.yaml
    $  kubectl get pods -l app=time
    NAME        READY   STATUS    RESTARTS   AGE
    time-demo   1/1     Running   0          60s
    

    这个时候验证下 Pod 时间是否和宿主机一致:

    $ date
    Thu Nov 14 14:55:02 CST 2019
    $ kubectl exec time-demo date
    Thu Nov 14 14:55:21 CST 2019
    

    可以看到已经同步了~,可以直接导出 Pod 的资源清单查看:

    $ kubectl get pod time-demo -o yaml
    apiVersion: v1
    kind: Pod
    metadata:
      annotations:
        kubectl.kubernetes.io/last-applied-configuration: |
          {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"labels":{"app":"time"},"name":"time-demo","namespace":"default"},"spec":{"containers":[{"image":"nginx","name":"time-demo","ports":[{"containerPort":80}]}]}}
        podpreset.admission.kubernetes.io/podpreset-time-preset: "1469811"
      creationTimestamp: "2019-11-14T06:54:06Z"
      labels:
        app: time
      name: time-demo
      namespace: default
      resourceVersion: "1470398"
      selfLink: /api/v1/namespaces/default/pods/time-demo
      uid: 37a7ab94-c2c3-4b10-88df-0c486fb8d422
    spec:
      containers:
      - image: nginx
        imagePullPolicy: Always
        name: time-demo
        ports:
        - containerPort: 80
          protocol: TCP
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /etc/localtime
          name: localtime
        - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
          name: default-token-5tsh4
          readOnly: true
      dnsPolicy: ClusterFirst
      enableServiceLinks: true
      nodeName: ydzs-node3
      priority: 0
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      serviceAccount: default
      serviceAccountName: default
      terminationGracePeriodSeconds: 30
      tolerations:
      - effect: NoExecute
        key: node.kubernetes.io/not-ready
        operator: Exists
        tolerationSeconds: 300
      - effect: NoExecute
        key: node.kubernetes.io/unreachable
        operator: Exists
        tolerationSeconds: 300
      volumes:
      - hostPath:
          path: /etc/localtime
          type: ""
        name: localtime
      - name: default-token-5tsh4
        secret:
          defaultMode: 420
          secretName: default-token-5tsh4
    status:
      conditions:
      - lastProbeTime: null
        lastTransitionTime: "2019-11-14T06:54:06Z"
        status: "True"
        type: Initialized
      - lastProbeTime: null
        lastTransitionTime: "2019-11-14T06:54:22Z"
        status: "True"
        type: Ready
      - lastProbeTime: null
        lastTransitionTime: "2019-11-14T06:54:22Z"
        status: "True"
        type: ContainersReady
      - lastProbeTime: null
        lastTransitionTime: "2019-11-14T06:54:06Z"
        status: "True"
        type: PodScheduled
      containerStatuses:
      - containerID: docker://a774e82d4ebfde98c37b675f1a46377ccf01c0bba931507172309616dbf49165
        image: nginx:latest
        imageID: docker-pullable://nginx@sha256:922c815aa4df050d4df476e92daed4231f466acc8ee90e0e774951b0fd7195a4
        lastState: {}
        name: time-demo
        ready: true
        restartCount: 0
        started: true
        state:
          running:
            startedAt: "2019-11-14T06:54:21Z"
      hostIP: 10.151.30.57
      phase: Running
      podIP: 10.244.3.64
      podIPs:
      - ip: 10.244.3.64
      qosClass: BestEffort
      startTime: "2019-11-14T06:54:06Z"
    

    从上面 YAML 中也可以看出 Pod 被自动注入了 PodPreset 声明的模板,而且该命名空间下面的所有 Pod 默认都会被注入该模板。

    需要注意的是 PodPreset 资源对象是 namespace scope 的,就是不同的命名空间需要创建不同的 PodPreset。

  • 相关阅读:
    noteOneForGephi
    麻省理工学院公开课:计算机科学及编程导论
    数据存储之归档
    UITableViewController之聊天窗口
    UI基础之UIScrollView
    UI基础之网易新闻demo小项目
    UI基础之UITableViewController
    UI基础之UINavigationController
    UITabBarController
    UIScrollView
  • 原文地址:https://www.cnblogs.com/sanduzxcvbnm/p/16193746.html
Copyright © 2020-2023  润新知