在K8S实战笔记–2中,我们针对K8S的前身,架构,服务发现等做了详细描述,并介绍了K8S中的一种网络解决方案,此篇将在此基础上对此网络方案与其他网络模型做相应的讲解。
1. 网络解决方案
1.1 K8S + Flannel
上一篇中已对此网络模型的工作原理做了阐述,在此过程中,ETCD与Flannel的关系为:
- ETCD负责存储Flannel中可分配的IP地址段。Flannel在启动后会向ETCD中插入可分配的网络资源,并将分配情况进行记录,防止该网段被Flannel重新分配。
- 监控ETCD中的每个Pod的实际地址,并建立维护Pod的节点路由表,用于在封装数据包时提供相应的信息。
1.2 网络分层
在K8S中,网络一共被分为三层,分别为Service网络、Pod网络与节点网络。在这几层网络中,具有真实物理网卡的只有节点网络,Pod网络与Service网络都是内部网络。
2. 集群资源分类
K8S中所有内容都抽象为资源,资源实例化后成为对象。
2.1 名称空间级别
例:系统组件默认放置与kube-system
下,在某一名称空间下的内容,在其他名称空间中无法被访问。
workload
:工作负载型资源,如Pod
、RS
、Deployment
、Statefulset
、DaemonSet
、Job
、CronJob
;ServiceDiscovery LoadBalance
:服务发现及负载均衡型资源,如Service
、Ingress
;- 配置与存储型资源:
volum
(存储卷)、CSI
(容器存储接口,可以扩展各种各样的第三方存储卷) - 特殊类型的存储卷:
ConfigMap
(当配置中心来使用的资源类型)、Secret
(保存敏感数据)、DownwardAPI
(将外部环境中的信息输出给容器)。
2.2 集群级别
在整个集群中都可以进行与调用的资源。
例:Namespace
、Node
、Role
、ClusterRole
、RoleBinding
、ClusterRoleBinding
。
2.3 元数据型
通过指标进行操作的资源。
例:HPA
、PodTemplate
、LimitRange
。
3. YAML格式
在K8S中,我们通过yaml
格式文件来创建自定义的Pod,这类文件就是资源清单。什么是YAML?YAML是一种以数据为中心的标记语言,通常,YAML缩进时不允许使用tab键而是使用空格,这点与Python类似。在解释器中,某些时候一个tab并不等于四个空格,但使用空格的数目未作规定,只需要将同级元素左侧对齐即可。在使用#
做内容的注释时,单行会被解释器忽略掉。
3.1 YAML支持的数据结构
- 对象:即键值对的即可,如
mapping
、hashes
、dictionary
等。在书写时,使用冒号隔开。且YAML也允许将所有键值对写成一个行内对象,也就是哈希。
例:hash:{name:codeName,age:18}
- 数组:一组连词线开头的行,组成一个数组。
例:
与对象相同,数组也支持行内表示:context - step1 - step2 - step3
context:[step1,step2,step3]
- 复合结构:由对象与数组共同构成
例:context: - step1 - step2 - step3 newContext: newKey1:newValue1 newKey2:newValue2 newKey3:newValue3
- 纯量:纯量是最基本的,不可再分的值。如:字符串、布尔值、整数、浮点数、null、日期、时间等,在YAML中,null使用
~
表示,不写也表示null。日期、时间采用ISO8601
格式,同时,YAML也允许使用两个感叹号强制转化数据类型。
例:
YAML与其他语言不同的是,在使用字符串时默认不使用引号表示,但需要将包含有特殊字符的字符串包含在引号中,此时单、双引号均可,但双引号不会对特殊字符进行转义。在出现的特殊字符为单引号时,需要连续使用两个单引号进行转义。arg:!!str 123
例:
输入:
解释:str:'Pite''s thing.'
在YAML文件中,有时需要书写较长的字符串,例如密钥等,为了考虑格式问题,YAML支持换行书写,如Pite's thing.
|
、>
。
这两种换行符在使用时有些许的不同,|
表示在实际解释时进行自动换行,>
表示在实际解释时不进行换行,后可跟+
、-
来规定是否需要在文末带有自动新增的空行。
例:str:|- code day one, code day two.
在换行符中:str:>+ code day one, code day two.
|
:代表在解释中自动换行,文末新增一空行;
|+
:代表在解释中自动换行,文末新增两空行;
|-
:代表在解释中自动换行,文末不新增行;
>
:代表在解释中不换行,文末新增一空行;
>+
:代表在解释中不换行,文末新增两空行;
<-
:代表在解释中不换行,文末不新增行;
3.2 YAML中必须存在的属性
version
:String类型,这里指的是K8S API的版本,可以使用kubectl api-version
查看;kind
:String类型,用于指明YAML定义的资源类型和角色,如:Pod、Service等;metadata
:Object类型,用于定义元数据对象。其中:metadata.name
是String类型,用于定义元数据对象的名字;metadata.namespace
也是String类型,用于定义其名称空间;
spec
:Object类型,用于详细定义对象。其中:spec.containers[]
是List类型,是spec对象的容器列表定义,是一个列表;spec.containers[].name
是String类型,用于定义容器的名字,不指明的话,会进行随机创建;spec.containers[].image
是String类型,用于定义要用到的镜像名称,是必须要指明的内容;spec.containers[].imagePullPolicy
是String类型,定义了镜像拉取的策略,有三种取值:Always
:是默认取值,表明每次都尝试从仓库拉取镜像,不使用本地镜像;Never
:表示仅使用本地镜像,没有就不用;IfNotPresent
:表示如果本地存在镜像,就使用本地镜像,否则从仓库拉取镜像来使用;
spec.containers[].command[]
:是List类型,用于指定容器启动命令,不指定则使用镜像打包时使用的启动命令;spec.containers[].args[]
:是List类型,用于指定容器启动命令参数;spec.containers[].workingDir
:用于指定容器的工作目录,即进入容器时所在的目录;
其余更多属性,可以使用kubectl explain *
来查看。
在排错时,可以使用kubectl describe pod *Pod名
来查看Pod的信息,在确定Pod中报错的容器后,使用kubectl log *Pod名 -c *容器名
来查看容器日志。在使用此命令时,若Pod中只有一个容器,则不需要参数c
来指向具体容器名。
4. 容器生命周期
在启动一个容器时,Kubectl发送指令到API Server,API Server会通过ETCD将指令调度到Kubelet,Kubelet会操作CRI进行容器环境的初始化。在初始化的过程中,首先初始化用于共享网络与存储栈的容器Pause。在Pod启动过程中,init
容器会按顺序在Pause初始化完成后开始启动,一个Pod中,可能会包含一个init或多个init,也可能不包含init,每个init容器都会在上一个init完成后启动,且必须运行到成功完成为止。init的作用在于,他们必须在主容器启动之前启动,可以为主容器起到一种延迟启动的效果,直到满足一组先决条件。init中可以运行一组实用工具,这些工具通常可以帮助主容器更加顺利地运行,但往往考虑到安全或冗余问题而不会出现在主容器中。若一个init启动失败,除非指定了Pod的重启策略,否则该init容器的启动操作会一直进行,直到成功启动为止,若Pod重启,所有的init必须重新运行。需要注意的是,对init容器spec的修改被限制在image字段,修改其他的字段都不会生效,但修改image字段,等价于重启Pod。
在一系列init容器启动成功后,会进入主容器的启动。在主容器启动和退出时,分别会执行启动和退出命令。在此过程中,会有Readiness
进程和Liveness
进程参与。Readiness即就绪检测,用于判断当前容器是否可用,在可用时将Pod状态改为Ready。若就绪检测失败,控制器将把与该Pod绑定的所有Service中该Pod的IP地址删除;Liveness即生存检测,用于检测当前容器内服务是否可用,在出现容器内服务失效的情况下控制容器删除或重启。若生存检测失败,Kubelet会杀死容器,并执行重启策略。在初始化主容器时,可以指明延迟执行就绪检测。
为了使容器状态可以被集群捕捉到,此时需要引入探针的概念。探针是一种针对容器的定期诊断,由每个node所在的Kubelet发起,通过调用如下三种Handler,来执行诊断:
ExecAction
:在容器内部执行指定命令,若命令退出代码为0,则诊断成功;TCPSocketAction
:对指定端口上容器的IP地址进行检查,若端口打卡,则诊断成功;HTTPGetAction
:对指定的端口和路径上的容器IP进行HTTP Get请求,若相应状态码大于等于200小于400,则诊断成功。
探针的返回结果,除了成功与失败外,还有一种探测无效的未知状态,在未知状态下,容器会被挂起,直到探测成功后进入就绪状态。在容器不提供探针的情况下,就绪检测与生存检测值默认为Success。
在使用kubectl get pods
命令时,Pods可能出现的状态有如下几种:
Pending
:挂起状态。说明Pod已被K8S集群接受,但仍有一个或多个容器尚未创建;Running
:运行状态。该Pod已被绑定到了一个节点上,Pod中所有容器都已被创建完成。此时Pod中至少有一个容器正在运行,或处于启动或重启状态;Succeeded
:成功状态。表明该Pod中所有容器都被成功终止,且不再重启;Failed
:失败状态。Pod中所有容器都已终止,且至少一个容器是因失败终止;Unknown
:未知状态。在无法与Pod联络的情况下出现此状态,通常是由于通信失败引起的。
那么如何为Pod配置初始容器呢?我们可以先定义一个Pod,在详细描述中定义初始容器并为其配置需要执行的命令。在如下案例中,我们在初始容器中配置了使用wget命令下载页面内容的功能,其中busybox
镜像为容器提供了精简的Linux命令支持,保证了初始容器的轻便性与可用性。
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
initContainers:
- name: install
image: busybox
command:
- wget
- "-O"
- "/work-dir/index.html"
- www.baidu.com
volumeMounts:
- name: workdir
mountPath: "/work-dir"
dnsPolicy: Default
volumes:
- name: workdir
emptyDir: {}
使用kubectl exec
命令进入容器内部,对nginx发送一个get请求,可以看到获取到的页面内容。
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>©2017 Baidu <a href=http://www.baidu.com/duty/>使用百度前必读</a> <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a> 京ICP证030173号 <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>