Pod的作用

在k8s中pod是最小的管理单位,在一个pod中通常会包含一个或多个容器。
在每一个Pod中都有一个特殊的Pause容器和一个或多个业务容器,Pause来源于pause-amd64镜像,Pause容器在Pod中具有非常重要的作用:

  • Pause容器作为Pod容器的根容器,其本地于业务容器无关,它的状态代表了整个pod的状态。
  • Pod里的多个业务容器共享Pause容器的IP,每个Pod被分配一个独立的IP地址,Pod中的每个容器共享网络命名空间,包括IP地址和网络端口。Pod内的容器可以使用localhost相互通信。k8s支持底层网络集群内任意两个Pod之间进行通信。
  • Pod中的所有容器都可以访问共享volumes,允许这些容器共享数据。volumes还用于Pod中的数据持久化,以防其中一个容器需要重新启动而丢失数据。

    kubernetes 的对象定义一般都用YAML或JSON格式的文件,可以使用这个网址验证yaml语法:https://codebeautify.org/yaml-validator

Pod的定义

Kubernetes中所有的资源对象都可以采用YAML或者JSON格式的文件来定义。
我们可以定义含有单个服务容器的pod,但是在另一种场景中,当两个服务应用为紧耦合的关系时,应该将他们组合成一个整体对外提供服务,如下定义,将两个容器打包为一个pod:

apiVersion: v1
kind: Pod
metadata: 
  name: redis-php
  labels:
    name: redis-php
spec:
  hostNetwork: true                        # 指定可以通过宿主机访问pod中的服务
  containers:
  - name: frontend
    image: kubeguide/guestbook-php-frontend:localredis
    ports:
    - containerPort: 80
    # 指定宿主机映射端口。在不与hostNetwork: true 同时使用时可以指定任意端口,但是在某些使用CNI插件的情况下可能不会生效。
    # 与hostNetwork使用的时候,只能与容器端口一致,且可以省略,一般只在测试时使用。
      hostPort: 80                          
  - name: redis
    image: kubeguide/redis-master
    ports:
    - containerPort: 6379
      hostPort: 6379

执行此文件:

kubectl create -f pod-test.yaml

查看pod信息,可以查看到容器的创建过程(Event 事件信息):

 kubectl  describe pod  redis-php

静态Pod

静态pod是仅仅存在于特定Node上的Pod。 它们不能通过API server进行管理,无法与RC,Deployment或者DaemonSet进行关联,并且kubelet也无法对其进行健康检查。
静态Pod总是由kubelet创建,并且总是在kubelet所在的节点Node上运行。
创建静态pod有两种方式:

  • 配置文件方式
  • HTTP方式

配置文件方式

1、首先在特定的节点上创建目录,并创建static-web.yaml的文件,此文件定义了静态pod的信息:

mkdir /etc/kubernetes.d/

cat /etc/kubernetes.d/static-web.yaml 

 apiVersion: v1
 kind: Pod
 metadata:
   name: static-web
   labels:
     name: static-web
 spec:
   containers:
     - name: static-web
       image: nginx:1.12.2
       ports:
         - name: web
           containerPort: 80
           protocol: TCP

2、修改kubelet启动文件参数,添加--pod-manifest-path=/etc/kubernetes.d/, 并重启kubelet。
3、Master节点上查看:

# kubectl get pod
NAME                  READY     STATUS    RESTARTS   AGE
static-web-10.0.0.3   1/1       Running   0          40s

本地node节点上已经启动:

docker ps|grep nginx

33620ea59551        4037a5562b03                             "nginx -g 'daemon ..."   5 minutes ago       Up 5 minutes                            k8s_static-web_static-web-10.0.0.3_default_3fcbc9dd218421884a0a557825af52d5_0

4、删除此静态pod,由于是在node节点由kubelet创建的,所以在master上执行删除并不会生效,需要到此node节点上删除此文件即可:

rm -f static-web.yaml

HTTP方式

