K8S 实战(三)| Pod 的本质

前言

上一节发布了一个容器到 K8S 中,但其实实际操作的是 Pod ,那么为什么是 Pod,而不是容器。

更新历史

Pod 的本质

Pod 包装了一个或多个容器。
Pod 是 K8S 的最小执行单元。
Pod 是 K8S 中的一个进程。
Pod 可包装 Docker,也支持包装其他类型容器。
Pod 包含封装的容器、存储资源、网络资源、以及指导容器如何运行的配置。

可以把容器理解为一个无挂钩的光秃秃的集装箱,K8S 这艘大船无法直接挂载它,通过给集装箱(容器)加装挂钩(IP地址)等形成一个 Pod,方便 K8S 来操作。

也可以把 Pod 理解为传统的虚拟机,而容器是传统虚拟机中运行的程序,只不过虚拟机是一个实体,而 Pod 是一个逻辑概念。

K8S 通过编排 Pod 来调度容器,而不是直接操作容器,K8S 无法直接操纵容器。

Pod 中的共享资源

Pod 为其中运行的多容器提供共享的网络、存储资源、命名空间。

网络

Pod 具有唯一 IP 地址,Pod 中的多个容器共享一个 IP 地址和网络端口等网络资源
Pod 中多容器可使用 localhost 通信
Pod 中容器和外部通信时候,多容器需要协调网络端口
Pod 中的容器获取的系统主机名与为 Pod 配置的 name 相同

存储

Pod 可指定一组存储卷
Pod 中多容器均可以访问该存储卷,以便互相共享数据
Pod 中的共享卷可以持久保存,防止容器重启丢失数据

Pod 的特点

如果使用 kind: Pod 的 yaml 文件来创建 Pod,当前节点服务器出现问题后,Pod 不能被自动调度到其他可用服务器。

一般使用 kind: Deployment 的 yaml 来创建 Pod。

Deployment 是一种控制器,可以用来创建、管理 Pod。如创建多副本 Pod,滚动更新 Pod。

当 Pod 所在节点出现问题,Deployment 控制器可以在集群中其他节点启动新 Pod。

Pod 模板

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']

镜像拉取策略

不具体指定的情况下,imagePullPolicy 是 Always,即 kubelet 会尝试从指定的仓库拉取每个镜像。
如果容器属性 imagePullPolicy 设置为 IfNotPresent , 则会优先使用本地镜像。
如果容器属性 imagePullPolicy 设置为 IfNotPresent Never, 则会一定使用本地镜像。

apiVersion: v1
kind: Pod
metadata:
  name: private-image-test-1
spec:
  containers:
    - name: uses-private-image
      image: nginx
      imagePullPolicy: Always
      command: [ "echo", "SUCCESS" ]

Pod 常用参数

NodeSelector

功能:将 Pod 与 Node 绑定

apiVersion: v1
kind: Pod
...
spec:
 nodeSelector:
   disktype: ssd

该 Pod 只能运行在携带了“disktype:ssd”标签(Label)的节点上,如果没有这种标签的节点,调度将失败。

NodeName

该字段一般由调度器设置,但我们测试时候可以手工设置该字段,让调度器认为该 Pod 已经被调度过了。

HostAliase

给 Pod 里各容器的 /etc/hosts 文件设置内容

apiVersion: v1
kind: Pod
......
spec:
  hostAliases:
  - ip: "10.20.20.20"
    hostnames:
    - "test1.com"
    - "test2.com"

进容器检查一下

[root@master01 ~]# kubectl exec -it nginx -- bash
root@nginx:/# cat /etc/hosts
......
# Entries added by HostAliases.
10.20.20.20     test1.com       test2.com

shareProcessNamespace

定义参数 shareProcessNamespace=true,那么该 Pod 中所有容器将共享 PID Namespace

创建一个包含两个容器的 Pod

[root@master01 ~]# cat nginx.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  shareProcessNamespace: true
  containers:
  - name: nginx
    image: nginx
  - name: shell
    image: busybox
    stdin: true
    tty: true

查看运行情况

[root@master01 ~]# kubectl get pods -o wide
NAME    READY   STATUS    RESTARTS   AGE    IP               NODE     NOMINATED NODE   READINESS GATES
nginx   2/2     Running   0          117s   192.10.137.131   work03   <none>           <none>

进入到 Pod nginx 中的名为 shell 的容器中

[root@master01 ~]# kubectl attach -it nginx -c shell
If you don't see a command prompt, try pressing enter.
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 /pause
    6 root      0:00 nginx: master process nginx -g daemon off;
   33 101       0:00 nginx: worker process
   34 root      0:00 sh
   39 root      0:00 ps aux

可以看到,shell 容器中可以看到 nginx 容器的进程

Pod 的预设参数 PodPreset

我们可以预先设置好一些通用的配置,当用户提交自己的个性化 Pod 配置时,PodPreset 就可以自动附加通用配置到对应的 Pod 上。

PodPreset 里定义的内容,只会在 Pod API 对象被创建之前追加在这个对象本身 上,而不会影响任何 Pod 的控制器的定义。

比如,我们现在提交的是一个 nginx-deployment,那么这个 Deployment 对象本身是永远不会被 PodPreset 改变的,被修改的只是这个 Deployment 创建出来的所有 Pod。

未启用 PodPreset 特性时

# kubectl get podpresets
error: the server doesn't have a resource type "podpresets"

启用 PodPreset 特性

