k8s四:深入理解Pod

pod、容器和service的关系
pod与容器是包含关系,通常一个pod包含了多个容器,在pod对应的定义文件中会有具体的定义。pod本质上是一个最小部署单元,不是一个程序/进程,是一个环境,包括容器\存储、网络ip:port

为什么有了pod还要有service?
pod是临时性的,当pod中的进程结束、node故障、资源短缺时,pod都有可能被干掉,即pod的ip:port也是动态变化的,所以不能作为服务提供方。service会被分配一个固定的集群ip

挂载 app-volume 到自己的 webapps 目录下。

Pod初了解:是 Kubernetes 项目中最小的 API 对象,也是k8s中最小的调度粒度,一般会包含多个密切协作的容器。

为什么需要Pod?

一方面:某些应用之间有着密切的协作关系,使得它们必须部署在同一台机器(同一个容器)上。
例如:互相之间会发生直接的文件交换、使用 localhost 或者 Socket 文件进行本地通信、会发生非常频繁的远程调用、需要共享某些 Linux Namespace。

另一方面:容器的本质是一种特殊的进程,希望一个容器中仅仅跑一个应用进程。

怎么解决上面的矛盾呢?

Pod结构

infra容器 + 多个相关的业务容器,如图:

k8s四:深入理解Pod_第1张图片

Pod 的实现需要使用一个中间容器,这个容器叫作 Infra 容器,永远都是第一个被创建的容器,而其他用户定义的容器,则通过 Join Network Namespace 的方式,与 Infra 容器关联在一起。同样的方式也可以声明共享同一个 Volume。

对于 Pod 里的容器 A 和容器 B 来说:
它们可以直接使用 localhost 进行通信;
它们看到的网络设备跟 Infra 容器看到的完全一样;
一个 Pod 只有一个 IP 地址,也就是这个 Pod 的 Network Namespace 对应的 IP 地址;
当然,其他的所有网络资源,都是一个 Pod 一份,并且被该 Pod 中的所有容器共享;
Pod 的生命周期只跟 Infra 容器一致,而与容器 A 和 B 无关。

所以:Pod其实是通过中间容器实现某些资源共享的容器组。

当用户想在一个容器里跑多个功能并不相关的应用时,应该优先考虑它们是不是更应该被描述成一个 Pod 里的多个容器。其实就是经典的容器设计模式:sidecar

即在 Pod 里面,可以定义一些专门的容器,来执行主业务容器所需要的一些辅助工作,比如:

1)业务镜像启动的准备工作,把镜像里的 WAR 包拷贝到共享目录里面

2)原本需要在容器里面执行 SSH 需要干的一些事情

3)日志收集

4)监控组件装到额外的小容器中获取业务容器的工作状态

docker自身也是支持多容器的网络和volume的共享的,跟Pod的实现有什么区别呢?
$ docker run --net=B --volumes-from=B --name=A image-A ...
容器 B 就必须比容器 A 先启动,这样一个 Pod 里的多个容器就不是对等关系,而是拓扑关系了。

所以:pod通过infra容器保证了业务容器的对等性。

存在如下场景
机器A: 2.5G内存,机器B: 3G内存
应用1 2 3容器分别需要1g内存,且因为要协作,需要部署到同一个机器。

swarm做法:另外两个容器上设置一个 affinity=1(与容器1有亲密性)的约束,即:它们俩必须和 容器1运行在同一台机器上。执行docker run后,1 和2进入机器a,此时容器2不行了。

k8s中Pod 是 Kubernetes 里的原子调度单位。三个容器组成的 Pod。Kubernetes 项目在调度时,自然就会去选择可用内存等于 3 GB 的机器a节点进行绑定,而根本不会考虑2。

所以:Pod 的设计更加有利于调度。

总结下pod带来的好处:

1)解决了有着密切的协作关系又不能运行在同一容器内的矛盾

2)引入了经典的设计模式:sidecar,更易于扩展和使用。

3)pod通过infra容器保证了业务容器的对等性

4)Pod 的设计更加有利于调度

pod的定义详解

pod级别的属性:调度、网络、存储,以及安全

对container的定义:Image(镜像)、Command(启动命令)、workingDir(容器的工作目录)、Ports(容器要开发的端口),以及 volumeMounts(容器要挂载的 Volume)

举个实例:
WAR 包与 Web 服务器:一个 Java Web 应用的 WAR 包,它需要被放在 Tomcat 的 webapps 目录下运行起来。