使用HTTP的方式也需要修改对应node节点的kubelet配置参数--manifest-url=<URL>
Kubelet将定期的从参数 --manifest-url=<URL> 配置的地址下载文件,并将其解析为 json/yaml 格式的 pod 描述。
它的工作原理与从 --pod-manifest-path=<directory> 中发现文件执行创建/更新静态 pod 是一样的,即,文件的每次更新都将应用到运行中的静态 pod 中。

Pod共享Volume

Pod的Volume用于同一个pod中的多个容器数据共享。
Kubernetes Volume具有明确的生命周期-与pod相同。因此,Volume的生命周期比Pod中运行的任何容器要持久,在容器重新启动时可以保留数据,当然,当Pod被删除不存在时,Volume也将消失。注意,Kubernetes支持许多类型的Volume,Pod可以同时使用任意类型/数量的Volume。
内部实现中,一个Volume只是一个目录,目录中可能有一些数据,pod的容器可以访问这些数据。至于这个目录是如何产生的、支持它的介质、其中的数据内容是什么,这些都由使用的特定Volume类型来决定。在定义Pod时,需要指定对应的Volume。

本地存储

  • emptyDir: 无需指定宿主机的对应目录路径,由Pod自动创建,Pod移除时数据会永久删除,作为容器间的共享目录。上面的示例就是此格式的Volume。
cat pod-volume.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: volume-pod
spec:
  containers:
  - name: tomcat
    image: tomcat
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: app-logs
      mountPath: /usr/local/tomcat/logs       # 容器中的日志目录
  - name: logreader                           # 容器名称,使用kubectl指定容器时需要使用此名称,无法使用容器ID
    image: busybox
    command: ["sh","-c","tail -f /logs/catalina*.log"]
    volumeMounts:
    - name: app-logs
      mountPath: /logs
  volumes:                                    # volume设定的具体信息
  - name: app-logs
    emptyDir: {}                              # volume的类型,emryDir的参数设置为{}       

这里的示例是将tomcat容器的日志目录和logreader的log目录共享,通过在logreader执行查看日志的命令,就可以在logreader中看到tomcat的启动日志。
查看日志:

 kubectl logs volume-pod -c logreader

也可以直接登录容器查看:

 kubectl exec -it volume-pod -c tomcat sh

或者不登录直接执行容器命令:

 kubectl exec -it volume-pod -c logreader  ls /logs
 kubectl exec -it volume-pod -c tomcat ls /usr/local/tomcat/logs
  • hostPath: Pod挂载宿主机上的文件和目录,可用于永久保存日志,容器内部访问宿主机数据,定义方式如下:
apiVersion: v1
kind: Pod
metadata:
  name: volume-pod
spec:
  containers:
  - name: tomcat
    image: tomcat
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: app-logs
      mountPath: /usr/local/tomcat/logs
  volumes:
  - name: app-logs
    hostPath:
      # 宿主机的本地目录,需要事先创建,否则pod无法启动
      path: /logs
      # this field is optional
      type: Directory

当pod删除时,数据依旧保存在宿主机目录中。

网络存储

k8s 也支持网络存储如NFS,分布式存储GlusterFS,对象存储Cephfs等,这里就不具体介绍,例如使用ceph的yaml配置如下:

apiVersion: v1
kind: Pod
metadata:
  name: cephfs
spec:
  containers:
  - name: cephfs-rw
    image: kubernetes/pause
    volumeMounts:
    - mountPath: "/mnt/cephfs"
      name: cephfs
  volumes:
  - name: cephfs
    cephfs:
      monitors:
      - 10.16.154.78:6789
      - 10.16.154.82:6789
      - 10.16.154.83:6789
      # by default the path is /, but you can override and mount a specific path of the filesystem by using the path attribute
      # path: /some/path/in/side/cephfs 
      user: admin
      secretFile: "/etc/ceph/admin.secret"
      readOnly: true

ConfigMap