修改
[/etc/kubernetes/manifests/kube-apiserver.yaml] 
中的
spec.containers.command: 
修改原
- --runtime-config=api/all=true
为
- --runtime-config=api/all=true,settings.k8s.io/v1alpha1=true
新加一行
- --enable-admission-plugins=PodPreset

3台MASTER均执行重启 kubelet
systemctl restart kubelet

预设值配置 preset.yaml

apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
  name: allow-tz-env
spec:
  selector:
    matchLabels:
  env:
    - name: TZ
      value: Asia/Shanghai

Pod配置 nginx.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx

创建 PodPreset

# kubectl get podpreset
No resources found in default namespace.

# kubectl apply -f podpreset.yaml
podpreset.settings.k8s.io/allow-tz-env created

# kubectl get podpreset
NAME           CREATED AT
allow-tz-env

创建 Pod

[root@master01 ~]# kubectl apply -f nginx.yaml 
pod/nginx created

查看该 Pod 是否被注入

# kubectl get pod nginx -o yaml 
...省略
spec:
  containers:
  - env:
    - name: TZ
      value: Asia/Shanghai
    image: nginx
    imagePullPolicy: Always
    name: nginx
    resources: {}
...省略

可以看到 Pod 被注入了名为 TZ 的 env

Init 容器

特点

  1. Init 容器在 Pod 内应用容器启动之前运行。
  2. 一个 Pod 可以有一个或多个 Init 容器。
  3. 每个 Init 容器运行必须完成。
  4. 如果 Init 容器运行失败,K8S 会不断重启该 Pod,直到 Init 容器运行成功。
  5. 但如果 Pod 对应的 restartPolicy 值为 Never,它不会重新启动。
  6. 如果一个 Pod 有多个 Init 容器,这些容器会按顺序逐个运行。每个 Init 容器必须运行成功,下一个才能够运行。
  7. Init 容器可以包含一些安装过程中应用容器中不存在的实用工具或个性化代码。例如,没有必要仅为了在安装过程中使用类似 sed、 awk、 python 或 dig 这样的工具而去FROM 一个镜像来生成一个新的镜像。
  8. Init 容器可以安全地运行这些工具,避免这些工具导致应用镜像的安全性降低。
  9. 应用镜像的创建者和部署者可以各自独立工作,而没有必要联合构建一个单独的应用镜像。
  10. Init 容器能以不同于 Pod 内应用容器的文件系统视图运行。因此,Init容器可具有访问 Secrets 的权限,而应用容器不能够访问。
  11. 由于 Init 容器必须在应用容器启动之前运行完成,因此 Init 容器提供了一种机制来阻塞或延迟应用容器的启动,直到满足了一组先决条件。一旦前置条件满足,Pod 内的所有的应用容器会并行启动。
  12. 在所有的 Init 容器没有成功之前,Pod 将不会变成 Ready 状态。
  13. Init 容器镜像的变更会引起 Pod 重启, 应用容器镜像的变更仅会重启应用容器。
  14. Pod 中每一个容器副本启动之前,都会执行一遍 Init 容器。

如何使用

  1. 定义一个具有 2 个 Init 容器的 Pod。
  2. init 容器为 myservice 和 mydb。 这两个 Init 容器都启动完成,Pod 才能启动 spec 区域中的应用容器 myapp-container

创建 Pod 的 YAML 文件:

cat myapp.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]

创建 mydb 和 myservice 两个 Service 的 YAML 文件:

cat myservice.yaml

kind: Service
apiVersion: v1
metadata:
  name: myservice
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
---
kind: Service
apiVersion: v1
metadata:
  name: mydb
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9377

要启动这个 Pod,可以执行如下命令:

kubectl apply -f myapp.yaml
pod/myapp-pod created

检查其状态:

kubectl get -f myapp.yaml
NAME        READY     STATUS     RESTARTS   AGE
myapp-pod   0/1       Init:0/2   0          6m

使用下面命令查看更详细的信息:

kubectl describe -f myapp.yaml

Name:          myapp-pod
Namespace:     default
[...]
Labels:        app=myapp
Status:        Pending
[...]
Init Containers:
  init-myservice:
[...]
    State:         Running
[...]
  init-mydb:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]
Containers:
  myapp-container:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]

查看Pod内 Init 容器的日志

$ kubectl logs myapp-pod -c init-myservice
$ kubectl logs myapp-pod -c init-mydb

此时,Init 容器将会等待直到发现名称为mydb和myservice的 Service。

创建mydb和myservice的 service:

$ kubectl create -f services.yaml
service "myservice" created
service "mydb" created

可以看到这些 Init 容器执行完毕,随后my-app的Pod转移进入 Running 状态:

$ kubectl get -f myapp.yaml
NAME        READY     STATUS    RESTARTS   AGE
myapp-pod   1/1       Running   0          9m

只有我们启动了 mydb 和 myservice 这两个 Service,Init 容器完成,myapp-pod 才能被创建。

Debug Pod

如果 Pod 被终止,可通过如下命令查看原因

kubectl describe pod pod名称

kubectl get pod -o go-template='{{range.status.containerStatuses}}{{"Container Name: "}}{{.name}}{{"\r\nLastState: "}}{{.lastState}}{{end}}'  pod名称

结束语

Pod 包装了容器,K8S 通过操作 Pod 来操作容器。

Pod 可以包含多个应用容器和多个 Init 容器。

人工直接创建的 Pod 在服务器节点故障时,没有自愈能力,需要使用控制器来解决这个问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351