Kubernetes使用共享网络将多个物理机或虚拟机汇集到一个集群中,在各服务器之间进行通信,该集群时配置Kubernetes的所有组件、功能和工作负载的物理平台。集群中一台服务器(或高可用部署中的一组服务器)作为Master,负责管理整个集群,其余作为Worker Node,集群中的主机可以是物理服务器,也可以是虚拟机(包括云VPS)。
¶集群概念
- Master节点
Master是集群的网关和中枢,负责为用户和客户端暴露API、状态信息、管理和调度负责,以及编排其它组件之间的通信等任务。单个Master节点可以完成所有功能,实际生产环境由于负载均衡等目的,需要协同部署多个Master节点。
- Node节点
Node节点属于Worker节点,由多个主机构建。Worker节点不暴露任何信息,不对外开发接口。
¶主要概念
Kubernetes由很多技术概念,同时对应很多API对象,API对象是k8s集群中的管理操作单元。
每个API对象都有3大类属性:元数据metadat、规范spec和状态status。元数据是用来表示API对象的,每个对象至少有3个元数据:namespace、name和uid;除此之外还有各种各样的标签label是用来标识和匹配不同的对象。规范spec描述了用户期望k8s集群中的分布式系统达到的理想状态(desired State),状态status描述了系统实际当前达到的状态(Status)。
k8s中的API对象设计理念之一,是所有操作都是声明式的(Declarative)。所以你会发现会有很多yaml配置。声明式操作在分布式系统中的好处是稳定,不怕丢失操作或运行多次。
下面简述一些常见的API对象。
¶Pod
Pod是k8s的最小调度单元,同一个Pod中的容器共享网络名称空间和存储资源,这些容器可经本地回环节口lo直接通信,但彼此又在Mount、User及PID等名称空间上保持隔离。
特性:
- 包含多个共享IPC、Network和UTC namespace的容器,可直接本地通信
- 所有Pod内容器都可以访问共享的Volume,可以访问共享数据
- Pod一旦调度后就跟Node绑定,即使Node挂掉也不会重新调度,推荐使用Deployments、Daemonsets等控制器来容错
- 优雅终止:Pod删除的时候先给其内的进程发送SIGTERM,等待一段时间(grace period)后才强制停止运行的进程
- 特权容器(通过SecurityContext配置)具有改变系统配置的权限
通过yaml或接送描述Pod和其内Container的运行环境以及期望状态,比如一个最简单的nginx pod可以定义为,
1 | apiVersion: v1 |
volume可以为容器提供持久化存储,比如,
1 | apiVersion: v1 |
重启机制,RestartPolicy
目前支持三种RestartPolicy
- Always: 只要退出就重启
- OnFailure: 失败退出时重启
- Never: 只要退出就不再重启
这里的重启指在Pod所在Node本地重启,它不会调度到其它Node上去。
环境变量为容器提供了一些重要的资源,包括容器和Pod的基本信息以及集群中服务的信息等,
1 | apiVersion: v1 |
ImagePullPolicy策略,
- Always: 不管镜像是否存在都会进行一次拉取。
- Never:不管镜像是否存在都不会进行拉取。
- IfNotPresent: 镜像不存在时,才进行拉取。
注意,
- 默认为IfNotPresent,但
:latest
标签的镜像默认为Always
。 - 拉取镜像时docker会进行MD5校验,如果镜像中的MD5没变,不会拉取镜像数据。
- 生产环境中应该尽量避免使用
:latest
标签,而开发环境可以借助:latest
标签自动拉取最新的镜像。
访问DNS策略,
通过设置dnsPolicy参数,设置Pod中容器访问DNS的策略
- ClusterFirst: 优先基于cluster domain后缀,通过kube-dns查询
- Default:首先从kubelet中配置的DNS查询
默认是ClusterFirst
通过hostIPC设置参数为Ture,使用主机的IPC明明空间,默认为False。
通过hostNetwork设置参数为True,使用主机的命名空间,默认为False。
通过hostPID设置参数为True,使用主机的PID命名空间,默认为False。
通过subdomain参数设置Pod的子域名,默认为空
1 | apiVersion: v1 |
kubernetes通过cgroups限制容器的CPU和内存等计算资源,包括requests和limits等:
spec.containers[].resources.limits.cpu
:CPU上限,可以短暂超过,容器也不会被停止spec.containers[].resources.limits.memory
:内存上限,不可以超过;如果超过,容器可能会被停止或调度到其它资源充足的机器上spec.containers[].resources.requests.cpu
:CPU请求,可以超过spec.containers[].resources.requests.memory
:内存请求,可以超过;但如果超过,容器可能会在Node内存不足时清理
1 | apiServersion: v1 |
为了保证容器在部署后确实处在正常运行状态,Kubernetes提供了两种探针(probe,支持exec、tcp和httpGet方式)来探测容器的状态:
- LivenessProbe:探测应用是否处于健康状态,如果不健康则删除重建容器
- ReadinessProbe:探测应用是否启动完成并且处于正常服务状态,如果不正常则更新容器状态
1 | apiVersion: v1 |
initContainers在容器执行前运行,常用来初始化容器操作,
1 | apiVersion: v1 |
容器生命周期钩子(Container Lifecycle Hooks)监听容器生命周期的特定事件,并在事件发生时执行已注册的回调函数。支持两种钩子:
- postStart:容器启动后执行,注意由于一步执行,它无法保证一定在ENTRYPOINT之后运行。如果失败,容器会被杀死,并根据RestartPolicy决定是否重启
- preStop:容器停止前执行,常用于资源清理。如果失败,容器同样也会被杀死
钩子的回调函数支持两种方式:
- exec: 在容器内执行命令
- httpGet:向指定URL发起GET请求
1 | apiVersion: v1 |
默认情况下,容器都是以非特权容器方式运行。比如,不能再容器中创建虚拟网卡、配置虚拟网络。
Kubernetes提供了修改Capabilities的机制,
1 | apiVersion: v1 |
可以通过给Pod增加 kubernetes.io/ingressbandwidth
和 kubernetes.io/egress-bandwidth
这两个annotation来限制Pod的网络带宽
1 | apiVersion: v1 |
可以通过nodeSelector、nodeAffinity、podAffinity以及Taints和tolerations等来讲Pod调度到需要的Node上。
也可以通过设置nodeName参数,将Pod调度到制定node节点上。
1 | apiVersion: v1 |
默认情况下,容器的/etc/hosts
时kubelet自动生成的,并且仅包含localhost和podName等。不建议直接修改/etc/hosts
文件,因为在Pod启动或重启时会被覆盖。
默认的/etc/hosts
文件格式如下,其中nginx-4217019353-fb2c5
是podName:
1 | kubectl exec nginx-app-5dd4f9fd4d-nm4sx -- cat /etc/hosts |
从v1.7开始,可以通过pod.Spec.HostAliases
来增加hosts内容,如,
1 | apiVersion: v1 |
1 | kubectl logs hostaliases-pod |
¶Namespace
Namespace是对一组资源和对象的抽象集合,比如可以用来将系统内部的对象划分为不同的项目组或用户组。常见的pods,services,replication controller和deployments等都是属于某一个namespace的(默认是default),而node,persistentVolumes等则不属于任何namespace。
Namespace常用来隔离不同的用户,比如Kubernetes自带的服务一般运行在kube-system namespace
中。
- 查询
名称空间的选项可以用--namespace
或-n
来指定,不指定默认就是default
。也可以通过--all-namespace=true
来查看所有namespace下的资源。
1 | kubectl get namespaces |
注意:kubectl get namespace
等效;namespace包含两种状态“Active”和“Terminating”。在namespace删除过程中,namespace状态被设置成“Terminating”。
- 创建
命令行直接创建,
1 | kubectl create namespace new-namespace |
通过文件创建,
1 | apiVersion: v1 |
1 | kubectl create -f ./my-namespace.yaml |
注意:namespace的name满足正则表达式[a-z0-9]([-a-z0-9]*[a-z0-9])?
,最大长度为63位,
- 删除
- 删除一个namespace会自动删除所有属于该namespace的资源
default
和kube-system
名称空间不可删除- PersistentVolumes是不属于任何namespace的,但PersistentVolumeClaim是属于某个特定namespace的
- Events是否属于namespace取决于产生events的对象
¶Node
Node是Pod真正运行的主机,可以是物理机,也可以是虚拟机。为了管理Pod,每个Node节点上至少运行container runtime(比如docker或rkt)、kubelet
和kube-proxy
服务。
- Node管理
不像其它的资源(如Pod和Namespace),Node本质上不是Kubernetes来创建的,Kubernetes只是管理Node上的资源。虽然可以通过Manifest创建一个Node对象(如下json),但Kubernetes也只是去检查是否真的是有这么一个Node,如果检查失败,也不会往上调度Pod。
1 | { |
这个检查是由Node Controller来完成的。Node Controller负责
- 维护Node状态
- 与Cloud Provider同步Node
- 给Node分配容器CIDR
- 删除带有
NoExecute
taint的Node上的Pods
默认情况下,kubelet在启动时会向master注册自己,并创建Node资源。
- Node的状态
每个Node都包括以下状态信息
- 地址:包括hostname、外网IP和内网IP
- 条件(Condition):包括OutOfDisk、Ready、MemoryPressure和DiskPressure
- 容量(Capacity):Node上的可用资源,包括CPU、内存和Pod总数
- 基本信息(Info):包括内核版本、容器引擎版本、OS类型等
- Taints和tolerations
Taints和tolerations用于保证Pod不被调度到不合适的Node上,Taint应用于Node上,而toleration则应用于Pod上(Toleration是可选的)。
比如,可以使用taint命令给node1添加taints:
1 | kubectl taint nodes node1 key1=value1:NoSchedule |
- Node维护模式
标识Node为不可调度,但不影响其上正在运行的Pod,这种维护Node时时非常有用的
1 | kubectl cordon $NODENAME |