06 . Kubernetes之Pod控制器详细介绍及应用

时间:2022-07-25
本文章向大家介绍06 . Kubernetes之Pod控制器详细介绍及应用,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Pod API属性详解

Pod是k8s集群中的最小编排单位。将这个设计落实到API对象上,容器就成了Pod属性里一个普通的字段。那么到底哪些属性属于Pod对象,哪些属性属于容器的呢?先看下面的一段描述: 假如把Pod看成传统环境里的"机器"、那么容器就是运行在这个"机器"里的"用户程序",这样很多关于Pod对象的设计就非常容易理解了。凡是调度、网络、存储,以及安全相关的属性,基本上是Pod级别的。他们的共同特征是:描述的是"机器"这个整体,而不是里面运行的"程序"。比如:

# 配置这个"机器"的网卡——Pod 的网络定义  
# 配置这个"机器"的磁盘——Pod 的存储定义  
# 配置这个"机器"的防火墙——Pod 的安全定义  
# 这台"机器"运行在哪个服务器之上——Pod 的调度
关于标签

所有的资源都可以设置标签,目的就是为了给资源贴标识符,使用时用选择器调用标签,用到标签的地方: 后端有以副本形式存在的Pod,这么多副本最好做负载均衡,此时就在前面创建一个service,一般情况下是先创建rc,然后创建service,创建完成后需要将service和后端的rc关联到一起,关联到一起就是用标签选择器关联的,即使不需要标签也最好设置一个能用的标签,标签可以由多个字典元素组成. 标签的格式: name:value,一个键值对表示一个标签,调用时一起调用,例如: 做nginx,这里是众多nginx的第一个,故可以设置这个标签由两个字典元素组成,标签可以有多行,但标签的值不能是纯数字.

Pod级别的相关属性

凡是跟Namespace都是Pod级别的,比如,容器的Linux Namespace、容器共享宿主机的 Namespace 原因:Pod 的设计就是要让它里面的容器尽可能多地共享Linux Namespace,仅保留必要的隔离和限制能力。如此Pod模拟出的效果才能跟虚拟机里程序间的关系非常类似.

apiVersion

除了deployment是v1的升级版,其他的基本都是v1。 # kubectl api-versions

kind

指定这个API对象的资源类型: Pod、Deployment、Job、Ingress、Service等,资源类型的首字母需大写.

metadata

描述创建资源的属性,比如Pod的名称,namspace、标签等信息.

spec

specification of the resource content: 指定该资源的内容,包括一些container,storage,volume以及其他k8s需要的参数,以及诸如是否在容器失败时重新启动容器的属性,可在特定Kubernetes API找到完整的Kubernetes Pod属性.

Spec常用字段

# Pod资源:
	spec.containers <[]object >
spec:
  containers:
- name <string>
  image <string>			# 仓库路径,项目名称,用户,镜像名称,镜像标签
  ports:
  - name: http
    containerPort: 80
  - name: https
    containerPort: 443
  imagePullPolicy: IfNotPresent	# 镜像获取策略
  
  # 修改镜像中的默认应用:
  	command,args
  # 标签<最多六十三个字符>
  	key=value
  		key: 字符,数字,_-,.,# 只能以字母数字开头及结尾
  		value: 可以为空,    # 只能字母或者数字开头及结尾,中间可使用:
  	# Always,Never,IfNotPresent
# Always: 本地不管有没有镜像都是要到仓库去下载,本地无论有还是没有到要去仓库下载,就算本地有镜像他也不用: 
# 虽然会导致容器启动变慢,但是可以防止被人恶意修改镜像不被中招:
# Never: 永远不下载,需要用户手动去拖镜像,镜像一旦创建,不允许被更改,一旦编辑就报错,除非删除再创建:
# IfNotPresent: 本地不存在就去下载:
# 如果是latest标签就是Always,否则就是IfNotPresent 
Example1(给Pod打标签示例)
cat demo1-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    ports:
    - name: http
      containerPort: 80
    - name: https
      containerPort: 443
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command:
    - "/bin/sh"
    - "-c"
    - "sleep 3600"
查看Pod标签
[root@master YAML]# kubectl get pods --show-labels
NAME          READY   STATUS    RESTARTS   AGE     LABELS
nginx-test2   1/1     Running   0          37m     run=nginx-test2
pod-demo      2/2     Running   0          2m30s   app=myapp,tier=frontend