docker实现:
一种方法是,把 WAR 包直接放在 Tomcat 镜像的 webapps 目录下,做成一个新的镜像运行起来。可是,这时候,如果你要更新 WAR 包的内容,或者要升级 Tomcat 镜像,就要重新制作一个新的发布镜像,非常麻烦。
另一种方法是,你压根儿不管 WAR 包,永远只发布一个 Tomcat 容器。不过,这个容器的 webapps 目录,就必须声明一个 hostPath 类型的 Volume,从而把宿主机上的 WAR 包挂载进 Tomcat 容器当中运行起来。不过,这样你就必须要解决一个问题,即:如何让每一台宿主机,都预先准备好这个存储有 WAR 包的目录呢?这样来看,你只能独立维护一套分布式存储系统了。

有了 Pod 之后,这样的问题就很容易解决了。我们可以把 WAR 包和 Tomcat 分别做成镜像,然后把它们作为一个 Pod 里的两个容器“组合”在一起。这个 Pod 的配置文件如下所示:

pod定义实例与讲解:

apiVersion: v1
kind: Pod
metadata:
  name: javaweb-2
spec:
  initContainers:
  - image: geektime/sample:v2
    name: war
    command: ["cp", "/sample.war", "/app"]
    volumeMounts:
    - mountPath: /app
      name: app-volume
  containers:
  - image: geektime/tomcat:7.0
    name: tomcat
    command: ["sh","-c","/root/apache-tomcat-7.0.42-v2/bin/start.sh"]
    volumeMounts:
    - mountPath: /root/apache-tomcat-7.0.42-v2/webapps
      name: app-volume
    ports:
    - containerPort: 8080
      hostPort: 8001 
  volumes:
  - name: app-volume
    emptyDir: {}

Pod 中,我们定义了两个容器,第一个容器使用的镜像是 geektime/sample:v2,这个镜像里只有一个 WAR 包(sample.war)放在根目录下。WAR 包容器的类型不再是一个普通容器,而是一个 Init Container 类型的容器。在 Pod 中,所有 Init Container 定义的容器,都会比 spec.containers 定义的用户容器先启动。并且,Init Container 容器会按顺序逐一启动,而直到它们都启动并且退出了,用户容器才会启动。
而第二个容器则使用的是一个标准的 Tomcat 镜像,挂载 app-volume 到自己的 webapps 目录下。
 

创建容器:kubctl create -f  yaml文件

删除容器 kubectl delete pod podName

进入容器内部:kubectl exec -it podName /bin/bash

获取pod列表 kubectl get pods

获取某个pod的详情 kubectl describe pod podName

2.3.1 pod的定义详解
https://www.cnblogs.com/sseban/p/13084416.html

pod级别的属性:调度、网络、存储,以及安全
NodeSelector:是一个供用户将 Pod 与 Node 进行绑定的字段。
apiVersion: v1
kind: Pod
...
spec:
 nodeSelector:
   disktype: ssd
 Pod 永远只能运行在携带了“disktype: ssd”标签(Label)的节点上;否则,它将调度失败。

NodeName:一旦 Pod 的这个字段被赋值,Kubernetes 项目就会被认为这个 Pod 已经经过了调度,调度的结果就是赋值的节点名字。所以,这个字段一般由调度器负责设置,但用户也可以设置它来“骗过”调度器,当然这个做法一般是在测试或者调试的时候才会用到。

HostAliases:定义了 Pod 的 hosts 文件(比如 /etc/hosts)里的内容,用法如下:
apiVersion: v1
kind: Pod
...
spec:
  hostAliases:
  - ip: "10.1.2.3"
    hostnames:
    - "foo.remote"
    - "bar.remote"
...
Pod 启动后,/etc/hosts 文件的内容将如下所示:
cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1 localhost
...
10.244.135.10 hostaliases-pod
10.1.2.3 foo.remote
10.1.2.3 bar.remote
说明:如果直接修改了 hosts 文件的话,在 Pod 被删除重建之后,kubelet 会自动覆盖掉被修改的内容。

跟容器的 Linux Namespace 相关的属性,也一定是 Pod 级别的。
shareProcessNamespace=true :这个 Pod 里的容器要共享 PID Namespace。