configMap资源提供了一种将配置数据注入到Pod中的方法。在应用中可以使用这种方式来提供应用需要的配置信息。 存储在ConfigMap对象中的数据可以在configMap类型的卷中引用,然后由运行在Pod中的容器化应用程序使用。
当引用一个configMap对象时,你可以简单地在卷中提供它的名字来引用它。 您还可以自定义用于ConfigMap中特定条目的路径。
一般有如下用法:

  • 生成容器内的环境变量
  • 设置容器启动命令的启动参数(需要设置环境变量)
  • 以Volume的方式挂载为容器内部的文件或目录

    在使用configMap时,需要先创建configMap,然后再在其他对象中引用。

一般创建configMap可以通过两种方式:

  • 通过yaml配置文件方式
  • 通过使用kubectl create configmap命令的方式创建

1、通过YAML文件创建ConfigMap资源对象

apiVersion: v1
kind: ConfigMap
metadata:
  name: config-test
data: 
  apploglevel: info
  appdatadir: /var/data

与一般的yaml文件一致,data区域为配置的参数区域。如果参数很复杂并且很多,使用这种方式并不方便管理和查看。

2、通过使用kubectl create configmap命令创建
命令格式:

   kubectl create configmap <map-name> <data-source>

map-name: 自定义的configMap名称
data-source: 指定的配置文件信息,可以是文件或文件目录。 无论是哪种文件,最终都必须是键值对形式。

指定目录创建

如在/tmp/configmap 有如下文件:

# ll

-rw-r--r-- 1 root root 157 Jun 15 17:19 game.properties
-rw-r--r-- 1 root root  83 Jun 15 17:26 ui.properties

# cat game.properties 
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30

# cat ui.properties 
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

创建cofigmap:

   # kubectl create configmap game-config --from-file=/tmp/configmap/

查看configmap:

# kubectl describe configmaps game-config
Name:         game-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
ui.properties:
----
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
Events:  <none>

将两个配置文件内容融合在了一起。也可以使用yaml的格式输出,这和自定义YAML的输出是相同的:

# kubectl get configmaps game-config -o yaml
apiVersion: v1
data:
  game.properties: |-
    enemies=aliens
    lives=3
    enemies.cheat=true
    enemies.cheat.level=noGoodRotten
    secret.code.passphrase=UUDDLRLRBABAS
    secret.code.allowed=true
    secret.code.lives=30
  ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true
    how.nice.to.look=fairlyNice
kind: ConfigMap
metadata:
  creationTimestamp: 2018-06-15T09:29:39Z
  name: game-config
  namespace: default
  resourceVersion: "150723"
  selfLink: /api/v1/namespaces/default/configmaps/game-config
  uid: a079ab6c-707e-11e8-96f7-000c295f81fb
指定文件

同理,也可以使用指定文件的方式:

   # kubectl create configmap game-spec --from-file=/tmp/configmap/game.properties
指定键值对

也可直接在命令行通过键值对直接指定:

# kubectl create configmap  man-spec --from-literal=name=config.test --from-literal=value=test

# kubectl get configmap man-spec -o yaml
apiVersion: v1
data:
  name: config.test
  value: test
kind: ConfigMap
metadata:
  creationTimestamp: 2018-06-15T09:47:35Z
  name: man-spec
  namespace: default
  resourceVersion: "151795"
  selfLink: /api/v1/namespaces/default/configmaps/man-spec
  uid: 21b019cb-7081-11e8-96f7-000c295f81fb
指定环境变量文件

可以使用--from-env-file 参数指定系统中环境变量文件,使用此参数时,指定文件有以下特点:

  • 文件包含列表类型的环境变量
  • 每一行都必须是 key=value 的格式
  • # 开头的行为注释,将会被忽略
  • 空行将会被忽略
  • 引号将不会被特殊处理,它将会作为configMap 值的一部分。

如下,使用环境变量文件,创建一个对应的confmap配置:

# 文件内容:
# cat game-env-file.properties 
enemies=aliens
lives=3
allowed="true"

# This comment and the empty line above it are ignored

# 创建ConfigMap
# kubectl create configmap game-env --from-env-file=/tmp/configmap/game-env-file.properties

