导图社区 Kubernetes
Kubernetes 基本概念,原理以及常用实例。便于快速入门,kubernetes,简称K8s,是用8代替8个字符“ubernete”而成的缩写。是一个开源的,用于管理云平台中多个主机上的容器化的应用。
编辑于2021-06-18 17:16:46Kubernetes
基本概念
概述
提供了一个可弹性运行分布式系统的框架
能做什么
服务发现和负载均衡
存储编排
自动挂载您选择的存储系统
自动部署和回滚
描述部署容器的所需状态,它可以以受控的速率将实际状态更改为所需状态
自我修复
密钥与配置管理
不是什么
不限制支持的应用程序类型
不部署源代码,也不构建您的应用程序
不提供应用程序级别的服务作为内置服务
不指定日志记录、监视或警报解决方案,提供了收集和导出指标的机制
不提供或不要求配置语言/系统(例如 jsonnet),它提供了声明性 API,该声明性 API 可以由任意形式的声明性规范所构成
不提供也不采用任何全面的机器配置、维护、管理或自我修复系统
Kubernetes 架构
Master 组件
对集群进行全局决策,并检测和响应集群事件
Master 负责管理集群 负责协调集群中的所有活动,例如调度应用程序,维护应用程序的状态,扩展和更新应用程序。
可以在集群中的任何节点上运行
通常在同一台机器上运行所有的 master 组件,且不在此机器上运行用户的容器
etcd
兼具一致性和高可用性的键值数据库. 使用的是raft一致性算法来实现的
保存集群所有的网络配置和对象的状态信息,默认的后台数据库。
kube-apiserver
通过水平扩展以到达性能要求
唯一入口,任何的资源请求/调用操作都是通过kube-apiserver提供的接口进行.(认证、授权、访问控制、API注册和发现)
kube-scheduler
资源调度, 监视新创建的未指定运行节点的 Pod,并选择合适的节点让 Pod 在上面运行。
步骤
找出该 Pod 的所有 可选节点.
没有找到,则 Pod 一直停留在 Pending 状态
按照某种方式对每一个 可选节点 评分
选择评分最高的 可选节点, 多个一样高,就随机.
将最终选择结果通知 API Server
这个过程,我们称其为绑定(binding)
Filtering
PodFitsHostPorts: 检查Pod需要的 hostPort 在该节点上是否可用
PodFitsHost:检查 Pod 是否通过 hostname 指定了节点,参考 将容器组调度到指定的节点
PodFitsResource:检查节点是否满足 Pod 的资源需求(例如,CPU 和 Memory),参考 管理容器的计算资源
PodMatchNodeSelector:检查 Pod 的节点选择器(nodeSelector)是否和节点的标签匹配,参考 将容器组调度到指定的节点
NoVolumeZoneConflict:评估 Pod 所需要的 数据卷是否在节点上可用(数据卷的 failure zone restrictions)
NoDiskConflict:评估Pod请求的数据卷是否和节点已经加载的数据卷冲突
MaxCSIVolumeCount:计算节点可以挂载多少个 CSI(Container Storage Interface)数据卷,确保不会超出限定的数字
CheckNodeMemoryPressure:检查节点是否有内存紧张的情况
CheckNodePIDPressure:检查节点是否有 PID 短缺的情况
CheckNodeDiskPressure:检查节点是否有存储空间吃紧的情况(文件系统已满,或者将要满)
CheckNodeCondition:检查节点的 Condition 字段,该字段中包含关于 文件系统已满、网络不可用、kubelet未就绪 等相关的条件
PodToleratesNodeTaints:检查 Pod 是否容忍 Pod 的污点,请参考 污点和容忍
CheckVolumeBinding:检查存储卷声明是否可绑定
Scoring
SelectorSpreadPriority:将 Pod 分散到不同的节点,主要考虑同属于一个 Service、StatefulSet、Deployment的情况
InterPodAffinityPriority:遍历 weightedPodAffinityTerm 并求和,找出结果最高的节点
LeastRequestedPriority:已被消耗的资源最少的节点得分最高。如果节点上的 Pod 越多,被消耗的资源越多,则评分约低
MostRequestedPriority:已被消耗的资源最多的节点得分最高。此策略会把 Pod 尽量集中到集群中的少数节点上
RequestedToCapacityRatioPriority:按 requested / capacity 的百分比评分
BalancedResourceAllocation:资源使用均衡的节点评分高
NodePreferAvoidPodsPriority:根据节点的 annotation scheduler.alpha.kubernetes.io/preferAvoidPods 评分。可使用此 annotation 标识哪些 Pod 不能够运行在同一个节点上
NodeAffinityPriority:基于 PreferredDuringSchedulingIgnoredDuringExecution 指定的 node affinity 偏好评分。参考 将容器组调度到指定的节点
TaintTolerationPriority: 根据节点上不可容忍的污点数评分
ImageLocalityPriority:有限选择已经有该 Pod 所需容器镜像的节点
ServiceSpreadingPriority:确保 Service 的所有 Pod 尽量分布在不同的节点上。
CalculateAntiAffinityPriorityMap:anti-affinty,参考将容器组调度到指定的节点
EqualPriorityMap:为每个节点指定相同的权重
参与评分的节点的比例
percentageOfNodesToScore
参数的可选值为 1 - 100 之间的数字,大于 100 的将被认为是 100%,0 代表忽略该配置。
当集群中可选节点数量少于 50 时,调度器仍将继续检查剩余的节点
集群的节点数不超过 1000 ,此参数无效.
调度器按照 round-robin(轮询)的方式遍历节点
调度因素
单个或多个 Pod 的资源需求
硬件、软件、策略(Policy)的限制
亲和与反亲和(affinity and anti-affinity)
满足此条件
数据本地化要求
工作负载间的相互作用
cordon 禁止可调度 / uncordon 可调度
drain 驱逐
从节点安全地逐出所有 Pods, 并禁止调度。
taint 污点
当一个或者多个污点添加到某个节点上,则意味着此节点不会接受任何不容忍这些污点的pod.
kube-controller-manager
主节点上运行控制器的组件,处理集群中常规任务的后台线程
节点控制器(Node Controller): 负责在节点出现故障时进行通知和响应。
副本控制器(Replication Controller): 负责为系统中的每个副本控制器对象维护正确数量的 Pod。
端点控制器(Endpoints Controller): 填充端点(Endpoints)对象(即加入 Service 与 Pod)。
服务帐户和令牌控制器(Service Account & Token Controllers): 为新的命名空间创建默认帐户和 API 访问令牌.
cloud-controller-manager
与具体云基础设施供应商互动的控制器, 只运行特定于云基础设施供应商的控制器
与云供应商相关的依赖
节点控制器:当某一个节点停止响应时,调用云供应商的接口,以检查该节点的虚拟机是否已经被云供应商删除
路由控制器:在云供应商的基础设施中设定网络路由
服务(Service)控制器:创建、更新、删除云供应商提供的负载均衡器
数据卷(Volume)控制器:创建、绑定、挂载数据卷,并协调云供应商编排数据卷
私有化部署Kubernetes
移除节点后,要自行通过 kubectl delete node 将节点对象从 Kubernetes 中删除
需要自行规划Kubernetes的拓扑结构,并做好路由配置
不支持 LoadBalancer 类型的 Service,如需要此特性,需要创建 NodePort 类型的 Service,并自行配置负载均衡器
需要自行创建和管理存储资源,并通过Kubernetes的存储类、存储卷、数据卷等与之关联
Node 节点
Worker节点 每个Worker节点都有一个Kubelet,它管理该Worker节点并负责与Master节点通信。该Worker节点还应具有用于处理容器操作的工具,例如Docker。
Node 组件
Node 组件运行在每一个节点上
负责维护运行中的 Pod 并提供 Kubernetes 运行时环境
kubelet
运行在每一个集群节点上的代理程序,管理该 Worker 节点并负责与 Master 节点通信
管理 Pod(容器组)和 Pod内运行的 Container(容器)的生命周期,同时也负责Volume(CSI)和网络(CNI)的管理
只管理通过 Kubernetes 创建的容器
基于 PodSpec 来工作, 每个 PodSpec 是一个描述 Pod 的 YAML 或 JSON 对象, kubelet 确保 PodSpec 定义中所描述的容器处于运行和健康的状态
Pod(容器组)总是在 Node(节点) 上运行,一个 Node(节点)可以有多个Pod(容器组)
三种方式将容器清单(manifest)提供给 kubelet
文件(File):利用命令行参数传递路径。kubelet 周期性地监视此路径下的文件是否有更新。 监视周期默认为 20s,且可通过参数进行配置。
HTTP 端点(HTTP endpoint):利用命令行参数指定 HTTP 端点。 此端点的监视周期默认为 20 秒,也可以使用参数进行配置。
HTTP 服务器(HTTP server):kubelet 还可以侦听 HTTP 并响应简单的 API (目前没有完整规范)来提交新的清单。
Kubelet 认证/鉴权
Kubelet 身份认证
默认情况下,未被已配置的其他身份认证方法拒绝的对 kubelet 的 HTTPS 端点的请求会被视为匿名请求, 并被赋予 system:anonymous 用户名和 system:unauthenticated 组
kube-proxy
网络代理程序,运行在集群中的每一个节点上,提供网络代理以及负载均衡,是实现 Kubernetes Service 概念的重要部分
在主机上维护网络规则并执行连接转发来实现 Kubernetes 服务抽象
如果操作系统中存在 packet filtering layer,kube-proxy 将使用 iptables代理模式 ,否则,kube-proxy将自行转发网络请求(User space代理模式)
Service提供cluster内部的服务发现和负载均衡
容器引擎
容器运行环境(如Docker)负责下载镜像、创建和运行容器等。
支持多种容器引擎:Docker 、containerd 、cri-o 、rktlet 以及任何实现了 Kubernetes容器引擎接口的容器引擎
Node 状态
kubectl describe nodes 节点名称
地址 Address
HostName:可以被 kubelet 中的 --hostname-override 参数替代。
ExternalIP:集群外部访问的 IP 地址。
InternalIP:集群内部使用的 IP,集群外部无法访问。
条件 Condition
OutOfDisk:磁盘空间不足时为 True
Ready 表示节点是健康的并已经准备好接收 Pods; False 表示节点不健康而且不能接收 Pods; Unknown 表示节点控制器在最近 node-monitor-grace-period 期间(默认 40 秒)没有收到节点的消息
MemoryPressure:当node有内存压力时为True,否则为False。
DiskPressure:当node有磁盘压力时为True,否则为False。
PIDPressure True 表示节点存在进程压力,即进程过多;否则为 False
NetworkUnavailable True 表示节点网络配置不正确;否则为 False
容量 Capacity
CPU、内存和可以调度到节点上的 Pods 的个数上限
capacity 块中的字段指示节点拥有的资源总量。allocatable 块指示节点上可供普通 Pod 消耗的资源量
Info:节点的一些版本信息,如OS、kubernetes、docker等
Node 管理
kubectl cordon <node>
kubectl drain <node>
删除该节点上的所有Pod(DaemonSet除外)
Addons 插件
kube-dns 负责为整个集群提供DNS服务
除了 DNS Addon 以外,其他的 addon 都不是必须
Ingress Controller 为服务提供外网入口
Heapster 提供资源监控
Dashboard 提供GUI
Federation 提供跨可用区的集群
Fluentd-elasticsearch 提供集群日志采集、存储与查询
ContainerResource Monitoring
Cluster-level Logging
Pod
存放容器和卷
标签 ( Labels )
可以附加在资源上的键值对, Label 可以用来组织资源或者选取资源特定 Kubernetes对象
key
标签名
标签名部分是必须的
不能多于 63 个字符
必须由字母、数字开始及结尾
可以包含字母、数字、减号-、下划线_、小数点.
标签前缀
标签前缀部分是可选的
如果指定,必须是一个DNS的子域名,例如:k8s.eip.work
不能多于 253 个字符
使用 / 和标签名分隔
可以有两个部分:可选的前缀和标签名,通过 / 分隔
Kubernetes 对象(Node、Deployment、Pod、Service 等)指派用于开发环境、测试环境或生产环境
嵌入版本标签,使用标签区别不同应用软件版本
标签选择器 ( label selector )
最主要的分类和筛选手段
包含多个条件,使用逗号分隔
equality-based 基于等式
三种操作符 =、==、!=
逗号分隔的两个等式 environment=production,tier!=frontend
set-based 基于集合
操作符有三种:in、notin、exists
多个选择器,用 , 分隔,, 相当于 AND 操作符
partition,environment notin (qa)
选择包含 `partition` 标签(不检查标签值)且 `environment` 不是 `qa` 的对象
一个空的 label 选择器(即有0个必须条件的选择器)会选择集合中的每一个对象。
一个 null 型 label 选择器(仅对于可选的选择器字段才可能)不会返回任何对象
注解(annotation)
记录非标识性的信息,即向 metadata.annotations 字段添加任意的信息
扩展资源的 spec/status
注解不是用来标记对象或者选择对象的
省略注解前缀,则注解的 key 将被认为是专属于用户的
kubernetes.io/ 和 k8s.io/ 这两个前缀是 Kubernetes 核心组件预留的
声明配置层管理的字段。使用annotation关联这类字段可以用于区分以下几种配置来源: 客户端或服务器设置的默认值; 自动生成的字段或自动生成的 auto-scaling 和 auto-sizing 系统配置的字段。
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: istio-manager spec: replicas: 1 template: metadata: annotations: alpha.istio.io/sidecar: ignore labels: istio: manager spec: serviceAccountName: istio-manager-service-account containers: - name: discovery image: harbor-001.jimmysong.io/library/manager:0.1.5 imagePullPolicy: Always args: ["discovery", "-v", "2"] ports: - containerPort: 8080 env: - name: POD_NAMESPACE valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.namespace - name: apiserver image: harbor-001.jimmysong.io/library/manager:0.1.5 imagePullPolicy: Always args: ["apiserver", "-v", "2"] ports: - containerPort: 8081 env: - name: POD_NAMESPACE valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.namespace # alpha.istio.io/sidecar 注解就是用来控制是否自动向 pod 中注入 sidecar 的
Kubernetes的系统组件(例如,kube-scheduler、kube-controller-manager、kube-apiserver、kubectl 或其他第三方组件)向用户的Kubernetes对象添加注解时,必须指定一个前缀
metadata: annotations: deployment.kubernetes.io/revision: 7 # 由Deployment控制器添加,用于记录当前发布的修改次数
key (同 Label)
字段选择器(Field Selector)
kubectl get statefulsets,services --all-namespaces --field-selector metadata.namespace!=default
基于的一个或多个字段的取值来选取一组Kubernetes对象
所有的对象类型都支持的两个字段是 metadata.name 和 metadata.namespace
操作符有 =、==、!=, 多个字段选择器,用逗号.
存储卷(Volume)
持久存储卷(Persistent Volume,PV)
持久存储卷声明(Persistent Volume Claim,PVC)
核心技术概念
服务(Service)
节点(Node)
集群联邦(Federation)
有状态服务集(PetSet)
密钥对象(Secret)
用户帐户(User Account)
服务帐户(Service Account)
名字空间(Namespace)
RBAC 访问授权
垃圾收集
垃圾收集器的角色是删除指定的对象,这些对象曾经有但以后不再拥有 Owner
具有 Owner 的对象被称为是 Owner 的 Dependent
每个 Dependent 对象具有一个指向其所属对象的 metadata.ownerReferences 字段
一个 ReplicaSet 是一组 Pod 的 Owner
如果删除对象时,不自动删除它的 Dependent,这些 Dependent 被称作是原对象的 孤儿
background 模式
Kubernetes 会立即删除 Owner 对象,然后垃圾收集器会在后台删除这些 Dependent
foreground 模式
根对象首先进入 “删除中” 状态
对象仍然可以通过 REST API 可见
会设置对象的 deletionTimestamp 字段
对象的 metadata.finalizers 字段包含了值 “foregroundDeletion”
对象的 ownerReference.blockOwnerDeletion=true ,删除 Owner 对象
Dependent 只有通过 ownerReference.blockOwnerDeletion 才能阻止删除 Owner 对象
安装配置
构建高可用群集
部署
部署Docker
设置Yum源
安装指定的Docker版本
设置cgroup驱动使用systemd
启动后台进程
部署kubadm和kubelet
设置kubernetes YUM仓库
安装软件包
配置kubelet
设置内核参数
设置开机启动
使用IPVS进行负载均衡
初始化集群部署Master
根据实际情况修改配置
执行初始化操作
[init]:指定版本进行初始化操作
[preflight] :初始化前的检查和下载所需要的Docker镜像文件。
[kubelet-start]:生成kubelet的配置文件”/var/lib/kubelet/config.yaml”,没有这个文件kubelet无法启动,所以初始化之前的kubelet实际上启动失败。
[certificates]:生成Kubernetes使用的证书,存放在/etc/kubernetes/pki目录中。
[kubeconfig] :生成 KubeConfig文件,存放在/etc/kubernetes目录中,组件之间通信需要使用对应文件。
[control-plane]:使用/etc/kubernetes/manifest目录下的YAML文件,安装 Master组件。
[etcd]:使用/etc/kubernetes/manifest/etcd.yaml安装Etcd服务。
[wait-control-plane]:等待control-plan部署的Master组件启动。
[apiclient]:检查Master组件服务状态。
[uploadconfig]:更新配置
[kubelet]:使用configMap配置kubelet。
[patchnode]:更新CNI信息到Node上,通过注释的方式记录。
[mark-control-plane]:为当前节点打标签,打了角色Master,和不可调度标签,这样默认就不会使用Master节点来运行Pod。
[bootstrap-token]:生成token记录下来,后边使用kubeadm join往集群中添加节点时会用到
[addons]:安装附加组件CoreDNS和kube-proxy
为kubectl准备Kubeconfig文件
部署网络插件
Flannel
推荐
Calico
Canal
部署Node节点
Master节点输出增加节点的命令
Node节点加入主节点
配置
私有仓库中的docker镜像
管理容器的计算资源
将容器组调度到指定的节点
手动生成 kubeconfig
# 创建 kubeconfig 入口 $ kubectl config set-cluster $CLUSTER_NICK \ --server=https://1.1.1.1 \ --certificate-authority=/path/to/apiserver/ca_file \ --embed-certs=true \ # 如果不需要 TLS ,则用下面两行替换 --certificate-authority 和 --embed-certs --insecure-skip-tls-verify=true \ --kubeconfig=/path/to/standalone/.kube/config # 创建用户入口 $ kubectl config set-credentials $USER_NICK \ # 加载在主机生成的 token。 --token=$token \ # 使用 username|password 或者 token,任意一种方式 --username=$username \ --password=$password \ --client-certificate=/path/to/crt_file \ --client-key=/path/to/key_file \ --embed-certs=true \ --kubeconfig=/path/to/standalone/.kube/config # 创建 context 入口 $ kubectl config set-context $CONTEXT_NAME \ --cluster=$CLUSTER_NICK \ --user=$USER_NICK \ --kubeconfig=/path/to/standalone/.kube/config
--embed-certs 是指需要产生一个独立的 kubeconfig
--kubeconfig 既加载配置参数,也加载保存的配置文件
部署kubernetes集群(手动)
创建TLS证书和秘钥
安装 CFSSL
创建 CA (Certificate Authority)
创建 CA 证书签名请求
生成 CA 证书和私钥
创建 kubernetes 证书
创建 admin 证书
创建 kube-proxy 证书
校验证书
分发证书
创建kubeconfig文件
创建 TLS Bootstrapping Token
创建 kubelet bootstrapping kubeconfig 文件
创建 kube-proxy kubeconfig 文件
分发 kubeconfig 文件
创建高可用etcd集群
TLS 认证文件
创建 etcd 的 systemd unit 文件
安装kubectl命令行工具
创建 kubectl kubeconfig 文件
部署master节点
TLS 证书文件
配置和启动 kube-apiserver
配置和启动 kube-controller-manager
配置和启动 kube-scheduler
验证 master 节点功能
安装flannel网络插件
部署node节点
TLS 证书文件
配置Docker
安装和配置kubelet
创建kubelet的service配置文件
配置 kube-proxy
安装kubedns插件
系统预定义的 RoleBinding
配置 kube-dns 服务
配置 kube-dns Deployment
检查 kubedns 功能
安装dashboard插件
安装 EFK 插件
对象管理
对象管理
kubectl 命令行工具支持多种不同的方式来创建和管理 Kubernetes 对象
应该只使用一种技术来管理 Kubernetes 对象。
无论是创建、修改,或者删除都是通过 Kubernetes API 实现
命令式命令
开发测试环境
在集群中的活动对象上进行操作
将操作传给 kubectl 命令作为参数或标志
直接在活动对象上操作,所以不提供以前配置的历史记录。
kubectl create deployment nginx --image=nginx
对比
与对象配置相比的优点: 命令简单,易学且易于记忆。 命令仅需一步即可对集群进行更改。
与对象配置相比的缺点: 命令不与变更审查流程集成。 命令不提供与更改关联的审核跟踪。 除了实时内容外,命令不提供记录源。 命令不提供用于创建新对象的模板
命令式对象配置
kubectl 命令指定操作(创建,替换等),可选标志和至少一个文件名
指定的文件必须包含 YAML 或 JSON 格式的对象的完整定义
kubectl create -f nginx.yaml
对比
与命令式命令相比的优点: 对象配置可以存储在源控制系统中,比如 Git。 对象配置可以与流程集成,例如在推送和审计之前检查更新。 对象配置提供了用于创建新对象的模板。
与命令式命令相比的缺点: 对象配置需要对对象架构有基本的了解。 对象配置需要额外的写 YAML 文件的步骤。
与声明式对象配置相比的优点: 命令式对象配置行为更加简单易懂。 从 Kubernetes 1.5 版本开始,命令式对象配置更加成熟。
与声明式对象配置相比的缺点: 命令式对象配置针对文件而不是目录上效果最佳。 对活动对象的更新必须反映在配置文件中,否则将在下一次替换是丢失。
声明式对象配置
生产环境
用户对本地存储的对象配置文件进行操作,但是用户未定义要对该文件执行的操作
kubectl 按对象检测来创建、更新和删除对象。
示例
处理 configs 目录中的所有对象配置文件,创建并更新活动对象。可以首先使用 diff 子命令查看将要进行的更改,然后在进行应用
kubectl diff -f configs/ kubectl apply -f configs/
kubectl diff -R -f configs/ kubectl apply -R -f configs/
递归处理目录
对比
与命令式对象配置相比的优点: 即使未将对活动对象所做的更改未合并回到配置文件中,也将保留这些更改。 声明性对象配置更好地支持对目录进行操作并自动检测每个对象的操作类型(创建,修补,删除)。
与命令式对象配置相比的缺点: 声明式对象配置难于调试并且出现异常时难以理解。 使用差异的部分更新会创建复杂的合并和补丁操作。
描述对象
Kubernetes 对象 是持久化的实体。Kubernetes 使用这些实体去表示整个集群的状态
可以被应用使用的资源
哪些容器化应用在运行(以及在哪个 Node 上)
关于应用运行时表现的策略,比如重启策略、升级策略,以及容错策略
API 请求必须在请求体中包含 JSON 格式的信息
kubectl 在发起 API 请求时,将 yaml 信息转换成 JSON 格式
对象 spec
spec 是必需的,它描述了对象的 期望状态(Desired State)希望对象所具有的特征
对象 status
status 描述了对象的 实际状态(Actual State) ,它是由 Kubernetes 系统提供和更新的
Kubernetes 控制面一直努力地管理着对象的实际状态以与期望状态相匹配
必需字段
kind - 想要创建的对象的类型
apiVersion - 创建该对象所使用的 Kubernetes API 的版本
metadata - 帮助识别对象唯一性的数据,包括一个 name 字符串、UID 和可选的 namespace
Names 名称
同一个名称空间下,同一个类型的对象,可以通过 name 唯一性确定
最长不超过 253个字符
必须由小写字母、数字、减号 -、小数点 . 组成
某些资源类型有更具体的要求
通过 namespace + name 唯一性地确定一个 RESTFUL 对象
/api/v1/namespaces/{namespace}/pods/{name}
UIDs
由 Kubernetes 系统生成的,唯一标识某个 Kubernetes 对象的字符串
所有的对象都是通过 name 和 UID 唯一性确定
namespace
提供虚拟的集群空间(权限控制,资源分配策略)
名字空间为名称提供了一个范围。资源的名称需要在名字空间内是唯一的,但不能跨名字空间
名字空间不能相互嵌套,每个 Kubernetes 资源只能在一个名字空间中。
说明: 避免使用前缀 kube- 创建名字空间,因为它是为 Kubernetes 系统名字空间保留的。
default 名称空间
default 默认名称空间,如果 Kubernetes 对象中不定义 metadata.namespace 字段,该对象将放在此名称空间下
kube-system Kubernetes 系统创建的对象放在此名称空间下
kube-public 此名称空间自动在安装集群是自动创建,所有用户都是可以读取的(即使是那些未登录的用户)。主要是为集群预留的.
kube-node-lease 此名字空间用于与各个节点相关的租期(Lease)对象; 此对象的设计使得集群规模很大时节点心跳检测性能得到提升。
DNS
创建一个 Service 时,Kubernetes 为其创建一个对应的 DNS 条目
DNS 记录的格式为 <service-name>.<namespace-name>.svc.cluster.local
在容器中只使用 <service-name>,其DNS将解析到同名称空间下的 Service
跨名称空间访问服务,则必须使用完整的域名(fully qualified domain name,FQDN)
名称空间的名字必须与 DNS 兼容
不能带小数点 .
不能带下划线 _
使用数字、小写字母和减号 - 组成的字符串
资源配额
类型
计算资源配额
apiVersion: v1 kind: ResourceQuota metadata: name: compute-resources namespace: spark-cluster spec: hard: pods: "20" requests.cpu: "20" requests.memory: 100Gi limits.cpu: "40" limits.memory: 200Gi
对象数量配额
apiVersion: v1 kind: ResourceQuota metadata: name: object-counts namespace: spark-cluster spec: hard: configmaps: "10" persistentvolumeclaims: "4" replicationcontrollers: "20" secrets: "10" services: "10" services.loadbalancers: "2"
存储资源配额
apiVersion: v1 kind: LimitRange metadata: name: mem-limit-range spec: limits: - default: memory: 50Gi cpu: 5 defaultRequest: memory: 1Gi cpu: 1 type: Container
策略
LimitRange (限制范围)
设置 namespace 中 Pod 的默认的资源 request 和 limit 值
若 POD 有指定,并在限制的范围内,以指定的值为准.
验证容器请求资源,在限制范围内.
设置前已经运行POD,不受影响.
ResourceQuota (资源配额)
限制 namespace 中所有的 Pod 占用的总的资源 request 和 limit
每个容器必须设置: 1, 内存请求(memory request) 2, 内存限额(memory limit) 3, cpu请求(cpu request) 4, cpu限额(cpu limit)
某些更低层级的对象,是不在任何名称空间中
并非所有对象都在名字空间中
大多数 kubernetes 资源(例如 Pod、Service、副本控制器等)都位于某些名字空间中
但是名字空间资源本身并不在名字空间中
而且底层资源,例如 节点 和持久化卷不属于任何名字空间。
# 位于名字空间中的资源 kubectl api-resources --namespaced=true # 不在名字空间中的资源 kubectl api-resources --namespaced=false
设置名字空间偏好
kubectl config set-context --current --namespace=<名字空间名称> # 验证之 kubectl config view | grep namespace:
网络配置
虚拟 IP 和服务代理
每个节点都运行了一个 kube-proxy,负责为 Service(ExternalName 类型的除外)提供虚拟 IP 访问
三种 proxy mode(代理模式)
User space 代理模式 (v1.0 +)
kube-proxy 监听 kubernetes master 以获得添加和移除 Service / Endpoint 的事件
kube-proxy 在其所在的节点(每个节点都有 kube-proxy)上为每一个 Service 打开一个随机端口
kube-proxy 安装 iptables 规则,将发送到该 Service 的 ClusterIP(虚拟 IP)/ Port 的请求重定向到该随机端口
任何发送到该随机端口的请求将被代理转发到该 Service 的后端 Pod 上(kube-proxy 从 Endpoint 信息中获得可用 Pod)
kube-proxy 在决定将请求转发到后端哪一个 Pod 时,默认使用 round-robin(轮询)算法,并会考虑到 Service 中的 SessionAffinity 的设定
Iptables 代理模式 (v1.1 +)
kube-proxy 监听 kubernetes master 以获得添加和移除 Service / Endpoint 的事件
kube-proxy 在其所在的节点(每个节点都有 kube-proxy)上为每一个 Service 安装 iptable 规则
iptables 将发送到 Service 的 ClusterIP / Port 的请求重定向到 Service 的后端 Pod 上
对于 Service 中的每一个 Endpoint,kube-proxy 安装一个 iptable 规则
默认情况下,kube-proxy 随机选择一个 Service 的后端 Pod
IPVS 代理模式 (v1.8 +)
kube-proxy 监听 kubernetes master 以获得添加和移除 Service / Endpoint 的事件
kube-proxy 根据监听到的事件,调用 netlink 接口,创建 IPVS 规则;并且将 Service/Endpoint 的变化同步到 IPVS 规则中
当访问一个 Service 时,IPVS 将请求重定向到后端 Pod
启动 kube-proxy 前为节点的 linux 启用 IPVS, 不然,使用 Iptables
负载均衡选项
rr: round-robin
lc: least connection (最小打开的连接数)
dh: destination hashing
sh: source hashing
sed: shortest expected delay
nq: never queue
IPVS 代理模式可以比 iptables 代理模式有更低的网络延迟,在同步代理规则时,也有更高的效率 与 user space 代理模式 / iptables 代理模式相比,IPVS 模式可以支持更大的网络流量
代理模式总结
发送到 Service 的 IP:Port 的请求将被转发到一个合适的后端 Pod
service.spec.sessionAffinity
默认值为 "None"
如果设定为 "ClientIP",则同一个客户端的连接将始终被转发到同一个 Pod
service.spec.sessionAffinityConfig.clientIP.timeoutSeconds
默认值为 10800 (3 小时)
设定会话保持的持续时间
虚拟 IP 的实现
配置Pod的 /etc/hosts
网络插件
flannel -> docker -> kubelet -> kube-proxy
Flannel
为每个node分配subnet,容器将自动从该子网中获取IP地址
当有node加入到网络中时,为每个node增加路由配置
Calico
Weave
网络策略( Network Policy )
Pod 间及与其他网络端点间所允许的通信规则的规范
使用 标签 选择 Pod,并定义选定 Pod 所允许的通信规则
隔离和非隔离的 Pod
默认情况下,Pod 是非隔离的,它们接受任何来源的流量
网络策略不会冲突,它们是累积的
命名空间中有网络策略选择了特定的 Pod, 该 Pod 会拒绝网络策略所不允许的连接
其他未被网络策略所选择的 Pod 会继续接收所有的流量
to 和 from 选择器的行为
出口(Egress)
入口(Ingress)
Network Policies - Default
规则
如果允许集群外部网络端点到 Pod 的通讯,那么该端点可以访问 Pod。
如果允许 Pod 到集群外部网络端点的通讯,那么 Pod 也可以访问该端点。
如果 Pod(A)到 Pod(B)的流量被允许,那么流量可以通过 A 的出口以及 B 的入口。注意这是单向的,流量从 B 到 A,那么只能从 B 的出口到 A 的入口。
默认拒绝所有的入方向流量
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-ingress spec: podSelector: {} policyTypes: - Ingress
默认允许所有的入方向流量
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-all-ingress spec: podSelector: {} ingress: - {} policyTypes: - Ingress
默认拒绝所有出方向流量
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-egress spec: podSelector: {} policyTypes: - Egress
默认拒绝所有入方向和出方向的网络流量
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-all spec: podSelector: {} policyTypes: - Ingress - Egress
SCTP 支持
示例
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: test-network-policy namespace: default spec: podSelector: matchLabels: role: db policyTypes: - Ingress - Egress ingress: - from: - ipBlock: cidr: 172.17.0.0/16 except: - 172.17.1.0/24 - namespaceSelector: matchLabels: project: myproject - podSelector: matchLabels: role: frontend ports: - protocol: TCP port: 6379 egress: - to: - ipBlock: cidr: 10.0.0.0/24 ports: - protocol: TCP port: 5978 说明: 除非选择支持网络策略的网络解决方案,否则将上述示例发送到API服务器没有任何效果。 policyTypes 字段的值应用于入口和出口
隔离 "default" 命名空间下 "role=db" 的 Pod (如果它们不是已经被隔离的话)。
(Ingress 规则)允许以下 Pod 连接到 "default" 命名空间下的带有 “role=db” 标签的所有 Pod 的 6379 TCP 端口:
"default" 命名空间下任意带有 "role=frontend" 标签的 Pod
带有 "project=myproject" 标签的任意命名空间中的 Pod
IP 地址范围为 172.17.0.0–172.17.0.255 和 172.17.2.0–172.17.255.255(即,除了 172.17.1.0/24 之外的所有 172.17.0.0/16)
(Egress 规则)允许从带有 "role=db" 标签的命名空间下的任何 Pod 到 CIDR 10.0.0.0/24 下 5978 TCP 端口的连接。
网络模型
Container-to-Container 的网络
Linux系统中,每一个进程都在一个 network namespace 中进行通信,network namespace 提供了一个逻辑上的网络堆栈
network namespace 为其中的所有进程提供了一个全新的网络堆栈
ip netns add ns1
/var/run/netns
Linux 将所有的进程都分配到 root network namespace,以使得进程可以访问外部网络
Pod 是一组 docker 容器的集合,共享一个 network namespace
使用该 network namespace 提供的同一个 IP 地址以及同一个端口空间
可以通过 localhost 直接与同一个 Pod 中的另一个容器通信
Pod-to-Pod 的网络
Pod 都有一个真实的 IP 地址,并且每一个 Pod 都可以使用此 IP 地址与 其他 Pod 通信
网桥的工作原理是,在源于目标之间维护一个转发表(forwarding table),通过检查通过网桥的数据包的目标地址(destination)和该转发表来决定是否将数据包转发到与网桥相连的另一个网段。桥接代码通过网络中具备唯一性的网卡MAC地址来判断是否桥接或丢弃数据
网桥实现了 ARP 协议,以发现链路层与 IP 地址绑定的 MAC 地址。当网桥收到数据帧时,网桥将该数据帧广播到所有连接的设备上(除了发送者以外),对该数据帧做出相应的设备被记录到一个查找表中(lookup table)。后续网桥再收到发向同一个 IP 地址的流量时,将使用查找表(lookup table)来找到对应的 MAC 地址,并转发数据包
数据包的传递:Pod-to-Pod,同节点
数据包的传递:Pod-to-Pod,跨节点
数据包从 Pod1 的网络设备 eth0,该设备通过 veth0 连接到 root namespace
数据包到达 root namespace 中的网桥 cbr0
网桥上执行 ARP 将会失败,因为与网桥连接的所有设备中,没有与该数据包匹配的 MAC 地址。一旦 ARP 失败,网桥会将数据包发送到默认路由(root namespace 中的 eth0 设备)。此时,数据包离开节点进入网络
假设网络可以根据各节点的CIDR网段,将数据包路由到正确的节点
数据包进入目标节点的 root namespace(VM2 上的 eth0)后,通过网桥路由到正确的虚拟网络设备(veth1)
最终,数据包通过 veth1 发送到对应 Pod 的 eth0,完成了数据包传递的过程
Pod-to-Service 的网络
netfilter and iptables
利用 Linux 内建的网络框架 - netfilter 来实现负载均衡
iptables 是一个 user-space 应用程序,可以提供基于决策表的规则系统,以使用 netfilter 操作或转换数据包
IPVS
Kubernetes v1.11 开始,可以选择用来实现集群内部的负载均衡
在节点上创建一个 dummy IPVS interface
将 Service 的 IP 地址绑定到该 dummy IPVS interface
为每一个 Service IP 地址创建 IPVS 服务器
数据包的传递:Pod-to-Service
数据包的传递:Service-to-Pod
Internet-to-Service 的网络
出方向 - 从集群内部访问互联网
数据包的传递:Node-to-Internet
入方向 - 从互联网访问Kubernetes
Layer 4:LoadBalancer
数据包的传递:LoadBalancer-to-Service
Layer 7:Ingress控制器
Ingress 能够解析 URL 路径(可基于路径进行路由)
Ingress 连接到 Service 的 NodePort
数据包的传递:Ingress-to-Service
创建 Ingress 之后,cloud controller 将会为其创建一个新的 Ingress Load Balancer
由于 Load Balancer 并不知道 Pod 的 IP 地址,当路由到达 Ingress Load Balancer 之后,会被转发到集群中的节点上(Service的节点端口)
节点上的 iptables 规则将数据包转发到合适的 Pod
Pod 接收到数据包
Layer 7 网络入方向访问在网络堆栈的 HTTP/HTTPS 协议层面工作,并且依赖于 KUbernetes Service
Service 指定为 NodtePort 类型
IP 分类
Pod IP:使用网络插件创建的IP(如flannel),使跨主机的Pod可以互通
Cluster IP:虚拟IP,通过iptables规则访问服务
Node IP:宿主机的IP地址
默认端口
控制平面节点
协议 方向 端口 范围 作用 使用者 TCP 入站 6443* Kubernetes API 服务器 所有组件 TCP 入站 2379-2380 etcd server client API kube-apiserver, etcd TCP 入站 10250 Kubelet API kubelet 自身、控制平面组件 TCP 入站 10251 kube-scheduler kube-scheduler 自身 TCP 入站 10252 kube-controller-manager kube-controller-manager 自身
工作节点
协议 方向 端口范围 作用 使用者 TCP 入站 10250 Kubelet API kubelet 自身、控制平面组件 TCP 入站 30000-32767 NodePort 服务** 所有组件
Cilium架构设计与概念解析
持久化
功能
数据共享
数据持久性
类型
nfs
在加载 NFS 数据卷前就在其中准备好数据
不同容器组之间共享数据
多个容器组加载并同时读写
容器组被移除时,将仅仅 umount(卸载)NFS 数据卷,NFS 中的数据仍将被保留。
cephfs
同 nfs
emptyDir
apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - image: k8s.gcr.io/test-webserver name: test-container volumeMounts: - mountPath: /cache name: cache-volume volumes: - name: cache-volume emptyDir: {}
在容器组被创建时分配给该容器组,直到容器组被移除,该数据卷才被释放,数据将被永久删除
emptyDir 中的数据在容器崩溃并重启后,仍然是存在的
用法
暂存空间
用作长时间计算崩溃恢复时的检查点
Web服务器容器提供数据时,保存内容管理器容器提取的文件
hostPath
apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - image: k8s.gcr.io/test-webserver name: test-container volumeMounts: - mountPath: /test-pd name: test-volume volumes: - name: test-volume hostPath: # directory location on host path: /data # this field is optional type: Directory
将 Pod 所在节点的文件系统上某一个文件或文件夹挂载进 Pod
除 path 字段以外,还可以指定 type 字段
DirectoryOrCreate 如果在给定的路径上没有任何东西存在,那么将根据需要在那里创建一个空目录,权限设置为 0755,与 Kubelet 具有相同的组和所有权。
Directory 给定的路径下必须存在目录
FileOrCreate 如果在给定的路径上没有任何东西存在,那么会根据需要创建一个空文件,权限设置为 0644,与 Kubelet 具有相同的组和所有权。
File 给定的路径下必须存在文件
Socket 给定的路径下必须存在 UNIX 套接字
CharDevice 给定的路径下必须存在字符设备
BlockDevice 给定的路径下必须存在块设备
configMap
向容器组注入配置信息。ConfigMap 中的数据可以被 Pod 中的容器作为一个数据卷挂载。
直接引用整个 ConfigMap 到数据卷,ConfigMap 中的每一个 key 对应一个文件名,value 对应该文件的内容
只引用 ConfigMap 中的某一个名值对,可以将 key 映射成一个新的文件名
secret
用来注入敏感信息(例如密码)到容器组。
使用 tmpfs(基于 RAM 的文件系统)挂载
local
apiVersion: v1 kind: PersistentVolume metadata: name: example-pv spec: capacity: storage: 100Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Delete storageClassName: local-storage local: path: /mnt/disks/ssd1
本地卷只能用作静态创建的 PersistentVolume
注意:本地 PersistentVolume 清理和删除需要手动干预
挂载
同一个 Pod 中的同一个数据卷可以被挂载到该 Pod 中的多个容器上
数据卷内子路径
apiVersion: v1 kind: Pod metadata: name: my-lamp-site spec: containers: - name: mysql image: mysql env: - name: MYSQL_ROOT_PASSWORD value: "rootpasswd" volumeMounts: - mountPath: /var/lib/mysql name: site-data subPath: mysql - name: php image: php:7.0-apache volumeMounts: - mountPath: /var/www/html name: site-data subPath: html volumes: - name: site-data persistentVolumeClaim: claimName: my-lamp-site-data # my-lamp-site-data 的 mysql 和 html 分别挂载在容器内不同的挂载点.
volumeMounts.subPath 属性
容器在挂载数据卷时指向数据卷内部的一个子路径,而不是直接指向数据卷的根路径。
通过环境变量指定数据卷内子路径
volumeMounts.subPathExpr 字段,可以通过容器的环境变量指定容器内路径
同一个 volumeMounts 中 subPath 字段和 subPathExpr 字段不能同时使用。
挂载传播
None: 默认值。在数据卷被挂载到容器之后,此数据卷不会再接受任何后续宿主机或其他容器挂载到该数据卷对应目录下的子目录的挂载。同样的,在容器中向该数据卷对应目录挂载新目录时,宿主机也不能看到。对应 Linux 的 private mount propagation 选项 Linux内核文档
HostToContainer:在数据卷被挂载到容器之后,宿主机向该数据卷对应目录添加挂载时,对容器是可见的。对应 Linux 的 rslave mount propagation 选项 Linux内核文档
Bidirectional:在数据卷被挂载到容器之后,宿主机向该数据卷对应目录添加挂载时,对容器是可见的;同时,从容器中向该数据卷创建挂载,同样也对宿主机可见。对应 Linux 的 rshared mount propagation 选项 Linux内核文档
数据卷的挂载传播(Mount Propagation)由 Pod 的 spec.containers[*].volumeMounts.mountPropagation 字段
状态
Available(可用)资源还没有被任何声明绑定
Bound(已绑定) 卷已经被声明绑定
Released(已释放) 声明被删除,但是资源还未被集群重新声明
Failed(失败) 该卷的自动回收失败
原始块卷支持
使用原始块卷作为持久化卷
apiVersion: v1 kind: PersistentVolume metadata: name: block-pv spec: capacity: storage: 10Gi accessModes: - ReadWriteOnce volumeMode: Block persistentVolumeReclaimPolicy: Retain fc: targetWWNs: ["50060e801049cfd1"] lun: 0 readOnly: false
持久化卷声明请求原始块卷
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: block-pvc spec: accessModes: - ReadWriteOnce volumeMode: Block resources: requests: storage: 10Gi
在 Pod 规格配置中为容器添加原始块设备
apiVersion: v1 kind: Pod metadata: name: pod-with-block-volume spec: containers: - name: fc-container image: fedora:26 command: ["/bin/sh", "-c"] args: [ "tail -f /dev/null" ] volumeDevices: - name: data devicePath: /dev/xvda volumes: - name: data persistentVolumeClaim: claimName: block-pvc
当为 Pod 增加原始块设备时,我们在容器中指定设备路径而不是挂载路径。
PV 和 PVC 的关系
PersistentVolume 是集群中的存储资源,通常由集群管理员创建和管理. 没有 namespace
PersistentVolumeClaim 是使用该资源的请求,通常由应用程序提出请求,并指定对应的 StorageClass 和需求的空间大小
PersistentVolumeClaim 可以做为数据卷的一种,被挂载到容器组/容器中使用, 有 namespace 限制
StorageClass 用于对 PersistentVolume 进行分类,根据 PersistentVolumeClaim 的请求动态创建 Persistent Volume
绑定是排他性的,不管它们是如何绑定的。 PVC 跟 PV 绑定是一对一的映射
PVC 管理过程
提供 Provisioning
静态提供 Static
先创建好一系列 PersistentVolume,它们包含了可供集群中应用程序使用的关于实际存储的具体信息。
动态提供 Dynamic
PersistentVolumeClaim 关联合适的 StorageClass(存储类), kubernetes 集群动态创建 PersistentVolume。
绑定 Binding
用户创建了一个 PersistentVolumeClaim 存储卷声明,并指定了需求的存储空间大小以及访问模式。Kubernets master 将立刻为其匹配一个 PersistentVolume 存储卷,并将存储卷声明和存储卷绑定到一起
PVC 没有可用的PV进行绑定,将始终停留在 未绑定 unbound 状态
使用 Using
将 PersistentVolumeClaim 所绑定的 PersistentVolume 挂载到容器组供其使用
使用中保护 Storage Object in Use Protection
删除了一个 pod 正在使用的 PVC,则该 PVC 不会被立即删除。PVC 的删除将被推迟,直到 PVC 不再被任何 pod 使用
PVC 的状态为 Teminatiing 时,PVC 受到保护,Finalizers 列表中包含 kubernetes.io/pvc-protection
回收 Reclaiming
删除掉其 PersistentVolumeClaim,此时其对应的 PersistentVolume 将被集群回收并再利用
回收策略
Retained(保留)
保留策略需要集群管理员手工回收该资源
Recycled(重复利用)
再利用策略将在 PersistentVolume 回收时,执行一个基本的清除操作(rm -rf /thevolume/*),并使其可以再次被新的 PersistentVolumeClaim 绑定。
Deleted(删除)
将从 kubernete 集群移除 PersistentVolume 以及其关联的外部存储介质
动态提供的 PersistentVolume 将从其对应的 StorageClass 继承回收策略的属性
PersistentVolume (PV)
apiVersion: v1 kind: PersistentVolume metadata: name: pv0003 spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: slow mountOptions: - hard - nfsvers=4.1 nfs: path: /tmp server: 172.17.0.2
PV 存储卷, 集群中的存储资源, 提供存储,由管理员设置的存储,它是群集的一部分
Capacity
一个 PersistentVolume 具有一个固定的存储容量(capacity)
Volume Mode
Kubernetes 1.9 之前的版本,所有的存储卷都被初始化一个文件系统。当前可选项有:
filesystem(默认值):使用一个文件系统
block:使用一个 块设备(raw block device)
Access Modes
可被单节点读写-ReadWriteOnce
可被多节点只读-ReadOnlyMany
可被多节点读写-ReadWriteMany
StorageClassName
带有存储类 StorageClassName 属性的 PersistentVolume 只能绑定到请求该 StorageClass 存储类的 PersistentVolumeClaim。
没有 StorageClassName 属性的 PersistentVolume 只能绑定到无特定 StorageClass 存储类要求的 PVC。
Reclaim Policy
保留 Retain – 手工回收
再利用 Recycle – 清除后重新可用 (rm -rf /thevolume/*)
删除 Delete – 删除 PV 及存储介质
Mount Options
挂载选项用来在挂载时作为 mount 命令的参数
状态 Phase
Available – 可用的 PV,尚未绑定到 PVC
Bound – 已经绑定到 PVC
Released – PVC 已经被删除,但是资源还未被集群回收
Failed – 自动回收失败
PersistentVolumeClaims (PVC)
--- kind: PersistentVolumeClaim apiVersion: v1 metadata: name: myclaim spec: accessModes: - ReadWriteOnce volumeMode: Filesystem resources: requests: storage: 8Gi storageClassName: slow selector: matchLabels: release: "stable" matchExpressions: - {key: environment, operator: In, values: [dev]} --- kind: Pod apiVersion: v1 metadata: name: mypod spec: containers: - name: myfrontend image: dockerfile/nginx volumeMounts: - mountPath: "/var/www/html" name: mypd volumes: - name: mypd persistentVolumeClaim: claimName: myclaim
是用户存储的请求
用来挂载 PersistentVolume 存储卷
使用该资源的请求,通常由应用程序提出请求,并指定对应的 StorageClass 和需求的空间大小
自动查看pv状态为Available并且根据声明pvc容量storage的大小进行筛选匹配,同时还会根据AccessMode,storageClassName进行匹配。如果pvc匹配不到pv会一直处于pending状态。( pv >= pvc )
读写模式 Access Modes
可被单节点读写-ReadWriteOnce (RWO)
可被多节点只读-ReadOnlyMany (ROM)
可被多节点读写-ReadWriteMany (RWM)
Volume Modes
block
filesystem - default
总量
请求存储空间的大小
存储类 StorageClass
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: standard provisioner: kubernetes.io/aws-ebs parameters: type: gp2 reclaimPolicy: Retain allowVolumeExpansion: true mountOptions: - debug volumeBindingMode: Immediate
管理员提供了描述存储 "class(类)" 的方法。 不同的 class 可能会映射到不同的服务质量等级或备份策略
StorageClass 中包含 provisioner、parameters 和 reclaimPolicy 字段,当 class 需要动态分配 PersistentVolume 时会使用到
StorageClass 对象的名称很重要,用户使用该类来请求一个特定的方法。 当创建 StorageClass 对象时,管理员设置名称和其他参数,一旦创建了对象就不能再对其更新。
回收策略 Reclaim Policy
默认 Delete
Retained(保留)、Deleted(删除)
存储卷绑定模式 Volume Binding Mode
控制了 卷绑定和动态分配 应该发生在什么时候
Immediate 模式表示一旦创建了 PersistentVolumeClaim 也就完成了卷绑定和动态分配
WaitForFirstConsumer 模式将延迟 PersistentVolume 的绑定和分配,直到使用该 PersistentVolumeClaim 的 Pod 被创建
mountOptions 字段指定的挂载选项
挂载选项在 StorageClass 和 PV 上都不会做验证,所以如果挂载选项无效,那么这个 PV 就会失败
Dynamic Volume Provisioning (动态卷供应)
提前创建了 PV,然后通过 PVC 申请 PV 并在 Pod 中使用,这种方式叫做静态供给(Static Provision)
如果没有满足 PVC 条件的 PV,会动态创建 PV。不需要提前创建 PV.
动态供给是通过 StorageClass 实现的,StorageClass 定义了如何创建 PV (相应供应支持)
ConfigMap 管理应用配置
必须先创建 ConfigMap,才能在容器组中引用 ConfigMap。
ConfigMap 存在于名称空间当中,只有同一个名称空间下的容器组才能引用
ConfigMap 作为一个数据卷(在挂载时不指定数据卷内子路径)挂载到容器,此时 ConfigMap 将映射成一个文件夹,每一个 KEY 是文件夹下的文件名,KEY 对应的 VALUE 是文件当中的内容
ConfigMap 的内容可以通过环境变量的形成传递给容器,也可通过和Volume的形式挂载到容器中。
创建 ConfigMap
kubectl命令创建ConfigMap
kubectl create configmap host-config --from-literal=host=www.k8s.io
kubectl create configmap host-config --from-literal=host=www.k8s.io --from-literal=port=443 --from-literal=ssl=on
通过YAML创建
kubectl create -f host-config-v2.yaml
configmap name 为文件名
通过文件创建
kubectl create configmap file-config --from-file=/etc/hosts
从目录创建
kubectl create configmap dir-config --from-file=/etc/kubernetes/
不支持递归
混合选项创建ConfigMap
kubectl create configmap colors \ --from-literal=text=black \ --from-file=./favorite \ --from-file=./primary/
Pod 中使用 ConfigMap
用 ConfigMap 来替代环境变量
--- apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: special.how: very special.type: charm --- apiVersion: v1 kind: ConfigMap metadata: name: env-config namespace: default data: log_level: INFO --- apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: gcr.io/google_containers/busybox command: [ "/bin/sh", "-c", "env" ] env: - name: SPECIAL_LEVEL_KEY valueFrom: configMapKeyRef: name: special-config key: special.how - name: SPECIAL_TYPE_KEY valueFrom: configMapKeyRef: name: special-config key: special.type envFrom: - configMapRef: name: env-config restartPolicy: Never
修改 ConfigMap ,不会同步更新 ENV
ENV 是在容器启动的时候注入
通过修改 pod annotations 的方式强制触发滚动更新
用 ConfigMap 设置命令行参数
--- apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: special.how: very special.type: charm --- apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: gcr.io/google_containers/busybox command: [ "/bin/sh", "-c", "echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ] env: - name: SPECIAL_LEVEL_KEY valueFrom: configMapKeyRef: name: special-config key: special.how - name: SPECIAL_TYPE_KEY valueFrom: configMapKeyRef: name: special-config key: special.type restartPolicy: Never
通过数据卷插件使用ConfigMap
--- apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: special.how: very special.type: charm --- apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: gcr.io/google_containers/busybox command: [ "/bin/sh", "-c", "cat /etc/config/special.how" ] volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: special-config restartPolicy: Never --- apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: gcr.io/google_containers/busybox command: [ "/bin/sh","-c","cat /etc/config/path/to/special-key" ] volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: special-config items: - key: special.how path: path/to/special-key restartPolicy: Never # ConfigMap值被映射的数据卷里控制路径
修改 ConfigMap, 会同步更新(10秒钟左右延时)
Secret 管理敏感数据
用来储存敏感信息, 降低信息泄露的风险
每个 secret 的大小限制为1MB
使用Secret存储Ingress TLS证书
查看 Secret
kubectl get secrets
kubectl describe secrets/db-user-pass
分类
Opaque
base64编码格式的Secret,用来存储密码、密钥等;
创建Secret
使用kubectl
echo -n 'admin' > ./username.txt
echo -n '1f2d1e2e67df' > ./password.txt
kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
kubectl create secret generic db-user-pass –from-literal=username=admin –from-literal=password=1f2d1e2e67df
密码中包含特殊字符需要转码(例如 $、*、\、!),请使用 \ 进行转码
通过文件创建(--from-file),则无需对文件中的密码进行转码
手动
yaml 文件中定义 Secret
data:使用 data 字段时,取值的内容必须是 base64 编码的
stringData:使用 stringData 时,取值以明文的方式写在 yaml 文件中
echo -n 'admin' | base64
echo -n '1f2d1e2e67df' | base64
创建 secret.yaml 文件
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: password: MWYyZDFlMmU2N2Rm username: YWRtaW4= # 定义 data --- apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque stringData: username: admin password: 1f2d1e2e67df # 定义 stringData 同时定义了 data 和 stringData,对于两个对象中 key 重复的字段,最终将采纳 stringData 中的 value
kubectl apply -f ./secret.yaml
将配置文件存入 Secret
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque stringData: config.yaml: |- apiUrl: "https://my.api.com/api/v1" username: user password: password
使用方式
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: password: MWYyZDFlMmU2N2Rm username: YWRtaW4=
作为 Pod 的数据卷 (Volume) 挂载
apiVersion: v1 kind: Pod metadata: labels: name: db name: db spec: volumes: - name: secrets secret: secretName: mysecret containers: - image: gcr.io/my_project_id/pg:v1 name: db volumeMounts: - name: secrets mountPath: "/etc/secrets" readOnly: true ports: - name: cp containerPort: 5432 hostPort: 5432
作为 Pod 的环境变量
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: wordpress-deployment spec: replicas: 2 strategy: type: RollingUpdate template: metadata: labels: app: wordpress visualize: "true" spec: containers: - name: "wordpress" image: "wordpress" ports: - containerPort: 80 env: - name: WORDPRESS_DB_USER valueFrom: secretKeyRef: name: mysecret key: username - name: WORDPRESS_DB_PASSWORD valueFrom: secretKeyRef: name: mysecret key: password
解码和编辑Secret
kubectl get secret mysecret -o yaml
echo 'YWRtaW4=' | base64 --decode
Service Account
用来访问Kubernetes API,由Kubernetes自动创建
自动挂载到Pod的 /run/secrets/kubernetes.io/serviceaccount目录中;
kubernetes.io/dockerconfigjson :用来存储私有docker registry的认证信息。
--- apiVersion: v1 kind: Secret metadata: name: myregistrykey data: .dockerconfigjson: UmVhbGx5IHJlYWxseSByZWVl..... type: kubernetes.io/dockerconfigjson --- apiVersion: v1 kind: Pod metadata: name: foo spec: containers: - name: foo image: janedoe/awesomeapp:v1 imagePullSecrets: - name: myregistrykey
向特性路径映射 secret 密钥
--- apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm --- apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" readOnly: true volumes: - name: foo secret: secretName: mysecret items: - key: username path: my-group/my-username # username secret 存储在 /etc/foo/my-group/my-username 文件中而不是 /etc/foo/username 中。 # password secret 没有被影射
如果使用了 spec.volumes[].secret.items,只有在 items 中指定的 key 被影射
Secret 文件权限
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mypod image: redis volumeMounts: - name: foo mountPath: "/etc/foo" volumes: - name: foo secret: secretName: mysecret defaultMode: 256 # JSON 规范不支持八进制符号,因此使用 256 值作为 0400 权限 # 容器内 $ ls /etc/foo/ username password $ cat /etc/foo/username admin $ cat /etc/foo/password 1f2d1e2e67df
secret 卷中以点号开头的文件
kind: Secret apiVersion: v1 metadata: name: dotfile-secret data: .secret-file: dmFsdWUtMg0KDQo= # 文件名以点号开头的文件 --- kind: Pod apiVersion: v1 metadata: name: secret-dotfiles-pod spec: volumes: - name: secret-volume secret: secretName: dotfile-secret containers: - name: dotfile-test-container image: gcr.io/google_containers/busybox command: - ls - "-l" - "/etc/secret-volume" volumeMounts: - name: secret-volume readOnly: true mountPath: "/etc/secret-volume"
Security Context
用来限制容器对宿主节点的可访问范围,以避免容器非法操作宿主节点的系统级别的内容
Container-level Security Context:仅应用到指定的容器
apiVersion: v1 kind: Pod metadata: name: hello-world spec: containers: - name: hello-world-container # The container definition # ... securityContext: privileged: true
Pod-level Security Context:应用到 Pod 内所有容器以及 Volume
apiVersion: v1 kind: Pod metadata: name: hello-world spec: containers: # specification of the pod's containers # ... securityContext: fsGroup: 1234 supplementalGroups: [5678] seLinuxOptions: level: "s0:c123,c456"
Pod Security Policies(PSP):应用到集群内部所有 Pod 以及 Volume
集群级的 Pod 安全策略,自动为集群内的 Pod 和 Volume 设置 Security Context
服务发现
API 对象,将符合 Service 指定条件的 Pod 作为可通过网络访问的服务提供给服务调用者
不断扫描符合该 selector 的 Pod,并将最新的结果更新到 Endpoint 对象中。
服务发现机制
Pod 有自己的 IP 地址
Service 被赋予一个唯一的 dns name
Service 通过 label selector 选定一组 Pod
Service 实现负载均衡,将请求均衡分发到选定的 Pod
VIP 和 Service 代理
kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式
kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除
userspace 代理模式 (v1.0)
默认的策略是,通过 round-robin 算法来选择 backend Pod. 实现基于客户端 IP 的会话亲和性,可以通过设置 service.spec.sessionAffinity 的值为 "ClientIP" (默认值为 "None")
iptables 规则,捕获到达该 Service 的 clusterIP(是虚拟 IP)和 Port 的请求,并重定向到代理端口,代理端口再代理请求到 backend Pod
隐藏了访问 Service 的数据包的源 IP 地址。 这使得一些类型的防火墙无法起作用
iptables 代理模式 (v1.1)
默认的策略是,随机选择一个 backend。 实现基于客户端 IP 的会话亲和性,可以将 service.spec.sessionAffinity 的值设置为 "ClientIP" (默认值为 "None")
iptables 代理不能自动地重试另一个 Pod,所以它需要依赖 readiness probes
iptables 代理不会隐藏 Kubernetes 集群内部的 IP 地址
ipvs 代理模式 (1.8.0)
必须安装 IPVS 内核模块
负载均衡算法
rr:轮询调度
lc:最小连接数
dh:目标哈希
sh:源哈希
sed:最短期望延迟
nq: 不排队调度
Endpoints Controller
Endpoint Endpoints 表示一个 Service 对应的所有 Pod 副本的访问地址
Endpoints Controller 负责生成和维护所有 Endpoints 对象的控制器。它负责监听 Service 和对应的 Pod 副本的变化
监测到 Service 被删除,则删除和该 Service 同名的 Endpoints 对象;
监测到新的 Service 被创建或修改,则根据该 Service 信息获得相关的 Pod 列表,然后创建或更新 Service 对应的 Endpoints 对象。
监测到 Pod 的事件,则更新它对应的 Service 的 Endpoints 对象。
kube-proxy进程获取每个Service的Endpoints,实现Service的负载均衡功能
Service 服务
一个抽象层,它选择具备某些特征(LabelSelector)的 Pod(容器组)并为它们定义一个访问方式
Pod 的指定端口公布到到集群外部,并支持负载均衡和服务发现。
服务类型
通过 spec.type 指定
ClusterIP
普通Service:通过为Kubernetes的Service分配一个集群内部可访问的固定虚拟IP(Cluster IP),实现集群内的访问
headless Service:该服务不会分配 Cluster IP,也不通过 kube-proxy 做反向代理和负载均衡。而是通过DNS提供稳定的网络ID来访问,DNS会将 headless service 的后端直接解析为 pod IP 列表。主要供StatefulSet使用
NodePort
apiVersion: v1 kind: Service metadata: name: my-service spec: type: NodePort selector: app: MyApp ports: # 默认情况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。 - port: 80 targetPort: 80 # 可选字段 # 默认情况下,为了方便起见,Kubernetes 控制平面会从某个范围内分配一个端口号(默认:30000-32767) nodePort: 30007
在每个Node上打开一个端口 (NodePort) 暴露 Service
--service-node-port-range 标志指定的范围内分配端口(默认值:30000-32767)
如没有指定 nodePort , 自动生成一个随机端口.
同时自动创建 ClusterIP 类型的访问方式
通过 <NodeIP>:spec.ports[*].nodePort 和 spec.clusterIp:spec.ports[*].port 访问从集群外访问服务
LoadBalancer
apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: MyApp ports: - protocol: TCP port: 80 targetPort: 9376 clusterIP: 10.0.171.239 loadBalancerIP: 78.11.24.19 type: LoadBalancer status: loadBalancer: ingress: - ip: 146.148.47.155
负载均衡器后端映射到各节点的nodePort
同时自动创建 NodePort 和 ClusterIP 类型的访问方式
通过云服务供应商的负载均衡器在集群外部暴露 Service
ExternalName
apiVersion: v1 kind: Service metadata: name: my-service namespace: prod spec: type: ExternalName externalName: my.database.example.com
将 Service 映射到 externalName 指定的地址(例如:foo.bar.example.com)
依赖的是dns层次,而不是通过kube-proxy
返回值是一个 CNAME 记录。不使用任何代理机制。
External IP
apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: MyApp ports: - name: http protocol: TCP port: 80 targetPort: 9376 externalIPs: - 80.11.12.10
spec.ports
port
暴露在 clusterIP 上的端口,clusterIP:Port 是提供给集群内部访问入口
nodePort
nodeIP:nodePort 提供给从集群外部访问入口
targetPort
是pod上的端口,从 port 和 nodePort 上访问数据经过kube-proxy流入到后端 pod 的 targetPort 上进入容器
多端口的Service
apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: MyApp ports: - name: http protocol: TCP port: 80 targetPort: 9376 - name: https protocol: TCP port: 443 targetPort: 9377
端口的名字只能包含小写字母、数字、-,并且必须以数字或字母作为开头及结尾
必须给出所有的端口的名称
spec.clusterIP 自定义 IP 地址
服务发现
环境变量
REDIS_MASTER_SERVICE_HOST=10.0.0.11 REDIS_MASTER_SERVICE_PORT=6379 REDIS_MASTER_PORT=tcp://10.0.0.11:6379 REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379 REDIS_MASTER_PORT_6379_TCP_PROTO=tcp REDIS_MASTER_PORT_6379_TCP_PORT=6379 REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11 名称为 "redis-master" 的 Service 暴露了 TCP 端口 6379,同时给它分配了 Cluster IP 地址 10.0.0.11
kubelet 会为每个活跃的 Service 添加一组环境变量
Service必须要先比该Pod创建
DNS
DNS 如何实现自动配置,依赖于 Service 是否定义了 selector
域名格式为: ${ServiceName}.${Namespace}.svc.${ClusterDomain}. 其中${ClusterDomain}的默认值是cluster.local,可以通过kubelet的命令行参数 --cluster-domain 进行配置。
Labels(标签)和 LabelSelector(标签选择器)
分发策略
RoundRobin:轮询模式,即轮询将请求转发到后端的各个pod上(默认模式);
SessionAffinity:基于客户端IP地址进行会话保持的模式,第一次客户端访问后端某个pod,之后的请求都转发到这个pod上。
创建 Service
kind: Service apiVersion: v1 metadata: name: my-service spec: selector: app: MyApp ports: - protocol: TCP port: 80 targetPort: 9376
Service 从自己的 IP 地址和 port 端口接收请求,并将请求映射到符合条件的 Pod 的 targetPort
4层的负载均衡
创建 Service(无 label selector)
--- kind: Service apiVersion: v1 metadata: name: my-service spec: ports: - protocol: TCP port: 80 targetPort: 9376 --- kind: Endpoints apiVersion: v1 metadata: name: my-service subsets: - addresses: - ip: 1.2.3.4 ports: - port: 9376 该 Service 没有 selector,相应的 Endpoint 对象就无法自动创建。 可以手动创建一个 Endpoint 对象,以便将该 Service 映射到后端服务真实的 IP 地址和端口. DNS 自动查找和比配, 确保名字一致.
service配置selector,endpoint controller才会自动创建对应的endpoint对象
如没有配置selector, DNS 系统会查找和配置
ExternalName 类型 Service 的 CNAME 记录
记录:与 Service 共享一个名称的任何 Endpoints,以及所有其它类型
Endpoint 中的 IP 地址不可以是集群中其他 Service 的 ClusterIP
Ingress提供外部访问
1: apiVersion: extensions/v1beta1 2: kind: Ingress 3: metadata: 4: name: test-ingress 5: spec: 6: rules: 7: - http: 8: paths: 9: - path: /testpath 10: backend: 11: serviceName: test 12: servicePort: 80 1-4行:跟Kubernetes的其他配置一样 5-7行: Ingress spec 中包含配置一个loadbalancer或proxy server的所有信息。最重要的是,它包含了一个匹配所有入站请求的规则列表。目前ingress只支持http规则 8-9行:每条http规则包含以下信息:一个host配置项(比如for.bar.com,在这个例子中默认是*),path列表(比如:/testpath),每个path都关联一个backend(比如test:80)。在loadbalancer将流量转发到backend之前,所有的入站请求都要先匹配host和path。 10-12行:正如 services doc中描述的那样,backend是一个service:port的组合。Ingress的流量被转发到它所匹配的backend。 全局参数: 在所有请求都不能跟spec中的path匹配的情况下,请求被发送到Ingress controller的默认后端,可以指定全局缺省backend。
Kubernetes集群外部访问集群内部服务的入口
Ingress 经常使用注解(annotations)来配置一些选项,具体取决于 Ingress 控制器
foo.bar.com -> 178.91.123.132 -> / foo s1:80 / bar s2:80
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: simple-fanout-example annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: foo.bar.com http: paths: - path: /foo backend: serviceName: service1 servicePort: 4200 - path: /bar backend: serviceName: service2 servicePort: 8080
foo.bar.com --| |-> foo.bar.com s1:80 | 178.91.123.132 | bar.foo.com --| |-> bar.foo.com s2:80
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: name-virtual-host-ingress spec: rules: - host: foo.bar.com http: paths: - backend: serviceName: service1 servicePort: 80 - host: bar.foo.com http: paths: - backend: serviceName: service2 servicePort: 80
更新Ingress
kubectl edit ing test
部署Traefik
创建ingress-rbac.yaml
apiVersion: v1 kind: ServiceAccount metadata: name: traefik-ingress-controller namespace: kube-system --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: traefik-ingress-controller rules: - apiGroups: - "" resources: - services - endpoints - secrets verbs: - get - list - watch - apiGroups: - extensions resources: - ingresses verbs: - get - list - watch --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: traefik-ingress-controller roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: traefik-ingress-controller subjects: - kind: ServiceAccount name: traefik-ingress-controller namespace: kube-system
创建名为traefik-ingress的ingress
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: ingress-test annotations: kubernetes.io/ingress.class: traefik spec: rules: - host: www.example.com http: paths: - backend: serviceName: secondapp servicePort: 80 path: /
使用DaemonSet类型来部署Traefik
apiVersion: extensions/v1beta1 kind: DaemonSet metadata: name: traefik-ingress-lb namespace: kube-system labels: k8s-app: traefik-ingress-lb spec: template: metadata: labels: k8s-app: traefik-ingress-lb name: traefik-ingress-lb spec: terminationGracePeriodSeconds: 60 hostNetwork: true restartPolicy: Always serviceAccountName: ingress containers: - image: traefik name: traefik-ingress-lb resources: limits: cpu: 200m memory: 30Mi requests: cpu: 100m memory: 20Mi ports: - name: http containerPort: 80 hostPort: 80 - name: admin containerPort: 8580 hostPort: 8580 args: - --web - --web.address=:8580 - --kubernetes nodeSelector: edgenode: "true"
Traefik UI
apiVersion: v1 kind: Service metadata: name: traefik-web-ui namespace: kube-system spec: selector: k8s-app: traefik-ingress-lb ports: - name: web port: 80 targetPort: 8580 --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: traefik-web-ui namespace: kube-system spec: rules: - host: traefik-ui.local http: paths: - path: / backend: serviceName: traefik-web-ui servicePort: web
TLS
apiVersion: v1 kind: Secret metadata: name: testsecret-tls namespace: default data: tls.crt: base64 encoded cert tls.key: base64 encoded key type: kubernetes.io/tls
Traefik Ingress Controller
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: td-ingress namespace: default annotations: traefik.frontend.rule.type: PathPrefixStrip kubernetes.io/ingress.class: traefik spec: rules: - host: "*.jimmysong.io" http: paths: - path: /docGenerate backend: serviceName: td-sdmk-docgenerate servicePort: 80 traefik.frontend.rule.type: PathPrefixStrip:表示将截掉URL中的path kubernetes.io/ingress.class:表示使用的ingress类型
Nginx Controller
最佳实践
Service 与 Controller 同名
Service 与 Controller 使用相同的 label selector
Headless Service
Service/Pod的DNS
Service Mesh
作用
Traffic Management:API网关
Observability:服务调用和性能分析
Policy Enforcment:控制服务访问策略
Service Identity and Security:安全保护
特点
专用的基础设施层
轻量级高性能网络代理
提供安全的、快速的、可靠地服务间通讯
扩展kubernetes的应用负载均衡机制,实现灰度发布
完全解耦于应用,应用可以无感知,加速应用的微服务和云原生转型
组件
Linkderd:https://linkerd.io,由最早提出Service Mesh的公司Buoyant开源,创始人来自Twitter
Envoy:https://www.envoyproxy.io/,Lyft开源的,可以在Istio中使用Sidecar模式运行
Istio:https://istio.io,由Google、IBM、Lyft联合开发并开源
流量管理:这是 Istio 的最基本的功能。
策略控制:通过 Mixer 组件和各种适配器来实现,实现访问控制系统、遥测捕获、配额管理和计费等。
可观测性:通过 Mixer 来实现。
安全认证:Citadel 组件做密钥和证书管理。
Conduit:https://conduit.io,同样由Buoyant开源的轻量级的基于Kubernetes的Service Mesh
专门处理服务通讯的基础设施层
代理在服务网格中被称为数据层或数据平面(data plane),管理流程被称为控制层或控制平面(control plane)
工作负载
容器组 ( Pod )
k8s 集群上的最基本的单元. Kubernetes 通过 Pod 管理容器,而不是直接管理容器
Pod 本身并不会运行,Pod 仅仅是容器运行的一个环境
存放一组 container(可包含一个或多个 container 容器),以及共享的资源
同一个Pod中的容器共享资源、网络环境和依赖,它们总是被同时调度
同一个Pod中的容器会自动的分配到同一个 node 上
共享存储,称为卷(Volumes),即图上紫色圆柱
如果多个容器紧密耦合并且需要共享磁盘等资源,则部署在同一个Pod 中
共享资源
网络 Networking
一个 Pod 中的所有容器 IP 地址都相同
同一个 Pod 中的不同容器不能使用相同的端口,否则会导致端口冲突
同一个 Pod 中的不同容器可以通过 localhost:port 进行通信
同一个 Pod 中的不同容器可以通过使用常规的进程间通信手段
存储 Volume
一个Pod指定多个共享的Volume
Pod中的所有容器都可以访问共享的volume
容器运行配置项
Pod Template
Pod 由控制器依据 Pod Template 创建以后,修改 Pod Template 的内容,已经创建的 Pod 不会被修改。
容器组
容器镜像
my-registry.example.com:5000/example/web-example:v1.0.1
registry 地址: my-registry.example.com
registry 端口: 5000
repository 名字: example
image 名字: web-example
image 标签: v1.0.1
Pod 中定义容器时,必须指定容器所使用的镜像
更新镜像
默认的镜像抓取策略是 IfNotPresent
本机有镜像的情况下,不会向镜像仓库抓取镜像
强制更新
设置 container 中的 imagePullPolicy 为 Always
省略 imagePullPolicy 字段,并使用 :latest tag 的镜像
省略 imagePullPolicy 字段和镜像的 tag
激活 AlwaysPullImages
生产环境部署时,避免使用 :latest
RuntimeClass
可以为容器选择运行时的容器引擎
两个 hook(钩子函数)
apiVersion: v1 kind: Pod metadata: name: lifecycle-demo spec: containers: - name: lifecycle-demo-container image: nginx lifecycle: postStart: exec: command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"] preStop: exec: command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]
PostStart
在容器创建后将立刻执行。但是不能保证该钩子函数在容器的 ENTRYPOINT 之前执行。该钩子函数没有输入参数。
如果 hook 执行的时间过长或者挂起了,容器将不能进入到 Running 状态
PreStop
在容器被 terminate(终止)之前执行
该函数的执行是同步的,即,kubernetes 将在该函数完成执行之后才删除容器
如果 hook 在执行过程中挂起了,Pod phase 将停留在 Terminating 的状态,并且在 terminationGracePeriodSeconds 超时之后,Pod被删除
hook 执行失败,则 Kubernetes 将 kill(杀掉)该容器。
Hook handler的实现
Exec - 在容器的名称空进和 cgroups 中执行一个指定的命令
HTTP - 向容器的指定端口发送一个 HTTP 请求
Hook handler的执行
当容器的生命周期事件发生时,Kubernetes 在容器中执行该钩子函数注册的 handler
生命周期
Pod phase (阶段)
Pending
Pod 已被 Kubernetes 系统接受, 此时可能有两种情况
正在从 docker registry 下载镜像
Pod 还未完成调度(例如没有合适的节点)
Running
该 Pod 已经被绑定到一个节点,并且该 Pod 所有的容器都已经成功创建。其中至少有一个容器正在运行或者正在启动/重启
Succeeded
Pod 中的所有容器都已经成功终止,并且不会再被重启
Failed
Pod 中的所有容器都已经终止,至少一个容器终止于失败状态:容器的进程退出码不是 0,或者被系统 kill
Unknown
因为某些未知原因,不能确定 Pod 的状态,通常的原因是 master 与 Pod 所在节点之间的通信故障
示意图
Pod conditions
每一个 Pod 都有一个数组描述其是否达到某些指定的条件
容器状态
Waiting
容器的初始状态。处于 Waiting 状态的容器,仍然有对应的操作在执行,例如:拉取镜像、应用 Secrets等。
Running
容器处于正常运行的状态。容器进入 Running 状态之后,如果指定了 postStart hook,该钩子将被执行。
Terminated
容器处于结束运行的状态。容器进入 Terminated 状态之前,如果指定了 preStop hook,该钩子将被执行。
restartPolicy 重启策略
Always (默认值)
OnFailure
Never
容器的检查
Pod 终止
gracefully terminate (优雅地终止)
直接 kill,此时进程没有机会执行清理动作
流程
用户发送删除pod的命令,默认宽限期是30秒;
在Pod超过该宽限期后API server就会更新Pod的状态为“dead”;
在客户端命令行上显示的Pod状态为“terminating”;
跟第三步同时,当kubelet发现pod被标记为“terminating”状态时,开始停止pod进程:
如果在pod中定义了preStop hook,在停止pod前会被调用。如果在宽限期过后,preStop hook依然在运行,第二步会再增加2秒的宽限期;
向Pod中的进程发送TERM信号;
跟第三步同时,该Pod将从该service的端点列表中删除,不再是replication controller的一部分。关闭的慢的pod将继续处理load balancer转发的流量;
过了宽限期后,将向Pod中依然运行的进程发送SIGKILL信号而杀掉进程。
Kubelet会在API server中完成Pod的的删除,通过将优雅周期设置为0(立即删除)。Pod在API中消失,并且在客户端也不可见。
强制删除Pod
Pod的强制删除是通过在集群和etcd中将其定义为删除状态
API server不会等待该pod所运行在节点上的kubelet确认,就会立即将该pod从API server中移除,这时就可以创建跟原pod同名的pod了
节点上的pod会被立即设置为terminating状态
Init Containers(InitC)初始化容器
优势
它们可以包含并运行实用工具,但是出于安全考虑,是不建议在应用程序容器镜像中包含这些实用工具的。
它们可以包含使用工具和定制化代码来安装,但是不能出现在应用程序镜像中。例如,创建镜像没必要 FROM 另一个镜像,只需要在安装过程中使用类似 sed、 awk、 python 或 dig 这样的工具。
应用程序镜像可以分离出创建和部署的角色,而没有必要联合它们构建一个单独的镜像。
Init 容器使用 Linux Namespace,所以相对应用程序容器来说具有不同的文件系统视图。因此,它们能够具有访问 Secret 的权限,而应用程序容器则不能。
它们必须在应用程序容器启动之前运行完成,而应用程序容器是并行运行的,所以 Init 容器能够提供了一种简单的阻塞或延迟应用容器的启动的方法,直到满足了一组先决条件。
资源使用的规则
在所有 Init 容器上定义的,任何特殊资源请求或限制的最大值,是 有效初始请求/限制
Pod 对资源的有效请求/限制要高于:
所有应用容器对某个资源的请求/限制之和
对某个资源的有效初始请求/限制
基于有效请求/限制完成调度,这意味着 Init 容器能够为初始化预留资源,这些资源在 Pod 生命周期过程中并没有被使用。
Pod 的 有效 QoS 层,是 Init 容器和应用容器相同的 QoS 层。
Init 容器总是运行到成功完成为止。
每个 Init 容器都必须在下一个 Init 容器启动之前成功完成。
Pod 对应的 restartPolicy 为 Never, 失败后,不重启.
所有的 Init 容器没有成功之前,Pod 将不会变成 Ready 状态
正在初始化中的 Pod 处于 Pending 状态,但应该会将 Initializing 状态设置为 true
更改 Init 容器的 image 字段,等价于重启该 Pod
Init 容器的代码应该是幂等的
容器探针
apiVersion: v1 kind: Pod metadata: labels: test: liveness name: liveness-http spec: containers: - args: - /server image: k8s.gcr.io/liveness livenessProbe: httpGet: # when "host" is not defined, "PodIP" will be used # host: my-host # when "scheme" is not defined, "HTTP" scheme will be used. Only "HTTP" and "HTTPS" are allowed # scheme: HTTPS path: /healthz port: 8080 httpHeaders: - name: X-Custom-Header value: Awesome initialDelaySeconds: 15 timeoutSeconds: 1 name: liveness
探测方式
httpGet 对指定端口和路径上容器的IP地址执行HTTP Get请求,当返回码是 200-399 之间的状态码时,则认为诊断是成功的;
ExecAction 在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
TCPSocketAction 通过探测容器的 IP 和 Port 进行 TCP 健康检查,如果 TCP 的链接能够被建立,那么这个容器是健康的。
探测结果
第一种是 success,表示 container 通过健康检查, Liveness probe 或 Readiness probe 是正常状态;
第二种是 Failure,表示 container 没有通过健康检查,如果没有通过健康检查的话,service 层将没有通过 Readiness 的 pod 进行摘除,而 Liveness 就是将这个 pod 进行重新拉起或者是删除。
第三种状态是 Unknown,Unknown 是表示说当前的执行的机制没有进行完整的一个执行,可能是因为类似像超时或者像一些脚本没有及时返回,那么此时 Readiness-probe 或 Liveness-probe 会不做任何的一个操作,会等待下一次的机制来进行检验。
kubelet 里面有一个叫 ProbeManager 的组件
包含 Liveness-probe 或 Readiness-probe,这两个 probe 会将相应的 Liveness 诊断和 Readiness 诊断作用在 pod 之上,来实现一个具体的判断
Readiness
readinessProbe: exec: command: - cat - /tmp/healthy initialDelaySeconds: 5 periodSeconds: 5
就绪指针,用来判断一个 pod 是否处在就绪状态
处在就绪状态的时候,它才能够对外提供相应的服务
使用命名的端口
ports: - name: liveness-port containerPort: 8080 hostPort: 8080 livenessProbe: httpGet: path: /healthz port: liveness-port
Liveness
存活指针,用来判断一个 pod 是否处在存活状态
livenessProbe 指定kubelet需要每隔多少秒执行一次liveness probe。
initialDelaySeconds 指定kubelet在该执行第一次探测之前需要等待多少秒钟
定义 liveness命令
apiVersion: v1 kind: Pod metadata: labels: test: liveness name: liveness-exec spec: containers: - name: liveness args: - /bin/sh - -c - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600 image: gcr.io/google_containers/busybox livenessProbe: exec: command: - cat - /tmp/healthy initialDelaySeconds: 5 periodSeconds: 5 # periodSeconds 规定kubelet要每隔5秒执行一次liveness probe。 initialDelaySeconds 告诉kubelet在第一次执行probe之前要的等待5秒钟。探针检测命令是在容器中执行 cat /tmp/healthy 命令。如果命令执行成功,将返回0,kubelet就会认为该容器是活着的并且很健康。如果返回非0值,kubelet就会杀掉这个容器并重启它
定义一个liveness HTTP请求
apiVersion: v1 kind: Pod metadata: labels: test: liveness name: liveness-http spec: containers: - name: liveness args: - /server image: gcr.io/google_containers/liveness livenessProbe: httpGet: path: /healthz port: 8080 httpHeaders: - name: X-Custom-Header value: Awesome initialDelaySeconds: 3 periodSeconds: 3 # 该探针将向容器中的server的8080端口发送一个HTTP GET请求。如果server的/healthz路径的handler返回一个成功的返回码,kubelet就会认定该容器是活着的并且很健康。如果返回失败的返回码,kubelet将杀掉该容器并重启它 # 任何大于200小于400的返回码都会认定是成功的返回码。其他返回码都会被认为是失败的返回码
定义TCP liveness探针
apiVersion: v1 kind: Pod metadata: name: goproxy labels: app: goproxy spec: containers: - name: goproxy image: gcr.io/google_containers/goproxy:0.1 ports: - containerPort: 8080 readinessProbe: tcpSocket: port: 8080 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: tcpSocket: port: 8080 initialDelaySeconds: 15 periodSeconds: 20
配置Probe
通用
initialDelaySeconds:容器启动后第一次执行探测是需要等待多少秒。
periodSeconds:执行探测的频率。默认是10秒,最小1秒。
timeoutSeconds:探测超时时间。默认1秒,最小1秒。
successThreshold:探测失败后,最少连续探测成功多少次才被认定为成功。默认是1。对于liveness必须是1。最小值是1。
failureThreshold:探测成功后,最少连续探测失败多少次才被认定为失败。默认是3。最小值是1。
HTTP probe
host:连接的主机名,默认连接到pod的IP。你可能想在http header中设置"Host"而不是使用IP。
scheme:连接使用的schema,默认HTTP。
path: 访问的HTTP server的path。
httpHeaders:自定义请求的header。HTTP运行重复的header。
port:访问的容器的端口名字或者端口号。端口号必须介于1和65535之间。
总结
Pause容器
在pod中担任Linux命名空间共享的基础;
启用pid命名空间,开启init进程。
图例
pause容器将内部的80端口映射到宿主机的8880端口,pause容器在宿主机上设置好了网络namespace后,nginx容器加入到该网络namespace中,我们看到nginx容器启动的时候指定了--net=container:pause,ghost容器同样加入到了该网络namespace中,这样三个容器就共享了网络,互相之间就可以使用localhost直接通信,--ipc=contianer:pause --pid=container:pause就是三个容器处于同一个namespace中,init进程为pause # ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 1024 4 ? Ss 13:49 0:00 /pause root 5 0.0 0.1 32432 5736 ? Ss 13:51 0:00 nginx: master p systemd+ 9 0.0 0.0 32980 3304 ? S 13:51 0:00 nginx: worker p node 10 0.3 2.0 1254200 83788 ? Ssl 13:53 0:03 node current/in root 79 0.1 0.0 4336 812 pts/0 Ss 14:09 0:00 sh root 87 0.0 0.0 17500 2080 pts/0 R+ 14:10 0:00 ps aux 在ghost容器中同时可以看到pause和nginx容器的进程,并且pause容器的PID是1。而在kubernetes中容器的PID=1的进程即为容器本身的业务进程。
docker run -d --name pause -p 8880:80 jimmysong/pause-amd64:3.0
docker run -d --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf --net=container:pause --ipc=container:pause --pid=container:pause nginx
nginx 容器将为localhost:2368创建一个代理, 即 80 转发到 2368
docker run -d --name ghost --net=container:pause --ipc=container:pause --pid=container:pause ghost
监听端口: 2368
访问http://localhost:8880/就可以看到ghost博客
Pod 安全策略(psp)
控制 Pod 运行的行为,以及它具有访问什么的能力
PodSecurityPolicy对象定义了一组条件,指示 Pod 必须按系统所能接受的顺序运行
策略
apiVersion: extensions/v1beta1 kind: PodSecurityPolicy metadata: name: permissive spec: seLinux: rule: RunAsAny supplementalGroups: rule: RunAsAny runAsUser: rule: RunAsAny fsGroup: rule: RunAsAny hostPorts: - min: 8000 max: 8080 volumes: - '*'
已授权容器的运行 privileged
为容器添加默认的一组能力 defaultAddCapabilities
为容器去掉某些能力 requiredDropCapabilities
容器能够请求添加某些能力 allowedCapabilities
控制卷类型的使用 volumes
主机网络的使用 hostNetwork
主机端口的使用 hostPorts
主机 PID namespace 的使用 hostPID
主机 IPC namespace 的使用 hostIPC
主机路径的使用 allowedHostPaths
AllowedHostPaths 是一个被允许的主机路径前缀的白名单。空值表示所有的主机路径都可以使用。
容器的 SELinux 上下文 seLinux
用户 ID runAsUser
MustRunAs - 必须配置一个 range。使用该范围内的第一个值作为默认值。验证是否不在配置的该范围内。
MustRunAsNonRoot - 要求提交的 Pod 具有非零 runAsUser 值,或在镜像中定义了 USER 环境变量。不提供默认值。
RunAsAny - 没有提供默认值。允许指定任何 runAsUser 。
配置允许的补充组 supplementalGroups
分配拥有 Pod 数据卷的 FSGroup fsGroup
必须使用一个只读的 root 文件系统
Pod hook
apiVersion: v1 kind: Pod metadata: name: lifecycle-demo spec: containers: - name: lifecycle-demo-container image: nginx lifecycle: postStart: exec: command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"] preStop: exec: command: ["/usr/sbin/nginx","-s","quit"]
exec:执行一段命令
HTTP:发送HTTP请求。
postStart或者preStop hook失败,将会终止容器
只能通过describe命令来获取日志
容器的Entrypoint执行之前,postStart执行完成后容器的状态才会被设置成为RUNNING
Pod Preset 准入控制器
在 Pod 被创建的时候向其中注入额外的运行时需求的 API 资源
在适当的时候修改 Pod spec 中的 spec.containers 字段。Pod Preset 中的资源定义将不会应用于 initContainers 字段。
执行流程
检索所有可用的 PodPresets。
检查 PodPreset 标签选择器上的标签,看看其是否能够匹配正在创建的 Pod 上的标签。
尝试将由 PodPreset 定义的各种资源合并到正在创建的 Pod 中。
出现错误时,在该 Pod 上引发记录合并错误的事件,PodPreset 不会注入任何资源到创建的 Pod 中。
注释刚生成的修改过的 Pod spec,以表明它已被 PodPreset 修改过。注释的格式为 podpreset.admission.kubernetes.io/podpreset-<pod-preset name>": "<resource version>"。
禁用特定 Pod 的 Pod Preset
在 Pod 的 Pod Spec 中添加注释:podpreset.admission.kubernetes.io/exclude:"true"
Pod中断与PDB(Pod中断预算)
裸 Pod
直接创建 Pod,同时指定其运行在特定的 Node 上
配置 Pod 的 Service Account
创建服务账号
kubectl create serviceaccount sample-sc
自动创建相应的 secret
kubectl get serviceaccount sample-sc -o yaml
apiVersion: v1 kind: ServiceAccount metadata: creationTimestamp: 2018-09-03T02:00:37Z labels: from: mofang name: mofang-viewer-sc namespace: default resourceVersion: "18914458" selfLink: /api/v1/namespaces/default/serviceaccounts/sample-sc uid: 26e129dc-af1d-11e8-9453-00163e0efab0 secrets: - name: sample-sc-token-9x7nk # serviceaccount 账号配置 kubeconfig 的时候需要使用到 sample-sc 的 token, 该 token 保存在该 serviceaccount 保存的 secret 中
kubectl get secret sample-sc-token-9x7nk
apiVersion: v1 data: ca.crt: Ci0tLS0tQkVHSU4gQ0VSVcvfUN....... namespace: ZGVmYXAsdA== token: ZXlKaGJHY2lPaUpTVXpJMU5pSXN....... kind: Secret metadata: annotations: kubernetes.io/service-account.name: sample-sc kubernetes.io/service-account.uid: 26e129dc-af1d-11e8-9453-00163e0efab0 creationTimestamp: 2018-09-03T02:00:37Z name: mofang-viewer-sc-token-9x7nk namespace: default resourceVersion: "18914310" selfLink: /api/v1/namespaces/default/secrets/sample-sc-token-9x7nk uid: 26e58b7c-af1d-11e8-9453-00163e0efab0 type: kubernetes.io/service-account-token # {data.token} 就会是我们的用户 token 的 base64 编码
创建角色( Role )或集群角色 ( ClusterRole )
apiVersion: rbac.authorization.k8s.io/v1 ## 这里也可以使用 Role kind: ClusterRole metadata: name: mofang-viewer-role labels: from: mofang rules: - apiGroups: - "" resources: - pods - pods/status - pods/log - services - services/status - endpoints - endpoints/status - deployments verbs: - get - list - watch
创建角色( RoleBinding ) 或集群角色( ClusterRoleBingding )绑定
apiVersion: rbac.authorization.k8s.io/v1 ## 这里也可以使用 RoleBinding kind: ClusterRoleBinding metadata: name: sample-role-binding labels: from: mofang roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: mofang-viewer-role subjects: - kind: ServiceAccount name: sample-sc namespace: default
配置 kubeconfig
apiVersion: v1 clusters: - cluster: ## 这个是集群的 TLS 证书,与授权无关,使用同一的就可以 certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS.... server: https://47.95.24.167:6443 name: beta contexts: - context: cluster: beta user: beta-viewer name: beta-viewer current-context: beta-viewer kind: Config preferences: {} users: - name: beta-viewer user: ## 这个使我们在创建 serviceaccount 生成相关 secret 之后的 data.token 的 base64 解码字符,它本质是一个 jwt token token: eyJhbGciOiJSUzI1NiIsInR5dffg6.....
为 service account 添加 ImagePullSecret
kubectl create secret docker-registry <name> \ --docker-server=DOCKER_REGISTRY_SERVER \ --docker-username=DOCKER_USER \ --docker-password=DOCKER_PASSWORD \ --docker-email=DOCKER_EMAIL --- apiVersion: v1 kind: Pod metadata: name: foo namespace: awesomeapps spec: containers: - name: foo image: janedoe/awesomeapp:v1 imagePullSecrets: - name: myregistrykey
1.直接在定义pod时定义imagePullSecret。(较不安全)
2.创建单独的账户(sa),在sa中定义imagePullSecret,然后在创建pod时定义serviceAccountName为此sa名称
kubectl get secrets myregistrykey
kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrykey"}]}'
修改命名空间的默认服务帐户,以将该 Secret 用作 imagePullSecret
kubectl get serviceaccounts default -o yaml
$ kubectl get serviceaccounts default -o yaml > ./sa.yaml $ cat sa.yaml apiVersion: v1 kind: ServiceAccount metadata: creationTimestamp: 2015-08-07T22:02:39Z name: default namespace: default resourceVersion: "243024" selfLink: /api/v1/namespaces/default/serviceaccounts/default uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6 secrets: - name: default-token-uudge $ vi sa.yaml [editor session not shown] [delete line with key "resourceVersion"] [add lines with "imagePullSecret:"] $ cat sa.yaml apiVersion: v1 kind: ServiceAccount metadata: creationTimestamp: 2015-08-07T22:02:39Z name: default namespace: default selfLink: /api/v1/namespaces/default/serviceaccounts/default uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6 secrets: - name: default-token-uudge imagePullSecrets: - name: myregistrykey $ kubectl replace serviceaccount default -f ./sa.yaml serviceaccounts/default
任务(Job)
执行一次的任务,将创建一个或多个 Pod,并确保指定数量的 Pod 可以成功执行到进程正常结束
创建 Job
apiVersion: batch/v1 kind: Job metadata: name: pi spec: template: metadata: name: pi spec: containers: - name: pi image: perl command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] restartPolicy: Never
当 Job 创建的 Pod 执行成功并正常结束时,Job 将记录成功结束的 Pod 数量
当成功结束的 Pod 达到指定的数量时,Job 将完成执行
删除 Job 对象时,将清理掉由 Job 创建的 Pod
Job的定义
.apiVersion
.kind
.metadata
Pod Selector
.spec.selector 字段是可选的
Pod Template
.spec.template 是必填字段
用于定义 pod template
与 Pod 有相同的字段内容,但由于是内嵌元素,pod template 不包括阿 apiVersion 字段和 kind 字段
必须指定合适的标签 .spec.template.spec.labels,参考 Pod Selector
指定合适的重启策略 restartPolicy .spec.template.spec.restartPolicy,此处只允许使用 Never 和 OnFailure 两个取值
Parallel Jobs
Non-parallel Jobs
只启动一个 Pod,除非该 Pod 执行失败
Pod 执行成功并结束以后,Job 也立刻进入完成 completed 状态
Parallel Jobs with a fixed completion count
.spec.completions 为一个非零正整数
Job 将创建至少 .spec.completions 个 Pod,编号为 1 - .spec.completions 尚未实现
Job 记录了任务的整体执行情况,当 1 - .spec.completions 中每一个编号都有一个对应的 Pod 执行成功时,Job 进入完成状态
Parallel Jobs with a work queue
不指定 .spec.completions,使用 .spec.parallelism
Pod 之间必须相互之间自行协调并发,或者使用一个外部服务决定每个 Pod 各自执行哪些任务。例如,某个Pod可能从带工作队列(work queue)中取出最多N个条目的批次数据
每个 Pod 都可以独立判断其他同僚(peers)是否完成,并确定整个Job是否完成
当 Job 中任何一个 Pod 成功结束,将不再为其创建新的 Pod
当所有的 Pod 都结束了,且至少有一个 Pod 执行成功后才结束,则 Job 判定为成功结束
一旦任何一个 Pod 执行成功并退出,Job 中的任何其他 Pod 都应停止工作和输出信息,并开始终止该 Pod 的进程
completions 和 parallelism
对于 non-parallel Job,.spec.completions 和 .spec.parallelism 可以不填写,默认值都为 1
对于 fixed completion count Job,需要设置 .spec.completions 为您期望的个数;同时不设置 .spec.parallelism 字段(默认值为 1)
对于 work queue Job,不能设置 .spec.completions 字段,且必须设置 .spec.parallelism 为0或任何正整数
Controlling Parallelism 并发控制
.spec.parallelism 可以被设置为0或者任何正整数,如果不设置,默认为1,如果设置为 0,则 Job 被暂停,直到该数字被调整为一个正整数。
处理Pod和容器的失败
.spec.template.spec.restartPolicy
.spec.backoffLimit 来设定 Job 最大的重试次数。该字段的默认值为 6.
Job的终止和清理
删除 Job 对象时,由该 Job 创建的 Pod 也将一并被删除
.spec.activeDeadlineSeconds 字段的优先级高于 .spec.backoffLimit
可能会非正常终止
某一个 Pod 执行失败(且 restartPolicy=Never)
某个容器执行出错(且 restartPolicy=OnFailure)
此时,Job 按照 处理Pod和容器的失败 中 .spec.bakcoffLimit 描述的方式进行处理
一旦重试次数达到了 .spec.backoffLimit 中的值,Job 将被标记为失败,且尤其创建的所有 Pod 将被终止
Job 中设置了 .spec.activeDeadlineSeconds。该字段限定了 Job 对象在集群中的存活时长,一旦达到 .spec.activeDeadlineSeconds 指定的时长,该 Job 创建的所有的 Pod 都将被终止,Job 的 Status 将变为 type:Failed 、 reason: DeadlineExceeded
Job 中有两个 activeDeadlineSeconds: .spec.activeDeadlineSeconds 和 .spec.template.spec.activeDeadlineSeconds
Job的自动清理
apiVersion: batch/v1 kind: Job metadata: name: pi-with-ttl spec: ttlSecondsAfterFinished: 100 template: spec: containers: - name: pi image: perl command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] restartPolicy: Never
Job pi-with-ttl 的 ttlSecondsAfterFinished 值为 100,则,在其结束 100 秒之后,将可以被自动删除
如果 ttlSecondsAfterFinished 被设置为 0,则 TTL 控制器在 Job 执行结束后,立刻就可以清理该 Job 及其 Pod
如果 ttlSecondsAfterFinished 值未设置,则 TTL 控制器不会清理该 Job
Job的模式
周期 ( CronJob )
apiVersion: batch/v1beta1 kind: CronJob metadata: name: hello spec: schedule: "*/1 * * * *" jobTemplate: spec: template: spec: containers: - name: hello image: busybox args: - /bin/sh - -c - date; echo Hello from the Kubernetes cluster restartPolicy: OnFailure
按照预定的时间计划(schedule)创建 Job, 执行周期性的重复任务
所有 CronJob 的 schedule 中所定义的时间,都是基于 master 所在时区来进行计算的
CronJob 只负责按照时间计划的规定创建 Job 对象,由 Job 来负责管理具体 Pod 的创建和执行
所有对 CronJob 对象作出的修改,都只对修改之后新建的 Job 有效,已经创建的 Job 不会受到影响
CronJob 在其计划的时间点应该创建 Job 时却创建失败,即被认为错过了一次执行
Job 程序必须是 幂等的
CronJob Spec
CronJob 对象需要 apiVersion、kind、metadata
.spec.schedule 是一个必填字段 (同Linux crontab)
问号 ? 与 星号 * 的含义相同,代表着该字段不做限定
.spec.jobTemplate 字段是必填字段,结构与 Job 相同
.spec.startingDeadlineSeconds :启动 Job 的期限(秒级别)
.spec.concurrencyPolicy:并发策略
后台支撑服务集(DaemonSet)
确保所有(或一部分)的节点都运行了一个指定的 Pod 副本。
特性
每当向集群中添加一个节点时,指定的 Pod 副本也将添加到该节点上
当节点从集群中移除时,Pod 也就被垃圾回收了
删除一个 DaemonSet 可以清理所有由其创建的 Pod
使用场景
在每个节点上运行集群的存储守护进程
在每个节点上运行日志收集守护进程
在每个节点上运行监控守护进程
创建 DaemonSet
apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-elasticsearch namespace: kube-system labels: k8s-app: fluentd-logging spec: selector: matchLabels: name: fluentd-elasticsearch template: metadata: labels: name: fluentd-elasticsearch spec: tolerations: # this toleration is to have the daemonset runnable on master nodes # remove it if your masters can't run pods - key: node-role.kubernetes.io/master effect: NoSchedule containers: - name: fluentd-elasticsearch image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2 resources: limits: memory: 200Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true terminationGracePeriodSeconds: 30 volumes: - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers
apiVersion
kind
metadata
.spec.selector
matchLabels
matchExpressions
字段定义了 DaemonSet 的 pod selector
必须与 .spec.template.metata.labels 字段匹配
DaemonSet 创建以后,.spec.selector 字段就不可再修改。修改 pod selector 可能导致 Pod 意外悬浮
指定了 .spec.selector,必须与 .spec.template.metadata.labels 相匹配
通常不应直接通过另一个 DaemonSet 或另一个工作负载资源(例如 ReplicaSet)来创建其标签与该选择器匹配的任何 Pod。否则,DaemonSet 控制器会认为这些 Pod 是由它创建的。出现不可预知的后果.
Pod Template
.spec.template 是必填字段,定义了 Pod 的模板,与定义 Pod 的 yaml 格式完全相同
必须指定 .spec.template.metadata.labels 字段和 .spec.tempalte.spec 字段
.spec.template.spec.restartPolicy 字段必须为 Always,或者不填
Pods支持taints and tolerations
DaemonSet 控制器调度
与 DaemonSet 通信
Push: DaemonSet 容器组用来向另一个服务推送信息,例如数据库的统计信息。这种情况下 DaemonSet 容器组没有客户端
NodeIP + Port: DaemonSet 容器组可以使用 hostPort,此时可通过节点的 IP 地址直接访问该容器组。客户端需要知道节点的 IP 地址,以及 DaemonSet 容器组的 端口号
DNS: 创建一个 headless service
,且该 Service 与 DaemonSet 有相同的 Pod Selector。此时,客户端可通过该 Service 的 DNS 解析到 DaemonSet 的 IP 地址
Service: 创建一个 Service,且该 Service 与 DaemonSet 有相同的 Pod Selector,客户端通过该 Service,可随机访问到某一个节点上的 DaemonSet 容器组
更新 DaemonSet
静态 Pod
在 Kubelet 监听的目录下创建一个 Pod 的 yaml 文件
不推荐
副本集(Replica Set,RS)
apiVersion: extensions/v1beta1 kind: ReplicaSet metadata: name: frontend # these labels can be applied automatically # from the labels in the pod template if not set # labels: # app: guestbook # tier: frontend spec: # this replicas value is default # modify it according to your case replicas: 3 # selector can be applied automatically # from the labels in the pod template if not set, # but we are specifying the selector here to # demonstrate its usage. selector: matchLabels: tier: frontend matchExpressions: - {key: tier, operator: In, values: [frontend]} template: metadata: labels: app: guestbook tier: frontend spec: containers: - name: php-redis image: gcr.io/google_samples/gb-frontend:v3 resources: requests: cpu: 100m memory: 100Mi env: - name: GET_HOSTS_FROM value: dns # If your cluster config does not include a dns service, then to # instead access environment variables to find service host # info, comment out the 'value: dns' line above, and uncomment the # line below. # value: env ports: - containerPort: 80
推荐使用 Deployment 替代 ReplicaSet
ReplicaSet 用来维护一个数量 replicas 指定的期望值的 Pod 副本集合
创建的 Pod 中,都有一个字段 metadata.ownerReferences 用于标识该 Pod 从属于哪一个 ReplicaSet
工作方式
selector: 用于指定哪些 Pod 属于该 ReplicaSet 的管辖范围
replicas: 副本数,用于指定该 ReplicaSet 应该维持多少个 Pod 副本
template: Pod模板,在 ReplicaSet 使用 Pod 模板的定义创建新的 Pod
ReplicaSet的定义
apiVersion:apps/v1
kind:始终为 ReplicaSet
metadata
spec: ReplicaSet 的详细定义
.spec.selector 字段为一个 标签选择器,用于识别可以接管哪些 Pod
.spec.template 字段是一个 Pod Template,为必填字段
且其中必须定义 .spec.template.metadata.labels 字段
.spec.template.spec.restartPolicy 的默认值为 Always
.spec.replicas 字段用于指定同时运行的 Pod 的副本数
.spec.template.metadata.labels 必须与 .spec.selector 匹配,否则将不能成功创建 ReplicaSet
只删除ReplicaSet
修改 Pod 的标签,可以使 Pod 脱离 ReplicaSet 的管理
删除ReplicaSet及其Pod
将Pod从ReplicaSet中隔离
伸缩ReplicaSet
替换 Replication Controller , 支持集合式 selector
复制控制器(Replication Controller,RC)
始终使用控制器来创建 Pod,而不是直接创建 Pod
水平扩展(运行 Pod 的多个副本)
rollout(版本更新)
self-healing(故障恢复)
推荐使用 Deployment 替代 Replication Controller
有状态服务集(StatefulSet)
--- apiVersion: v1 kind: Service metadata: name: kafka-svc labels: app: kafka spec: ports: - port: 9093 name: server clusterIP: None selector: app: kafka --- apiVersion: policy/v1beta1 kind: PodDisruptionBudget metadata: name: kafka-pdb spec: selector: matchLabels: app: kafka minAvailable: 2 --- apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: kafka spec: serviceName: kafka-svc replicas: 3 template: metadata: labels: app: kafka spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: "app" operator: In values: - kafka topologyKey: "kubernetes.io/hostname" podAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 podAffinityTerm: labelSelector: matchExpressions: - key: "app" operator: In values: - zk topologyKey: "kubernetes.io/hostname" terminationGracePeriodSeconds: 300 containers: - name: k8skafka imagePullPolicy: Always image: harbor-001.jimmysong.io/library/kafka:2.10-0.8.2.1 resources: requests: memory: "1Gi" cpu: 500m env: - name: KF_REPLICAS value: "3" ports: - containerPort: 9093 name: server command: - /bin/bash - -c - "/opt/kafka/bin/kafkaGenConfig.sh && /opt/kafka/bin/kafka-server-start.sh /opt/kafka/config/server.properties" env: - name: KAFKA_HEAP_OPTS value : "-Xmx512M -Xms512M" - name: KAFKA_OPTS value: "-Dlogging.level=DEBUG" readinessProbe: tcpSocket: port: 9092 initialDelaySeconds: 15 timeoutSeconds: 1
用于管理 Stateful(有状态)的应用程序
使用场景
稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
稳定、唯一的网络标识(dnsname),即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现
有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
有序收缩,有序删除(即从N-1到0)
限制
给定 Pod 的存储必须由 PersistentVolume Provisioner 根据请求的 storage class 进行配置,或由管理员预先配置
删除或 scale StatefulSet 将不会删除与 StatefulSet 相关联的 volume
创建 StatefulSet
apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 80 name: web clusterIP: None selector: app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: selector: matchLabels: app: nginx # has to match .spec.template.metadata.labels serviceName: "nginx" replicas: 3 # by default is 1 template: metadata: labels: app: nginx # has to match .spec.selector.matchLabels spec: terminationGracePeriodSeconds: 10 containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: www spec: accessModes: [ "ReadWriteOnce" ] storageClassName: "my-storage-class" resources: requests: storage: 1Gi
Pod 的标识
序号
每一个 Pod 都会被分配一个序号,序号的取值范围从 0 到 N - 1,并且该序号在 StatefulSet 内部是唯一的
稳定的网络标识
StatefulSet 中 Pod 的 hostname 格式为 $(StatefulSet name)-$(Pod 序号)。上面的例子将要创建三个 Pod,其名称分别为: web-0,web-1,web-2。
StatefulSet 可以使用 Headless Service 来控制其 Pod 所在的域。该域(domain)的格式为 $(service name).$(namespace).svc.cluster.local,其中 “cluster.local” 是集群的域。
StatefulSet 中每一个 Pod 将被分配一个 dnsName,格式为: $(podName).$(所在域名)
Pod的DNS格式为: statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local
serviceName为Headless Service的名字
0..N-1为Pod所在的序号,从0开始到N-1
statefulSetName为StatefulSet的名字
namespace为服务所在的namespace,Headless Servic和StatefulSet必须在相同的namespace
.cluster.local为Cluster Domain
稳定的存储
Kubernetes 为每一个 VolumeClaimTemplate 创建一份 PersistentVolume(存储卷)
当 Pod 或 StatefulSet 被删除时,其关联的 PersistentVolumeClaim(存储卷声明)以及其背后的 PersistentVolume(存储卷)仍然存在。
如果相同的 Pod 或 StatefulSet 被再次创建,则,新建的名为 web-0 的 Pod 仍将挂载到原来名为 web-0 的 Pod 所挂载的存储卷声明及存储卷。
这确保了 web-0、web-1、web-2 等,不管被删除重建多少次,都将 “稳定” 的使用各自所对应的存储内容
该标识始终与 Pod 绑定,无论该 Pod 被调度(重新调度)到哪一个节点上
部署和伸缩执行顺序
在创建一个副本数为 N 的 StatefulSet 时,其 Pod 将被按 {0 ... N-1} 的顺序逐个创建
在删除一个副本数为 N 的 StatefulSet (或其中所有的 Pod)时,其 Pod 将按照相反的顺序(即 {N-1 ... 0})终止和删除
在对 StatefulSet 执行扩容(scale up)操作时,新增 Pod 所有的前序 Pod 必须处于 Running(运行)和 Ready(就绪)的状态
终止和删除 StatefulSet 中的某一个 Pod 时,该 Pod 所有的后序 Pod 必须全部已终止
StatefulSet 中 pod.spec.terminationGracePeriodSeconds 不能为 0
Pod 管理策略
Parallel
.spec.podManagementPlicy 的取值为 Parallel,则 StatefulSet Controller 将同时并行地创建或终止其所有的 Pod
此选项只影响到伸缩(scale up/scale down)操作。更新操作不受影响。
OrderedReady 是 .spec.podManagementPlicy 的默认值
更新策略
设定 .spec.updateStrategy 字段,以便您可以在改变 StatefulSet 中 Pod 的某些字段时(container/labels/resource request/resource limit/annotation等)禁用滚动更新。
On Delete
.spec.updateStrategy.type 字段被设置为 OnDelete
当您修改 .spec.template 的内容时, 重新创建新的 Pod, 原相关 Pod 不自动删除.
Rolling Updates
.spec.updateStrategy.type 字段的默认值是 RollingUpdate
更新 StatefulSet 的 .spec.tempalte 字段时,StatefulSet Controller 将自动地删除并重建 StatefulSet 中的每一个 Pod
Partitions
指定 .spec.updateStrategy.rollingUpdate.partition 字段,可以分片(partitioned)执行RollingUpdate 更新策略
序号大于或等于 .spec.updateStrategy.rollingUpdate.partition 的 Pod 将被删除重建
序号小于 .spec.updateStrategy.rollingUpdate.partition 的 Pod 将不会更新,及时手工删除该 Pod,kubernetes 也会使用前一个版本的 .spec.template 重建该 Pod
如果 .spec.updateStrategy.rollingUpdate.partition 大于 .spec.replicas,更新 .spec.tempalte 将不会影响到任何 Pod
使用场景
执行预发布
执行金丝雀更新
执行按阶段的更新
Forced Rollback
更新 Pod template 后,该 Pod 始终不能进入 Running 和 Ready 的状态, 需要进行人工干预
集群外部访问StatefulSet的Pod
kubectl label pod zk-0 zkInst=0
kubectl label pod zk-1 zkInst=1
kubectl expose po zk-0 --port=2181 --target-port=2181 --name=zk-0 --selector=zkInst=0 --type=NodePort
kubectl expose po zk-1 --port=2181 --target-port=2181 --name=zk-1 --selector=zkInst=1 --type=NodePort
部署 (Deployment)
Deployment为Pod和ReplicaSet提供了一个声明式定义(declarative)方法,部署无状态服务的方式
创建 Deployment
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80
将创建一个名为 nginx-deployment 的 Deployment(部署),名称由 .metadata.name 字段指定
该 Deployment 将创建 3 个 Pod 副本,副本数量由 .spec.replicas 字段指定
.spec.selector 字段指定了 Deployment 如何找到由它管理的 Pod。此案例中,我们使用了 Pod template 中定义的一个标签(app: nginx)。对于极少数的情况,这个字段也可以定义更加复杂的规则
.template 字段包含了如下字段:
.template.metadata.labels 字段,指定了 Pod 的标签(app: nginx)
.template.spec.containers[].image 字段,表明该 Pod 运行一个容器 nginx:1.7.9
.template.spec.containers[].name 字段,表明该容器的名字是 nginx
kubectl apply -f nginx-deployment.yaml --record
--record 写入 Deployment 的 annotation(注解) kubernetes.io/change-cause
kubectl get deployments
UP-TO-DATE Deployment 中,符合当前 Pod Template 定义的 Pod 数量
kubectl rollout status deployment.v1.apps/nginx-deployment
查看 Deployment 的发布状态(rollout status)
kubectl get pods --show-labels
查看 Pod 的标签
Deployment 中的 .spec.selector 和 .template.metadata.labels 定义一个合适唯一的标签
Pod-template-hash 标签
区分 Deployment 中哪个 ReplicaSet 创建了哪些 Pod
更新 Deployment
当 Deployment 的 Pod template(.spec.template)字段中的内容发生变更时(例如标签、容器的镜像被改变),Deployment 的发布更新(rollout)将被触发
Deployment 将确保更新过程中,任意时刻只有一定数量的 Pod 被关闭。默认情况下,Deployment 确保至少 .spec.replicas 的 75% 的 Pod 保持可用(25% max unavailable)
Deployment 将确保更新过程中,任意时刻只有一定数量的 Pod 被创建。默认情况下,Deployment 确保最多 .spec.replicas 的 25% 的 Pod 被创建(25% max surge)
kubectl --record deployment.apps/nginx-deployment set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1
建议更改文件
kubectl edit deployment.v1.apps/nginx-deployment
kubectl rollout status deployment.v1.apps/nginx-deployment
查看发布更新(rollout)的状态
kubectl describe deployments
kubectl get deployments
查看更新后的 Deployment 的详情
kubectl get rs
kubectl get pods
覆盖更新 Rollover (更新过程中再更新)
假设您创建了一个 Deployment 有 5 个 nginx:1.7.9 的副本; 您立刻更新该 Deployment 使得其 .spec.replicas 为 5,容器镜像为 nginx:1.9.1,而此时只有 3 个 nginx:1.7.9 的副本已创建; 此时,Deployment Controller 将立刻开始 kill 已经创建的 3 个 nginx:1.7.9 的 Pod,并开始创建 nginx:1.9.1 的 Pod。Deployment Controller 不会等到 5 个 nginx:1.7.9 的 Pod 都创建完之后在开始新的更新
回滚 Deployment
当且仅当 Deployment 的 .spec.template 字段被修改时,才会创建一个 Deployment revision(版本)
Deployment Controller 将自动停止有问题的更新(rollout),不会继续 scale up 新的 ReplicaSet。maxUnavailable 参数指定了最多会有几个 Pod 副本卡住,该参数的默认值是 25%
检查 Deployment 的更新历史
kubectl rollout history deployment.v1.apps/nginx-deployment
制定 CHANGE-CAUSE 信息
为 Deployment 增加注解,kubectl annotate deployment.v1.apps/nginx-deployment kubernetes.io/change-cause="image updated to 1.9.1"
执行 kubectl apply 命令时,增加 --record 选项
手动编辑 Deployment 的 .metadata.annotation 信息
查看 revision(版本)的详细信息
kubectl rollout history deployment.v1.apps/nginx-deployment --revision=2
回滚到前一个 revision
kubectl rollout undo deployment.v1.apps/nginx-deployment
kubectl rollout undo deployment.v1.apps/nginx-deployment --to-revision=2
伸缩 Deployment
改变 Deployment 中 Pod 的副本数量
kubectl scale deployment.v1.apps/nginx-deployment --replicas=10
kubectl autoscale deployment.v1.apps/nginx-deployment --min=10 --max=15 --cpu-percent=80
集群支持 horizontal pod autoscaling
按比例伸缩
更大比例的新 Pod 数被分配到副本数最多的 ReplicaSet
更小比例的新 Pod 数被分配到副本数最少的 ReplicaSet
如果还有剩余的新 Pod 数未分配,则将被增加到副本数最多的 ReplicaSet
副本数为 0 的 ReplicaSet,scale up 之后,副本数仍然为 0
同一时间点运行应用程序的多个版本,自动伸缩器(autoscaler),滚动更新(RollingUpdate)
暂停和继续 Deployment
先暂停 Deployment,然后再触发一个或多个更新,最后再继续(resume)该 Deployment
暂停 Deployment
kubectl rollout pause deployment.v1.apps/nginx-deployment
kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1
kubectl rollout history deployment.v1.apps/nginx-deployment
继续(resume)该 Deployment
kubectl rollout resume deployment.v1.apps/nginx-deployment
1, 暂停更新,确保更新不被触发. 2, 多次变更信息. 3, 继续更新,将变更一次生效.
不能回滚(rollback)一个已暂停的 Deployment,除非您继续(resume)该 Deployment
Label selector 更新
不建议更新 label selector
查看 Deployment 状态
Progressing
Deployment 创建了一个新的 ReplicaSet
Deployment 正在 scale up 其最新的 ReplicaSet
Deployment 正在 scale down 其旧的 ReplicaSet
新的 Pod 变为 就绪(ready) 或 可用(available)
complete
该 Deployment 中的所有 Pod 副本都已经被更新到指定的最新版本
该 Deployment 中的所有 Pod 副本都处于 可用(available) 状态
该 Deployment 中没有旧的 ReplicaSet 正在运行
fail to progress
集群资源不够
就绪检查(readiness probe)失败
权限不够
资源限制
范围限制
应用程序的配置错误导致启动失败
无效的引用
不可读的 probe failure
镜像拉取错误
清理策略
.spec.revisionHistoryLimit 字段,可指定为该 Deployment 保留多少个旧的 ReplicaSet (默认 10)
金丝雀发布(灰度发布)
Deployment 结构示意图
Pod Template
.spec.template 是 .spec中唯一要求的字段
.spec.template 是 pod template. 它跟 Pod有一模一样的schema,除了它是嵌套的并且不需要apiVersion 和 kind字段
.spec.selector是可选字段,用来指定 label selector ,圈定Deployment管理的pod范围
如果被指定, .spec.selector 必须匹配 .spec.template.metadata.labels,否则它将被API拒绝
不应该再创建其他label跟这个selector匹配的pod
多个controller使用了重复的selector,controller们就会互相争议管理并导致不正确的行为
.spec.strategy 指定新的Pod替换旧的Pod的策略
.spec.paused是可以可选配置项,boolean值。用来指定暂停和恢复Deployment
所有对paused deployment中的PodTemplateSpec的修改都不会触发新的rollout
Deployment被创建之后默认是非paused
调度和驱逐 ( Scheduling and Eviction )
Pod 优先权
Pod 的调度顺序
资源耗尽时,从节点上驱逐 Pod 的顺序
定义 优先级 priority,用于标识该 Pod 相对于其他 Pod 的重要程度
Node
kubectl cordon <node>
kubectl drain <node>
删除该节点上的所有Pod(DaemonSet除外)
亲和性与反亲和性(affinity / anti-affinity)
增强点
表达方式更加有效(不仅仅是多个精确匹配表达式的“和”关系)
可以标识该规则为“soft” / “preference” (软性的、偏好的)而不是 hard requirement(必须的),此时,如果调度器发现该规则不能被满足,Pod 仍然可以被调度
可以对比节点上(或其他拓扑域 topological domain)已运行的其他 Pod 的标签,而不仅仅是节点自己的标签,此时,可以定义类似这样的规则:某两类 Pod 不能在同一个节点(或拓扑域)上共存
配置项
requiredDuringSchedulingIgnoredDuringExecution (hard,目标节点必须满足此条件)
preferredDuringSchedulingIgnoredDuringExecution (soft,目标节点最好能满足此条件)
weight 字段取值范围为 1-100
IgnoredDuringExecution 意味着:如果 Pod 已经调度到节点上以后,节点的标签发生改变,使得节点已经不再匹配该亲和性规则了,Pod 仍将继续在节点上执行(这一点与 nodeSelector 相似)
requiredDuringSchedulingRequiredDuringExecution
当节点的标签不在匹配亲和性规则之后,Pod 将被从节点上驱逐
操作符
In、NotIn、Exists、DoesNotExist、Gt、Lt
Node 节点亲和性
apiVersion: v1 kind: Pod metadata: name: with-node-affinity spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/e2e-az-name operator: In values: - e2e-az1 - e2e-az2 preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 preference: matchExpressions: - key: another-node-label-key operator: In values: - another-node-label-value containers: - name: with-node-affinity image: k8s.gcr.io/pause:2.0
Pod Spec 中通过 affinity.nodeAffinity 字段来定义节点亲和性
节点亲和性规则只在调度该 Pod 时发生作用
基于节点的标签来限定 Pod 可以被调度到哪些节点上
同时指定了 nodeSelector 和 nodeAffinity,则目标节点必须同时满足两个条件,才能将 Pod 调度到该节点上
nodeAffinity 指定多个 nodeSelectorTerms,则目标节点只需要满足任意一个 nodeSelectorTerms 的要求,就可以将 Pod 调度到该节点上。
nodeSelectorTerms 指定多个 matchExpressions,则目标节点必须满足所有的 matchExpressions 的要求,才能将 Pod 调度到该节点上
Pod 亲和性与反亲和性
apiVersion: v1 kind: Pod metadata: name: with-pod-affinity spec: affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: # hard,目标节点必须满足此条件 - labelSelector: matchExpressions: - key: security operator: In values: - S1 topologyKey: failure-domain.beta.kubernetes.io/zone podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: # soft,目标节点最好能满足此条件 - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: security operator: In values: - S2 topologyKey: failure-domain.beta.kubernetes.io/zone containers: - name: with-pod-affinity image: k8s.gcr.io/pause:2.0
Pod 的 affinity 定义了一个 Pod 亲和性规则和一个 Pod 反亲和性规则,例子中, podAffinity 是 requiredDuringSchedulingIgnoredDuringExecution,而 podAntiAffinity 则是 preferredDuringSchedulingIgnoredDuringExecution。
Pod 亲和性规则要求,该 Pod 可以被调度到的节点所在的可用区 zone 必须已经有一个已经运行的 Pod 包含标签 key=security,value=S1,或者更准确地说,节点必须满足如下条件:
节点包含 key 为 failure-domain.beta.kubernetes.io/zone 的标签,假设该标签的值为 V
至少有一个包含 key 为 failure-domain.beta.kubernetes.io/zone 且 value 为 V 的标签的节点已经运行了一个包含标签 key 为 security 且 value 为 S1 的 Pod
Pod 反亲和性规则要求,该 Pod 最好不要被调度到已经运行了包含 key 为 security 且 value 为 S2 的标签的 Pod 的节点上,或者更准确地说,必须满足如下条件:
如果 topologyKey 是 failure-domain.beta.kubernetes.io/zone,则,Pod不能被调度到同一个 zone 中的已经运行了包含标签 security: S2 的节点上
topologyKey 有如下限制
对亲和性以及 requiredDuringSchedulingIgnoredDuringExecution Pod 反亲和性,topologyKey 不能为空
对 requiredDuringSchedulingIgnoredDuringExecution Pod 反亲和性,管理控制器 LimitPodHardAntiAffinityTopology 被用来限制 topologyKey 必须为 kubernetes.io/hostname。如果想要使用其他的自定义 topology,必须修改该管理控制器,或者将其禁用
对 preferredDuringSchedulingIgnoredDuringExecution Pod 反亲和性,如果 topologyKey 为空,则代表所有的 topology (此时,不局限于 kubernetes.io/hostname、failure-domain.beta.kubernetes.io/zone 和 failure-domain.beta.kubernetes.io/region 的组合)
除了上述的情形以外,topologyKey 可以是任何合法的标签 Key
除了 labelSelector 和 topologyKey 以外,还可以指定一个 namespaces 的列表,用作 labelSelector 的作用范围(与 labelSelector 和 topologyKey 的定义为同一个级别)。如果不定义或者该字段为空,默认为 Pod 所在的名称空间。
所有与 requiredDuringSchedulingIgnoredDuringExecution 亲和性和反亲和性关联的 matchExpressions 必须被满足,Pod 才能被调度到目标节点
污点(Taint)和容忍 (Toleration)
污点和容忍(taints and tolerations)成对工作,作用于 node 和 pod 上, 以确保 Pod 不会被调度到不合适的节点上
节点中存在属性 污点 taints,使得节点可以排斥某些 Pod
通过给 pod 设置 nodeSelector 将 pod 调度到具有匹配标签的节点上
规则
可以为节点增加污点(taints,一个节点可以有 0-N 个污点)
可以为 Pod 增加容忍(toleration,一个 Pod 可以有 0-N 个容忍)
如果节点上存在污点,则该节点不会接受任何不能容忍(tolerate)该污点的 Pod。
匹配规则
键(key)相同
效果(effect)相同
污点的 operator 为:
Exists (此时污点中不应该指定 value)
Equal (此时容忍的 value 应与污点的 value 相同)
如果不指定 operator,则其默认为 Equal
如果未被过滤的污点中存在至少一个 effect 值为 NoSchedule 的污点, 则 Kubernetes 不会将 Pod 分配到该节点。
如果未被过滤的污点中不存在 effect 值为 NoSchedule 的污点, 但是存在 effect 值为 PreferNoSchedule 的污点, 则 Kubernetes 会 尝试 将 Pod 分配到该节点。
如果未被过滤的污点中存在至少一个 effect 值为 NoExecute 的污点, 则 Kubernetes 不会将 Pod 分配到该节点(如果 Pod 还未在节点上运行), 或者将 Pod 从该节点驱逐(如果 Pod 已经在节点上运行)。
向节点添加污点
kubectl taint nodes node1 key=value:NoSchedule
给节点 node1 增加一个污点,它的键名是 key,键值是 value,效果是 NoSchedule。
只有拥有和这个污点相匹配的容忍度的 Pod 才能够被分配到 node1 这个节点
kubectl taint nodes node1 key:NoSchedule-
最后有一个减号
taints affect
NoSchedule
只会影响新的 pod 调度
NoExecute
不能在节点上运行(如果已经运行,将被驱逐)
PreferNoSchedule
尽量避免将没有匹配容忍的 Pod 调度到该节点上,但这不是强制的
向 Pod 添加容忍
tolerations: - key: "key" operator: "Equal" value: "value" effect: "NoSchedule" --- tolerations: - key: "key" operator: "Exists" effect: "NoSchedule"
通过 PodSpec 中 tolerations 字段添加容忍
如果一个容忍度的 key 为空 (key没有指定,而不是指key为空字符串)且 operator 为 Exists, 表示这个容忍度与任意的 key 、value 和 effect 都匹配,即这个容忍度能容忍任意 taint。
如果 effect 为空,则可以与所有键名 key 的效果相匹配。
基于污点的驱逐
如果 Pod 不能忍受 effect 值为 NoExecute 的污点,那么 Pod 将马上被驱逐
如果 Pod 能够忍受 effect 值为 NoExecute 的污点,但是在容忍度定义中没有指定 tolerationSeconds,则 Pod 还会一直在这个节点上运行。
如果 Pod 能够忍受 effect 值为 NoExecute 的污点,而且指定了 tolerationSeconds, 则 Pod 还能在这个节点上继续运行这个指定的时间长度。
内置的污点
node.kubernetes.io/not-ready:节点未准备好。这相当于节点状态 Ready 的值为 "False"。
node.kubernetes.io/unreachable:节点控制器访问不到节点. 这相当于节点状态 Ready 的值为 "Unknown"。
node.kubernetes.io/out-of-disk:节点磁盘耗尽。
node.kubernetes.io/memory-pressure:节点存在内存压力。
node.kubernetes.io/disk-pressure:节点存在磁盘压力。
node.kubernetes.io/network-unavailable:节点网络不可用。
node.kubernetes.io/unschedulable: 节点不可调度。
node.cloudprovider.kubernetes.io/uninitialized:如果 kubelet 启动时指定了一个 "外部" 云平台驱动, 它将给当前节点添加一个污点将其标志为不可用。在 cloud-controller-manager 的一个控制器初始化这个节点后,kubelet 将删除这个污点。
当某种条件为真时,节点控制器会自动给节点添加一个污点
基于节点状态添加污点
节点亲和性是 Pod 的一种属性,它使 Pod 被吸引到一类特定的节点。 这可能出于一种偏好,也可能是硬性要求。 Taint(污点)则相反,它使节点能够排斥一类特定的 Pod。 容忍度(Tolerations)是应用于 Pod 上的,允许(但并不要求)Pod 调度到带有与之匹配的污点的节点上。 污点和容忍度(Toleration)相互配合,可以用来避免 Pod 被分配到不合适的节点上。 每个节点上都可以应用一个或多个污点,对于那些不能容忍这些污点的 Pod,是不会被该节点接受的。
kube-scheduler 主要负责将 Pod 调度到集群的 Node 上
调度器性能调优
满足一个 Pod 调度请求的所有 Node 称之为 可调度 Node
对这些可调度 Node 打分, 之后选出其中得分最高的 Node 来运行 Pod
调度器将这个调度决定告知 kube-apiserver,这个过程叫做 绑定(Binding)
将 Pod 分配给节点
HPA ( HorizontalPodAutoscaler )
仅适用于Deployment和ReplicaSet
通过API server、Heapseter或者kube-aggregator来获取监控指标
Metrics支持
autoscaling/v1
CPU
kubectl autoscale deployment foo --min=2 --max=5 --cpu-percent=80
autoscaling/v1alpha1
内存
自定义metrics
kubernetes1.6起支持自定义metrics,但是必须在kube-controller-manager中配置如下两项:
--horizontal-pod-autoscaler-use-rest-clients=true
--api-server指向kube-aggregator,也可以使用heapster来实现,通过在启动heapster的时候指定--api-server=true。查看kubernetes metrics
多种metrics组合
HPA会根据每个metric的值计算出scale的值,并将最大的那个值作为扩容的最终结果。
根据监测到的 CPU 利用率自动的扩容 replication controller,deployment 和 replica set
控制循环实现,循环周期由 controller manager 中的 --horizontal-pod-autoscaler-sync-period 标志指定(默认是 30 秒)
两种不同的方式获取 metric
直接的 Heapster 访问
需要在集群上部署 Heapster 并在 kube-system namespace 中运行
REST 客户端访问
资源配额
见 对象管理 - 名称空间
准入控制器
图示
kubectl
语法格式:kubectl an_action [a_resource] a_resource_name [–flags]
终端下kubectl命令自动补全
yum install bash-completion
source /usr/share/bash-completion/bash_completion
~/.bashrc
echo 'source <(kubectl completion bash)' >>~/.bashrc
kubectl completion bash >/etc/bash_completion.d/kubectl
source <(kubectl completion zsh) # setup autocomplete in zsh
命令
基础命令
help—获取帮助
get— 显示有关一个或多个资源的信息
describe—显示关于一个或多个资源的详细信息
logs—显示容器日志
exec—进入容器中一个正在运行的进程
delete—删除一个或多个资源
create
先删除所有现有的东西,重新根据yaml文件生成新的。所以要求yaml文件中的配置必须是完整的
用同一个yaml 文件执行替换命令replace,将不会成功,fail 掉
覆盖更新,创建不存在的资源
apply
根据yaml文件里面列出来的内容,升级现有的资源对象,所以yaml文件的内容可以只写需要升级的属性
增量更新
run
set
edit
explain
部署命令
rollout
rolling-update
scale
autoscale
集群管理命令
certificate
cluster-info
top
cordon
uncordon
drain
taint
故障排查和调试命令
describe
logs
attach
exec
port-forward
将本机指定端口映射到Pod资源对象的端口
proxy
将本机指定端口映射到 kube-apiserver
cp
用于 Pod 和主机交换文件
auth
高级命令
diff
apply
patch
replace
wait
convert
kustomize
设置命令
label
annotate
completion
其他命令
config
plugin
version
api-versions
api-resources
options
kubectl的身份认证
CA证书:API server与其它几个组件之间都是通过这种方式认证的
HTTP base:即在API server的启动参数中指定的--token-auth-file=/etc/kubernetes/token.csv文件中明文的用户、组、密码和UID配置
bearer token:HTTP请求中header中传递的Autorization:Bearer token,这个token通常保存在创建角色跟serviceaccount绑定的时候生成的secret中。
kube-shell
开源项目kube-shell可以为kubectl提供自动的命令提示和补全
日常操作
Kubectl 上下文和配置
$ kubectl config view # 显示合并后的 kubeconfig 配置 # 同时使用多个 kubeconfig 文件并查看合并后的配置 $ KUBECONFIG=~/.kube/config:~/.kube/kubconfig2 kubectl config view # 获取 e2e 用户的密码 $ kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}' $ kubectl config current-context # 显示当前的上下文 $ kubectl config use-context my-cluster-name # 设置默认上下文为 my-cluster-name # 向 kubeconf 中增加支持基本认证的新集群 $ kubectl config set-credentials kubeuser/foo.kubernetes.com --username=kubeuser --password=kubepassword # 使用指定的用户名和 namespace 设置上下文 $ kubectl config set-context gce --user=cluster-admin --namespace=foo \ && kubectl config use-context gce
创建对象
$ kubectl create -f ./my-manifest.yaml # 创建资源 $ kubectl create -f ./my1.yaml -f ./my2.yaml # 使用多个文件创建资源 $ kubectl create -f ./dir # 使用目录下的所有清单文件来创建资源 $ kubectl create -f https://git.io/vPieo # 使用 url 来创建资源 $ kubectl run nginx --image=nginx # 启动一个 nginx 实例 $ kubectl explain pods,svc # 获取 pod 和 svc 的文档 # 从 stdin 输入中创建多个 YAML 对象 $ cat <<EOF | kubectl create -f - apiVersion: v1 kind: Pod metadata: name: busybox-sleep spec: containers: - name: busybox image: busybox args: - sleep - "1000000" --- apiVersion: v1 kind: Pod metadata: name: busybox-sleep-less spec: containers: - name: busybox image: busybox args: - sleep - "1000" EOF # 创建包含几个 key 的 Secret $ cat <<EOF | kubectl create -f - apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: password: $(echo "s33msi4" | base64) username: $(echo "jane" | base64) EOF
显示和查找资源
# Get commands with basic output $ kubectl get services # 列出所有 namespace 中的所有 service $ kubectl get pods --all-namespaces # 列出所有 namespace 中的所有 pod $ kubectl get pods -o wide # 列出所有 pod 并显示详细信息 $ kubectl get deployment my-dep # 列出指定 deployment $ kubectl get pods --include-uninitialized # 列出该 namespace 中的所有 pod 包括未初始化的 # 使用详细输出来描述命令 $ kubectl describe nodes my-node $ kubectl describe pods my-pod $ kubectl get services --sort-by=.metadata.name # List Services Sorted by Name # 根据重启次数排序列出 pod $ kubectl get pods --sort-by='.status.containerStatuses[0].restartCount' # 获取所有具有 app=cassandra 的 pod 中的 version 标签 $ kubectl get pods --selector=app=cassandra rc -o \ jsonpath='{.items[*].metadata.labels.version}' # 获取所有节点的 ExternalIP $ kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}' # 列出属于某个 PC 的 Pod 的名字 # “jq”命令用于转换复杂的 jsonpath,参考 https://stedolan.github.io/jq/ $ sel=${$(kubectl get rc my-rc --output=json | jq -j '.spec.selector | to_entries | .[] | "\(.key)=\(.value),"')%?} $ echo $(kubectl get pods --selector=$sel --output=jsonpath={.items..metadata.name}) # 查看哪些节点已就绪 $ JSONPATH='{range .items[*]}{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status};{end}{end}' \ && kubectl get nodes -o jsonpath="$JSONPATH" | grep "Ready=True" # 列出当前 Pod 中使用的 Secret $ kubectl get pods -o json | jq '.items[].spec.containers[].env[]?.valueFrom.secretKeyRef.name' | grep -v null | sort | uniq
更新资源
$ kubectl rolling-update frontend-v1 -f frontend-v2.json # 滚动更新 pod frontend-v1 $ kubectl rolling-update frontend-v1 frontend-v2 --image=image:v2 # 更新资源名称并更新镜像 $ kubectl rolling-update frontend --image=image:v2 # 更新 frontend pod 中的镜像 $ kubectl rolling-update frontend-v1 frontend-v2 --rollback # 退出已存在的进行中的滚动更新 $ cat pod.json | kubectl replace -f - # 基于 stdin 输入的 JSON 替换 pod # 强制替换,删除后重新创建资源。会导致服务中断。 $ kubectl replace --force -f ./pod.json # 为 nginx RC 创建服务,启用本地 80 端口连接到容器上的 8000 端口 $ kubectl expose rc nginx --port=80 --target-port=8000 # 更新单容器 pod 的镜像版本(tag)到 v4 $ kubectl get pod mypod -o yaml | sed 's/\(image: myimage\):.*$/\1:v4/' | kubectl replace -f - $ kubectl label pods my-pod new-label=awesome # 添加标签 $ kubectl annotate pods my-pod icon-url=http://goo.gl/XXBTWq # 添加注解 $ kubectl autoscale deployment foo --min=2 --max=10 # 自动扩展 deployment “foo”
修补资源
$ kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}' # 部分更新节点 # 更新容器镜像; spec.containers[*].name 是必须的,因为这是合并的关键字 $ kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve-hostname","image":"new image"}]}}' # 使用具有位置数组的 json 补丁更新容器镜像 $ kubectl patch pod valid-pod --type='json' -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"new image"}]' # 使用具有位置数组的 json 补丁禁用 deployment 的 livenessProbe $ kubectl patch deployment valid-deployment --type json -p='[{"op": "remove", "path": "/spec/template/spec/containers/0/livenessProbe"}]'
编辑资源
$ kubectl edit svc/docker-registry # 编辑名为 docker-registry 的 service $ KUBE_EDITOR="nano" kubectl edit svc/docker-registry # 使用其它编辑器
Scale 资源
$ kubectl scale --replicas=3 rs/foo # Scale a replicaset named 'foo' to 3 $ kubectl scale --replicas=3 -f foo.yaml # Scale a resource specified in "foo.yaml" to 3 $ kubectl scale --current-replicas=2 --replicas=3 deployment/mysql # If the deployment named mysql's current size is 2, scale mysql to 3 $ kubectl scale --replicas=5 rc/foo rc/bar rc/baz # Scale multiple replication controllers
删除资源
$ kubectl delete -f ./pod.json # 删除 pod.json 文件中定义的类型和名称的 pod $ kubectl delete pod,service baz foo # 删除名为“baz”的 pod 和名为“foo”的 service $ kubectl delete pods,services -l name=myLabel # 删除具有 name=myLabel 标签的 pod 和 serivce $ kubectl delete pods,services -l name=myLabel --include-uninitialized # 删除具有 name=myLabel 标签的 pod 和 service,包括尚未初始化的 $ kubectl -n my-ns delete po,svc --all # 删除 my-ns namespace 下的所有 pod 和 serivce,包括尚未初始化的
与运行中的 Pod 交互
$ kubectl logs my-pod # dump 输出 pod 的日志(stdout) $ kubectl logs my-pod -c my-container # dump 输出 pod 中容器的日志(stdout,pod 中有多个容器的情况下使用) $ kubectl logs -f my-pod # 流式输出 pod 的日志(stdout) $ kubectl logs -f my-pod -c my-container # 流式输出 pod 中容器的日志(stdout,pod 中有多个容器的情况下使用) $ kubectl run -i --tty busybox --image=busybox -- sh # 交互式 shell 的方式运行 pod $ kubectl attach my-pod -i # 连接到运行中的容器 $ kubectl port-forward my-pod 5000:6000 # 转发 pod 中的 6000 端口到本地的 5000 端口 $ kubectl exec my-pod -- ls / # 在已存在的容器中执行命令(只有一个容器的情况下) $ kubectl exec my-pod -c my-container -- ls / # 在已存在的容器中执行命令(pod 中有多个容器的情况下) $ kubectl top pod POD_NAME --containers # 显示指定 pod 和容器的指标度量
与节点和集群交互
$ kubectl cordon my-node # 标记 my-node 不可调度 $ kubectl drain my-node # 清空 my-node 以待维护 $ kubectl uncordon my-node # 标记 my-node 可调度 $ kubectl top node my-node # 显示 my-node 的指标度量 $ kubectl cluster-info # 显示 master 和服务的地址 $ kubectl cluster-info dump # 将当前集群状态输出到 stdout $ kubectl cluster-info dump --output-directory=/path/to/cluster-state # 将当前集群状态输出到 /path/to/cluster-state # 如果该键和影响的污点(taint)已存在,则使用指定的值替换 $ kubectl taint nodes foo dedicated=special-user:NoSchedule
格式化输出
输出格式 描述 -o=custom-columns=<spec> 使用逗号分隔的自定义列列表打印表格 -o=custom-columns-file=<filename> 使用 文件中的自定义列模板打印表格 -o=json 输出 JSON 格式的 API 对象 -o=jsonpath=<template> 打印 jsonpath 表达式中定义的字段 -o=jsonpath-file=<filename> 打印由 文件中的 jsonpath 表达式定义的字段 -o=name 仅打印资源名称 -o=wide 以纯文本格式输出任何附加信息,对于 Pod ,包含节点名称 -o=yaml # -o 或者 -output 输出 YAML 格式的 API 对象
集群管理
配额问题
Etcd 存储
扩展API服务
CRD
创建 CRD
apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: # 名称必须符合下面的格式:<plural>.<group> name: crontabs.stable.example.com spec: # REST API使用的组名称:/apis/<group>/<version> group: stable.example.com # REST API使用的版本号:/apis/<group>/<version> versions: - name: v1 # 可以通过 served 来开关每个 version served: true # 有且仅有一个 version 开启存储 storage: true schema: openAPIV3Schema: type: object properties: spec: type: object properties: cronSpec: type: string image: type: string replicas: type: integer # Namespaced或Cluster scope: Namespaced names: # URL中使用的复数名称: /apis/<group>/<version>/<plural> plural: crontabs # CLI中使用的单数名称 singular: crontab # CamelCased格式的单数类型。在清单文件中使用 kind: CronTab # CLI中使用的资源简称 shortNames: - ct # resourcedefinition.yaml
kubectl create -f resourcedefinition.yaml
创建自定义对象
apiVersion: "stable.example.com/v1" kind: CronTab metadata: name: my-new-cron-object spec: cronSpec: "* * * * */5" image: my-awesome-cron-image # my-crontab.yaml
kubectl create -f my-crontab.yaml
删除 CustomResourceDefinition 时,服务器将删除 RESTful API 端点并删除存储在其中的所有自定义对象。
服务目录(Service Catalog)
QoS(服务质量等级)
是通过配置 CPU/内存的 limits 与 requests 值的大小来确认服务质量等级
Guaranteed:Pod 里的每个容器都必须有内存/CPU 限制和请求,而且值必须相等。
spec: containers: ... resources: limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi
Burstable:Pod 里至少有一个容器有内存或者 CPU 请求且不满足 Guarantee 等级的要求,即内存/CPU 的值设置的不同。
spec: containers: ... resources: limits: memory: "180Mi" requests: memory: "100Mi"
BestEffort:容器必须没有任何内存或者 CPU 的限制或请求。
管理集群中的TLS
Kubelet 认证
启动 kubelet 时指定 --anonymous-auth=false 标志
HTTPS 端点启用 X509 客户端证书身份验证
启动 kubelet 时指定 --client-ca-file 标志,提供 CA bundle 以验证客户端证书
启动 apiserver 时指定 --kubelet-client-certificate 和 --kubelet-client-key 标志
参阅 apiserver 认证文档 获取更多详细信息。
API bearer token
确保在 API server 中开启了 authentication.k8s.io/v1beta1 API 组。
启动 kubelet 时指定 --authentication-token-webhook, --kubeconfig 和 --require-kubeconfig 标志
Kubelet 在配置的 API server 上调用 TokenReview API 以确定来自 bearer token 的用户信息
IP 伪装代理
ip-masq-agent
安全权限
用户认证
用户授权
权限控制RBAC
Role: 角色,命名空间范围内的一个权限集合。
ClusterRole:集群角色,集群范围内的一个权限的集合
RoleBinding 角色绑定
ClusterRoleBinding 集群角色绑定
把声明的 Subject 和 Role 进行绑定的过程(给某个用户绑定上操作的权限) 区别: RoleBinding 只会影响到当前 namespace 下面的资源操作权限, ClusterRoleBinding 会影响到所有的 namespace
创建用户凭证
openssl genrsa -out jenkins.key 2048
openssl req -new -key jenkins.key -out jenkins.csr -subj "/CN=jenkins/O=vmware"
openssl x509 -req -in jenkins.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out jenkins.crt -days 365
kubectl config set-credentials jenkins --client-certificate=jenkins.crt --client-key=jenkins.key
kubectl config set-context jenkins-context --cluster=kubernetes --namespace=jenkins --user=jenkins
kubectl get pods --context=jenkins-context
创建角色
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: jenkins-role namespace: jenkins rules: - apiGroups: ["", "extensions", "apps"] resources: ["deployments", "replicasets", "pods"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
创建角色绑定
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: jenkins-rolebinding namespace: jenkins subjects: - kind: User name: jenkins apiGroup: "" roleRef: kind: Role name: jenkins-role apiGroup: ""
networkpolicy
最佳实践
使用Init container初始化应用配置
apiVersion: v1 kind: Pod metadata: name: init labels: app: init annotations: pod.beta.kubernetes.io/init-containers: '[ { "name": "download", "image": "axeclbr/git", "command": [ "git", "clone", "https://github.com/mdn/beginner-html-site-scripted", "/var/lib/data" ], "volumeMounts": [ { "mountPath": "/var/lib/data", "name": "git" } ] } ]' spec: containers: - name: run image: docker.io/centos/httpd ports: - containerPort: 80 volumeMounts: - mountPath: /var/www/html name: git volumes: - emptyDir: {} name: git
使容器内时间与宿主机同步
volumeMounts: - name: host-time mountPath: /etc/localtime readOnly: true volumes: - name: host-time hostPath: path: /etc/localtime
配置Pod使用外部DNS
apiVersion: v1 kind: ConfigMap metadata: name: kube-dns namespace: kube-system data: stubDomains: | {"k8s.com": ["192.168.10.10"]} upstreamNameservers: | ["8.8.8.8", "8.8.4.4"]
Configuration Best Practices
构建最少容器 alpine
性能好
攻击面小,安全
创建不同 namespace 进行隔离
备忘录
包管理
Helm
是一个kubernetes应用的包管理工具
管理charts——预先配置好的安装包资源
应用程序封装 版本管理 依赖检查 便于应用程序分发
部署Helm客户端
Helm部署应用
helm search jenkins
helm repo list
helm install stable/jenkins
原理图
APIs
CRD ( CustomResourceDefinitions )
对K8S API的扩展,代表了一个特定的kubetnetes的定制化安装
基于 CRD 技术,用户能将自定义资源注册到 kubernetes 系统,并像使用原生资源(如 pod、statefulset )一样对自定义资源进行创建、查看、修改、删除等操作,实现了类似于插件式的功能增强
将Custom resources添加到集群
Custom Resource Definitions (CRDs):更易用、不需要编码。但是缺乏灵活性。
apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: # 称必须与下面的spec字段匹配,格式为: <plural>.<group> name: crontabs.crd.test.com spec: # 用于REST API的组名称: /apis/<group>/<version> group: crd.test.com versions: - name: v1 # 每个版本都可以通过服务标志启用/禁用。 served: true # 必须将一个且只有一个版本标记为存储版本。 storage: true scope: Namespaced # 指定crd资源作用范围在命名空间或集群 names: # URL中使用的复数名称: /apis/<group>/<version>/<plural> plural: crontabs # 在CLI(shell界面输入的参数)上用作别名并用于显示的单数名称 singular: crontab kind: CronTab # 短名称允许短字符串匹配CLI上的资源,意识就是能通过kubectl 在查看资源的时候使用该资源的简名称来获取。 shortNames: - ct validation: openAPIV3Schema: properties: spec: properties: cronSpec: #必须是字符串、符合正则规则 type: string pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' replicas: #设置副本数的限制 type: integer minimum: 1 maximum: 10 additionalPrinterColumns: - name: Replicas type: integer JSONPath: .spec.replicas - name: Age type: date JSONPath: .metadata.creationTimestamp subresources: scale: specReplicasPath: .spec.replicas statusReplicasPath: .status.replicas --- apiVersion: crd.test.com/v1 kind: CronTab metadata: name: my-test-crontab spec: cronSpec: "* * * * */10" image: my-test-image replicas: 2
自定义资源-validations
validation这个验证是为了在创建好自定义资源后,通过该资源创建对象的时候,对象的字段中存在无效值,则创建该对象的请求将被拒绝,否则会被创建。我们可以在crd文件中添加“validation:”字段来添加相应的验证机制。
自定义资源-additionalPrinterColumns
当我们在查看自定义资源信息的时候显示出我们需要的列表信息。通过在crd文件中添加“additionalPrinterColumns:”字段,在该字段下声明需要打印列的的信息(kubectl get)
自定义资源-subresources
没有在自定义资源当中配置关于资源对象的伸缩和状态信息的一些相关配置的话,那么在当我们通过该自定义资源创建对象后,又想通过“kubectl scale”来弹性的扩展该对象的容器的时候就会无能为力
CRD可以允许我们添加该方面的相关配置声明,从而达到我们可以对自定义资源对象的伸缩处理。添加“ subresources:”字段来声明状态和伸缩信息
API Aggregation:需要编码,允许通过聚合层的方式提供更加定制化的实现。
CRD Controller的开发逻辑
控制器的目的是让 CRD 定义的资源达到我们预期的一个状态,要达到我们定义的状态,我们需要监听触发事件
触发事件的概念是从硬件信号产生 中断 的机制衍生过来的
水平触发 : 系统仅依赖于当前状态。即使系统错过了某个事件(可能因为故障挂掉了),当它恢复时,依然可以通过查看信号的当前状态来做出正确的响应。
边缘触发 : 系统不仅依赖于当前状态,还依赖于过去的状态。如果系统错过了某个事件(“边缘”),则必须重新查看该事件才能恢复系统。
Operator
coreos公司提出的一个概念
Kubernetes API 可扩展性对 Kubernetes 生态系统及其平台自身的重要性,并构建了相应的 API 扩展框架,谷歌将其命名为 Third Party Resource,简称“TPR”
由 Custom Resource Definition(简称 CRD)资源模型范式代替
通过扩展 Kubernetes 原生 API 的方式为 Kubernetes 应用提供创建、配置以及运行时刻生命周期管理能力,与此同时用户可以利用 Operator 方便的对应用模型进行更新、备份、扩缩容及监控等多种复杂运维操作
希望解决的问题
不同场景下的分布式系统中通常维护了一套自身的模型定义规范,如何在 Kubernetes 平台中表达或兼容出应用原先的模型定义?
当应用系统发生扩缩容或升级时,如何保证当前已有实例服务的可用性?如何保证它们之间的可连通性?
如何去重新配置或定义复杂的分布式应用?是否需要大量的专业模板定义和复杂的命令操作?是否可以向无状态应用那样用一条 kubectl 命令就完成应用的更新?
如何备份和管理系统状态和应用数据?如何协调系统集群各成员间在不同生命周期的应用状态?
开源生命周期流程
开发者首先使用 Operator SDK 创建一个 Operator 项目;
利用 SDK 我们可以生成 Operator 对应的脚手架代码,然后扩展相应业务模型和 API,最后实现业务逻辑完成一个 Operator 的代码编写;
参考社区测试指南进行业务逻辑的本地测试以及打包和发布格式的本地校验;
在完成测试后可以根据规定格式向社区提交PR,会有专人进行 review;
待社区审核通过完成 merge 后,终端用户就可以在 OperatorHub.io 页面上找到业务对应的 Operator;
用户可以在 OperatorHub.io 上找到业务 Operator 对应的说明文档和安装指南,通过简单的命令行操作即可在目标集群上完成 Operator 实例的安装;
Operator 实例会根据配置创建所需的业务应用,OLM 和 Operator Metering 等组件可以帮助用户完成业务应用对应的运维和监控采集等管理操作。
开放接口
CRI(Container Runtime Interface):容器运行时接口,提供计算资源
CRI中定义了容器和镜像的服务的接口,因为容器运行时与镜像的生命周期是彼此隔离的,因此需要定义两个服务。该接口使用Protocol Buffer,基于gRPC
RuntimeService:容器和Sandbox运行时管理。
ImageService:提供了从镜像仓库拉取、查看、和移除镜像的RPC。
CNI(Container Network Interface):容器网络接口,提供网络资源
用于配置Linux容器的网络接口的规范和库组成
CNI仅关心容器创建时的网络分配,和当容器被删除时释放网络资源
根据此规则编写网络插件
保证每个Pod拥有一个集群内唯一的IP地址
保证不同节点的IP地址划分不会重复
保证跨节点的Pod可以互相通信
保证不同节点的Pod可以与跨节点的主机互相通信
CSI(Container Storage Interface):容器存储接口,提供存储资源
https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#-strong-api-overview-strong-
API设计原则
API对象
使用 Kubernetes API 访问集群
使用 kubectl
kubectl config view
直接访问 REST API
使用 kubectl 代理
以代理模式运行 kubectl(推荐)。 推荐使用此方法,因为它用存储的 apiserver 位置并使用自签名证书验证 API 服务器的标识。 使用这种方法无法进行中间人(MITM)攻击
kubectl proxy --port=8080 &
curl http://localhost:8080/api/
不使用 kubectl 代理
# 查看所有的集群,因为你的 .kubeconfig 文件中可能包含多个上下文 kubectl config view -o jsonpath='{"Cluster name\tServer\n"}{range .clusters[*]}{.name}{"\t"}{.cluster.server}{"\n"}{end}' # 从上述命令输出中选择你要与之交互的集群的名称 export CLUSTER_NAME="some_server_name" # 指向引用该集群名称的 API 服务器 APISERVER=$(kubectl config view -o jsonpath="{.clusters[?(@.name==\"$CLUSTER_NAME\")].cluster.server}") # 获得令牌 TOKEN=$(kubectl get secrets -o jsonpath="{.items[?(@.metadata.annotations['kubernetes\.io/service-account\.name']=='default')].data.token}"|base64 -d) # 使用令牌玩转 API curl -X GET $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure
你可以直接为 HTTP 客户端提供位置和身份认证。 这适用于被代理混淆的客户端代码。 为防止中间人攻击,你需要将根证书导入浏览器。
将身份认证令牌直接传给 API 服务器,可以避免使用 kubectl 代理
编程方式访问 API
使用与 kubectl 命令行工具相同的 kubeconfig 文件 定位和验证 API 服务器
Kubernetes API 访问控制
术语
Annotations
注解是以键值对的形式给资源对象附加随机的无法标识的元数据
像工具和软件库这样的客户端可以检索这些元数据
API Group
Kubernetes API 中的一组相关路径
API group 在 REST 路径和序列化对象的 apiVersion 字段中指定
cgroup (控制组)
是一个 Linux 内核特性,对一组进程的资源使用(CPU、内存、磁盘 I/O 和网络等)进行限制、审计和隔离
CustomResourceDefinition
通过定制化的代码给您的 Kubernetes API 服务器增加资源对象,而无需编译完整的定制 API 服务器
DaemonSets
保证在每个Node上都运行一个容器副本,常用来部署一些集群的日志、监控或者其他系统管理应用
Deployments
Deployment 是管理应用副本的 API 对象
为Pod和Replica Set 提供声明式更新
Horizontal Pod Autoscaling
可以根据CPU使用率或应用自定义metrics自动扩展Pod数量(支持replication controller、deployment和replica set)
Images(镜像)
Ingress Resources
Ingress就是为进入集群的请求提供路由规则的集合
Jobs
Job负责批量处理短暂的一次性任务 (short lived one-off tasks),即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束。
Job 是需要运行完成的确定性的或批量的任务
Job 创建一个或多个 Pod 对象,并确保指定数量的 Pod 成功终止。随着各 Pod 成功结束,Job 会跟踪记录成功完成的个数
CronJob
即定时任务,就类似于Linux系统的crontab,在指定的时间周期运行指定的任务
Labels and Selectors
标签其实就一对 key/value ,被关联到对象上
标示对象的特殊特点
标签可以用来划分特定组的对象
通过label选择器,客户端/用户能方便辨识出一组对象
Namespaces
Namespace是对一组资源和对象的抽象集合
可以用来将系统内部的对象划分为不同的项目组或用户组,提供虚拟的隔离作用
默认命名空间default和系统命名空间kube-system
Network Policies
Network Policy提供了基于策略的网络控制,用于隔离应用并减少攻击面。它使用标签选择器模拟传统的分段网络,并通过策略控制它们之间的流量以及来自外部的流量。
Nodes
Node是Pod真正运行的主机,可以物理机,也可以是虚拟机
每个Node节点上至少要运行container runtime(比如docker或者rkt)、kubelet和kube-proxy服务
Pods
Pod是最小的,管理,创建,计划的最小单元
包含一个或者多个紧密相连的应用
被创建,被销毁,但不可复活
ReplicaSets
ReplicaSet是下一代复本控制器
ReplicaSet和 Replication Controller之间的唯一区别是现在的选择器支持
ReplicaSet还支持新的,基于集合的selector(version in (v1.0, v2.0)或env notin (dev, qa))
可被 Deployments 管理
StatefulSet
是为了解决有状态服务的问题
PetSet (之前)
有状态服务集
每个Pod的名字都是事先确定的,不能更改
Replication Controller
保证了在所有时间内,都有特定数量的Pod副本正在运行
通过 Pod template 创建 Pod
监控的Pod的数量是是由一个叫 label selector(标签选择器)决定的
Replication Controller只支持基于等式的selector(env=dev或environment!=qa)
可被 Deployments 管理
PodPreset
用来给指定标签的Pod注入额外的信息,如环境变量、存储卷等
Resource Quotas
资源配额(Resource Quotas)是用来限制用户资源用量的一种机制
Secrets
Secret解决了密码、token、密钥等敏感数据的配置问题
Security Context
Security Context的目的是限制不可信容器的行为,保护系统和其他容器不受其影响。
Container-level Security Context:仅应用到指定的容器
Pod-level Security Context:应用到Pod内所有容器以及Volume
Pod Security Policies(PSP):应用到集群内部所有Pod以及Volume
RBAC访问授权
基于角色的访问控制(Role-based Access Control,RBAC)的授权模式
主要是引入了角色(Role)和角色绑定(RoleBinding)的抽象概念
Services
一个定义了一组Pod的访问策略的抽象
被服务标记的Pod都是(一般)通过label Selector决定的
Service Accounts
Service account是为了方便Pod里面的进程调用Kubernetes API或其他外部服务而设计的
服务账户为计算机进程和K8s集群中运行的Pod提供账户标识
User account
用户帐户, 是为人设计的, 跨namespace的
Volumes
拥有明确的生命周期,与所在的Pod的生命周期相同
Persistent Volumes
持久存储卷, 集群中已由管理员配置的一段网络存储
Persistent Volume Claim
持久存储卷声明, 用户存储的请求
StorageClass
为管理员提供了一种描述他们提供的存储的“类”的方法
ConfigMap
用于保存配置数据的键值对,可以用来保存单个属性,也可以用来保存配置文件。
Service Mesh
Istio:IBM、Google、Lyft共同开源,详细文档见Istio官方文档
Linkerd:原Twitter工程师开发,现为CNCF中的项目之一
Envoy:Lyft开源的,可以在Istio中使用Sidecar模式运行
Conduit:同样由Buoyant开源的轻量级的基于Kubernetes的Service Mes
责服务之间的网络调用、限流、熔断和监控
PodSecurityPolicy
Pod 安全策略
kube-apiserver
主节点上负责提供 Kubernetes API 服务的组件;它是 Kubernetes 控制面的前端
部署多个实例可以实现扩缩
kube-proxy
是集群中每个节点上运行的网络代理,实现 Kubernetes Service 概念的一部分
Kubectl
kubectl 是用来和 Kubernetes API 服务器进行通信的命令行工具
Kubelet
一个在集群中每个节点上运行的代理。它保证容器都运行在 Pod 中
kubelet 不会管理不是由 Kubernetes 创建的容器
Kubernetes API
Kubernetes API 是通过 RESTful 接口提供 Kubernetes 功能服务并负责集群状态存储的应用程序
LimitRange
提供约束来限制命名空间中每个 容器 或 Pod 的资源消耗。
Static Pod
由特定节点上的 kubelet 守护进程直接管理的 pod
API 服务器不了解它的存在
Selector
选择算符允许用户通过标签对一组资源对象进行筛选过滤
Workload
JSON 或 YAML 格式的 Kubernetes API 对象规范
Resource Quotas
资源配额提供了限制每个 命名空间 的资源消耗总和的约束
Init Container
应用容器运行前必须先运行完成的一个或多个初始化容器
初始化(init)容器必须在应用容器启动前运行完成。Init 容器的运行顺序:一个初始化(init)容器必须在下一个初始化(init)容器开始前运行完成。
Taint
一个核心对象,由三个必需的属性组成:键,值和效果。污点会阻止在节点或节点组上调度 Pod
污点 和 容忍度 一起工作,以确保不会将 Pod 调度到不适合的节点上。一个或多个污点应用于 节点。节点应该仅能调度那些带着能与污点相匹配容忍度的 pod。
Toleration
容忍度 和 污点 共同作用以确保不会将 Pod 调度在不适合的节点上。在同一 pod 上可以设置一个或者多个容忍度。容忍度表示在匹配节点或节点组上的 污点 调度 pod 是允许的(但不必要)
cordon
禁止调度
drain
驱逐
日志
Kubebuilder
核心概念
Kubebuilder 是一个基于 CRD 来构建 Kubernetes API 的框架,可以使用 CRD 来构建 API、Controller 和 Admission Webhook
提供代码库封装底层的 K8s go-client
Group/Version/Kind/Resource
GVKs&GVRs
GVK = GroupVersionKind,GVR = GroupVersionResource
Group:资源组,也称APIGroup,常见的有core、apps、extensions等
每个 Group 拥有一或多个 Versions,用于接口的演进
Version:资源版本,也称APIVersion,常见的有v1、v1beta1
Kind:资源种类,描述资源的类别,例如pod类别、svc类别等
Resource:资源实例对象,也称为APIResource
SubResource:子资源,部分资源实例会 有子资源,例如Deployment资源会拥有Status子资源
CRD: Custom Resource Definitions,用户自定义资源类型
Scheme
每一组 Controllers 都需要一个 Scheme,提供了 Kinds 与对应 Go types 的映射,也就是说给定 Go type 就知道他的 GVK,给定 GVK 就知道他的 Go type
Manager
负责运行所有的 Controllers;
初始化共享 caches,包含 listAndWatch 功能;
初始化 clients 用于与 Api Server 通信。
Cache
Kubebuilder 的核心组件,负责在 Controller 进程里面根据 Scheme 同步 Api Server 中所有该 Controller 关心 GVKs 的 GVRs,其核心是 GVK -> Informer 的映射,Informer 会负责监听对应 GVK 的 GVRs 的创建/删除/更新操作,以触发 Controller 的 Reconcile 逻辑。
Controller
Kubebuidler 为我们生成的脚手架文件,我们只需要实现 Reconcile 方法即可。
Clients
通过该 Clients 实现的,其中查询功能实际查询是本地的 Cache,写操作直接访问 Api Server
Index
由于 Controller 经常要对 Cache 进行查询,Kubebuilder 提供 Index utility 给 Cache 加索引提升查询效率
Finalizer
在 K8s 中,只要对象 ObjectMeta 里面的 Finalizers 不为空,对该对象的 delete 操作就会转变为 update 操作,具体说就是 update deletionTimestamp 字段,其意义就是告诉 K8s 的 GC“在deletionTimestamp 这个时刻之后,只要 Finalizers 为空,就立马删除掉该对象”
OwnerReference
K8s GC 在删除一个对象时,任何 ownerReference 是该对象的对象都会被清除,与此同时,Kubebuidler 支持所有对象的变更都会触发 Owner 对象 controller 的 Reconcile 方法。
关系图
子主题
工作流程
设计哲学
能使用 go 接口和库,就不使用代码生成
能使用代码生成,就不用使用多于一次的存根初始化
能使用一次存根,就不 fork 和修改 boilerplate
绝不 fork 和修改 boilerplate
创建一个新的工程目录
创建一个或多个资源 API CRD 然后将字段添加到资源
在控制器中实现协调循环(reconcile loop),watch 额外的资源
在集群中运行测试(自动安装 CRD 并自动启动控制器)
更新引导集成测试测试新字段和业务逻辑
使用用户提供的 Dockerfile 构建和发布容器
流程图
开启 shell 自动补全
Bash 脚本补全依赖于bash-completion
命令: kubebuilder completion bash
文件: .bash_profile
. <(kubebuilder completion)
多版本
任意两个版本间转换
控制器运行时会根据 “hub 和 spoke” 模型-我们将一个版本标记为“hub”,而所有其他版本只需定义为与 hub 之间的来源即可
减少了我们所需定义转换函数的数量
与 Webhooks 关系
API 客户端(例如 kubectl 或你的控制器)请求特定的版本的资源,Kubernetes API 服务器需要返回该版本的结果。但是,该版本可能不匹配 API 服务器实际存储的版本
由于转换不是 CRD 内置的,于是 Kubernetes API 服务器通过调用 Webhook 来执行转换
Webhook 通过控制器运行时来执行 hub-and-spoke 的转换
Webhooks
Webhooks 以阻塞方式发送的信息请求
1)准入 webhook (admission webhook)
准入 webhooks 是对 mutating 和 validating 资源在准入 API server 之前的 HTTP 回调。
默认情况下 apiserver 自己没有对 webhook 进行认证
变更准入 Webhook 这种类型的 webhook 会在对象创建或是更新且没有存储前改变操作对象,然后才存储。它可以用于资源请求中的默认字段,比如在 Deployment 中没有被用户制定的字段。它可以用于注入 sidecar 容器。
验证准入 Webhook 这种类型的 webhook 会在对象创建或是更新且没有存储前验证操作对象,然后才存储。它可以有比纯基于 schema 验证更加复杂的验证。比如:交叉字段验证和 pod 镜像白名单。
核心类型的准入 Webhook
由于 kubebuilder 不支持核心类型的 webhook 自动生成,您必须使用 controller-runtime 的库来处理它
2)CRD 转换 webhook
3)授权 webhook (authorization webhook)
Config/Code 生成标记
controller-gen的工具来生成公共的代码和 Kubernetes YAML 文件
这些代码和配置的生成是由 Go 代码中特殊存在的“标记注释”来控制的
标记都是以加号开头的单行注释,后面跟着一个标记名称,而跟随的关于标记的特定配置则是可选的
// +kubebuilder:validation:Optional // +kubebuilder:validation:MaxItems=2 // +kubebuilder:printcolumn:JSONPath=".status.replicas",name=Replicas,type=string
生成 CRD
控制器。控制器通过informer机制,与apiserver交互,按照预先设计的逻辑,对watch资源进行不断地修正、收敛,使资源的状态 不断接近、最终达成指定的目标状态。显而易见的是,CRD的设计核心,同样也是控制器
生成代码 & 制品
make manifests 用来生成 Kubernetes 对象的 YAML 文件,像CustomResourceDefinitions,WebhookConfigurations 和 RBAC roles。
make generate 用来生成代码,像runtime.Object/DeepCopy implementations。
标记语法
Empty (+kubebuilder:validation:Optional):空标记像命令行中的布尔标记位-- 仅仅是指定他们来开启某些行为。
Anonymous (+kubebuilder:validation:MaxItems=2):匿名标记使用单个值作为参数。
Multi-option (+kubebuilder:printcolumn:JSONPath=".status.replicas",name=Replicas,type=string):多选项标记使用一个或多个命名参数。第一个参数与名称之间用冒号隔开,而后面的参数使用逗号隔开。参数的顺序没有关系。有些参数是可选的。
https://book.kubebuilder.io/reference/markers.html
在集成测试中使用 envtest
运行和部署 controller
make install
make run
本地运行 webhooks
make docker-build docker-push IMG=<some-registry>/<project-name>:tag
构建你的镜像
make deploy IMG=<some-registry>/<project-name>:tag
部署到你的集群