凡是 Pod 中的容器要共享宿主机的 Namespace,也一定是 Pod 级别的定义,如:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  hostNetwork: true
  hostIPC: true
  hostPID: true
  containers:
  - name: nginx
    image: nginx
  - name: shell
    image: busybox
    stdin: true
    tty: true
这个 Pod 里的所有容器,会直接使用宿主机的网络、直接与宿主机进行 IPC 通信、看到宿主机里正在运行的所有进程。


对container的定义:
Image(镜像)、Command(启动命令)、workingDir(容器的工作目录)、Ports(容器要开发的端口),以及 volumeMounts(容器要挂载的 Volume
还有:
ImagePullPolicy:值默认是 Always,即每次创建 Pod 都重新拉取一次镜像。另外,当容器的镜像是类似于 nginx 或者 nginx:latest 这样的名字时,ImagePullPolicy 也会被认为 Always。而如果它的值被定义为 Never 或者 IfNotPresent,则意味着 Pod 永远不会主动拉取这个镜像,或者只在宿主机上不存在这个镜像时才拉取。

Lifecycle :
例子:
apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
      preStop:
        exec:
          command: ["/usr/sbin/nginx","-s","quit"]
postStart:在容器启动后,立刻执行一个指定的操作。在 Docker 容器 ENTRYPOINT 执行之后,但它并不严格保证顺序。也就是说,在 postStart 启动时,ENTRYPOINT 有可能还没有结束。

preStop发生的时机,则是容器被杀死之前,阻塞当前的容器杀死流程,直到操作完成之后,才允许容器被杀死,这跟 postStart 不一样。


2.3.2 pod的基本用法
对docker镜像的要求:必须以一个前台命令作为启动命令。原因如下:
如果镜像启动命令是后台执行程序,那么k8s创建包含该容器的pod之后,命令同时运行完成,就会认为Pod执行结束,会立刻销毁该pod。如果该pod定义了rc,那么k8s会再次创建一个新的Pod,从而进入死循环。

pod中容器定义的一个规则
如果两个容器应用为紧耦合的关系,应该组合成一个整体对外提供服务时,应该将两个容器打包为一个Pod。原因如下:
属于一个pod的多个容器应用之间通过localhost就可以直接通信。

实例:frontend容器需要调用redis容器的服务,那么pod的定义文件fronted-redis-pod.yaml定义如下:
apiVersion: v1
kind: Pod
metadata:
 name: redis-php
 labels: 
  name: redis-php
spec:
 containers:
 - name: fronted
  image: php-frontend:localredis
  ports:
  - containerPort: 80
 - name: redis
  image: kubeguide/redis-master
  ports:
  - containerPort: 6379
在fronted对应的容器中,通过localhost:6379可以对同属一个pod的redis容器可以直接访问。

创建pod指令 kubectl create -f fronted-redis-pod.yaml
获取pod kubectl get pods
获取pod描述 kubectl describe pods

2.3.3 静态pod
不能通过apiServer进行管理,仅能通过kubelet管理,且无法与rc、deployment等进行关联。
创建静态pod的两种方式
1)配置文件方式
设置kubelet的启动参数“--config”,指定kubelet需要监控的配置文件的目录,kubelet会定期扫描该目录,根据目录中的.yaml或json文件进行创建操作。
设置配置目录为:--config=/etc/kubelet.d/,并重启kubelet服务。
在/etc/kubelet.d/目录下增加static-web.yml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
 name: static-web
 labels: 
  name: static-web
spec:
 containers:
 -name: static-web
  image: nginx
  ports:
   -name: web
    containerPort: 80
稍等一会,查看已经启动的容器:docker ps  
master节点查看pod,kubectl get pods
删除pod(实际因为静态pod无法被apiserver直接管理,所以状态会变为pending,且不会被删除): kubectl delete pod static-web-node1
正取的删除方式:
删除/etc/kubelet.d/目录下的static-web.yml文件即可
2) http方式
设置kubelet的启动参数--manifest-url

2.3.4 pod容器共享Volume
同一个pod中的多个容器能够共享Pod级别的存储卷Volume。即定义一个卷,然后将该卷挂在为多个容器的内部目录。
如下pod实例,pod-volume.yml内容如下:
apiVersion: v1
kind: Pod
metadata:
 name: volume-pod