[root@master YAML]# kubectl get pods -L app
# 显示指定资源类别对象下所有标签的值. 
NAME          READY   STATUS    RESTARTS   AGE   APP
nginx-test2   1/1     Running   0          36m   
pod-demo      2/2     Running   0          71s   myapp
  

[root@master YAML]# kubectl get pods -l app  
# -l才是过滤
NAME       READY   STATUS    RESTARTS   AGE
pod-demo   2/2     Running   0          84s
修改标签
# 如果期望将金丝雀改变为稳定版,觉得版本发布没问题,可以改过去
kubectl  label pods pod-demo release=canar    	    

# 标签不能重名,如果需要重名加上--overwrite覆盖即可.
kubectl label pod pod-demo release=stable --overwrite

# 修改节点标签
kubectl label nodes node1 disktype=ssd
标签选择器

等值关系: =,==,!=都表示等值关系

kubectl get pods -l release=stable,app --show-labels
NAME       READY   STATUS    RESTARTS   AGE   LABELS
pod-demo   2/2     Running   0          11m   app=myapp,release=stable,tier=frontend

# 集合关系:
# KEY in (VALUE1,VALUE2...)
# KEY notin (VALUE1,VALUE2...)

nodeSelector <map[string]string>能影响Pod调度算法

如果我们需要对一些节点拥有ssd硬盘的机器做监控就需要将相应节点打上ssd标签.然后通过标签选择器将Pod运行在指定节点上. 我们在之前那个demo-pod配置文件加上一个字段即可.

我们先给节点打标签

kubectl get nodes --show-labels  
NAME     STATUS   ROLES    AGE   VERSION   LABELS  
master   Ready    master   43h   v1.17.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/master=  
node1    Ready    <none>   43h   v1.17.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>   43h   v1.17.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux  

kubectl label nodes node1 disktype=ssd  
node/node1 labeled  

kubectl get nodes --show-labels |grep disktype=ssd  
node1    Ready    <none>   43h   v1.17.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux

我们重新修改下demo1-pod.yaml

cat demo1-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    ports:
    - name: http
      containerPort: 80
    - name: https
      containerPort: 443
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command:
    - "/bin/sh"
    - "-c"
    - "sleep 3600"
  nodeSelector:
    disktype: ssd
kubectl create -f demo1-pod.yaml

kubectl get pods -o wide
kubectl get pods -o wide
NAME          READY   STATUS    RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
nginx-test2   1/1     Running   0          49m   10.244.0.18   master   <none>           <none>
pod-demo      2/2     Running   0          53s   10.244.1.15   node1    <none>           <none>

annotations:

与label不同地方在于,他不能用于挑选资源对象,没有字符限制,仅用于对象提供“元数据”和注解.

cat demo1-pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels:
    app: myapp
    tier: frontend
  annotations:
    youmen.com/create-by: "youmen"
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    ports:
    - name: http
      containerPort: 80
    - name: https
      containerPort: 443
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command:
    - "/bin/sh"
    - "-c"
    - "sleep 3600"
  nodeSelector:
    disktype: ssd

kubectl create -f pod-demo.yaml
kubectl describe pods pod-demo |grep Annotations
Annotations:  youmen.com/create-by: youmen
Pod生命周期

Pod生命周期 状态

Pending:  # 调度尚未完成
Running:  # 正在运行
Failed:   # 失败
Succeded: # 成功
Unknown:  # 未知状态.

创建Pod过程

当用户创建Pod时,这个请求提交给API Server,他先将状态保存在etcd中,APIServer,接下来会请求Scheduler进行调度,如果调度成功后会将结果保存在etcd资源,更新到etcd状态资源中,随后目标节点调到node1上,根据那个清单创建所需要的Pod.

Probe机制
# Pod生命周期的重要行为:
#	初始化容器:
#	容器探测:
#		1. lieness probe:  <是否存活>
		# 如果检查失败,杀死容器,根据Pod的restartPolicy来操作
#		2. readiness probe: <是否就绪>
		# 如果检查失败,Kubernetes会把Pod从service endpoints中删除.

#			两种都支持三种探测行为:
#			1. exec: 执行自定义命令,看返回状态码是否为0
#			2. tcpSocket: 向指定TCP套接字发请求,端口发请求,发起
#			3. httpGet: 向指定http服务发请求,应用层get,url响应码.
#				存活性探测: 主要用于判定主容器是否处于运行状态
#				就绪性探测: 适用于判定容器中主进程是否处于就绪并可以对外进行服务.

