什么是容器?
进程的特点及带来的问题
进程的特点
- 第一,这些进程可以相互看到、相互通信;
- 第二,它们使用的是同一个文件系统,可以对同一个文件进行读写操作;
- 第三,这些进程会使用相同的系统资源。
这样的三个特点会带来什么问题呢?
- 因为这些进程能够相互看到并且进行通信,高级权限的进程可以攻击其他进程;
- 因为它们使用的是同一个文件系统,因此会带来两个问题:这些进程可以对于已有的数据进行增删改查,具有高级权限的进程可能会将其他进程的数据删除掉,破坏掉其他进程的正常运行;此外,进程与进程之间的依赖可能会存在冲突,如此一来就会给运维带来很大的压力;
- 因为这些进程使用的是同一个宿主机的资源,应用之间可能会存在资源抢占的问题,当一个应用需要消耗大量 CPU 和内存资源的时候,就可能会破坏其他应用的运行,导致其他应用无法正常地提供服务。
问题的解决与容器的本质
针对上述的三个问题,如何为进程提供一个独立的运行环境呢?
- 针对不同进程使用同一个文件系统所造成的问题而言,Linux 和 Unix 操作系统可以通过 chroot 系统调用将子目录变成根目录,达到视图级别的隔离;进程在 chroot 的帮助下可以具有独立的文件系统,对于这样的文件系统进行增删改查不会影响到其他进程;
- 因为进程之间相互可见并且可以相互通信,使用 Namespace 技术来实现进程在资源的视图上进行隔离。在 chroot 和 Namespace 的帮助下,进程就能够运行在一个独立的环境下了;
- 但在独立的环境下,进程所使用的还是同一个操作系统的资源,一些进程可能会侵蚀掉整个系统的资源。为了减少进程彼此之间的影响,可以通过 Cgroup 来限制其资源使用率,设置其能够使用的 CPU 以及内存量。
那么,应该如何定义这样的进程集合呢?
其实,容器就是一个视图隔离、资源可限制、独立文件系统的进程集合。所谓“视图隔离”就是能够看到部分进程以及具有独立的主机名等;控制资源使用率则是可以对于内存大小以及 CPU 使用个数等进行限制。容器就是一个进程集合,它将系统的其他资源隔离开来,具有自己独立的资源视图。
什么是Pod?
pod原意是豆荚
在k8s中,pod指的是一组(一个或多个) 容器,类似"进程组",这些容器共享存储、网络、以及怎样运行这些容器的声明(specification,之后yaml文件的spec)。
Pod 中的内容总是并置(co-located)的并且一同调度,在共享的上下文中运行。Pod 的共享上下文包括一组 Linux 名字空间、控制组(cgroup)和可能一些其他的隔离 方面,即用来隔离 Docker 容器的技术。 在 Pod 的上下文中,每个独立的应用可能会进一步实施隔离。
Kubernetes 假设 Pod 可与其它 Pod 通信,不管它们在哪个主机上。 Kubernetes 给每一个 Pod 分配一个集群私有 IP 地址,所以没必要在 Pod 与 Pod 之间创建连接或将容器的端口映射到主机端口。 这意味着同一个 Pod 内的所有容器能通过 localhost 上的端口互相连通,集群中的所有 Pod 也不需要通过 NAT 转换就能够互相看到。
pause容器
生命周期
Pod 遵循一个预定义的生命周期,起始于 Pending 阶段,如果至少 其中有一个主要容器正常启动,则进入 Running,之后取决于 Pod 中是否有容器以 失败状态结束而进入 Succeeded 或者 Failed 阶段。
取值 | 描述 |
Pending (悬决) |
Pod 已被 Kubernetes 系统接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间。 |
Running (运行中) |
Pod 已经绑定到了某个节点,Pod 中所有的容器都已被创建。至少有一个容器仍在运行,或者正处于启动或重启状态。 |
Succeeded (成功) |
Pod 中的所有容器都已成功终止,并且不会再重启。 |
Failed (失败) |
Pod 中的所有容器都已终止,并且至少有一个容器是因为失败终止。也就是说,容器以非 0 状态退出或者被系统终止。 |
Unknown (未知) |
因为某些原因无法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。 |
如果某节点死掉或者与集群中其他节点失联,Kubernetes 会实施一种策略,将失去的节点上运行的所有 Pod 的 phase 设置为 Failed。
探针(probe)
探测机制
所谓的探测机制,就是运行命令
exec
在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
例如,
echo $?
tcpSocket
对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。 如果远程系统(容器)在打开连接后立即将其关闭,这算作是健康的。
例如,
telnet ip port
httpGet
对容器的 IP 地址上指定端口和路径执行 HTTP GET 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。
例如,
curl -I http://ip:port
grpc(测试中)
使用 gRPC 执行一个远程过程调用。了解一下即可。
探针类型
看 deploy coredns 的yaml
kubectl get deploy coredns -n kube-system -oyaml
后面的为截取的内容
livenessProbe
存活探针,指示容器是否正在运行。如果存活状态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定何时重启。如果容器不提供存活探针, 则默认状态为Success。
livenessProbe: failureThreshold: 5 httpGet: path: /health port: 8080 scheme: HTTP initialDelaySeconds: 60 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 5
- failureThreshold:失败次数
- initialDelaySeconds:初始化时间
- periodSeconds:检测间隔时间
- successThreshold:成功次数
- timeoutSeconds:超时时间
使用HttpGet的方式,发送数据包来探测。整个流程大致为,发送http数据包,若在5秒内收到200-400状态码,则成功,否则等待10s继续发送,直至失败5次,就认为失败了,容器没有存活。
readinessProbe
就绪探针,指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。 初始化之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪探针,则默认状态为Success。
readinessProbe: failureThreshold: 3 httpGet: path: /ready port: 8181 scheme: HTTP periodSeconds: 10 successThreshold: 1 timeoutSeconds: 1
同样使用HttpGet的方式,发送数据包来探测
startupProbe
启动探针,指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器,而容器依其 重启策略进行重启。 如果容器没有提供启动探测,则默认状态为 Success。
Init Containers(初始化容器)
创建pod命令执行后,使用get po的时候看到的 Init,用于初始化,一般很小。
• 一直运行,直到完成。
• 每个都必须在下一个启动之前成功完成。
Ephemeral Containers(临时容器)
用于调试、故障排查
临时容器与其他容器的不同之处在于,它们缺少对资源或执行的保证,并且永远不会自动重启, 因此不适用于构建应用程序。 临时容器使用与常规容器相同的 ContainerSpec 节来描述,但许多字段是不兼容和不允许的。
• 临时容器没有端口配置,因此像 ports,livenessProbe,readinessProbe 这样的字段是不允许的。
• Pod 资源分配是不可变的,因此 resources 配置是不允许的。
模版
一个比较全的Pod yaml文件模板
apiVersion: v1 # 必选,API的版本号 kind: Pod # 必选,类型 这里填Pod metadata: # 必选,元数据 name: nginx # 必选,符合RFC 1035规范的Pod名称 namespace: default # 可选,Pod所在的命名空间,不指定默认为default,一般在kubectl中使用-n指定namespace labels: # 可选,标签选择器,一般用于过滤和区分Pod,可以写多个 app: nginx role: frontend annotations: # 可选,注释列表,可以写多个 app: nginx spec: # 必选,用于定义容器的详细信息 initContainers: # 可选,初始化容器,在容器启动之前执行的一些初始化操作 - command: - sh - -c - echo "I am a InitContainer that init some configurations" image: busybox imagePullPolicy: IfNotPresent # 可选,镜像拉取策略,宿主机有了就不拉取 name: init-container containers: # 必选,容器列表 - name: nginx # 必选,符合RFC 1035规范的容器名称 image: nginx:latest # 必选,容器所用的镜像的地址 imagePullPolicy: Always # 可选,镜像拉取策略 command: # 可选,容器启动执行的命令 - nginx - -g - "daemon off;" workingDir: /usr/share/nginx/html # 可选,容器的工作目录 volumeMounts: # 可选,存储卷配置,可以配置多个 - name: webroot # 存储卷名称 mountPath: /usr/share/nginx/html # 挂载目录 readOnly: true # 只读 ports: # 可选,容器需要暴露的端口号列表 - name: http # 端口名称 containerPort: 80 # 端口号 protocol: TCP # 端口协议,默认TCP env: # 可选,环境变量配置列表 - name: TZ # 变量名 value: Asia/Shanghai # 变量的值 - name: LANG value: en_US.utf8 resources: # 可选,资源限制和资源请求限制 limits: # 最大限制设置 cpu: 1000m memory: 1024Mi requests: # 启动所需的资源 cpu: 100m memory: 512Mi startupProbe: # 可选,检测容器内进程是否完成启动。注意三种检查方式同时只能使用一种。 httpGet: # httpGet检测方式,生产环境建议使用httpGet实现接口级健康检查,健康检查由应用程序提供。 path: /api/successStart # 检查路径 port: 80 readinessProbe: # 可选,健康检查。注意三种检查方式同时只能使用一种。 httpGet: # httpGet检测方式,生产环境建议使用httpGet实现接口级健康检查,健康检查由应用程序提供。 path: / # 检查路径 port: 80 # 监控端口 livenessProbe: # 可选,健康检查 exec: # 执行容器命令检测方式 command: - cat - /health httpGet: # httpGet检测方式 path: /_health # 检查路径 port: 8080 httpHeaders: # 检查的请求头 - name: end-user value: Jason tcpSocket: # 端口检测方式 port: 80 initialDelaySeconds: 60 # 初始化时间 timeoutSeconds: 2 # 超时时间 periodSeconds: 5 # 检测间隔 successThreshold: 1 # 检查成功为1次表示就绪 failureThreshold: 2 # 检测失败2次表示未就绪 lifecycle: # 可选,生命周期 postStart: # 容器创建完成后执行的指令, 可以是exec httpGet TCPSocket exec: command: - sh - -c - 'mkdir /data/ ' preStop: # 容器停止前执行的命令 httpGet: path: / port: 80 exec: command: - sh - -c - sleep 9 restartPolicy: Always # 可选,默认为Always,总是拉取。Never不管是否存储都不拉取 nodeSelector: # 可选,指定Node节点 region: subnet7 imagePullSecrets: # 可选,拉取镜像使用的secret,可以配置多个 - name: default-dockercfg-86258 hostNetwork: false # 可选,是否为主机模式,如是,会占用主机端口 volumes: # 共享存储卷列表 - name: webroot # 名称,与上述对应 emptyDir: {} # 挂载目录 hostPath: # 挂载本机目录 path: /etc/hosts
我下载了k8s源代码,可以查看一下PullPolicy
- Always:kubelet pull最新镜像,pull失败,容器就启动失败。
- Nerver:kubelet 不pull镜像,使用本地镜像,没有就失败。
- IfNotPresent:kubelet在镜像没有时就pull,如果本地没有,pull也失败,容器启动失败。
博主喜欢使用第3个
创建
或许你看到前面的很长的yaml已经蒙了,那在这里我们就创建一个简单的仅包含一个nginx容器的Pod
apiVersion: v1 kind: Pod metadata: name: nginx-test spec: containers: - name: nginx image: nginx ports: - name: http containerPort: 80
kubectl create -f nginx-test.yaml -n lady-killer9
可以使用 -n 指定命名空间
由于实际使用中基本不会使用原生的Pod,而是使用Deployment等资源,这里博主就不再创建了。建议读者耐心看看模板,然后添加写挂载卷、探针等。
查看
查看部分信息
kubectl get po -n lady-killer9
查看详细信息
kubectl get po -n lady-killer9 -o wide
修改
kubectl replace -f nginx-test.yaml -n lady-killer9
删除
删除pod
kubectl delete -f nginx-test.yaml
强制删除
有时可能遇到这种情况
这时候需要强制删除pod
kubectl delete -f nginx.yaml --force --grace-period=0
参考
CNCF x Alibaba 云原生技术公开课 - 云原生教程-容器基本概念
更多k8s相关内容,请看文章:k8s学习-思维导图与学习笔记