spec:
 containers:
 - name: tomcat
  image: tomcat
  ports:
  - containerPort: 8080
  volumeMounts:
  - name: app-logs #使用名称为app-logs的卷
   mountPath: /usr/local/tomcat/logs #将卷挂载到该路径,这个路径是容器内的绝对路径
 - name: busybox
  image: busybox
  command: ["sh", "-c", "tail -f /logs/catalinz*.log"]
  volumeMounts:
  - name: app-logs
   mountPath: /logs
 volumse: #定义pod中的公共volume
 - name: app-logs #volume名称为app-logs
  emptyDir: {} #
说明:pod中定义了名称为app-logs的卷,然后应用到多个pod中。

emptyDir:k8s不显式声明宿主机目录的 Volume。所以,Kubernetes 也会在宿主机上创建一个临时目录,这个目录将来就会被绑定挂载到容器所声明的 Volume 目录上。此时并不关心卷定义在宿主机的哪个目录,主要是通过该目录,实现Pod中多个容器对卷的共享。

Kubernetes 也提供了显式的 Volume 定义,它叫作 hostPath。比如下面的这个 YAML 文件:
 ...   
    volumes:
      - name: nginx-vol
        hostPath: 
          path:  " /var/data"

Kubernetes 中,有几种特殊的 Volume:
存在的意义不是为了存放容器里的数据,也不是用来进行容器和宿主机之间的数据交换。这些特殊 Volume 的作用,是为容器提供预先定义好的数据,现在支持四种:
Secret; pod中创建保密信息
ConfigMap; 获取一般配置
Downward API;让 Pod 里的容器能够直接获取到这个 Pod API 对象本身的信息。
ServiceAccountToken。

Secret。它的作用,是帮你把 Pod 想要访问的加密数据,存放到 Etcd 中。然后,你就可以通过在 Pod 的容器里挂载 Volume 的方式,访问到这些 Secret 里保存的信息了。
Secret 最典型的使用场景,莫过于存放数据库的 Credential 信息,比如:
apiVersion: v1
kind: Pod
metadata:
  name: test-projected-volume 
spec:
  containers:
  - name: test-secret-volume
    image: busybox
    args:
    - sleep
    - "86400"
    volumeMounts:
    - name: mysql-cred
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: mysql-cred
    projected:
      sources:
      - secret:
          name: user
      - secret:
          name: pass
声明挂载的 Volume,并不是常见的 emptyDir 或者 hostPath 类型,而是 projected 类型。而这个 Volume 的数据来源(sources),则是名为 user 和 pass 的 Secret 对象,分别对应的是数据库的用户名和密码。

Kubernetes 中创建secret对象
方法一:
$ cat ./username.txt
admin
$ cat ./password.txt
c1oudc0w!

$ kubectl create secret generic user --from-file=./username.txt
$ kubectl create secret generic pass --from-file=./password.txt
kubectl get secrets:查看这些 Secret 对象

方法二:yaml方式
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  user: YWRtaW4=
  pass: MWYyZDFlMmU2N2Rm
说明:Secret 对象要求这些数据必须是经过 Base64 转码的,如下:
$ echo -n 'admin' | base64
YWRtaW4=
$ echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm

创建pod
$ kubectl create -f test-projected-volume.yaml
查看secret数据
$ kubectl exec -it test-projected-volume -- /bin/sh
$ ls /projected-volume/
user
pass
$ cat /projected-volume/user
root
$ cat /projected-volume/pass
1f2d1e2e67df

与 Secret 类似的是 ConfigMap,它与 Secret 的区别在于,ConfigMap 保存的是不需要加密的、应用所需的配置信息。


Service Account: Kubernetes 系统内置的一种“服务账户”,它是 Kubernetes 进行权限分配的对象。比如,Service Account A,可以只被允许对 Kubernetes API 进行 GET 操作,而 Service Account B,则可以有 Kubernetes API 的所有操作权限。

ServiceAccountToken。任何运行在 Kubernetes 集群上的应用,都必须使用这个 ServiceAccountToken 里保存的授权信息,也就是 Token,才可以合法地访问 API Server。

2.3.5 pod的配置管理 - ConfigMap
概述
configMap主要将应用所需的配置信息与程序分离,通过环境变量或者外挂文件的方式在创建容器时进行配置的注入。
典型用法:
1)生成为容器内的环境变量
2)设置容器启动命令的启动参数
3)以volume的形式挂载为容器内部的文件或目录

创建configMap资源对象
1)通过yaml配置文件
假设cm-appvars.yaml的内容如下:
apiVersion: v1
kind: ConfigMap
metadata: 
 name: cm-appvars