# 查看创建的configmap:
# kubectl get configmap game-env -o yaml
apiVersion: v1
data:
  allowed: '"true"'
  enemies: aliens
  lives: "3"
kind: ConfigMap
metadata:
  creationTimestamp: 2018-06-15T10:17:59Z
  name: game-env
  namespace: default
  resourceVersion: "153612"
  selfLink: /api/v1/namespaces/default/configmaps/game-env
  uid: 60bb2081-7085-11e8-96f7-000c295f81fb
在指定文件的时候指定KEY

使用指定文件的方式定义ConfigMap默认情况是使用的文件名为KEY,其实我们也可以自定义key值:

kubectl create configmap game-3 --from-file=<my-key-name>=<path-to-file>

3、定义Pod使用ConfigMap
Pod中加载ConfigMap常见的有两种方式:

  • 在Pod中定义环境变量参数,引用ConfigMap
  • 通过volume mount使用ConfigMap
Pod中指定引用ConfigMap定义环境变量参数

如果ConfigMap有如下定义:

# cat config-map.yaml 

apiVersion: v1
kind: ConfigMap
metadata:
  name: config-test
data: 
  apploglevel: info
  appdatadir: /var/data

在pod中引用此configmap:

# cat configmap-pod.yaml

   apiVersion: v1
   kind: Pod
   metadata:
     name: configmap-pod
   spec:
     containers:
       - name: test-container
         image: busybox
         command: [ "/bin/sh", "-c", "env|grep TEST" ]   # 获取只包含TEST开头的环境变量名称
         env:
           # Define the environment variable
           - name: TEST-KEY1                             # 容器中的环境变量参数
             valueFrom:                                  
               configMapKeyRef:                          # 参数来源于configmap
                 # The ConfigMap containing the value you want to assign to TEST-KEY1
                 name: config-test                       # 指定已经存在的configmap的名称
                 key: apploglevel                        # 指定此configmap中需要引用的key
           - name: TEST-KEY2
             valueFrom:
               configMapKeyRef:
                 # The ConfigMap containing the value you want to assign to TEST-KEY2
                 name: config-test                       # 这里可以指定不同configmap
                 key: appdatadir
     restartPolicy: Never                                # 执行完启动命令后将退出,不再被系统自动重启

创建configmap和定义的POD:

kubectl create -f config-map.yaml
kubectl create -f configmap-pod.yaml

查看pod,执行完命令之后已经正常退出:

# kubectl get pod -o wide
NAME            READY     STATUS      RESTARTS   AGE       IP         NODE
configmap-pod   0/1       Completed   0          57s       10.2.6.3   10.0.0.3

查看输出的环境变量:

# kubectl logs configmap-pod

TEST-KEY1=info
TEST-KEY2=/var/data
Pod中引用指定configmap的所有参数

当我们需要引用configmap中的定义的所有参数和变量时,可以使用envFrom 参数来指定configMap:

# cat pod-configmap1.yaml 
   apiVersion: v1
   kind: Pod
   metadata:
     name: test-pod
   spec:
     containers:
       - name: test-container
         image: busybox
         command: [ "/bin/sh", "-c", "env" ]
         envFrom:                             
         - configMapRef:                       # 此处不再指定key
             name: config-test                 # 指定configmap
     restartPolicy: Never

运行pod,可以发现和configmap中定义的参数一致:

# kubectl logs test-pod |grep app

apploglevel=info
appdatadir=/var/data
通过VolumeMount使用configmap

我们也可以在定义pod时,将configmap以卷的形式挂载到容器中的目录,这样就相当于容器直接使用这个配置文件了:

apiVersion: v1
kind: Pod
metadata:
  name: volume-test-pod
spec:
  containers:
    - name: test-container
      image: busybox
      command: [ "/bin/sh", "-c", "ls -l /etc/config/" ]
      volumeMounts: 
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap: 
        # Provide the name of the ConfigMap containing the files you want
        # to add to the container
        name: config-test 
  restartPolicy: Never

