kubernetes 学习实践 -- pod

云原生现在是炙手可热的技术,而这里面最核心的当属大名鼎鼎的Kubernetes;K8S号称是未来时代的操作系统,是云原生时代IT工程师必备的技能。

学习应当有输出,下面是对pod相关知识的学习


pod 的一些基础概念:

pod是K8S中最小的调度单位,那么为啥不是docker容器呢?答案可能是从最开始K8S就没有将容器作为架构的核心,正如K8S的前身--Google的Borg系统研究人员提出的:

运行在大规模集群中的各种任务之间,实际上存在各种各样的关系。这些关系的处理,才是作业编排和管理系统最困难的地方

pod扮演的是传统部署环境中“虚拟机”的角色,而容器则是这个“虚拟机”中运行的用户程序。

pod只是一个逻辑概念,K8S真正处理的是宿主机OS上容器的namespace和cgroups,并不存在所谓的pod边界或者隔离,pod内部是有一个infra容器存在的,它是pod内的第一个容器。

infra容器是一个用汇编语言编写的,永远处于“暂停”状态的容器,大小只有几百KB,举例:

image

pod的生命周期,只跟其内部的infra容器有关系,而与其他容器无关;infra容器永远是第一个被创建的容器

pod是一组共享了某些资源的容器,这些容器可共享的资源有: 网络namespace、pv卷、security context

用户定义的容器,则可通过join network namespace的方式与infra容器关联一起;对同一个pod里的所有用户来讲,进出的流量都可认为是通过infra容器完成的。

对于volume,K8S只需要把volume的定义设计在pod层级即可,一个volume对应的宿主机目录对于pod来说就只有一个,pod里的容器只要声明挂载这个volume,就可以共享这个volume对应的宿主机目录。

pod内部容器之间的超亲密关系,形成了sidecar模式,即容器设计模式,常见的场景包括日志收集、通信适配等,而这也是service mesh项目如istio微服务治理的原理。

pod相关的属性:

pod级别的属性应该是如下所描述的属性:

  • 和调度、网络、存储、安全相关的属性
  • 和容器linux namespace相关的属性
  • 容器要共享宿主机的namespace,也一定是pod级别的定义

pod的四大类属性(应该是API对象的四大类属性),及每个大类里常见的子属性:

  • typemeta: 最基本的定义
    • Group
    • Kind
    • Version
  • metadata:内有2个最重要的属性,即namespace和name,它俩可唯一确定某个对象实例;其余常见的字段还有
    • Label
    • Annotation
    • Finalizer
    • ResourceVersion
  • spec : 核心属性,每个对象独有,用户的期望状态,由创建对象的用户端来定义
  • status : 核心属性,每个对象独有,对象的实际状态,由对应的控制器收集状态并更新

pod的status状态:

  • pending :api对象已创建在etcd中,pod里的有些容器因为某种原因无法顺利创建

  • running :至少有一个容器已经在运行了

  • succeeded :pod里的所有容器都跑完且退出,在运行job任务时常见

  • failed :至少有一个容器是不正常的状态

  • unknown :异常状态,pod状态无法被kubelet汇报给kube-apiserver,比如从节点挂了

  • CrashLoopBackOff :比如拉镜像失败

对于pod的状态并不需要死记硬背,记住下面两个原理即可:

  • 只要pod的restartPolicy指定的策略允许重启异常容器(eg:always),那么这个pod就会保持running状态,并进行容器重启; 否则就会进入failed状态

  • 对于包含多个容器的pod,只有它里面所有容器都进入异常状态,pod才会进入failed状态;在此之前pod都是running状态,且此时ready字段会显示正常容器个数

一些命令与操作:

  • 首先可以设置kubectl命令别名,因为它实在有点长:
    alias k=kubectl

  • 通过explain可以获取字段的详细解释,可以快速了解对象字段的意义:
    k explain xxx

root@ubuntu:/home/kubeyaml# k explain pods
KIND:     Pod
VERSION:  v1

DESCRIPTION:
     Pod is a collection of containers that can run on a host. This resource is
     created by clients and scheduled onto hosts.

FIELDS:
   apiVersion   
     APIVersion defines the versioned schema of this representation of an
     object. Servers should convert recognized schemas to the latest internal
     value, and may reject unrecognized values. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
  ...

并且可以指定下层属性:

root@ubuntu:/home/kubeyaml# k explain pods.apiVersion
KIND:     Pod
VERSION:  v1

FIELD:    apiVersion 

DESCRIPTION:
     APIVersion defines the versioned schema of this representation of an
     object. Servers should convert recognized schemas to the latest internal
     value, and may reject unrecognized values. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
  • 获取已创建pod的完整信息,可以选择是yaml或json格式:

k get pod name -o yaml/json

  • label相关

label可以附加到任何K8S对象上,不仅仅是pod

修改现有pod的label:
k label po nginx app=label1

如果修改的是已经存在的label,要加上--overwirte参数:
k label po nginx app=label1 --overwrite

删除label:
k label po nginx app- (后边带一个 - 号)

获取pod有哪些label:
k get pod xx --show-labels

只对某些label感兴趣,可以指定-L参数指定它们,如果label不存在,这一列就会为空:
k get pod -L app,bpp