data:
 apploglevel: info
 appdatadir: /var/data
创建configMap: kubectl create -f cm-appvars.yaml
查看: kubectl get configmap
获取详情: kubectl describe configmap cm-appvars
2) 直接通过kubectl命令方式创建 -- 不推荐,难以维护和复用

在pod中使用configMap
1)通过环境白能量方式适用configMap
以使用前面定义的cm-appvars为例。在POd中,将ConifgMap中的内容以环境变量设置为容器内部的环境变量。pod定义cm-test-pod.yml如下:
apiVersion: v1
kind: Pod
metadata:
 name: cm-test-pod
spec:
 containers:
 - name: cm-test
  image: busybox
  command: ["/bin/sh", "-c", "env | grep APP"]
  env:
  - name: APPLOGLEVEL #定义环境变量的名称
  valueFrom:          #定义环境变量的值
   configMapKeyRef:
    name: cm-appvare  #环境变量取自名为cm-appvare的configMap
    key: apploglevel #key为configMap中的apploglevel

kubectl create -f cm-test-pod.yml #创建pod
kubectl get pods --show-all 
kubectl logs cm-test-pod 获取pod日志
2)通过volumeMount使用configMap

使用configMap的限制条件 先定义configMap后再Pod中使用,configmap受namspace限制,只有处于相同namespace的pod可以使用他

2.3.6 容器内获取Pod信息 (downward API)
pod成功创建之后,会被分配唯一的名字、ip、和特定的namespace,可以通过downward api在容器内使用这些信息。两种方式:
1)环境变量,用于的单个变量,将Pod信息和container信息注入容器内部
下面是通过downward API将Pod的IP\名称和所在namespace注入容器的环境变量中,容器应用使用env命令将全部环境变量大隐刀标准输出:
dapi-test-pod.yml
apiVersion: v1
kind: Pod
metadata:
 name: dapi-test-pod
spec:
 containers:
 - name: test-container
  image: busybox
  command: ["/bin/sh", "-c", "env | grep APP"]
  env:
  - name: MY_POD_NAME 
   valueFrom: #Downward APi的语法
    fieldRef: 
     fieldPath: metadata.name #Pod的名称
  -name: MY_POD_NAMESPACE
   valueFrom:
    fieldRef:
     fieldPath: metadata.namespace
  -name: MY_POD_IP
   valueFrom:
    fieldRef:
     fieldPath: status.podIP
创建Pod:kubectl create -f dapi-test-pod.yml
查看日志:kubectl logs dapi-test-pod

上面metadata.name metadata.namespace status.podIP是downward api提供的语法,分别对应到是pod名称、命名空间、ip

将容器资源注入容器,如
env:
 - name: MY_CPU_REQUEST
  valueFrom:
   resourceFieldRef:
    containerName: test-container
    resource: requests.cpu #容器的cpu请求值
requests.cpu:容器的CPU请求值
limits.cpu:容器的CPU限制值
requests.memory:容器的内存请求值
limits.memory:容器内存限制值

2)Volume挂载,将数组类信息生成文件,挂载到容器内部

downward api的价值:
集群中节点需要将自身的标识及绑定IP地址等信息事先写入配置文件中,然后在进程启动的时候读取这些信息。


2.3.7 pod生命周期和重启策略
pod的生命周期(本质以容器的状态来决定)

pending:YAML 文件已经提交给了 Kubernetes,API 对象已经被创建并保存在 Etcd 当中。但是,这个 Pod 里有些容器因为某种原因而不能被顺利创建。比如,调度不成功。
running:Pod 已经调度成功,跟一个具体的节点绑定。它包含的容器都已经创建成功,并且至少有一个正在运行中。
succeeded:Pod 里的所有容器都正常运行完毕,并且已经退出了。这种情况在运行一次性任务时最为常见。
failed:Pod 里至少有一个容器以不正常的状态(非 0 的返回码)退出。意味着你得想办法 Debug 这个容器的应用,比如查看 Pod 的 Events 和日志。
unknown: Pod 的状态不能持续地被 kubelet 汇报给 kube-apiserver,这很有可能是主从节点(Master 和 Kubelet)间的通信出现了问题。

pod的重启策略,本质上也是应用于pod内的所有容器,pod的重启策略包括:
always:容器失效时,kubelet自动重启该容器
onFailure:容器非成功退出时,kubelet会重启该容器
never:kubelet从不重启容器