readiness probe:

我们为动态有生命周期Pod提供一个固定的端点,service用标签选择器关联到各个Pod资源: 此处有一个问题,如果有一个新的Pod,这个新Pod刚好符合service条件加入到后端,而有新请求进来就会被调度到新的Pod,但这个时候新Pod里面服务很有可能还没有就绪,里面文件还没有展开,而这个时候去访问就有可能出现访问失败,如果不做就绪性探测,Pod一创建就关联到service后出现大量访问失败:

	restartPolicy: <重启策略>
		Always,UnFailure,Never,Default to Always.
		Always: # 一旦Pod中容器挂了就地将他重启.
		UnFailure: # 状态为错误才重启,正常终止不重启: # 延时重启
		Never: # 挂了就挂了,从来不重启.

Example

cat pod-probe.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
    livenessProbe:
      httpGet:
        path: /index.html
        port: 80

kubectl logs nginx-pod
10.244.3.1 - - [22/Dec/2019:11:03:04 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.17" "-"
10.244.3.1 - - [22/Dec/2019:11:03:14 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.17" "-"
10.244.3.1 - - [22/Dec/2019:11:03:24 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.17" "-"
10.244.3.1 - - [22/Dec/2019:11:03:34 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.17" "-"
# 我们可以看到每十秒都会进行一次测试
# 接下来我们把那个页面删除,看会怎么样
kubectl exec -it nginx-pod /bin/bash
rm -rf usr/share/nginx/html/index.html
# 然后我们去事件里面看看他做了什么处理.
Events:
  Type     Reason     Age                  From               Message
  ----     ------     ----                 ----               -------
  Normal   Scheduled  3m35s                default-scheduler  Successfully assigned default/nginx-pod to node1
  Normal   Pulling    43s (x2 over 3m34s)  kubelet, node1     Pulling image "nginx"
  Warning  Unhealthy  43s (x3 over 63s)    kubelet, node1     Liveness probe failed: HTTP probe failed with statuscode: 404
  Normal   Killing    43s                  kubelet, node1     Container nginx failed liveness probe, will be restarted
  Normal   Pulled     21s (x2 over 3m14s)  kubelet, node1     Successfully pulled image "nginx"
  Normal   Created    21s (x2 over 3m14s)  kubelet, node1     Created container nginx
  Normal   Started    21s (x2 over 3m14s)  kubelet, node1     Started container nginx
# 我们可以看到当检测不到报错的话,他会自己重新创建一个新镜像.此处只是个页面检测,
# 如果是其他类型服务也可以使用TCP套接字做检测.
Pod的排错
kubectl describe TYPE  NAME_PREFIX
kubectl  logs  nginx-xx
kubectl  exec -it  nginx-xx bash or /bin/bash
许多资源支持内嵌字段

matchLabels: 直接给定键值 matchExpresslons: 基于给定的表达式来定义使用标签选择器,{key:"KEY",operator: "OPERATOR" ,values: [VAL1,VAL2...]}

# 操作符
# In,NotIn: values字段的值必须为空列表.
# Exists,NotExists: values字段的值为空列表.

Pod控制器

Pod控制器用于实现代我们管理Pod中间层,帮我们确保Pod是我们所期望的目标状态,如果出现故障,他会尝试重启,如果重启失败,他会尝试重新编排重新构建一个,如果Pod副本数量低于用户定义目标数量,他会自动补全,反之终止多余目标资源 但此处Pod控制器只是一个泛称,包含以下控制器

ReplicaSet控制器(ReplicationController)

核心作用在于代用户创建指定数量的副本,并确保副本数量一直符合用户所定义状态,还支持扩缩容机制 kubernetes不建议我们直接使用ReplicaSet,而是用Deployment,因为它还支持滚动更新,回滚,声明式配置更新 简写为rs

cat rs-demo1.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myapp
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
      release: canary 
  template:
    metadata: 
      name: myapp-pod
      labels:
        app: myapp
        release: canary
        environment: qa
    spec:
      containers:
      - name: myapp-container
        image: ikubernetes/myapp:v1
        ports:
        - name: http
          containerPort: 80
# 我们此处的标签尽量条件匹配复杂一点,避免以后创建Pod标签冲突,多退少补
# 如果需要添加副本数量直接edit配置文件即可,最好使用打补丁方式修改.
# 如果需要更新版本,只有重建Pod才能更新成功.
# 还能控制更新节奏,加一个删一个,加一个删两个,加两个删两个.d
# 灰度发布: 删一个创建一个
# 金丝雀发布: 删一个,等过段时间看用户反应,没什么不好的影响全部给升级
# 蓝绿发布: 在创建个RS,改下service匹配条件,让请求都匹配到RS2上.
# Deployment就建构在RS之上.一个Deployment可以管理多个RS,
Deployment控制器

kubernetes不建议我们直接使用ReplicaSet,而是用Deployment,因为它还支持滚动更新,回滚,声明式配置更新,管理无状态非常好

cat pod-deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
      release: canary
  template:
    metadata:
      labels:
        app: myapp
        release: canary
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        ports:
        - name: http
          containerPort: 80

kubectl get pods
NAME                            READY   STATUS    RESTARTS   AGE
myapp-deploy-7d574d56c7-44mqw   1/1     Running   0          52s
myapp-deploy-7d574d56c7-5msnk   1/1     Running   0          52s
# 此处的5msnk是模板的哈希值.我们可以通过apply增加副本数量,但是后面哈希值不变
kubectl describe deploy myapp-deploy

我们可以实时更新一下

kubectl get pods -l app=myapp -w
NAME                            READY   STATUS    RESTARTS   AGE
myapp-deploy-7d574d56c7-44mqw   1/1     Running   0          2m56s
myapp-deploy-7d574d56c7-5msnk   1/1     Running   0          2m56s
# 我们再另开一个终端修改配置文件或者打补丁,然后实时查看此处的哈希变化.
kubectl edit deploy myapp-deploy
replicas: 4
- image: ikubernetes/myapp:v2
kubectl get pods -l app=myapp -w
NAME                            READY   STATUS    RESTARTS   AGE
myapp-deploy-7d574d56c7-44mqw   1/1     Running   0          8m15s
myapp-deploy-7d574d56c7-5msnk   1/1     Running   0          8m15s
myapp-deploy-7d574d56c7-gvl9q   0/1     Pending   0          0s
myapp-deploy-798dc9b584-z7jhh   0/1     Pending   0          0s
myapp-deploy-7d574d56c7-gvl9q   0/1     Pending   0          0s
myapp-deploy-7d574d56c7-nkwt6   0/1     Pending   0          0s
myapp-deploy-798dc9b584-z7jhh   0/1     Pending   0          1s
myapp-deploy-7d574d56c7-nkwt6   0/1     Pending   0          1s
myapp-deploy-7d574d56c7-gvl9q   0/1     ContainerCreating   0          1s
myapp-deploy-7d574d56c7-nkwt6   0/1     ContainerCreating   0          1s
myapp-deploy-798dc9b584-q8fxd   0/1     Pending             0          0s
myapp-deploy-798dc9b584-q8fxd   0/1     Pending             0          0s
myapp-deploy-798dc9b584-z7jhh   0/1     ContainerCreating   0          1s
myapp-deploy-7d574d56c7-gvl9q   0/1     Terminating         0          1s
myapp-deploy-798dc9b584-q8fxd   0/1     ContainerCreating   0          0s
myapp-deploy-7d574d56c7-gvl9q   0/1     Terminating         0          1s
myapp-deploy-7d574d56c7-nkwt6   1/1     Running             0          3s
myapp-deploy-798dc9b584-z7jhh   1/1     Running             0          7s
myapp-deploy-7d574d56c7-nkwt6   1/1     Terminating         0          7s
myapp-deploy-798dc9b584-sjj65   0/1     Pending             0          0s
myapp-deploy-798dc9b584-sjj65   0/1     Pending             0          0s
myapp-deploy-798dc9b584-sjj65   0/1     ContainerCreating   0          0s
myapp-deploy-7d574d56c7-gvl9q   0/1     Terminating         0          8s
myapp-deploy-7d574d56c7-gvl9q   0/1     Terminating         0          8s
myapp-deploy-7d574d56c7-nkwt6   0/1     Terminating         0          8s
myapp-deploy-7d574d56c7-nkwt6   0/1     Terminating         0          9s
myapp-deploy-7d574d56c7-nkwt6   0/1     Terminating         0          9s
myapp-deploy-798dc9b584-sjj65   1/1     Running             0          2s
myapp-deploy-7d574d56c7-44mqw   1/1     Terminating         0          9m13s
myapp-deploy-798dc9b584-rdtmd   0/1     Pending             0          0s
myapp-deploy-798dc9b584-rdtmd   0/1     Pending             0          0s
myapp-deploy-798dc9b584-rdtmd   0/1     ContainerCreating   0          0s
myapp-deploy-7d574d56c7-44mqw   0/1     Terminating         0          9m14s
myapp-deploy-798dc9b584-q8fxd   1/1     Running             0          10s
myapp-deploy-798dc9b584-rdtmd   1/1     Running             0          2s
myapp-deploy-7d574d56c7-5msnk   1/1     Terminating         0          9m15s
myapp-deploy-7d574d56c7-5msnk   0/1     Terminating         0          9m16s
myapp-deploy-7d574d56c7-5msnk   0/1     Terminating         0          9m17s
myapp-deploy-7d574d56c7-5msnk   0/1     Terminating         0          9m17s
myapp-deploy-7d574d56c7-44mqw   0/1     Terminating         0          9m22s
myapp-deploy-7d574d56c7-44mqw   0/1     Terminating         0          9m22s

我们可以查看一下rs,此处有两个版本了,一个v1一个v2

kubectl get rs -o wide
NAME                      DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES                 SELECTOR
myapp-deploy-798dc9b584   4         4         4       4m15s   myapp        ikubernetes/myapp:v2   app=myapp,pod-template-hash=798dc9b584,release=canary
myapp-deploy-7d574d56c7   0         0         0       13m     myapp        ikubernetes/myapp:v1   app=myapp,pod-template-hash=7d574d56c7,release=canary
# 但是myapp:v1为历史版本,会保留着随时等待被回滚.

# 可以使用下面命令进行版本回滚
kubectl rollout history deployment myapp-deploy
deployment.apps/myapp-deploy 
REVISION  CHANGE-CAUSE
1         <none>
2         <none>

我们可以使用patch打补丁方式修改实时更新策略

kubectl patch deployment myapp-deploy -p '{"spec":{"replicas":5}}'
# 此时副本数量就为5个了

kubectl describe deployment myapp-deploy	# 修改deployment最大不可用和最小保留

kubectl describe deployment myapp-deploy |grep RollingUpdateStrategy
RollingUpdateStrategy:  0 max unavailable, 1 max surge
# 接下来我们修改下镜像版本,然后更新看一看(金丝雀发布)
kubectl set image deployment myapp-deploy myapp=ikubernetes/myapp:v3 && kubectl rollout pause deployment myapp-deploy
deployment.apps/myapp-deploy image updated
deployment.apps/myapp-deploy paused
# 另一个终端使用以下命令实时查看
kubectl get pods -l app=myapp -w
NAME                            READY   STATUS    RESTARTS   AGE
myapp-deploy-798dc9b584-bcdxf   1/1     Running   0          14m
myapp-deploy-798dc9b584-q8fxd   1/1     Running   0          57m
myapp-deploy-798dc9b584-rdtmd   1/1     Running   0          57m
myapp-deploy-798dc9b584-sjj65   1/1     Running   0          57m
myapp-deploy-798dc9b584-z7jhh   1/1     Running   0          57m
myapp-deploy-5dc9c974d7-qlpmq   0/1     Pending   0          0s
myapp-deploy-5dc9c974d7-qlpmq   0/1     Pending   0          0s
myapp-deploy-5dc9c974d7-qlpmq   0/1     ContainerCreating   0          0s
myapp-deploy-5dc9c974d7-qlpmq   1/1     Running             0          9s

kubectl rollout status deployment myapp-deploy
Waiting for deployment "myapp-deploy" rollout to finish: 1 out of 5 new replicas have been updated...
# 此时我们发现他会一直卡在这一个Pod更新,接下来我们让他全部更新
kubectl rollout resume deployment myapp-deploy

# 我们发现此时有了第三个版本
 kubectl get rs -o wide
NAME                      DESIRED   CURRENT   READY   AGE    CONTAINERS   IMAGES                 SELECTOR
myapp-deploy-5dc9c974d7   5         5         5       5m4s   myapp        ikubernetes/myapp:v3   app=myapp,pod-template-hash=5dc9c974d7,release=canary
myapp-deploy-798dc9b584   0         0         0       64m    myapp        ikubernetes/myapp:v2   app=myapp,pod-template-hash=798dc9b584,release=canary
myapp-deploy-7d574d56c7   0         0         0       73m    myapp        ikubernetes/myapp:v1   app=myapp,pod-template-hash=7d574d56c7,release=canary

# 接下来我们可以用回滚
kubectl rollout undo deployment myapp-deploy --to-revision=1	# 回滚到第一个版本
kubectl rollout history deployment myapp-deploy
deployment.apps/myapp-deploy 
REVISION  CHANGE-CAUSE
2         <none>
3         <none>
4         <none>	# 此时我们看到已经有第四个版本了,而此时工作的是v1版
curl 10.244.2.20
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
DaemonSet控制器

在集群中每个节点只运行一个Pod副本 也可以根据自己需求,在部分节点运行一个Pod副本 DaemonSet有以下特点

# 1. 一般是无状态的
# 2. 这种服务必须是守护进程,持续运行在后台

Example1

cat pod-ds.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
      role: logstor
  template:
    metadata:
      labels:
        app: redis
        role: logstor
    spec:
      containers:
      - name: redis
        image: redis:4.0-alpine
        ports:
        - name: redis
          containerPort: 6379
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: myapp-ds
  namespace: default
spec:
  selector:
    matchLabels:
      app: filebeat
      release: stable
  template:
    metadata:
      labels:
        app: filebeat
        release: stable
    spec:
      containers:
      - name: filebeat
        image: ikubernetes/filebeat:5.6.5-alpine 
        env:
        - name: REDIS_HOST
          value: redis.default.svc.cluster.local
        - name: REDIS_LOG_LEVEL
          value: info

kubectl exec -it redis-646cf89449-b5qkr /bin/bash
Address 1: 10.96.249.167 redis.default.svc.cluster.local

DaemonSet也支持滚动更新

kubectl get ds -o wide
NAME       DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE   CONTAINERS   IMAGES                              SELECTOR
myapp-ds   2         2         2       2            2           <none>          23m   filebeat     ikubernetes/filebeat:5.6.5-alpine   app=filebeat,release=stable
[root@master ~]# kubectl set image daemonsets myapp-ds filebeat=ikubernetes/filebeat:5.6.6-alpine

Example2 (Pod中容器和宿主机共享网络、进程通信和进程PID Namespace)

cat ns1.yaml   
apiVersion: v1  
kind: Pod  
metadata:  
 name: web-nginx2  
spec:  
 hostNetwork: true  
 hostIPC: true  
 hostPID: true  
 containers:  
 - name: web-nginx2  
 image: daocloud.io/library/nginx  
 - name: shell  
 image: busybox  
 stdin: true  
 tty: true  
  
kubectl apply -f ns1.yaml  
  
kubectl attach -it web-nginx2 -c shell  
/ # ps ax |grep network  
 3522 root     15:32 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --  
kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml   
--cgroup-driver=systemd --network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.1  
23958 root      0:00 grep network  
  
# Pod中的所有容器直接使用宿主机的网络、直接与宿主机进行IPC通信,且能够看到宿主机正在运行的所有进程  
Job

此类控制器主要为了完成指定任务、作业,只要没完成作业就会不断重建直至完成 帮我们确保Pod任务是正常完成的,没有出现什么异常 但是只是一次性任务 也可以使周期性任务,但他们不需要持续在后台运行,CronJob

以上控制器只能针对无状态应用,

StatefulSet

针对每一种服务每一种报错都要定义相应的恢复操作 比如说你Mysql主从节点的从节点宕机了,你要定义很多操作创建一个从节点然后让他们衔接,稍有不慎,整个集群就崩溃了 我们可以通过Operator将一些运维技能给灌进去,但是支持的应用有限 直到后来出现了helm,类似于yum,类似于模板

Example1(Pod中容器共用一个Pid Namespace)
cat ns.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: web-nginx
spec:
  shareProcessNamespace: true
  containers:
  - name: web-nginx
    image: daocloud.io/library/nginx
  - name: shell
    image: busybox
    stdin: true
    tty: trdue
# 1、shareProcessNamespace: true表示此Pod中的容器共享PID,即每个容器都能看到其他容器内的进程  
# 2. 定义两个容器:一个nginx;一个等同于使用-it选项的容器  
此Pod被创建后,就可以使用shell容器的tty跟这个容器进行交互。

kubectl apply -f ns.yaml

kubectl attach -it web-nginx -c shell
If you don't see a command prompt, try pressing enter.
/ # ps ax
PID   USER     TIME  COMMAND
    1 root      0:00 /pause
    6 root      0:00 nginx: master process nginx -g daemon off;
   11 101       0:00 nginx: worker process
   21 root      0:00 sh
   26 root      0:00 ps ax
# 在容器里能同时看到nginx和busybox以及Infra容器的/pause的进程,整个Pod里的每个容器进程对于所有容器来说都是可见的,
# 因为它们共享同一个 PID Namespace。

node的相关属性

nodeName: 这个字段一般由调度器负责设置,用户也可以设置他来'骗过'调度器,一旦Pod的这个字段被赋值,k8s就会被认为此Pod已经调度过,调度的结果就是赋值的节点名字,这个做法一般是测试或者调试的时候才会用到,简单来说就是不让调度器去选择此Pod运行在哪个node上,而是用户自行指定该Pod运行的node. nodeSelector: 和nodeName的作用一样,是一个供用户将Pod与Node进行绑定的字段,例如: 设置Pod永远只能运行在携带了: "Kubernetes.io/hostname: k8s-node1"标签的节点上: 否则,他将调度失败.

Example1(使用nodeSelector指定pod运行的节点)

查看k8s-node1上的标签都有哪些

kubectl describe pod web-nginx |grep ^Labels -C3
Priority:     0
Node:         node2/172.19.0.54
Start Time:   Thu, 19 Dec 2019 17:45:43 +0800
Labels:       <none>
Annotations:  <none>
Status:       Running
IP:           10.244.1.5

创建一个Pod指定运行在node1上

cat nginx1.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: test-nginx
  labels:
    web: nginx2
    nginx: nginx3
spec:
#  nodeName: 192.168.122.5 #也可以使用下面的nodeSelector。
  containers:
    - name: web-test
      image: daocloud.io/library/nginx
      ports:
        - containerPort: 80
  nodeSelector: 
    kubernetes.io/hostname: node1 
kubectl apply -f nginx1.yaml
容器中的hosts文件解析

HostAliases: 定义Pod容器中的/etc/hosts文件,解析主机名,且在k8s中,一定要通过此方法设置hosts文件中的内容,如果直接修改hosts文件,在Pod被删除重建之后,kubelet会自动覆盖被修改的内容

cat nginx2.yaml 
---
apiVersion: v1
kind: Pod
metadata:
  name: web-nginx3
  labels:
    web: nginx4
    nginx: nginx5
spec:
  hostAliases:
  - ip: "1.1.1.1"
    hostnames:
    - "cluster1.com"
    - "cluster2.com"
    - "cluster3.com"
  containers:
    - name: web2
      image: daocloud.io/library/nginx
      ports:
        - containerPort: 80

kubectl apply -f nginx2.yaml 

kubectl exec -it web-nginx3 /bin/bash
root@web-nginx3:/# ls
bin   dev  home  lib64	mnt  proc  run	 srv  tmp  var
boot  etc  lib	 media	opt  root  sbin  sys  usr
root@web-nginx3:/# cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.244.1.6	web-nginx3

# Entries added by HostAliases.
1.1.1.1	cluster1.com	cluster2.com	cluster3.com

多个ip对应不同域名的写法
spec:  
hostAliases:  
- ip: "1.1.1.1"  
hostnames:  
- "cluster1.com"  
- ip: "2.2.2.2"  
hostnames:  
- "cluster2.com"

容器的属性

容器是Pod中最重要的字段,有"Containers" 和"Init Containers" 两种写法,内容完全相同. Init Containers: 其生命周期会先于所有的容器(即优先启动),并且严格按照定义的顺序执行. containers: 指定多个名称表示多个容器,每一个容器都是列表中的一个元素,每一列元素中还会有字典,字典不同的元素表示容器的不同属性,容器可以设置复杂的属性,包括容器启动运行的命令,使用的参数、工作目录以及每次实例化是否拉取新的副本,还可以指定更深入的信息,例如容器的退出日志的位置等. 包括: name、image、command、args、workingDir、ports、env、resource、volumeMounts、livenessProbe、readinessProbe、livecycle、terminationMessagePath、imagePullPolicy、securityContext、stdin、stdinOnce、tty

name:容器的名字,起个有意义的名字  
image:容器镜像  
command:容器启动运行的命令  
args:容器使用的参数  
workingDir:容器的工作目录  
ports:容器监听的端口  
env:定义容器的环境变量,主要是给mysql这种应用使用的spec:containers:env  
  env:  
    - name: a  # 变量名称  
      
value: "hello"  # 变量值,必须加引号  
volumeMounts:# 共享卷  
imagePullPolicy:# 镜像拉取策略,此属性有bug。  
# 默认值Always表示每次创建Pod都重新拉取一次镜像,实际上是镜像在本地无需拉取;  
Never:# 不拉取镜像,本地有直接使用,本地不存在就报异常错误  
IfNotPresent: # 表示宿主机上不存在这个镜像时才拉取,建议使用。
官方提供的钩子示例

容器成功启动之后,在/usr/share/message中写入了一句“欢迎信息”(PostStart定义),在这个容器被删除之前,则先调用nginx的退出命令(preStop定义),从而实现容器中的应用从而关闭.

cat message.yaml
apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: daocloud.io/library/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"]
kubectl apply -f message.yaml

注释

postStart: 开始之后执行的动作,虽然理论上是在Docker容器ENTRYPOINT执行之后,但它并不严格保证顺序。即:在postStart启动时,ENTRYPOINT有可能还没有结束。如果postStart执行超时或者错误,k8s会在该Pod的Events中报出该容器启动失败的错误信息,导致Pod也处于失败的状态。 preStop:停止之前指定的动作。操作的执行是同步的,所以它会阻塞当前的容器杀死流程,直到这个 Hook定义操作完成之后,才允许容器被杀死。

Pod | docker 容器的生命周期

Pod声明周期的变化,主要体现在Pod API对象的Status部分,这是除了Metadata和Spec之外的第三个重要字段,其中Pod.status.phase就是Pod的当前状态,有如下几种可能的情况: Pending: 此状态表示Pod的YAML文件已经提交给了kubernetes,API对象已经被创建并保存在Etcd中.但是这个Pod里有些容器因为某种原因不能被顺利创建,比如: 调度不成功. (准备状态: 准备好了就running,准备不好就是failed)

# Running: 此状态表示Pod已经调度成功,跟一个具体的节点绑定,它包含的容器都已经创建成功,并且至少有一个正在运行中.
# Succeeded: 此状态表示Pod里的所有容器都正常运行完毕,并且已经退出了,这种情况在运行一次性任务最为常见.
# Failed: 此状态表示Pod里至少有一个容器以不正常的状态退出,这个状态出现以为这个你得Debug这个容器的应用,比如查看Pod的Events和日志.
# Unknown: 这是一个异常状态,表示Pod的状态不能持续的kubelet汇报给kube-apiserver,这很有可能是主从节点(Master和Kubelet)之间的通信出现了问题.

细分Pod对象的Status字段

Pod对象的Status字段还可以细分一组Conditions,包括: PodScheduled、Ready、Initialized、以及Unschedulable,他们主要用于描述当前Status的具体原因,比如: Pod当前的Status是Pending,对应的Condition是Unschedulable,这表示他的调度出现了问题. 投射数据卷Projected Volume 是Kubernetes v1.11之后的新特性,在K8s中,有几种特殊的Volume,他们既不是存放容器里的数据,也不是用来和宿主机之间的数据交换,而是为容器提供预先定义好的为新建容器提供字符串的,字符串有长有短,就可以使用不同类型的投射数据卷,当然这个工作普通的volumn也可以实现,但是普通的volumn很容易暴露密码,token或者秘钥等敏感数据,而使用投射数据卷方式可以把敏感数据的资源直接存放在k8s集群的数据库中,可以更方便的控制这些数据,并减少暴露的风险,从容器的角度来看,这些Volume里的信息仿佛是被k8s"投射"(Project)进入容器当中的.

k8s支持的Projected Volume

# Secret: 设置密码可以使用
# ConfigMap: 准备配置文件使用
# Downward API: 设置环境变量
# ServiceAccountToken: 属于一种特殊的Secret,做认证使用.

# 使用时分两步:
# 做出相应的投射数据卷存放到数据库
# 创建容器时使用.

Pod的重启策略

三种重启策略
Always:    # 当容器停止,总是重建容器,默认策略
OnFailure:   # 当容器异常退出(退出状态码非0)时,才重启容器
Never:   # 当容器终止退出,从不重启容器.
Example
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  restartPolicy: OnFailure