root@ubuntu:/home/kubeyaml# k get po curl-custom-ambassador -L app,bpp,cpp
NAME                     READY   STATUS    RESTARTS   AGE   APP      BPP      CPP
curl-custom-ambassador   2/2     Running   29         62d   label1   label2   

使用label选择器列出想要的 pod,超多写法:
k get po -l xx=yy (找出带有xx标签,且值等于yy的pod)
k get po -l xx!=yy (选择带有xx标签,且值不等于yy的pod)
k get po -l xx (选择带有xx标签的pod)
k get po -l '!xx' (注意单引号)
k get po -l env in (prod,dev)
k get po -l env not in (prod,dev)
k get po -l env=prod,app=label1 (多个标签的情况,全部匹配才算成功)

  • 注解
    注解也是键值对的形式,与label不同的是,注解并不是为了保存标识信息而存在,它们不能像标签一样用于对象分组;注解的目的主要是为了添加说明,比如指定创建对象的人员姓名,可以让集群中的工作人员之间协作更便利。

添加注解:
k annotate pod xx mycompany.com/somenot="foo bar"
同样的,如果想覆盖之前的注解,可添加--overwrite参数

查看刚刚添加的注解:
k describe po curl-custom-ambassador | grep Annotation

root@ubuntu:~# k describe po curl-custom-ambassador | grep Annotation
Annotations:  mycomp.com/somenot: foo bar
  • namespace

创建一个namespace,可以不用yaml文件,直接用命令:
k create namespace xxxx
不同namespace中的pod对象是可以重名的。
大多数对象的名称必须符合RFC1035域名规定的命名规范,即只能包含字母、数字、横杠-,点号,但namespace中是不能有点号的。

如何快速切换到不同的namespace:
alias kcd='kubectl config set-context $(kubectl config current-context) --namespace'
然后,就可以使用 kcd some-namespace在namespace之间快速切换
举例,先切换到my这个namespace下,查看pod,发现是空的

root@ubuntu:~# kcd my
Context "minikube" modified.
root@ubuntu:~# k get po
No resources found in my namespace.

然后切到default namespace下,pod又出现了

root@ubuntu:~# kcd default
Context "minikube" modified.
root@ubuntu:~# k get po
NAME                                            READY   STATUS             RESTARTS   AGE
curl-custom-ambassador                          2/2     Running            31         62d
grafana-69855b9d58-5g86k                        1/1     Running            11         56d
prometheus-alertmanager-998d6d5f8-kkhz7         2/2     Running            22         56d

可以通过-A参数来快速查询所有namespace下的资源,例如:
k get po -A

  • 删除操作

通过标签选择器删除:
k delete po -l xx=yy
删除namespace时,其下的所有pod也会被删除:
k delete ns xx
也可以只删除namespace下的所有pod,保留namespace:
k delete po --all
当然可以更暴力一些:
k delete all --all
此时该namespace下绝大部分的资源类型的实例,都将会删除,比如pod,rc,rs等;也有一些资源此时不会删除,比如secret

  • 保持pod的健康---存活探针

存活探针 liveness probe (还有就绪探针readiness probe, 他们俩的使用场景完全不一样)
1. 当pod调度到某个node,该node上的kubelet就会运行该pod;如果容器的主进程崩溃,kubelet就会重启容器。
2. 即使进程没有崩溃,比如JVM发生了OOM,进程依然可以运行,但此时希望能有办法向K8S发送信号。
3. 当应用死循环或者死锁,就会停止响应,为了确保程序此时可以重启,应当从外部检查应用的情况,而不是依赖程序内部的检测

存活探针的3种机制:

  1. HTTP GET,通过get请求的响应码来判断是否要重启容器
  2. tcp套接字,与容器指定端口建立tcp连接,如果建立不成功则重启容器
  3. exec, 在容器内执行任意命令,并检查命令的退出码

存活探针中重要的附加属性:

  1. timeoutSeconds: 容器必须在这个时间值内做出响应,否则就被认为失败,默认为1s
  2. periodSeconds: 多长时间探测一次,默认10s
  3. failureThreshold: 连续探测 x 次失败之后,重启容器,默认为1
  4. successThreshold: 连续探测 x 次成功之后才算成功,默认为1
  5. initialDelaySeconds: 初始延迟,务必设置,如果不设置,探针会在容器启动时立即开始探测,默认为0
livenessProbe:
  httpGet:
    path: /
    port: 8080
  initialDelaySeconds: 15

在生产中的pod,一定要定义存活探针;如果没有定义,k8s是不会知道应用是否还活着的。

一些推荐的做法:

  1. 确保探针的请求端点不需要认证,比如/health,否则探测会一直失败
  2. 要检查程序内部的状态(从内部对运行的所有重要组件执行状态检查),而不受到外部因素的影响。例如,当web服务器无法连接到后端数据库时,web服务器的存活探针不应该返回失败;因为如果问题是出在数据库,重启web服务器不会解决问题的。
  3. 保持探针轻量,因为探测器执行频率相对较高,探针的CPU时间会计入容器的CPU时间配额,过重的探针会影响容器的运行
  4. 不要在探针中实现重试循环,k8s本身会进行若干次的尝试

参考内容:

极客时间《深入剖析kubernetes》
《Kubernetes in action中文版》

你可能感兴趣的:(kubernetes 学习实践 -- pod)