status可以再细分出一组conditions,值包括:PodScheduled、Ready、Initialized,以及 Unschedulable。
Pod 当前的 Status 是 Pending,对应的 Condition 是 Unschedulable,这就意味着它的调度出现了问题。
而其中,Ready 这个细分状态非常值得我们关注:它意味着 Pod 不仅已经正常启动(Running 状态),而且已经可以对外提供服务了。

当前可用于管理Pod的控制器包括ReplicationController、Job、DaemonSe,另外静态Pod有kubelet直接管理。
每种控制器对Pod的重启策略如下:
RC和DaemonSet:必须设置为always,需要保证该容器持续运行
Job:OnFailure或Never,保证容器执行完成后不再重启
kubelet:Pod失效时自动重启,不论将Restartpolicy设置为什么值,均不会对Pod进行健康检查


2.3.8 pod健康检查
对Pod的健康检查有两类探针:livenessProbe 和  ReadinessProbe
LivenessProbe探针:判断容器是否存活(running状态),如果探测到容器不健康,kubelet将杀掉该容器;并根据重启策略进行处理;如果不存在livenessProbe,那么按照success
ReadinessProbe探针:判断容器是否启动完成(ready状态)。如果检测到失败,则修改pod状态,endpointController将从service的Endpoint中删除容器所在Pod的endPoint。

Kubernetes 的 Pod 中,还有一个叫 readinessProbe 的字段。虽然它的用法与 livenessProbe 类似,但作用却大不一样。readinessProbe 检查结果的成功与否,决定的这个 Pod 是不是能被通过 Service 的方式访问到

pod定义如下:

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: test-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
说明:容器主要住了两件事:
第一件事,就是在 /tmp 目录下创建了一个 healthy 文件,以此作为自己已经正常运行的标志。而 30 s 过后,它会把这个文件删除掉。
二件事:livenessProbe(健康检查)。它的类型是 exec,这意味着,它会在容器启动后,在容器里面执行一条我们指定的命令,比如:“cat /tmp/healthy”。这时,如果这个文件存在,这条命令的返回值就是 0,Pod 就会认为这个容器不仅已经启动,而且是健康的。这个健康检查,在容器启动 5 s 后开始执行(initialDelaySeconds: 5),每 5 s 执行一次(periodSeconds: 5)。


$ kubectl create -f test-liveness-exec.yaml
$ kubectl get pod
NAME                READY     STATUS    RESTARTS   AGE
test-liveness-exec   1/1       Running   0          10s
30 s 之后,我们再查看一下 Pod 的 Events:
$ kubectl describe pod test-liveness-exec
Pod 在 Events 报告了一个异常:

FirstSeen LastSeen    Count   From            SubobjectPath           Type        Reason      Message
--------- --------    -----   ----            -------------           --------    ------      -------
2s        2s      1   {kubelet worker0}   spec.containers{liveness}   Warning     Unhealthy   Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory

再次查看一下这个 Pod 的状态:
$ kubectl get pod test-liveness-exec
NAME           READY     STATUS    RESTARTS   AGE
liveness-exec   1/1       Running   1          1m
RESTARTS 字段从 0 到 1 的变化,就明白原因了:这个异常的容器已经被 Kubernetes 重启了。

 Pod 恢复机制,也叫 restartPolicy。它是 Pod 的 Spec 部分的一个标准字段(pod.spec.restartPolicy),默认值是 Always,即:任何时候这个容器发生了异常,它一定会被重新创建。
 但一定要强调的是,Pod 的恢复过程,永远都是发生在当前节点上,而不会跑到别的节点上去。事实上,一旦一个 Pod 与一个节点(Node)绑定,除非这个绑定发生了变化(pod.spec.node 字段被修改),否则它永远都不会离开这个节点。

 这也就意味着,如果这个宿主机宕机了,这个 Pod 也不会主动迁移到其他节点上去。而如果你想让 Pod 出现在其他的可用节点上,就必须使用 Deployment 这样的“控制器”来管理 Pod,哪怕你只需要一个 Pod 副本。

restartPolicy,改变 Pod 的恢复策略。除了 Always,它还有 OnFailure 和 Never 两种情况:
Always:在任何情况下,只要容器不在运行状态,就自动重启容器;
OnFailure: 只在容器 异常时才自动重启容器;
Never: 从来不重启容器。

