k8s 实践经验:实操中学 k8s 五种资源Pod
Posted 看,未来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了k8s 实践经验:实操中学 k8s 五种资源Pod相关的知识,希望对你有一定的参考价值。
文章目录
本期我们在 k8s 集群上部署 nginx 服务。
namespace
Namespace是kubernetes系统中的一种非常重要资源,它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。
默认情况下,kubernetes集群中的所有的Pod都是可以相互访问的。但是在实际中,可能不想让两个Pod之间进行互相的访问,那此时就可以将两个Pod划分到不同的namespace下。kubernetes通过将集群内部的资源分配到不同的Namespace中,可以形成逻辑上的"组",以方便不同的组的资源进行隔离使用和管理。
可以通过kubernetes的授权机制,将不同的namespace交给不同租户进行管理,这样就实现了多租户的资源隔离。此时还能结合kubernetes的资源配额机制,限定不同租户能占用的资源,例如CPU使用量、内存使用量等等,来实现租户可用资源的管理。
kubernetes在集群启动之后,会默认创建几个namespace:
[root@master ~]# kubectl get namespace
NAME STATUS AGE
default Active 5d1h # 所有未指定Namespace的对象都会被分配在default命名空间
kube-node-lease Active 5d1h # 集群节点之间的心跳维护,v1.13开始引入
kube-public Active 5d1h # 此命名空间下的资源可以被所有人访问(包括未认证用户)
kube-system Active 5d1h # 所有由Kubernetes系统创建的资源都处于这个命名空间
增删查改
1、创建命名空间
[root@k8s-master ~]# kubectl create ns test
namespace/test created
2、查看命名空间
全局查看的演示已经在上面了。
此处再学一个专项查看的:
[root@k8s-master ~]# kubectl describe ns/default
Name: default
Labels: <none>
Annotations: <none>
Status: Active
No resource quota.
No LimitRange resource.
3、删除命名空间
[root@k8s-master ~]# kubectl delete ns test
namespace "test" deleted
删除会比较慢一些、
配置文件操作
vi ns-test.yaml
apiVersion: v1
kind: Namespace
metadata:
name: test
这种 yaml 脚本,说真的我只会看不会写。但是看多了,就该会了。
创建:kubectl create -f ns-test.yaml
删除:kubectl delete -f ns-test.yaml
Pod
前面那个只是开胃菜,这个才是重头戏哈。
● Pod是kubernetes集群进行管理的最小单元,程序要运行必须部署在容器中,而容器必须存在于Pod中。
● Pod可以认为是容器的封装,一个Pod中可以存在一个或者多个容器。
为什么需要 Pod?
对于这些对pod概念的总结大多殊路同归。
在理解概念前不妨先想一个问题:
明明有了容器,为什么还需要pod?
因为:
为了解决容器不足,或者说容器无法满足需求。
确实是这样,先分析一下容器的本质:
namespace做隔离
Cgroup做限制
rootfs做文件系统
三者相辅相成组成容器基本模型
但是,宏观的来说,容器的本质是系统中的一个进程。只不过这个进程启动时被附加了以上等等一些特殊的属性。
所谓容器只是一个恰当的说法。
就如系统中的大多数进程来说,不是单个进程独自工作,而是以“进程组”的方式“原则性”的组织在一起,互相协作,完成复杂任务。当然,它们之间也会共享一些资源,例如pid,namespace,存储等等。
在实际开发和运维中也是随处可见的这种问题,应用之间有深切的联系和依赖。
比如说,我要将一个应用容器化,这个应用由负责各个功能的5个进程组成,这时候,问题来了。
正是容器的局限性:单进程模型
单进程不是指容器中只能运行一个进程,而是容器无法去管理多个进程
例如,容器中有pid=1的进程,还有一个pid=5的进程,当这个pid=5的进程异常退出时,后续的垃圾回收等处理工作又由谁去做呢?
所以,这个应用的5个模块就必须分别制成5个容器,而且必须在同一个机器上运行。
随之而来的又是一个问题:
也就是容器调度问题,例如有两个容器调度节点node1和node2,可用内存分别为5G和4.5G,
每个容器分1G内存。由于5个容器必须在一台机器,正常全部调度到node1刚刚好,没有任何问题。
但是,因为是以容器为单位调度,有这样一些特殊情况。当前4个容器被调度到node2上时,空间只有0.5G,不足以运行最后一个容器,它有只能在node1运行,这就是以容器为调度单位的缺点。
当然也有解决方案:如Mesos中的资源囤积(resource hoarding),也就是所有调度任务都到达了才进行调度,也有谷歌Omega论文提出乐观调度,就是先不管冲突,而是在冲突之后通过一系列回滚机制解决冲突。
但这些都没有很完善的解决容器调度问题,但是在kubernetes中,这些问题都迎刃而解。
因为不在按照传统思维的将容器作为调度单位
kubernetes中的项目调度器是统一按照pod的资源需求做调度计算
也就是开始总结的那句话: pod是kubernetes项目的原子调度单位
pod结构
如图所示,一个pod包含了两类容器:
- 用户容器
- Pause容器,也常常被称为“根容器”(貌似老版本叫做infra容器)
用户容器好理解,但是Pause容器,也就是根容器,他是做什么用的呢?
pause容器主要为每个用户容器提供以下功能:
① PID命名空间:Pod中的不同应用程序可以看到其他应用程序的进程ID。
② 网络命名空间:Pod中的多个容器能够访问同一个IP和端口范围。
③ IPC命名空间:Pod中的多个容器能够使用SystemV IPC或POSIX消息队列进行通信。
④ UTS命名空间:Pod中的多个容器共享一个主机名;Volumes(共享存储卷):
⑤ Pod中的各个容器可以访问在Pod级别定义的Volumes。
pod实现原理
一定要明白一点:pod只是一个逻辑上的概念
因为kubernetes真正处理的还是宿主机操作系统上容器的Namespace和Cgroup,也就是说没有所谓的pod边界或隔离环境。
所以说,pod就是一组共享了某些资源的容器
在一个pod中所有容器是共享一个Network Namespace
的,根据声明的不同来实现不同的资源共享
但是容器间的复杂关系在容器上难以解决,所以kubernetes项目里,pod的实现借用了一个中间容器,也就是常常说的根容器(也叫pause容器或infra容器,现在好像都统称pause容器,infra容器已经不再使用)。
在pod中,根容器永远是第一个创建的容器,用户后面定义的容器会加入进pod的Network Namespace
,从而在视图上容器都在pod里。
不妨在k8s中看看这个容器镜像:
[root@master ~]# docker images | grep pause
registry.aliyuncs.com/google_containers/pause 3.2 80d28bedfe5d 15 months ago 683kB
#当然,主机上有多少个pod就能docker ps 过滤看到多少pause容器
pause镜像大小只有683k,它是由汇编语言写的镜像
在一个pod中的容器,他们的Namespace文件,是一样的
也就意味着:
- pod内的容器可以使用localhost进行通信
- 根容器能看到的网络资源,其他容器都能看到,也就是pod的网络资源和pod内的容器共享
- 一个pod有一个ip地址,和pod对应的Network namespace的ip一致
- pod的生命周期只与根容器有关,与pod内的容器无关
- 从pod里的容器的视角来说,它们的流量进出可以看做是通过根容器完成的
pod 操作
1、当我们搭建成功集群之后,可以看到已经有几个 Pod 在运行中了,这是支持我们集群运行的重要组件:
kubectl get pods -A
在实习的时候不常用 -A,实在太多了,都是用 -n 筛选一下命名空间,比如这里就可以这样:
当然,有时候晒过还是很多,再细分这个就是我们的经验了。
2、 创建一个 pod
kubectl run mynginx --image=nginx[:1.17.1] --port=80 [--namespace=dev]
这里的镜像它会去下载,如果本地没有的话。这里的本地指的是 k8s 存储镜像的地方,这些比较细的东西可能后面会整个社群讲吧。
3、 查看 pod 的详细信息
kubectl describe pod pod的名称 [-n 命名空间名称]
4、访问容器
kubectl get pods -n dev -o wide
获取到容器 IP。
配置文件形式
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
spec:
containers:
- image: nginx:1.17.1
imagePullPolicy: IfNotPresent
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
kubectl create -f pod-nginx.yaml
kubectl delete -f pod-nginx.yaml
发现上面那个小细节还真不好找,找半天我也没看到离线的时候到底是从哪里找的镜像去进行的安装。
注意看这一行:imagePullPolicy: IfNotPresent
k8s 有三个镜像拉取策略:
Always 镜像标签为latest或镜像不存在时总是从指定的仓库中获取镜像
IfNotPresent 仅当本地镜像缺失时方才从目标仓库下载镜像
Never 禁止从仓库下载镜像,即仅使用本地镜像
对于标签latest的镜像文件,其默认的镜像获取策略即为Always,而对于其他标签的镜像,其默认策略则为IfNotPresent。需要注意的是,使用私有仓库中的镜像时通常需要由Registry服务器完成认证后才能进行。认证过程要么需要在相关节点上交互式执行docker login命令来进行,要么就是将认证信息定义为专有的Secret资源,并配置Pod通过imagePullSecredtes字段调用此认证信息完成。
对于这个配置文件的写法,我特地做了个表格,不放出来可惜了。
Pod 定义
属性名称 | 取值类型 | 是否必选 | 取值说明 |
---|---|---|---|
apiVersion: v1 | String | Requried | 版本号,例如v1,版本号必须可以用 kubectl api-versions 查询到 |
kind: Pod | String | Requried | Pod |
metadata: | Object | Requried | 元数据 |
name: string | String | Requried | Pod名称,需符合RFC 1035规范 |
namespace: string | String | Requried | Pod所属的命名空间,默认为"default" |
labels: | List | 自定义标签 | |
- name: string | String | 自定义标签名字 | |
annotations: | List | 自定义注释列表 | |
- name: string | |||
spec: | Object | Requried | Pod中容器的详细定义 |
containers: | List | Requried | Pod中容器列表 |
- name: string | String | Requried | 容器名称,需符合RFC 1035规范 |
image: string | String | Requried | 容器的镜像名称 |
imagePullPolicy: [ Always|Never|IfNotPresent ] | String | 获取镜像的策略,默认值为Alawys,Alawys表示每次都尝试下载镜像,IfnotPresent表示优先使用本地镜像,否则下载镜像,Nerver表示仅使用本地镜像 | |
command: [string] | List | 容器的启动命令列表,如不指定,使用打包时使用的启动命令 | |
args: [string] | List | 容器的启动命令参数列表 | |
workingDir: string | String | 容器的工作目录 | |
volumeMounts: | List | 挂载到容器内部的存储卷配置 | |
- name: string | String | 引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名 | |
mountPath: string | String | 存储卷在容器内mount的绝对路径,应少于512字符 | |
readOnly: boolean | Boolean | 是否为只读模式,默认为读写模式 | |
ports: | List | 需要暴露的端口库号列表 | |
- name: string | String | 端口的名称 | |
containerPort: int | Int | 容器需要监听的端口号 | |
hostPort: int | Int | 容器所在主机需要监听的端口号,默认与Container相同 | |
protocol: string | String | 端口协议,支持TCP和UDP,默认TCP | |
env: | List | 容器运行前需设置的环境变量列表 | |
- name: string | String | 环境变量名称 | |
value: string | String | 环境变量的值 | |
resources: | Object | 资源限制和请求的设置 | |
limits: | Object | 资源限制的设置 | |
cpu: string | String | CPU的限制,单位为core数,将用于docker run --cpu-shares参数 | |
memory: string | String | 内存限制,单位可以为Mib/Gib,将用于docker run --memory参数 | |
requests: | Object | 资源限制的设置 | |
cpu: string | String | CPU请求,容器启动的初始可用数量 | |
memory: string | String | 内存请求,容器启动的初始可用数量 | |
livenessProbe: | Object | 对Pod内各容器健康检查的设置,当探测无响应几次后将自动重启该容器,可设置的方法包括exec、httpGet和tcpSocket,对一个容器仅需设置其中一种健康检查方法 | |
exec: | Object | 对Pod容器内检查方式设置为exec方式 | |
command: [string] | String | exec方式需要制定的命令或脚本 | |
httpGet: | Object | 对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、port | |
path: string | |||
port: number | |||
host: string | |||
scheme: string | |||
HttpHeaders: | |||
- name: string | |||
value: string | |||
tcpSocket: | Object | 对Pod内个容器健康检查方式设置为tcpSocket方式 | |
port: number | |||
initialDelaySeconds: 0 | Number | 容器启动完成后首次探测的时间,单位为秒 | |
timeoutSeconds: 0 | Number | 对容器健康检查探测等待响应的超时时间,单位秒,默认1秒,若超过该超时时间设置,则认为该容器不健康,会重启该容器 | |
periodSeconds: 0 | Number | 对容器监控检查的定期探测时间设置,单位秒,默认10秒一次 | |
successThreshold: 0 | |||
failureThreshold: 0 | |||
securityContext: | |||
privileged: false | |||
restartPolicy: [Always | Never | OnFailure] | String | Pod的重启策略,(1)Always表示一旦不管以何种方式终止运行,kubelet都将重启它,(2)OnFailure表示只有Pod以非0退出码退出才重启,如果容器正常结束(退出码为0)kubelet将不会重启它,(3)Nerver:Pod终止后,kubelet将退出码报告给Master,不会再重启该Pod。 | |
nodeSelector: obeject | Object | 设置NodeSelector表示将该Pod调度到包含这个label的node上,以key:value的格式指定 | |
imagePullSecrets: | Object | Pull镜像时使用的secret名称,以name:secretkey格式指定 | |
- name: string | |||
hostNetwork: false | Boolean | 是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络,不再使用Docker网桥,该Pod将无法在同一宿主机上启动第2哥副本 | |
volumes: | List | 在该pod上定义共享存储卷列表 | |
- name: string | String | 共享存储卷名称 ,在一个Pod中每个存储卷定义一个名称,应符合RFC 1035规范。容器定义部分的containers[].volumeMounts[].name将引用该共享存储卷的名称。Volume的类型包括:emptyDir、hostPath、gcePersistentDisk、awsElasticBlockStore、gitRepo、secret、nfs、iscsi、glusterfs、persistentVolumeClaim、rbd、flexVolume、cinder、cephfs、flocker、downwardAPI、fc、azureFile、configMap、vsphereVolume,可以定义多个Volume,每个Volume的name保持唯一。 | |
emptyDir: | Object | 类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值 | |
hostPath: string | Object | 类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录 | |
path: string | String | Pod所在宿主机的目录,将被用于同期中mount的目录 | |
secret: | Object | 类型为secret的存储卷,挂载集群与定义的secre对象到容器内部 | |
scretname: string | |||
items: | |||
- key: string | |||
path: string | |||
configMap: | Object | 类型为configMap的存储卷,挂载预定义的configMap对象到容器内部 | |
name: string | |||
items: | |||
- key: string | |||
path: string |
滚动更新 pod
- 更新
kubectl replace -f hello-world-pod.yaml
但是由于Pod的很多属性没办法修改,比如容器镜像,这时候可以采用–force参数
- 重建Pod
kubectl replace --force -f hello-world-pod.yaml
删除Pod
- 通过kubectl delete删除指定Pod
kubectl delete pod hello-world
- 通过kubectl delete批量删除全部Pod
kubectl delete pod --all
不过只要那个 pod 是健康的,这样删不掉。
静态 Pod
什么是 Static Pod
静态 Pod 在指定的节点上由 kubelet 守护进程直接管理,不需要 API 服务器监管。 与由控制面管理的 Pod(例如,[Deployment]) 不同;kubelet 监视每个静态 Pod(在它崩溃之后重新启动)。
静态 Pod 永远都会绑定到一个指定节点上的 [Kubelet]。
kubelet 会尝试通过 Kubernetes API 服务器为每个静态 Pod 自动创建一个 [镜像 Pod]。 这意味着节点上运行的静态 Pod 对 API 服务来说是可见的,但是不能通过 API 服务器来控制。 Pod 名称将把以连字符开头的节点主机名作为后缀。
最常见的 Static Pod
- etcd
- kube-apiserver
- kube-controller-manager
- kube-scheduler
创建静态Pod有两种方式:配置文件方式和HTTP方式。
配置文件方式
可以通过kubelet的启动参数查看kubelet扫描静态Pod配置文件的路径,如下:
可以到kubelet是由这个配置文件进行启动的,在通过查看此配置文件,内容如下:
apiVersion: kubelet.config.k8s.io/v1beta1
.....
.....
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 0s
syncFrequency: 0s
volumeStatsAggPeriod: 0s
其中staticPodPath: /etc/kubernetes/manifests就是正在运行的kubelet需要监控的配置文件所在的目录,kubelet会定期扫描该目录,并根据该目录下的.yaml或.json文件进行创建操作。
在目录/etc/kubernetes/manifests中放入static-web.yaml文件,内容如下:
---
apiVersion: v1
kind: Pod
metadata:
name: static-web
labels:
name: static-web
spec:
containers:
- name: static-web
image: nginx
ports:
- name: web
containerPort: 80
等待一会儿,查看本机中已经启动的容器:
[root@k8s-node0 ~]# docker ps|grep static-web
85fd075aaddd nginx "/docker-entrypoint.…" About an hour ago Up About an hour k8s_static-web_static-web-k8s-node0_default_d9890f08374e33f99c31a2a034276178_0
可以看到一个Nginx容器已经被kubelet成功创建了出来。
到Master上查看Pod列表,可以看到这个static pod:
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-f89759699-gn2wn 1/1 Running 1 17h
static-web-k8s-node0 1/1 Running 0 12s
由于静态Pod无法通过API Server直接管理,所以在Master上尝试删除这个Pod时,会使其变成Pending状态,且不会被删除。
[root@k8s-master ~]# kubectl delete pod static-web-k8s-node0
pod "static-web-k8s-node0" deleted
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-f89759699-gn2wn 1/1 Running 1 18h
static-web-k8s-node0 0/1 Pending 0 2s
删除该Pod的操作只能是到其所在Node上将其定义文件static-web.yaml从/etc/kubernetes/manifests目录下删除。
[root@k8s-node0 ~]# rm /etc/kubernetes/manifests/static_pod_web_nginx.yaml
rm: remove regular file ‘/etc/kubernetes/manifests/static_pod_web_nginx.yaml’? y
[root@k8s-node0 ~]# docker ps|grep static-web
[root@k8s-node0 ~]# docker ps|grep static-web
在Master上查看这个Pod:
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-f89759699-gn2wn 1/1 Running 1 18h
HTTP方式
通过设置kubelet的启动参数–manifest-url或kubelet的配置文件加上此配置项,kubelet将会定期从该URL地址下载Pod的定义文件,并以.yaml或.json文件的格式进行解析,然后创建Pod。其实现方式与配置文件方式是一致的。
以上是关于k8s 实践经验:实操中学 k8s 五种资源Pod的主要内容,如果未能解决你的问题,请参考以下文章