这样就将对应的参数挂载到了容器中的/etc/config目录,登录容器,可以看到:

 # ls -l /etc/config/
total 0
lrwxrwxrwx    1 root     root            17 Jun 17 09:07 appdatadir -> ..data/appdatadir
lrwxrwxrwx    1 root     root            18 Jun 17 09:07 apploglevel -> ..data/apploglevel

对应的文件中记录了参数值,/etc/config 路径下如果有其他文件,使用这种方式会清除此路径下之前的所有文件。

在实际的应用中,一般是通过configmap指定文件,挂载的configmap卷中会有一个以key值命名的文件,文件中包含了参数信息:

# kubectl  get configmap game-config -o yaml
apiVersion: v1
data:
  game.properties: |-
    enemies=aliens
    lives=3
    enemies.cheat=true
    enemies.cheat.level=noGoodRotten
    secret.code.passphrase=UUDDLRLRBABAS
    secret.code.allowed=true
    secret.code.lives=30
  ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true
    how.nice.to.look=fairlyNice
kind: ConfigMap
metadata:
  creationTimestamp: 2018-06-15T09:29:39Z
  name: game-config
  namespace: default
  resourceVersion: "150723"
  selfLink: /api/v1/namespaces/default/configmaps/game-config
  uid: a079ab6c-707e-11e8-96f7-000c295f81fb

创建使用volume挂载此configmap的pod:

apiVersion: v1
kind: Pod
metadata:
  name: volume-test-pod
spec:
  containers:
    - name: test-container
      image: busybox
      command: [ "/bin/sh", "-c", "sleep 150" ]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        # Provide the name of the ConfigMap containing the files you want
        # to add to the container
        name: game-config
  restartPolicy: Never

登录此容器,可以看到对应的两个文件game.propertiesui.properties,里面的参数都是以键值存储:

# kubectl exec -it volume-test-pod sh
/ # ls -l /etc/config/
total 0
lrwxrwxrwx    1 root     root            22 Jun 17 09:22 game.properties -> ..data/game.properties
lrwxrwxrwx    1 root     root            20 Jun 17 09:22 ui.properties -> ..data/ui.properties

/etc/config # cat game.properties 
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30/etc/config # 

/etc/config # cat ui.properties 
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

当然,这里我们也可以定义挂载的文件名称和路径,使用path指定,对上面的例子进行修改:

apiVersion: v1
kind: Pod
metadata:
  name: volume-test-pod
spec:
  containers:
    - name: test-container
      image: busybox
      command: [ "/bin/sh", "-c", "sleep 150" ]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        # Provide the name of the ConfigMap containing the files you want
        # to add to the container
        name: game-config
        items:
        - key: ui.properties         # 这里的KEY为confmap中的data 的key 
          path: ui/keys              # 文件路径,这里表示参数文件为 /etc/config/ui/keys
  restartPolicy: Never

登录pod后,查看内容如下:

/etc/config # ls
ui
/etc/config # cat ui/keys 
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

使用volume的方式指定路径后,此路径的下之前的文件都会被清除,只能显示指定的item文件

使用ConfigMap的限制条件:

  • ConMap必须在Pod之前创建,否则将无法创建需要引用configMap的Pod,除非Pod中指定此ConfigMap为可选。
  • ConMap受Namespace限制,只有处于相同namespace中的Pod才可以引用它。
  • kebulete只支持可以被API Server管理的Pod使用ConfigMap。kubelet在本地Node上通过Kubelet REST API,--manifest-url或--config自动创建的静态Pod将无法引用ConfigMap。
  • 在Pod对configMap进行挂载时,挂载的目录下如果原来还有其他文件,则容器内的该目录将会被挂载的ConfigMap覆盖。如果应用程序要保留原来的其他文件,需要进行特殊处理。
  • 特殊处理思路:可以将ConfigMap挂载到容器的临时目录,再通过启动脚本将配置文件复制到或者链接到应用所用的实际配置目录下。