livenessProbe 也可以定义为发起 HTTP 或者 TCP 请求的方式,定义格式如下:
...
livenessProbe:
     httpGet:
       path: /healthz
       port: 8080
       httpHeaders:
       - name: X-Custom-Header
         value: Awesome
       initialDelaySeconds: 3
       periodSeconds: 3

    ...
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20
 

三种方式均需要如下参数:
initialDelaySeconds:启动容器后进行首次健康检查的等待时间,单位s
timeOutSeconds:健康检查发送请求后等待响应的超时时间,单位为s。当超时发生时,kubelet会任务容器已经服务提供服务,江湖重启该容器。

Kubernetes 能不能自动给 Pod 填充某些字段呢?
PodPresent
开发人员定义的pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: website
  labels:
    app: website
    role: frontend
spec:
  containers:
    - name: website
      image: nginx
      ports:
        - containerPort: 80
PodPreset 对象。在这个对象中,凡是他想在开发人员编写的 Pod 里追加的字段,都可以预先定义好。比如这个 preset.yaml:
apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
  name: allow-database
spec:
  selector:
    matchLabels:
      role: frontend
  env:
    - name: DB_PORT
      value: "6379"
  volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
    - name: cache-volume
      emptyDir: {}
 追加的定义仅作用与selector 所定义的、带有“role: frontend”标签的 Pod 对象,这就可以防止“误伤”。
假定运维人员先创建了这个 PodPreset,然后开发人员才创建 Pod:
$ kubectl create -f preset.yaml
$ kubectl create -f pod.yaml

Pod 运行起来之后,我们查看一下这个 Pod 的 API 对象:

$ kubectl get pod website -o yaml
apiVersion: v1
kind: Pod
metadata:
  name: website
  labels:
    app: website
    role: frontend
  annotations:
    podpreset.admission.kubernetes.io/podpreset-allow-database: "resource version"
spec:
  containers:
    - name: website
      image: nginx
      volumeMounts:
        - mountPath: /cache
          name: cache-volume
      ports:
        - containerPort: 80
      env:
        - name: DB_PORT
          value: "6379"
  volumes:
    - name: cache-volume
      emptyDir: {}
说明:PodPreset 里定义的内容,只会在 Pod API 对象被创建之前追加在这个对象本身上,而不会影响任何 Pod 的控制器的定义。比如,我们现在提交的是一个 nginx-deployment,那么这个 Deployment 对象本身是永远不会被 PodPreset 改变的,被修改的只是这个 Deployment 创建出来的所有 Pod

 

2.3.10 InitContainer 初始化容器
引入initCOntainer的原因,应用启动之前可能需要进行如下的初始化操作,如:
1)等待其他关联组件正取运行
2)基于环境变量或配置模板生成配置文件
3)从远程数据库获取本地所需配置,或者将自身注册到某个中央数据库。
所以k8s引入了初始化容器,用于在启动应用陈程序之前启动一个或多个”初始化“容器。Init container与应用容器本质上是一样的,但其是仅运行一次就结束的任务,并且在成功执行完成后,系统才能继续执行下一个容器。
下面是一个实例:
dapi-test-pod.yml
apiVersion: v1
kind: Pod
metadata:
 name: nginx
spec:
 initContainers: #初始化容器,负责容器的初始化
 - name: install
  image:busybox
  command:
  - wget
  - "O"
  - "/work-dir/index.html"
  - http://kubenetes.io
  volumeMounts:
  - name: workdir
   mountPath: "/work-dir"
 containers:
 - name: nginx
  image: busybox
  ports:
  - containerPort: 80
  volumeMounts:
  - name: workdir
   mountPath: /usr/share/nginx/html
 volumes:
 - name: workdir
  emptyDir: {}

init container与应用容器的区别:
1)先于应用容器运行,如果有多个init container,则逐个运行。当所有的initContainer都成功运行后,k8s才会初始化pod的各种信息,并开始创建和运行应用容器
2)initContainer中也可以设置资源限制、volume的使用和安全策略2.3.11 Pod的升级和回滚

问题:

pod是怎么与service关联的?
pod的rc文件中:
      labels:
        app: mysql   

service定义文件中:  
spec:
  ports:
    - port: 3306                           #Service提供服务的端口号
  selector:                                #Service对应的Pod拥有这里定义的标签
    app: mysql
 

附:k8s pod定义详解

k8s四:深入理解Pod_第2张图片

k8s四:深入理解Pod_第3张图片

你可能感兴趣的:(k8s)