基础对象
文档: https://v1-13.docs.kubernetes.io/docs/concepts/
对象 | 说明 |
---|---|
Pod | k8s最小单位 |
Service | 网络、自动发现 |
Volume | 存储 |
Namespace | 命名空间 |
基于基础对象抽象出以下Controllers
对象 | 说明 |
---|---|
ReplicaSet | |
Deployment | 无状态服务 |
StatefulSet | 有状态服务 |
jobs |
Kubernetes Components
master
对象 | 说明 |
---|---|
kube-apiserver | 暴露Kubernetes api |
etcd | etcd 用来存储所有的集群数据 |
kube-scheduler | 调度pods |
kube-controller-manager | 控制Controller |
cloud-controller-manager |
Node
对象 | 说明 |
---|---|
kubelet | 保证容器跑在pod里 并且保证pod的健康,kubelet只管理用Kubernetes创建的容器。 |
kube-proxy | 网络和连接转发 |
Container Runtime |
Addons(插件)
Client Libraries:自己写代码调用Kubernetes API
yaml写法
yaml spec pod
yaml spec deployment
labels
annotations
Namespaces
使用场景: 多团队或者多项目 ,如果只是为了区分服务,使用 labels就可以了。区分开发、测试、预发布、正式环境
通过Resource Quotas分配集群资源
创建和删除namespaces
#查看命名空间
kubectl get namespaces
#设置命名空间
kubectl --namespace=<insert-namespace-name-here> run nginx --image=nginx
kubectl --namespace=<insert-namespace-name-here> get pods
#设置默认使用哪个命名空间
kubectl config set-context $(kubectl config current-context) --namespace=<insert-namespace-name-here>
创建Service的时候会创建对应的dns, dns的入口是..svc.cluster.local,这表示dns只解析本的命名空间请求,这在使用命名空间区分测试、预发布、正式环境很有用。如果你需要跨命名空间访问则需要域名。
并不是所有的Kubernetes 资源都在同一个命名空间里,可以使用以下的命令查看
#在namespace里的资源
kubectl api-resources --namespaced=true
#不在namespace里的资源
kubectl api-resources --namespaced=false
Labels and Selectors
Labels 对object而言的key是唯一的。
一个合理的key应该由前缀和名称组成,使用/分隔。
名称不超过63个字符,可以使用- _ .分隔
前缀是可选的,如果指定了不超过253个字符 并且以/结尾
value不超过63个字符,不要有除- _ .外的特殊字符。
labels搜索
操作 | 说明 | 示例 |
---|---|---|
= | 等于 | environment=production |
== | 等于 | environment==production |
!= | 不等于 | environment!=production |
in | in | environment in (production, qa) |
notin | notin | tier notin (frontend, backend) |
exists | exists只检测key有没有存在 | partition |
noexists | exists只检测key有没有存在 | !partition |
多个合并操作environment=production,tier!=frontend |
#查询environment=production&&tier=frontend
kubectl get pods -l environment=production,tier=frontend
kubectl get pods -l 'environment in (production),tier in (frontend)'
#查询key是environment && environment值不是frontend
kubectl get pods -l 'environment,environment notin (frontend)'
#matchExpressions,key、operator、values ,operator支持In、NotIn、Exists、DoesNotExist
selector:
matchLabels:
component: redis
matchExpressions:
- {key: tier, operator: In, values: [cache]}
- {key: environment, operator: NotIn, values: [dev]}
Annotations
Labels 用来搜索object和object集合,相反 Annotations用来注释让其他工具或者类库可以检索到metadata。
#annotations可以包含labels中不允许的字符
"metadata": {
"annotations": {
"key1" : "value1",
"key2" : "value2"
}
}
Field Selectors
使用resource字段值查询Kubernetes resource 比如:
metadata.name=my-service
metadata.namespace!=default
status.phase=Pending
#使用--field-selector查询pod为Running状态
kubectl get pods --field-selector status.phase=Running
支持的fields因不同的资源类型而不通,所有的资源类型都支持metadata.name和metadata.namespace
但其他的fields就不一定,如果遇到不支持的fields会报错。
操作符支持=、==和!=
kubectl get services --field-selector metadata.namespace!=default
链式操作
kubectl get pods --field-selector=status.phase!=Running,spec.restartPolicy=Always
同时搜索多资源
kubectl get statefulsets,services --field-selector metadata.namespace!=default
推荐做法
有状态服务
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app.kubernetes.io/name: mysql #名称
app.kubernetes.io/instance: wordpress-abcxzy #唯一名称,标识应用程序
app.kubernetes.io/version: "5.7.21" #版本
app.kubernetes.io/component: database #架构中的组件
app.kubernetes.io/part-of: wordpress #哪个程序的一部分
app.kubernetes.io/managed-by: helm #用什么工具管理
无状态服务labels例子:
#Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/name: myservice
app.kubernetes.io/instance: myservice-abcxzy
...
#Service
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/name: myservice
app.kubernetes.io/instance: myservice-abcxzy
...
#注意同一个服务里的 Deployment和Service里的labels是一样
包含数据库的web应用
#Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/name: wordpress
app.kubernetes.io/instance: wordpress-abcxzy
app.kubernetes.io/version: "4.9.4"
app.kubernetes.io/managed-by: helm
app.kubernetes.io/component: server
app.kubernetes.io/part-of: wordpress
...
#Service
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/name: wordpress
app.kubernetes.io/instance: wordpress-abcxzy
app.kubernetes.io/version: "4.9.4"
app.kubernetes.io/managed-by: helm
app.kubernetes.io/component: server
app.kubernetes.io/part-of: wordpress
...
#Mysql StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app.kubernetes.io/name: mysql
app.kubernetes.io/instance: wordpress-abcxzy
app.kubernetes.io/managed-by: helm
app.kubernetes.io/component: database
app.kubernetes.io/part-of: wordpress
app.kubernetes.io/version: "5.7.21"
...
#Mysql Service
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/name: mysql
app.kubernetes.io/instance: wordpress-abcxzy
app.kubernetes.io/managed-by: helm
app.kubernetes.io/component: database
app.kubernetes.io/part-of: wordpress
app.kubernetes.io/version: "5.7.21"
...
Kubernetes Object Management对象管理
管理技巧
Warning: Kubernetes 对象有多种管理技巧,不要混用。
命令行管理
#创建Deployment 对象
kubectl run nginx --image nginx
kubectl create deployment nginx --image nginx
#创建Deployment
kubectl run
#创建Service 对象
kubectl expose
#创建一个可自动扩缩容的controller,比如Deployment
kubectl autoscale
#指定子命令
create <objecttype> [<subtype>] <instancename>
kubectl create service nodeport <myservicename>
#查看帮助
kubectl create service nodeport -h
#水平扩容
kubectl scale
#添加或者删除注释
kubectl annotate
#添加或者删除label
kubectl label
#设置object里的字段
kubectl set <field>
#直接用编辑器打开配置修改
kubectl edit
#使用patch来修改object
kubectl patch
#使用set命令在创建objects 之前修改内容
#kubectl create service -o yaml --dry-run 这个命令不会发送给Kubernetes 而是输出YAML 内容到标准输出,通过管道发送给下一个命令
#kubectl set selector --local -f - -o yaml 从管道接收配置然后写入到YAML
#kubectl create -f - 执行创建命令
kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run | kubectl set selector --local -f - 'environment=qa' -o yaml | kubectl create -f -
#使用--edit在创建前修改objects
kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run > /tmp/srv.yaml
kubectl create --edit -f /tmp/srv.yaml
Imperative object configuration命令式对象配置
#创建objects
kubectl create -f <filename|url>
kubectl create -f nginx.yaml
#更新objects
kubectl replace -f <filename|url>
kubectl replace -f nginx.yaml
replace局限性:
1.从一个配置文件里创建一个object
2.用其他方式修改object 里的某些字段
3.再修改第1步的配置文件然后使用replace 执行,这样第2步修改的内容就会丢失。
所以要注意不要用多种方式管理同一个object 。如果你想用多个文件管理同一个object要使用kubectl apply来管理。
#删除objects
kubectl delete -f <filename|url>
kubectl delete -f nginx.yaml -f redis.yaml
kubectl delete <type>/<name>
kubectl delete deployment/nginx
#查看objects , -o yaml 输出完整的object 配置,使用kubectl get -h查看详细内容
kubectl get -f <filename|url> -o yaml
#查看pod的输出或者错误
kubectl logs
#修改object 但不保存在配置文件里
kubectl create -f <url> --edit
#从命令行管理转成配置管理 imperative object configuration
#1.导出object配置到本地文件
kubectl get <kind>/<name> -o yaml --export > <kind>_<name>.yaml
#2从导出的配置文件中删除status 字段
#3使用replace更新
kubectl replace -f <kind>_<name>.yaml
定义controller selectors和PodTemplate labels
警告:强烈建议不要修改controller的selectors,推荐的方法是使用PodTemplate
selector:
matchLabels:
controller-selector: "extensions/v1beta1/deployment/nginx"
template:
metadata:
labels:
controller-selector: "extensions/v1beta1/deployment/nginx"
Declarative object configuration声明式对象配置
#创建objects
kubectl apply -f <directory>/
#simple_deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
minReadySeconds: 5
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
#查看变动,类似 git diff
kubectl diff -f simple_deployment.yaml
#执行变动
kubectl apply -f simple_deployment.yaml
#打印配置信息
kubectl get -f simple_deployment.yaml -o yaml
#递归查看变动
kubectl diff -R -f configs/
#递归执行变动
kubectl apply -R -f configs/
#使用kubectl scale扩容
kubectl scale deployment/nginx-deployment --replicas=2
#删除objects
#推荐使用imperative command 去删除objects
kubectl delete -f <filename>
#prune 还处于alpha阶段,后续版本可能会修改,并且该命令是查询label来删除的~ 所以可能删除其他东西
kubectl apply -f <directory/> --prune -l <labels>
#查看object配置
kubectl get -f <filename|url> -o yaml
Nodes
Node Status包含以下信息
对象 | 包含内容 |
---|---|
Addresses |
HostName, 可以使用kubelet --hostname-override修改 ExternalIP:集群外IP InternalIP:集群内IP |
Condition |
OutOfDisk :添加新pod空间不足为True,否则为FalseReady :node 正常为True,node不健康为False,node控制器没有接收到node-monitor-grace-period为Unknown,默认接收间隔时间40SMemoryPressure node内存不足为True,否则为FalsePIDPressure node进程太多True,否则为FalseDiskPressure 磁盘大小不足为True,否则为FalseNetworkUnavailable 网络配置不正确为True,否则为Falsenode状态为Unknown或者False时间超过pod-eviction-timeout设定的时间(默认5分钟),出问题的node上的所有pod会被删除。 在某些情况下,node因为网络问题 unreachable,apiserver 无法和kubelet 通讯, 删除pod命令在恢复通讯后发送。 在Kubernetes 1.5版本以前node controller 会强制删除unreachable 的pod,在1.5 版本以后不会强制删除。 |
Capacity |
描述node上的可用资源: CPU,内存以及可以在节点上调度的最大pod数 |
Info |
node的一般信息比如内核版本、Kubernetes 版本、Docker版本、OS 名称。由Kubelet从node收集。 |
管理node
Kubernetes本身不创建node,他通过外部提供者创建,比如本地物理机、虚拟机、谷歌云等。所以Kubernetes创建node的时候其实是创建一个object 来表示node。一旦创建成功,Kubernetes会检查node是否有效。比如如果你用下面的内容创建node:
{
"kind": "Node",
"apiVersion": "v1",
"metadata": {
"name": "10.240.79.157",
"labels": {
"name": "my-first-k8s-node"
}
}
}
Kubernetes创建一个内部node对象,并且通过metadata.name检查node是否健康。如果node有效,所有的服务都会执行。否则集群会忽略它直到node变成有效
Kubernetes 会不断的检查无效的node对象。你必须删除node才能停止检查。
node controller、 kubelet和 kubectl 三个组件和Kubernetes node接口交互。
Node Controller
node controller是Kubernetes的主要组件,管理node的各方面。
node controller 在node的生命周期里扮演多个角色。
1.注册的时候为node 分配CIDR 块(如果CIDR 开启)。
2.node controller云服务商的可用集群列表最新的内部节点列表。当在node跑在云环境中,每当node unhealthy, node controller 会请求云服务商 那个node的VM 是否依旧可用。如果不可用,node controller 会从node列表里把这个node删除。
3.监控nodes的健康。当一个node变成unreachable时 node controller负责把NodeStatus 从NodeReady 变成ConditionUnknown 。如果node在--node-monitor-period 设置的秒数中一直处于ConditionUnknown,那么五分钟后会开始终止该node下的pods。
Kubernetes 1.13版本之前,node的心跳是NodeStatus。1.13版本开始node lease 做为alpha被引入。如果开启了node lease 每个节点会定期更新kube-node-lease namespace 的 Lease,然后NodeStatus 和 node lease 都会被当成node的心跳。Node leases 会经常更新,但是只有NodeStatus有变更或者超过设定的报告时间时(默认1分钟) 才会从node报告给master 。node lease 比NodeStatus轻量,并且让心跳在可扩展性和性能上的代价更小。
在 Kubernetes 1.4,我们更新了 node controller 的逻辑,当大量node节点到master出问题的时候能更好的处理。从1.4开始node controller 在决定剔除一个pod 的时候会先查看集群里的所有node的状态。
大多数情况下,node controller 通过--node-eviction-rate(默认0.1) 参数来限制驱除率,pods 驱除效率不会超过10秒/1node 。
当node在给定的可用区域里变成unhealthy的时候 node驱逐行为会发生变化,同时node controller也会检查区域里的node的unhealthy 百分比。如果node unhealthy 数小于--unhealthy-zone-threshold (默认0.55)驱逐率就会开始减小:如果集群node数小于--large-cluster-size-threshold(默认50),驱逐将会停止,否则驱逐率会降到--secondary-node-eviction-rate(默认0.01)) 每秒。以上的方案会在每个可用的分区里生效的原因是,可用区域可能会从master上分区,而其他的还保留连接。如果你的集群没有跨多服务商的可用区域,那么只会有一个可用区域。
让你node跨可用区域的主要原因是当一个区域不可用的时候可以通过workload 转移到健康的区域。因此当区域里所有node的都unhealthy,node controller会以正常的--node-eviction-rate 驱逐节点。当集群里所有node都unhealthy,node controller假设master的连接出现问题,会停止所有的驱逐直到连接恢复。
从Kubernetes 1.6开始,如果pods 不允许有NoExecute,NodeController 还负责驱逐node中有NoExecute状态的pods,另外作为一个alpha 特性,这个功能默认是关闭的,NodeController 负责添加taints 到相应的问题node ,比如node unreachable 或 not ready。NoExecute详细信息可以查看https://v1-13.docs.kubernetes.io/docs/concepts/configuration/taint-and-toleration/
node自注册
kubelet 带参数--register-node=true(默认情况下),kubelet会尝试使用API server 自注册。
对于自注册,kubelet 还带有以下参数。
对象 | 说明 |
---|---|
--kubeconfig | 验证node的证书路径 |
--cloud-provider | 告知云提供商如何读取metadata |
--register-node | 使用API server 自动注册 |
--register-with-taints | 用给定的taints注册节点,如果register-node是false 这个操作不会执行。 |
--node-ip | nodeIP地址 |
--node-labels | 点击查看 |
--node-status-update-frequency | 指定kubelet 发送node status 到master的频率。 |
当Node authorization mode 和NodeRestriction admission plugin 被启用。kubelets只会修改他自己节点的资源。
手动管理node
kubelet 参数--register-node=false 开启手动创建node。
不管--register-node怎么设置,管理员都可以修改node里的资源。包括设置labels和让node变得不可调度。
node的Labels 和 pods 上的node selectors 可以结合起来控制调度,比如控制一个pod只能在某些node上执行。
控制一个node不接受新的pod调度请求,并且不影响任何已经在该node上的pod,这在重启node之前的准备是很有用的。
#pod变得不可调度
kubectl cordon $NODENAME
node上DaemonSet类型的pods会绕过Kubernetes 的调度器 并且不会遵守不可调度属性。这假设daemons是机器上的即使它耗尽资源要重启。
Node capacity
node容量是node对象的一部分。通常 ,node在创建的时候会注册自己并且报告容量。如果你使用手动管理,就必须在添加node的时候设置node容量。
Kubernetes 调度器确保node上的所有pod有足够的资源。它会检查node上的所有容器加起来不会超过node的容量。包含所有用kubelet启动的容器。但是不包含执行容器(docker、cri等占用的容量) 或所有在容器外的进程。
如果你想明确的为非pod进程保留资源,查看
node api
Master-Node communication
这个文档记录master和Kubernetes 集群的通讯方式。允许用户可以自定义加强网络,这样集群就可以跑在不信任的网络上,或者公网IP上。
Cluster to Master
从集群到master的所有通讯方式都在apiserver终止。在一个典型的deployment,apiserver用来监听客户端的HTTPS 远程连接。 如果允许匿名请求或者 service account tokens ,那么 authorization必须启用。
Nodes必须提供一个公共根证书这样他们就可以使用有效的客户端凭证来安全的连接apiserver 。比如在一个默认的GKE deployment,提供给kubelet的客户端凭证采用客户端证书的形式。查看自动配置kubelet 证书
pods希望能借助service account来安全的连接到apiserver,这样Kubernetes 将会自动注入公共的根证书并且当它实例化的时候有一个有效的bearer token 给pod 。kubernetes service 配置一个虚拟IP地址来转发到apiserver的HTTPS端(通过 kube-proxy转发)。
master组件还通过安全端口与群集通信。
从集群到master连接操作模式默认是安全的,并且可以在不可信网络或者公网上执行。
Master to Cluster
master到集群的通讯主要有两种
1.apiserver 到集群上的每个node上的kubelet 进程。
2.apiserver 通过apiserver的proxy 到任何node、pod、或者service。
apiserver to kubelet
从apiserver到kubelet的连接用于:
1.从pod接收日志
2.附加到执行的pod
3.提供kubelet的端口转发功能
这些连接在kubelet的HTTPS端终止。默认情况下apiserver 不会检查kubelet的服务证书,这样这些连接会遭到中间人攻击(中间人攻击是一种功网络攻击方式),在不可信任或者公网上会不安全。
要校验这次连接,使用--kubelet-certificate-authority参数 提供给apiserver 一个根证书来验证kubelet的证书。
如果无法使用--kubelet-certificate-authority, 用 SSH tunneling来在不可信或者公网上通信apiserver 和kubelet 。
最后Kubelet authentication and/or authorization并且启用才能安全的使用 kubelet API。
apiserver to nodes, pods, and services
apiserver 到nodes, pods, 和 services 默认是通过HTTP连接,因此既未经过身份验证也未加密。可以在连接前面加上https:前缀来安全的链接,但是在HTTPS 端没有验证证书的提供方也没有提供客户端凭证来加密连接,也没有提供完整性保证。这些请求在不可信网络和公网上目前还不安全。
SSH Tunnels
Kubernetes 支持SSH隧道来保护master->集群的通信方式。在这种配置中,apiserver 同步发起SSH 隧道到集群里的每个node并且通过隧道传递 kubelet, node, pod, or service的所有流量。隧道可确保流量不会在node的网络外部暴露。
SSH隧道已经被弃用了,不应该使用它。替代方案还在设计中。
Concepts Underlying the Cloud Controller Manager
Images
Updating Images
更新镜像默认是IfNotPresent ,如果镜像在本地已经存在就会跳过更新。如果要强制更新可以使用下面的方法:
1.imagePullPolicy设置成 Always
2.忽略imagePullPolicy,对要更新的镜像使用 :latest tag
3.忽略imagePullPolicy,对要更新的镜像加上tag
4.启用AlwaysPullImages功能
Building Multi-architecture Images with Manifests
Docker CLI 现在支持在docker manifest (create|push|annotate) 。这些命令现在可以用来build 和push manifests。你可以用docker manifest inspect
来查看manifest。docker文档查看
以上的命令完全依赖Docker CLI。你需要编辑$HOME/.docker/config.json
设置experimental成enabled 或者你可以在使用docker CLI命令的时候设置DOCKER_CLI_EXPERIMENTAL 环境变量为enabled。
请使用Docker 18.06以上的版本,低于这个版本 要嘛有bug 要嘛不支持这个特性。
如果你上传manifests出现问题,只要把$HOME/.docker/manifests
清空就可以刷新了。
对于Kubernetes 我们使用镜像会带上-$(ARCH)后缀。为了兼容,对旧的镜像请生成带后缀的. 好的做法对有manifest的是生成带pause,对需要向后兼容的生成带pause-amd64。
使用私有仓库可能需要秘钥才能读取镜像。私有仓库
Docker把秘钥存储在$HOME/.dockercfg
或者$HOME/.docker/config.json文件里。如果你把秘钥文件放在以下的路径,kubelet 在pull 镜像的时候也会使用。
{--root-dir:-/var/lib/kubelet}/config.json
{cwd of kubelet}/config.json
${HOME}/.docker/config.json
/.docker/config.json
{--root-dir:-/var/lib/kubelet}/.dockercfg
{cwd of kubelet}/.dockercfg
${HOME}/.dockercfg
/.dockercfg
这里有一些推荐步骤来配置你的私有仓库。
- docker login [server] 会在
$HOME/.docker/config.json
里存储登录信息 - 查看
$HOME/.docker/config.json
看是不是你需要的
3.获取node列表
nodes=$(kubectl get nodes -o jsonpath='{range.items[*].metadata}{.name} {end}')
nodes=$(kubectl get nodes -o jsonpath='{range .items[*].status.addresses[?(@.type=="ExternalIP")]}{.address} {end}')
4.把你本地的.docker/config.json复制到其他机器for n in $nodes; do scp ~/.docker/config.json root@$n:/var/lib/kubelet/config.json; done
创建一个pod来验证配置对不对:
kubectl create -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: private-image-test-1
spec:
containers:
- name: uses-private-image
image: $PRIVATE_IMAGE_NAME
imagePullPolicy: Always
command: [ "echo", "SUCCESS" ]
EOF
pod/private-image-test-1 created
使用kubectl logs private-image-test-1
和kubectl describe pods/private-image-test-1 | grep "Failed"
来看pod是否创建成功。
你必须保证所有节点都有相同的.docker/config.json 不然有些节点的pod会成功 有些节点的pod会失败。如果你使用node自动扩容,那么每个实例模板都需要包含.docker/config.json 或者挂载一个包含该文件的驱动器。
私有秘钥一加入 .docker/config.json所有pod有访问私有仓库的权限。
Pre-pulling Images 预加载镜像
在Google Kubernetes Engine环境下在每个节点下 会有一个.dockercfg文件凭证来访问google容器仓库。你就不用使用这种方法管理。
如果你可以控制node配置,那么适合使用这种方法。这种方法不能在GCE或者任何会自动更换node的提供商环境。
默认情况下,kubelet 会尝试从指定的仓库pull镜像。但是如果imagePullPolicy设置成IfNotPresent
或Never
,那么就会优先使用本地镜像。
如果你想使用预加载的镜像,你必须保证集群里的所有node 都有相同的镜像。
Specifying ImagePullSecrets on a Pod pod上指定ImagePullSecrets
kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
secret/myregistrykey created.
如果你是以秘钥文件形式来跑以上命令 可以查看Create a Secret based on existing Docker credentials.这在同时使用多个私有仓库的时候很有用。如果你用的是kubectl create secret docker-registry
那么只会使用单个仓库。
pod拉取镜像只对本命令空间有效,如果其他namespace也要这样执行命令才能拉取对应的镜像。
例子:
apiVersion: v1
kind: Pod
metadata:
name: foo
namespace: awesomeapps
spec:
containers:
- name: foo
image: janedoe/awesomeapp:v1
imagePullSecrets:
- name: myregistrykey
查看在 serviceAccount 如何设置imagePullSecrets 字段.更详细的信息查看 Add ImagePullSecrets to a Service Account
可以和.docker/config.json结合使用。
配置私有镜像有多种方案,以下是建议的方案:
-
集群只跑开源镜像。不必隐藏镜像。
只使用Docker hub 上的镜像,不必做任何配置。
-
集群跑一些私有镜像对公司外部隐藏,但是对集群使用者不隐藏。
2.1 使用私有托管<a href="https://docs.docker.com/registry/"> Docker registry</a>、阿里云dockerhub 等。然后在` .docker/config.json `的每个node做手动配置。 2.2 或者使用自己搭建的私有仓库 2.3 在谷歌的Kubernetes 云服务上使用 Google Container Registry 。 2.4 使用imagePullSecrets
-
集群上使用私有镜像,但是对某些重要镜像进行更严格的权限控制。
3.1 确保<a href="https://v1-13.docs.kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#alwayspullimages">AlwaysPullImages admission controller </a>开启。否则所有的pod可能都有所有镜像的访问权限。 3.2 把敏感信息保存成Secret资源 而不是直接打包在镜像里。
-
一个多用户集群的每个用户需要有自己的私有仓库
4.1 确保<a href="https://v1-13.docs.kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#alwayspullimages">AlwaysPullImages admission controller </a>开启。否则所有的pod可能都有所有镜像的访问权限。 4.2 私有仓库开启需要验证。 4.3 每个用户生成私有仓库访问凭证,保存在secret资源,放入每个用户的namespace里。 4.4 每个用户 使用imagePullSecrets
如果你需要访问多个仓库,你可以为每个仓库创建一个或多个secret 。Kubelet 会把所有的imagePullSecrets合并到一个虚拟的
.docker/config.json
文件里
Container Environment Variables 容器环境变量
Kubernetes Container environment 为容器提供了一些重要的资源
- 文件系统 volumes和镜像的组合。
- 容器自己的信息
- 集群里其他objects 的信息
Container information 容器信息
hostname
容器里pod的hostname
Pod名称和namespace 通过downward API变成可用的环境变量
用户在pod里自定义的变量 在容器里也是可用的 ,和 Docker 镜像里的静态变量一样。
Cluster information 集群信息
当容器创建的时候所有service的列表可以用做容器的环境变量,这些变量和Docker links的语法一样。
FOO_SERVICE_HOST=<the host the service is running on>
FOO_SERVICE_PORT=<the port the service is running on>
Runtime Class
RuntimeClass 是一个alpha 特性,用来选择容器运行时的配置。
Container Lifecycle Hooks 容器生命周期 hook
Containers有两个hook:
-
PostStart
容器创建后执行。 -
PreStop
容器通过API 终止之前 或者在 活动探测失败,抢占,资源争用等 事件之前执行。如果容器已经终止或者处于事情完成状态去调用PreStop hook 会失败。PreStop 是同步阻塞的,所以在容器删除之前肯定会执行完成。该hook没有参数。
Hook handler implementations hook实现
实现hook有两种方法:
-
Exec
在cgroups和容器命名空间中 执行一个指定的脚本 比如 pre-stop.sh。命令消耗的资源由容器计算。 -
HTTP
HTTP请求
Hook handler execution hook执行
Hook 会在pod容器的上下文同步执行。 对于 PostStart
hook 容器ENTRYPOINT 和hook 是异步激活的。如果hook任务执行花太久时间容器就读不到running
状态
对于PreStop
如果hook 在执行期间挂起,那么Pod 的状态就会是Terminating
并且会在terminationGracePeriodSeconds
之后kill。
所以使用hook处理器的时候需要尽可能轻量。但是在有些情况下长时间的任务是有意义的 比如在停止Container之前保存状态。
Hook delivery guarantees hook调用保证
hook有可能会有多次调用。所以实现hook的时候要注意。
HTTPhook只进行单次调用执行, 如果hook接收器或者网络出问题不会重复发送调用。
但是如果kubelet 在重启的过程当中发送hook,那么kubelet 重启完后还会再发送一次hook。
Debugging Hook handlers debug hook
Pod事件日志中是没有Hook 的。如果hook处理失败 会广播一个事件:PostStart
: 广播FailedPostStartHook
事件PreStop
:广播 FailedPreStopHook
事件
使用 kubectl describe pod <pod_name>
就能看到这些事件了
Pod Overview
Pods 有两种使用方法
- pod单个容器运行。 每个pod一个容器是Kubernetes 最常用的方式。这种方式下 Kubernetes 是直接管理pod
- pod多个容器协同运行。Pod将这些容器和存储资源作为单个可管理实体包装在一起
The Distributed System Toolkit: Patterns for Composite Containers
Container Design Patterns
每个pod执行单个应用实例。如果要扩容应用,你要使用多个pod,每个应用一个pod。Kubernetes里这种pod通常指replication。Replicated pod的创建和管理抽象成 Controller。
pod管理多个容器
pod管理的多个容器,这些容器都在同一台的物理机或者虚拟机里。这些容器可以共享资源、相互依赖、互相通讯,协同如何停止运行。
pod管理多个容器是比较新的功能,你应该只有在容器是紧耦合的情况下才使用它。
比如你有一个容器 是共享volume文件web应用,并且有一个单独的容器从远程更新这些文件。
pod对组成他的容器提供两个资源共享方式:Networking(网络)和storage(存储)。
Networking
每个pod会分配一个唯一的IP。Pod 里的每个容器共享网络namespace,包括IP地址、网络端口。Pod 里的容器是通过localhost互相访问的。当pod里的容器和pod外的容器通讯的时候,他们必须协调如何使用共享网络资源。
Storage
pod可以指定一组共享的存储volumes。所有pod里的容器都可以访问这个共享volumes 来共享数据。
Working with Pods
你很少会直接创建单个pod,这是因为pod被设计成 短暂的 一次性的。当创建pod的时候,pod被调度在node上跑,pod就保存在该node上直到进程结束。当node资源不够 或者node失效了,pod会被删除。
重启容器不要与重启pod混淆概念了,pod本身是不会运行的,它提供了容器的持续运行环境,直到被删除为止。
pod不能自我修复。如果pod调度给一个node失败,或者调度自己的操作失败,pod会被删除。pod资源不足或者node处于维护状态的时候会被删除。管理pod实例的操作叫做Controller。虽然pod可以直接使用,但是在Kubernetes 中 用Controller管理pod是更普遍的做法。查看Pods and Controllers来看 Kubernetes 如何使用Controllers来扩容和复原pod。
Pods and Controllers
你可以用Controller 创建和管理多个pod,也可以在集群里复制、回滚、自动恢复pod。比如: 如果node失效,Controller 会自动在其他的node上调度pod代替原来的pod。
例子:
通常,Controllers 使用Pod Template 来创建他负责的pod。
Pod Templates
Pod templates是包含在其他对象的pod的格式,比如 Replication Controllers, Jobs, and DaemonSets等等。
Controllers 使用Pod Templates 来创建实际的pod。以下有个简单的 pod例子:
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
pod templates 类似cookie cutters,当一个cookie 被分割之后它和原来的cookie就没有关系了,已经创建的pods 不会直接受影响。
Pods
- 单个pod里可以有多个容器
- 单个pod里的容器共享IP和端口,并且容器之间通过
localhost
互相发现,并且可以使用进程通讯。 - 不通pod的容器有独立的IP,并且容器之间不能使用IPC 通讯, 而是通过IP通讯。
- 如果node挂了,node会在一定的时间后被删除,node里的pod不会被调度到新的node上,而是直接在新的node上重新生成需要的pod。
pod删除
- 用户发送删除命令给pod,默认删除时间30S。
- API server 里的pod随时间更新直到删除时间到了Pod变成dead
- 此时使用命令行展示pod列表的时候会显示状态是
Terminating
- 当Kubelet监控到Pod 被标记成terminating,它会开始关闭pod里的进程。
4.1 如果pod容器有定义preStop hook,这个时候会被调用。如果preStop hook超过默认删除时间还在执行,会以2S每次的速率来更新第2步的状态。
4.2 容器会发送TERM
信号。不是所有pod的容器都能在同一时间接收到TERM
信号的,所以如果关闭顺序很重要,最好每个容器都有preStop hook。
- pod从service的列表里删除,并且不会再被视为 replication controllers 里的一部分。Pods会从负载均衡里被慢慢删除。
- 如果默认删除时间超,pod上的进程会接收到SIGKILL然后被kill。
- 如果默认删除时间设置成0,Kubelet 会立即从API server上删除pod。
- kubectl delete支持
--grace-period=<seconds>
设置默认删除时间。设置成0会暴力删除pod。 - kubectl version >= 1.5,你可以在--grace-period=0后面加上--force 来执行暴力删除。
pod特权模式
从Kubernetes v1.1开始,pod里的容器可以通过在SecurityContext里设置 privileged 来启用特权模式。如果你想使用linux的功能比如操作网络堆栈、访问设备,这就很有用了。容器中的进程和容器外的进程几乎拥有相同的权限。在特权模式下,将网络和volume插件写成不需要编译到kubelet中的独立pod会更容易。
如果master版本是Kubernetes v1.1及以后的版本,node版本低于1.1, 那么特权pod会被api-server接受,但是会被忽略。pod变成pending 状态。
如果这个时候用户调用kubectl describe pod FooPodName
,用户可以看到pod是pending 的原因。
如果master 版本低于1.1,特权 pod不会被创建。
Pod Lifecycle 生命周期
pod status
字段,包含一个phase
字段
phase字段包含以下值
值 | 说明 |
---|---|
Pending | pod已经在Kubernetes里了但是容器镜像还没有被创建前的状态。 |
Running | pod已经绑定在node上,所有容器已经创建,至少有一个容器在正常执行、准备开始或重启。 |
Succeeded | 所有pod里的容器已经终止,并且不会重启。 |
Failed | 所有pod里的容器已经终止,最少有一个容器终止失败,就是说有容器被非正常退出或者被系统终止。 |
Unknown | 因为某些原因无法获得pod状态,通常是与pod所在的主机通讯出现错误。 |
Completed | pod执行完所有的任务并且没有其他的需要执行的。比如完成Jobs。 |
CrashLoopBackOff | pod里的某个容器非正常退出,并且在重启后还有non-zero错误 |
Pod conditions
PodCondition有6个可能的字段
-
lastProbeTime
pod condition最近一次探查的时间。 -
lastTransitionTime
pod condition 最近一次 状态改变的时间。 -
message
以人类可读的信息说明过渡有关的详细信息 -
reason
最近一次转换的原因 -
status
字符串,可能的值有True
、False
、Unknown
-
type
有以下的值-
PodScheduled
: pod已经被安排给一个node -
Ready
: pod能接收请求并且被加入到负载均衡上 -
Initialized
: pod里的所有容器初始化成功 -
Unschedulable
: 调度器无法调度pod,比如资源不足或者其他限制 -
ContainersReady
: pod里的所有容器都准备好了
-
Container probes容器探测
通过容器上的kubelet定期进行诊断。要执行诊断,kubelet 调用一个容器实现的Handler。以下有三种处理器:
-
ExecAction
: 执行容器内执行的命令。如果命令以状态码0退出,那么诊断就是成功的。 -
TCPSocketAction
: 针对容器IP地址和指定的端口进行TCP检查。如果端口是开启的诊断就是成功的。 -
HTTPGetAction
: 针对容器IP 端口 path进行http检查。status code 大于等于200 并且小于400 ,诊断就是成功的。
每个探测有以下三种结果:
- Success : 容器通过诊断
- Failure :容器诊断失败
- Unknown :诊断本身失败,所以不采取任何操作
kubelet 在执行中的容器中 可以选择执行和应对两种类型的诊断
- livenessProbe : 判断容器是否在执行。如果诊断没有执行,kubelet 会杀掉这个容器,重启就会受restart policy影响.如果容器没有活跃度检测,默认状态是
Success
. - readinessProbe : 判断容器是否准备好接受请求。如果检测是失败的,controller 会把pod的ip 从所有服务上删除。在初始化延迟之前默认状态是
Failure
.如果容器没有提供检测结果,默认状态是Success
.
When should you use liveness or readiness probes? 什么时候使用
- 如果容器中的进程会自己崩溃,不一定需要活跃度检测;kubelet 会根据
restartPolicy
来自动执行正确的操作。 - 如果你不想在检测失败后 容器被杀掉并且重启,可以指定一个活跃度检测并且指定
restartPolicy
值为Always或者OnFailure。 - 如果你只想在检查成功时才开始发送流量给pod,可以指定readiness probes。在这种情况下readiness probes可能和liveness probes 一样,但是readiness probe 情况下 pod只会在readiness probes成功后才开始接收流量。如果你的容器需要加载大量数据、配置文件、或者启动期间的迁移,可以指定readiness probe.
- 如果你想让你的容器在维护阶段自动down, 你可以指定一个readiness probe检查特定端点是否处于准备状态,和liveness 不同。
- 如果你想再pod删除后可以消耗请求,你不必使用readiness probe;在pod删除时,Pod 会自动把它处于unready 状态不会管readiness probe有没有存在。pod会处于unready 状态知道pod里的容器停止。
更多的信息可以查看 Configure Liveness and Readiness Probes
Container States
pod 一旦通过调度器分配给node,kubelet使用container runtime开始创建容器。容器有三种状态:Waiting, Running 和 Terminated.可以使用kubectl describe pod [POD_NAME]
检查容器状态。会列出pod内的每个容器的状态。
- Waiting : 容器的默认状态。容器状态不是Running 或Terminated 那就是Waiting。容器在Waiting 状态的时候还是会执行一些需要的操作,比如pulling images、applying Secrets 等等。这种状态下,会有个reason 来展示更详细的信息。比如:
...
State: Waiting
Reason: ErrImagePull
...
- Running:表示容器正在执行并且没有遇到问题。一旦容器进入
Running
状态, 如果有设置postStart hook 就会执行。还能查询到容器是什么时候进入Running 状态的。
...
State: Running
Started: Wed, 30 Jan 2019 16:46:38 +0530
...
- Terminated: 容器执行完成并且停止执行了。容器执行完成或者容器因为某些原因失败了就会进入这种状态。不会去管原因和退出code,以及容器的开始和结束时间。在容器进入Terminated之前,如果有设置
preStop
hook 那么会执行。
...
State: Terminated
Reason: Completed
Exit Code: 0
Started: Wed, 30 Jan 2019 11:45:26 +0530
Finished: Wed, 30 Jan 2019 11:45:26 +0530
...
Pod readiness gate
这是个还处于beta的功能。
Restart policy
pod定义文件有个restartPolicy
字段 值是Always, OnFailure, 和 Never. 默认值是Always。restartPolicy
会应用到pod里的所有容器。restartPolicy
仅对在相同node上使用kubelet 重启的容器。退出的容器通过kubelet 重启 会有一个指数的延迟重启时间 (10s, 20s, 40s …) 限制在五分钟内,并且时间会在成功重启后的十分钟被重置。一旦pod被绑定到node上后,pod就不会被绑定到其他node上。
Pod lifetime
通常pod不会消失 直到有人摧毁他们。可能是人也可能是控制器。但是有一种例外,当pod有phase
字段并且值是Succeeded或Failed 时间超过master 上设置的terminated-pod-gc-threshold
值,会自动摧毁。
以下有三种可用控制器:
-
Job
类型的pod 是会终止的,比如批量计算。 job只适合restartPolicy
等于OnFailure 或Never的pod。 - ReplicationController, ReplicaSet, or Deployment的pod没有预期的终止时间,比如web servers。ReplicationControllers只适合
restartPolicy
为Always的 pod。 - DaemonSet 的pod需要在每个机器执行, 以为他们提供机器专用服务。
以上的三种控制器都包含一个PodTemplate。建议用这个PodTemplate来创建合适的控制器并用它来创建pod,而不是你自己来创建pod。这是因为单独的pod不能弹性处理机器故障,但是controllers 可以。
如果node死掉 或者集群里的其他服务无法连接到该node,Kubernetes 将会在这个node上应用phase
设置的方法到所有的pod上。
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
-
pod执行中并且有一个容器。pod在执行成功后退出。
- 记录完成的事件
-
如果restartPolicy设置成:
- Always: 重启容器,pod
phase
保持执行。 - OnFailure: pod
phase
变成Succeeded。 - Never: pod
phase
变成Succeeded。
- Always: 重启容器,pod
-
pod执行中并且有一个容器。pod在执行失败后退出。
- 记录失败事件。
-
如果restartPolicy设置成:
- Always: 重启容器,pod
phase
保持执行。 - OnFailure: pod
phase
变成Succeeded。 - Never: pod
phase
变成Succeeded。
- Always: 重启容器,pod
-
pod执行中并且有两个容器。其中一个容器执行失败并退出
- 记录失败事件。
-
如果restartPolicy设置成:
- Always: 重启容器,pod
phase
保持执行。 - OnFailure: 重启容器,pod
phase
保持执行。 - Never:不会重启容器,pod
phase
保持执行。
- Always: 重启容器,pod
-
如果容器1没有执行并且容器2退出;
- 记录失败事件。
-
如果restartPolicy设置成:
- Always: 重启容器,pod
phase
保持执行。 - OnFailure: 重启容器,pod
phase
保持执行。 - Never:pod
phase
变成Failed
- Always: 重启容器,pod
-
pod执行并且有一个容器,容器内存不足。
- 容器停止
- 记录OOM事件
-
如果restartPolicy设置成:
- Always: 重启容器,pod
phase
保持执行。 - OnFailure: 重启容器,pod
phase
保持执行。 - Never:记录失败事件,pod
phase
变成Failed
- Always: 重启容器,pod
-
pod执行中,磁盘挂了
- 杀掉所有容器
- 记录适当的事件
- pod
phase
变成Failed - 如果是通过controller执行的,pod会在其他地方重新创建。
-
pod执行中,node被分割出来
- Node 控制器等待超时
- node控制器把Pod
phase
设置成Failed - 如果是通过controller执行的,pod会在其他地方重新创建。
Init Containers 初始化容器
Understanding Init Containers
pod可以有多个运行应用的容器,并且也可以有一个或多个在应用容器启动好之间的初始化容器。
Init Containers 和普通的容器的区别:
- 他们总会执行完
- 有依赖性,一个跑完另外一个才会接着跑
如果pod里的Init Containers 失败了,Kubernetes 会反复重启pod直到Init Container成功。如果pod的restartPolicy
设置成Never
,pod就不会重启。
要指定一个容器为 Init Container,添加initContainers
字段到PodSpec。 init containers的状态从.status.initContainerStatuses
返回。
和正常的容器的区别;
Init Containers 支持所有的应用容器的字段,包括资源限制、volumes和安全设置。但是Init Container的资源请求和限制有些微不同, Init Containers 不支持 readiness probes,因为他们必须在pod变成ready之前执行完。
如果一个pod指定了多个 Init Containers,这些容器会按顺序依次执行。一个执行成功才会执行下一个。如果所有的Init Containers全部执行完,Kubernetes 初始化pod 并且正常执行应用容器。
What can Init Containers be used for? Init Containers有什么用?
因为Init Containers具有来自app Containers的独立镜像,它们对于启动相关代码有一些优势:
- 他们可以包含和执行出于安全原因没有包含在应用容器镜像上的实用程序。
- 他们可以包含或自定义在应用镜像里没有的程序和code。比如在启动过程中有一个工具不必从其他镜像上
FROM
,比如sed
,awk
,python
, ordig
. - 应用程序镜像 builder 和 deployer 可以独立的工作而不必一起构建单独的app镜像。
- 他们使用Linux namespaces所以他们和应用容器有不同的文件系统视图,他们可以给进行加密让应用容器无法访问。
- 他们在任何应用容器启动之前会执行完成。而应用容器是并行执行的,所以 Init Containers 提供一个简单的方法来阻塞或延迟应用程序容器启动。
例子
以下有一些如何使用Init Containers的例子:
- 使用shell命令等待服务创建:
for i in {1..100}; do sleep 1; if dig myservice; then exit 0; fi; done; exit 1 -
使用命令从远程服务器注册pod
curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
- 应用容器启动前等待一段时间 比如
sleep 60
- 克隆一个git仓库到volume
- 将值放入配置文件中并且对主要应用容器执行一个模板工具来动态生成配置文件。比如把POD_IP 值放入配置文件并且使用Jinja生成主要应用配置文件。
Init Containers in use
以下yaml 文件基于Kubernetes 1.5,有两个 Init Containers。第一个等待myservice
,第二个等待 mydb
。一旦两个容器都完成,pod就会开始。
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
annotations:
pod.beta.kubernetes.io/init-containers: '[
{
"name": "init-myservice",
"image": "busybox:1.28",
"command": ["sh", "-c", "until nslookup myservice; do echo waiting for myservice; sleep 2; done;"]
},
{
"name": "init-mydb",
"image": "busybox:1.28",
"command": ["sh", "-c", "until nslookup mydb; do echo waiting for mydb; sleep 2; done;"]
}
]'
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
以下是基于Kubernetes 1.6的新语法,久的通过annotation 来定义的语法只支持到1.6 and 1.7. 新的语法必须在1.8 及以上的版本使用。在新语法中我们把Init Containers的定义移到spec
:
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
1.5的语法在1.6依旧能使用,但是我们推荐使用1.6的语法。在1.6中 Init Containers变成API里的一个字段。annotation 只支持到1.6和1.7 ,但是1.8及以上版本不支持。以下是mydb
和myservice
的services:
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
kind: Service
apiVersion: v1
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
这个pod可以用以下的命令启动和debug:
kubectl create -f myapp.yaml
kubectl get -f myapp.yaml
kubectl describe -f myapp.yaml
kubectl logs myapp-pod -c init-myservice # Inspect the first init container
kubectl logs myapp-pod -c init-mydb # Inspect the second init container
一旦我们启动mydb
和myservice
服务,我们可以看到 Init Containers执行完毕,myapp-pod
创建完成:
kubectl create -f services.yaml
kubectl get -f myapp.yaml
这个例子很简单,但是可以在你写自己的Init Containers的时候给你一些灵感
详细行为
在pod启动期间, Init Containers在network 和volumes 初始化后按顺便执行。每个Init Containers必须在成功退出后才会接着下一个执行。如果一个Init Containers执行失败,将会按照pod 设置的restartPolicy
处理。如果restartPolicy
设置成Always, Init Containers 在RestartPolicy
OnFailure的时候使用。
pod只有在所有的 Init Containers成功后才会变成Ready
。Init Container 的端口不会再一个服务下聚合。pod在初始化中的状态是Pending
但是Initializing
值会设置成true。
如果pod重启,所有的Init Containers 都会重新执行。
修改 Init Container spec 仅限于容器镜像字段。修改Init Container镜像字段相当于重启pod。
因为 Init Containers 可以重启、重试、重新执行,Init Container code必须幂等。特别是code会写到EmptyDirs
的文件,这个文件在写入前必须已经存在。
Init Containers有应用容器的所有字段。但是Kubernetes 禁止使用readinessProbe
因为 Init Containers不能定义与完成不同的readiness状态。这是强制的。
在pod上使用activeDeadlineSeconds
和在容器上使用livenessProbe
来防止Init Containers 永远失败。
活跃的deadline 包含 Init Containers.
一个pod上的应用容器名称和Init Container必须唯一。如果有同名的容器 验证的时候就会抛出错误。
Resources
Init Containers适用以下的规则:
- 在所有的Init Containers定义的任何特定资源中的最高请求或者限制 是
effective init request/limit
-
对于一个资源 Pod的
effective init request/limit
最高 :- 一个资源的所有的应用容器request/limit 总和。
- 资源的有效初始化request/limit
- 调度基于有效请求/限制完成,这意味着 Init Containers在pod的整个生命周期中都能保留那么没使用资源来初始化。
- Pod的有效QoS层的QoS层是Init Containers和app容器的QoS层。
基于有效的Pod请求和限制来对应用配额和限制
pod的cgroups 级别基于有效的pod请求和限制,和scheduler一样。
Pod restart reasons
pod重启,导致Init Containers重新执行,是以下的原因:
- 用户更新PodSpec 导致 Init Container 镜像修改。应用容器镜像修改只会重启应用容器。
- Pod 基础架构容器重新启动。这种情况很罕见 并且必须由具有root访问权限的人来完成。
- 当
restartPolicy
设置成Always的时候 pod里的所有容器会终止,强制重启, Init Container 由于垃圾收集,完成记录已丢失。
Support and compatibility支持和兼容性
Apiserver 1.6.0及以上的版本支持 Init Containers使用.spec.initContainers
字段。之前的版本使用annotations字段。Kubelets 1.3.0及以上的版本.spec.initContainers会映射到annotations ,所以1.6 apiserver对于已经创建的pod可以安全的回滚到1.5.x 并且不会丢失 Init Container功能
Apiserver 和Kubelet 1.8.0及以上版本,对annotations 的支持已经删除了,并且把annotations 转到.spec.initContainers。
这个beta功能将在 1.6退出,在PodSpec 里 Init Containers可以和应用容器数组并列。beta下的annotation 会覆盖PodSpec 字段的值,但是他们已经在 1.6 、 1.7. 、 1.8 被废弃了。annotations 不会支持太久,需要转化成PodSpec 的字段。
Pod Preset
在pod创建的时候把某些信息注入到pod里。这些信息包括 secrets, volumes, volume mounts, and environment variables.
Understanding Pod Presets
Pod Preset 是一个在pod创建时注入额外需要的信息的 API 资源。使用 label selectors
来指定给哪个pod应用 Pod Preset 。
使用 Pod Preset 允许 pod template作者不必明确的为每个pod提供所有的信息。用这种方式,使用特定服务的pod模板的作者不需要知道有关该服务的所有详细信息。查看design proposal for PodPreset了解更多信息
How It Works
Kubernetes 提供一个入场管理器(PodPreset),当启用的时候,将Pod Presets加入pod 创建请求。当一个pod创建请求发生的时候,系统会做以下操作:
1.检索可供使用的所有PodPresets
2.pod要创建的时候检查有没有符合PodPreset的 label selectors.
3.pod要创建的时候PodPreset尝试合并各个资源到pod里
4.出错,在pod上抛出一个记录合并错误的事件,并且pod的创建不会从PodPreset注入任何资源。
5.Annotate 通过PodPreset修改的。annotation 的格式是podpreset.admission.kubernetes.io/podpreset-<pod-preset name>: "<resource version>"
每个pod都可以有一个或多个 Pod Presets; 每个PodPreset 都可以应用到多个pod里。当PodPreset使用到一个或多个Pods,Kubernetes 会修改Pod Spec。为了修改Env
、EnvFrom
、VolumeMounts
,Kubernetes 修改Pod里的所有容器的spec 。为了修改Volume
,Kubernetes修改 Pod Spec. 备注:合适的时候Pod Preset会修改pod的.spec.containers字段。Pod Preset中的资源定义不会应用于
initContainers`字段。
Disable Pod Preset for a Specific Pod
可能有一些情况你希望pod不会被任何 Pod Preset及变种修改。可以添加podpreset.admission.kubernetes.io/exclude: "true"
到pod Spec 的 annotation 。
Enable Pod Preset
为了在你的集群里使用Pod Presets 你需要保证以下:
1.你已经启用settings.k8s.io/v1alpha1/podpreset
API.可以在api server的--runtime-config
选项中包含settings.k8s.io/v1alpha1=true
.如果使用的是minikube可以在集群开始的时候添加--extra-config=apiserver.runtime-config=settings.k8s.io/v1alpha1=true
.
2.启用PodPreset入场控制器。可以在api server的--enable-admission-plugins选项值里包含PodPreset。如果使用的是minikube 可以在集群开始的时候添加--extra-config=apiserver.enable-admission-plugins=Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,PodPreset
3.在你使用的namespace 中通过创建PodPreset对象定义。
Disruptions 中断
这个指南是给想创建高可用应用,从而需要知道pod会有哪几种中断事件的人看的。也是给想使用更新和自动扩容的集群管理员看的。
Voluntary and Involuntary Disruptions
pod不会消失,除非有人摧毁他们或者遇到不可避免的硬件错误或系统错误。
我们把这些不可避免的事件叫非自愿中断
,比如:
- node的物理机硬件故障
- 集群管理员不小心删除了虚拟实例。
- 云服务商或者管理程序故障让虚拟机消失。
- 内核出错
- node由于网络问题从集群中消失
- node由于自愿不足 把pod驱逐出去。
除了资源不足,其他的故障大部分用户都很熟悉,他们不是因为Kubernetes出错的。
我们把其他的情况叫自主性中断,包含由应用程序拥有者发起和集群管理发起。通常应用程序拥有者操作包括:
- 删除管理的deployment 或其他控制器。
- 更新部署的pod template 导致重启。
- 直接删除pod(比如意外).
集群管理员的操作包括:
- 更新或修复导致耗尽node
- 扩容导致耗尽node
- 从node删除pod,好让其他东西使用。
这些操作有可能是集群管理员直接做的,或者集群管理员通过自动化执行做的,或者你的云主机提供商做的。
问你的集群管理员或者你的云提供商或者文档来确定你的集群里是否有启动自主性中断。 如果没有启用你可以跳过创建Pod Disruption Budgets。 警告:不是所有的自主性中断都受Pod Disruption Budgets限制的,比如删除deployments 或者pod绕过Pod Disruption Budgets
Dealing with Disruptions 处理中断
以下是一些减少自主性中断的方法:
- 确保你pod有足够的资源。
- 如果你需要高可用,请复制你的应用。
- 为了更高的可用性,可以横向扩展或跨区域(跨区域集群)部署应用
自主中断的频率各不相同。在一个基础的Kubernetes 集群,不会有自主中断。即使你的集群管理员或主机提供商可能会执行一些额外的会导致中断的服务。比如回滚node软件更新可能导致中断。一些集群自动扩容的实现也可能导致自主中断 来整理和压缩node。
Kubernetes 提供一些特性来帮助在频繁的自主中断中执行高可用的应用。我们把这些特性叫Disruption Budgets
。
How Disruption Budgets Work
应用所有者可以对每个应用创建一个PodDisruptionBudget
(PDB) 对象。PDB会限制复制的应用同时主动中止的数量。比如一个基于数量的应用将会确保执行中的复制数量不会低于需要的数量。web前端可能想确保加载的复制服务不会定于某个百分比。
集群管理者和主机供应商应该调用Eviction API 而不是直接删除pod或deployments来使用Pod Disruption Budgets的工具。比如kubectl drain
命令和Kubernetes-on-GCE 更新脚本(cluster/gce/upgrade.sh
)。
当集群管理者想移除一个node 他们可以使用kubectl drain
命令。这个工具尝试把机器上的所有pods 驱逐出去。驱逐请求可能会被暂时拒绝,这个工具会定时重试所有失败的请求直到所有的pod都终止,或者时间超时了。
PDB 指定应用可以复制的数量。比如一个Deployment 有一个 .spec.replicas: 5
,他支持同时有5个pod。如果这个时候PDB 只允许同时存在4个pod,那么Eviction API 会主动中断一个。
使用一个 label selector来指定一组pod,和使用应用控制器一样 (deployment, stateful-set, etc).
pods的数量是通过pod控制器的 .spec.replicas
来计算的。控制器使用.metadata.ownerReferences
来发现这个对象。
PDB 无法防止非主动中断,但是他们可以计算非主动中断数量。
pod被删除或者由于滚动升级变得不可用 会计入非正常中断数量,但是deployment 和 stateful-set控制器当发生滚动升级的时候不会受PDB 限制,如何处理在应用 更新期间发生的故障 会定义在控制器的spec。
当pod使用eviction API 驱逐一个pod的时候,它会优雅的终止
PDB Example
一个包含3个node的集群,node-1
到node-3
,这个集群上跑着一些应用,其中有一个node 有3个复制叫pod-a
, pod-b
, and pod-c
。其余无关的pod没有PDB叫pod-x
,pod的部署情况如下:
node-1 | node-2 | node-3 |
---|---|---|
pod-a available | pod-b available | pod-c available |
pod-x available |
3个pod都是deployment的一部分,他们都有一个 PDB ,不管什么时候 3个pod中的2个都会是可用的。
比如,假设集群管理者想重启机器升级一个新的内核版本来修复一个内核bug。集群管理者首先尝试使用kubectl drain
命令来移除node-1。这个工具会尝试驱逐pod-a
和 pod-x
。这操作一成功 所有的pod都会同时进入terminating
状态。会变成以下的状态:
node-1 | node-2 | node-3 |
---|---|---|
pod-a terminating | pod-b available | pod-c available |
pod-x terminating |
deployment 注意到其中一个pod是terminating,所以他会创建一个pod-d
来替换他。由于node-1被*,它会在其他node上创建,同样也会创建pod-y
来替代pod-x
。
(备注: 对于StatefulSet,pod-a的名字将会被称为像 pod-1 ,必须在被替换前完整的终止,他还是会叫pod-1 但是有一个不同的UID。)
现在集群处于以下的状态:
node-1 | node-2 | node-3 |
---|---|---|
pod-a terminating | pod-b available | pod-c available |
pod-x terminating | pod-d starting | pod-y |
一段时间后,pod会被终止,集群就变成下面这种:
node-1 | node-2 | node-3 |
---|---|---|
pod-b available | pod-c available | |
pod-d starting | pod-y |
现在,集群管理者尝试移除node-2
。移除命令奖按照某种顺序来尝试移除2个pods。先是pod-b
然后是pod-d
。他会成功移除pod-b,但是当他想移除pod-d
的时候 会被拒绝,因为只剩下一个可供部署的pod。
deployment 创建一个替换pod-b
的pod 叫 pod-e
。因为没有足够的资源调度pod-e
移除命令将会阻塞。集群的状态就是下面的样子:
node-1 drained | node-2 | node-3 | no node |
---|---|---|---|
无 | pod-b available | pod-c available | pod-e pending |
无 | pod-d available | pod-y |
现在,集群管理员必须添加一个node来继续升级操作。
你可以看到Kubernetes 的中断时间变化,根据:
- 需要多少应用复制
- 优雅的关系一个实例需要多少时间
- 启动一个新的实例需要多少时间
- controller的类型
- 集群的资源大小
(Separating Cluster Owner and Application Owner Roles)[https://v1-13.docs.kubernetes.io/docs/concepts/workloads/pods/disruptions/#separating-cluster-owner-and-application-owner-roles]
这对思考集群管理员和应用程序拥有者的角色分离很有用。这种职责的分离在以下的场景里是有很有意义的:
- 多个应用组共享一个Kubernetes 集群,角色可以专业管理自己。
- 第三方工具或服务自动化管理集群
PDB支持在两个角色之间提供一个借口来分离角色。
如果你不需要职责分离,你可能不需要使用PDB。
How to perform Disruptive Actions on your Cluster如何执行中断操作
如果你是一个集群管理员,你需要中断你集群里的所有node,比如node升级或者系统软件升级,以下有一些选项:
- 在升级期间停机。
-
故障转移到其他完整的集群副本。
- 不会停机,但是复制node 和人工协调集群的成本都很高。
-
编写容错中断应用程序并使用PDB
- 不会停机。
- 复制成本最小
- 允许更多的集群自动化管理
- 编写这样的程序很麻烦,但是编写自主性中断在很大程序上和支持自动扩容和非自主性中断 的工作是重叠的。
# ReplicaSet
ReplicaSet是为了维持任何时候都能有一组稳定的pod副本。因此,它经常用来保证相同的pod的可用数量。
How a ReplicaSet works
ReplicaSet的字段,包含一个selector 用来搜索pod、一个replicas 用来声明需要保持的pod数量、和一个pod template 来声明新的pod需要的数据 用来确定副本需要的数量。然后ReplicaSet 会通过创建和删除pod来满足它读取到的预期的数量。当ReplicaSet 需要创建一个新的pod,他会使用它的Pod template来创建。
ReplicaSet 通过Pods的metadata.ownerReferences字段 来和他的pod连接,指定它当前拥有的资源数量。通过ReplicaSet 获取的pod在它们的ownerReferences存有它们自己的标识信息。通过这样的连接让ReplicaSet 知道他正在维护的pod的状态并相应的进行计划。
ReplicaSet 通过pod的selector来识别新的pod。如果一个pod 没有OwnerReference
字段或者OwnerReference 不是一个controller,但是这个pod被ReplicaSet的selector搜索到,他会立即被这个ReplicaSet获取到。
When to use a ReplicaSet
ReplicaSet 保证pod副本执行的数量。Deployment 是一个管理ReplicaSets 和提供更多有用功能的更高层次的概念.因此,我们不会直接使用ReplicaSets 而是使用Deployments ,除非您需要自定义更新编排或者根本不需要更新。
这意味着你可能永远不需要操作ReplicaSet对象:转而使用Deployment来代替,以下有例子:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
labels:
app: guestbook
tier: frontend
spec:
# modify replicas according to your case
replicas: 3
selector:
matchLabels:
tier: frontend
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
以上的配置写入文件frontend.yaml然后在k8s集群里执行: kubectl create -f frontend.yaml
, 然后使用kubectl get rs
查看ReplicaSets 。也可以使用kubectl describe rs/frontend
来查看状态. kubectl get pods POD_NAME -o yaml
查看pod的ownerReferences是不是ReplicaSet。
Non-Template Pod acquisitions
你可以只创建裸pod 这是没有问题的。这也是非常推荐的,确保裸pod没有匹配ReplicaSets选择器的labels。这是因为ReplicaSet 不限制Pod指定它的模板,它可以按照前面部分中指定的方式获取其他Pod。
采取前面的frontend ReplicaSet 例子,pod使用以下的配置指定(文件名pod-rs.yaml):
apiVersion: v1
kind: Pod
metadata:
name: pod1
labels:
tier: frontend
spec:
containers:
- name: hello1
image: gcr.io/google-samples/hello-app:2.0
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
labels:
tier: frontend
spec:
containers:
- name: hello2
image: gcr.io/google-samples/hello-app:1.0
就像那些没有Controller 并且匹配frontend ReplicaSet 选择器,他们会立即被获取到。
假设你要在frontend ReplicaSet部署好后创建pod,并且创建足够的初始化的pod副本数来满足replica 配置:
kubectl create -f pod-rs.yaml
新的pod会被ReplicaSet获取到,然后会立即被中止,因为因为ReplicaSet将超过其所需的数量。然后获取pod kubectl get Pods
.
如果你先创建pod: kubectl create -f pod-rs.yaml
,然后再创建ReplicaSet:kubectl create -f frontend.yaml
,你将会看到ReplicaSet 已经获取到pod并且创建新pods直到满足spec指定的数量,查看pod:kubectl get Pods
。
Writing a ReplicaSet manifest
和其他的Kubernetes API 对象一样,ReplicaSet 需要apiVersion
、kind
、metadata
字段。对于ReplicaSets,kind
字段总是ReplicaSet
. Kubernetes 1.9,apps/v1 API版本 是当前版本,默认是启用的。API版本 apps/v1beta2已经被废弃了。参考frontend.yaml
例子的第一行.
ReplicaSet 也需要一个 .spec 。
Pod Template
.spec.template是一个有labels 的 pod template ,在frontend.yaml例子中我们有一个label:tier: frontend
.注意不要被其他controllers的selectors 给重复了,免得他们尝试获取这个pod。
对于模板的 restart policy 字段,.spec.template.spec.restartPolicy
,只允许值是Always
.
Pod Selector
.spec.selector
是一个 label selector 。如之前所述,这些labels 用来鉴定潜在的能获取的pod。在我们的frontend.yaml
例子中,selector 是
matchLabels:
tier: frontend
在ReplicaSet,.spec.template.metadata.labels
必须匹配spec.selector
, 否则就会被API拒绝。 注意:对于2个ReplicaSets ,指定一样的.spec.selector 但是不一样的.spec.template.metadata.labels 和不一样的.spec.template.spec,每个ReplicaSet 都会忽略其他ReplicaSet创建的Pods
Replicas
你可以通过指定.spec.replicas
来指定同时有多少pod执行。ReplicaSet 将会 创建/删除 pod来匹配这个数量.如果没有指定.spec.replicas
,默认是1。
Working with ReplicaSets
Deleting a ReplicaSet and its Pods
使用kubectl delete
来删除ReplicaSet和它所有的pods。 Garbage collector
会自动删除所有依赖的pod。
当使用REST API或client-go
类库,你必须在 -d 选项设置propagationPolicy
成Background
或Foreground
:
kubectl proxy --port=8080
curl -X DELETE 'localhost:8080/apis/extensions/v1beta1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
> -H "Content-Type: application/json"
Deleting just a ReplicaSet
你可以使用 kubectl delete
带上 --cascade=false
参数来删除一个ReplicaSet ,这样不会影响它的pod。当使用REST API或client-go
类库,你必须设置propagationPolicy 为Orphan,例子:
kubectl proxy --port=8080
curl -X DELETE 'localhost:8080/apis/extensions/v1beta1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
> -H "Content-Type: application/json"
一旦 原始的删除了,你可以创建一个新的ReplicaSet 来代替它。一旦旧的和新的.spec.selector
一样,新的ReplicaSet 就会获取到旧的pod。但是新的ReplicaSet 不会做任何事情来让旧的pod匹配它,和pod template 不一样。要把pod更新 新的spec ,使用rolling update
Isolating Pods from a ReplicaSet 隔离pod
你可以通过修改labels从ReplicaSet 移除pod。这个技巧也可以用来 从debug、data recovery 等删除pod。pod用这种方式删除 将会自动替换(假设replicas 数量没有修改).
Scaling a ReplicaSet
ReplicaSet用 .spec.replicas
字段可以很容易的进行扩容和缩容。ReplicaSet controller 确保 匹配label selector的pod的可用和可操作数量。
ReplicaSet as a Horizontal Pod Autoscaler Target
ReplicaSet 也可以是 Horizontal Pod Autoscalers (HPA)的指标。那样 ReplicaSet 会被HPA自动扩缩容.以下是例子:
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: frontend-scaler
spec:
scaleTargetRef:
kind: ReplicaSet
name: frontend
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 50
保存成hpa-rs.yaml
,然后执行。应该就会创建一个基于cpu使用量自动扩缩容的Pods副本。kubectl create -f hpa-rs.yaml
另外你也可以用kubectl autoscale
来完成同样的结果: kubectl autoscale rs frontend --max=10
Alternatives to ReplicaSet 替代品
Deployment (recommended)
Deployment
可以有自己的ReplicaSets 并且通过声明来更新ReplicaSets 和Pods,滚动升级服务端。ReplicaSets 可以独立的使用,今天我们主要通过Deployments 来协调pod的创建、删除和更新。当你使用Deployments 你不用担心ReplicaSets 的管理。Deployments 会自己管理ReplicaSets。因此,当你想用ReplicaSets的时候推荐你使用Deployments 。
Bare Pods
与直接创建pod不用,ReplicaSet 会替换 因任何原因被删除或中止的pod。比如node故障或者node维护的时候故障,比如内核更新。因此我们推荐你使用ReplicaSet ,即使你的应用只需要一个pod。 它与supervisor类似,只有它监控多node里的多pod,而不是只监控单个node。ReplicaSet 委托node上的一些代理重启本地容器(比如Kubelet or Docker)。
Job
当想要pod自己会终止,使用job来代替ReplicaSet。
DaemonSet
使用DaemonSet
来代替ReplicaSet ,好让pod提供机器级别的功能, 比如机器监控或者机器日志。这些pod都有一些与机器有关的生命周期:pod需要在其他pod开始前执行,当机器准备重启/关闭,pod可以安全的中断。
ReplicationController
ReplicaSets 的后任是 ReplicationControllers
。这两个服务都有相同的目的,并且表现相似,除了ReplicationController 不支持基于selector ,因此首选ReplicaSets。
ReplicationController
注意:Deployment配置ReplicaSet是现在推荐的创建副本的做法。
ReplicationController 保证在任何时候都有指定数量pod副本在执行。换一种说法,ReplicationController 确保pod或者一套同样性质的pod用于都是可用的。
How a ReplicationController Works
如果pod太多了,ReplicationController 会中断额外的pod。如果pod太少了,ReplicationController 会启动更多的pod。与手动创建pod不同,如果pod故障、被删除、或者被中止,会通过ReplicationController 自动替换。比如,你的pod在破坏性操作后在node上重新创建 比如内核更新。因此,你必须使用ReplicationController 即使你的应用只需要单个pod。ReplicationController 类似supervisor,但是不是监督单个节点上的单个进程,ReplicationController 监控多node上的多个pod.
ReplicationController 在讨论的时候经常简称成“rc” 或 “rcs”, 这也是kubectl 命令里的快捷方式.
简单使用是创建单个ReplicationController 对象来稳定的无期限的 执行pod实例。复杂的使用是运行一些相同的副本服务,比如web servers。
Running an example ReplicationController
这个ReplicationController 例子配置了三个 nginx web server 副本。
apiVersion: v1
kind: ReplicationController
metadata:
name: nginx
spec:
replicas: 3
selector:
app: nginx
template:
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
执行以下kubectl create -f replication.yaml
,查看状态 kubectl describe replicationcontrollers/nginx
如果pod没有在执行,那么可能在做拉取镜像等操作。需要等一会再看看。
以机器可读的形式列出属于ReplicationController 的所有pod,可以使用:
pods=$(kubectl get pods --selector=app=nginx --output=jsonpath={.items..metadata.name})
echo $pods
这里的selector和ReplicationController 的选择器一样,查看kubectl describe
的输出,在replication.yaml里以不同的方式。--output=jsonpath
是可选的,用来获取在返回列表里的每个pod的名字。
Writing a ReplicationController Spec
与所有其他Kubernetes配置一样,ReplicationController 需要apiVersion
、kind
、metadata
字段。有关使用配置文件的一般信息,查看object management
ReplicationController 也需要.spec
Pod Template
.spec.template
只需要一个字段 .spec
. .spec.template
是一个(pod template)[https://v1-13.docs.kubernetes.io/docs/concepts/workloads/pods/pod-overview/#pod-templates],他和pod有相同的语法,除了他嵌套并且没有apiVersion
和kind
.
除了pod的必填字段,ReplicationController 的pod template 必须指定合适的labels和合适的restart policy。对于labels,确保没有和其他controllers的labels重复。 .spec.template.spec.restartPolicy
只允许是 Always
并且默认也是 Always
。
对于本地容器重启,ReplicationControllers 委托给node上的代理,比如 Kubelet or Docker.
(Labels on the ReplicationController)[https://v1-13.docs.kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/#labels-on-the-replicationcontroller]
ReplicationController 本身也有labels(.metadata.labels
)。通常你可以设置得和.spec.template.metadata.labels
一样;如果.metadata.labels
没有指定,默认是.spec.template.metadata.labels
。但是也允许不同,并且.metadata.labels
不会影响ReplicationController的行为.
Pod Selector
.spec.selector
字段是一个 label selector
。ReplicationController 管理所有被selector匹配labels 的pod。 他不会区分pod是自己删除/创建还是是由其他人创建/删除的还是由进程创建/删除的。允许ReplicationController 更换而不影响在执行中的pod。
如果指定了.spec.template.metadata.labels
必须和.spec.selector
一样,否则会被API拒绝。如果.spec.selector
没有指定,默认和.spec.template.metadata.labels
一样.
此外,您通常不应使用其他的ReplicationController或其他的controller 来创建任何与此selector匹配的pod。如果你这样做,ReplicationController 会认为它创建了其他的pod,Kubernetes不会阻止你这样做。
如果你最终有多个重叠selectors 的controllers,你必须自己管理删除,看下面:
Multiple Replicas
你可以通过设置.spec.replicas
来指定同时有多少pod在运行。在任何时候这个执行的数量可能会更高或者更低,比如副本刚好在增加或者减少或者pod在优雅的关闭,但是一个替换的的pod启动得比较早。如果没有指定,.spec.replicas
默认是1。
Working with ReplicationControllers
Deleting a ReplicationController and its Pods
使用kubectl delete
来删除ReplicationController 和它的所有pod。Kubectl 将会把ReplicationController 缩成0 并且在删除ReplicationController 之前会先把每个pod都删除掉。如果kubectl 的命令被打断,它可以重新开始删除。
当你使用 REST API 或者go类库,你必须明确的执行这些步骤(缩容replicas 成0,等待pod删除,然后删除ReplicationController)
Deleting just a ReplicationController
你可以只删除ReplicationController 而不影响他的pod。
使用kubectl delete
并指定--cascade=false参数.
如果使用 REST API 或者go类库,只删除ReplicationController 对象就可以了。
删除ReplicationController 后,你可以创建一个新的ReplicationController 来代替它。只要旧的和新的.spec.selector
,新的ReplicationController就会获取到旧的pod。和pod template 不一样,这不会影响任何已经存在的pod。如果要以受控方式pod更新spec,使用rolling update
Isolating pods from a ReplicationController 隔离pod
pod可以通过修改labels 来从ReplicationController移除。这个技巧也可以用来 从debug、data recovery 等删除pod。pod用这种方式删除 将会生成新的自动替换(假设replicas 数量没有修改).
Common usage patterns 常见的使用
Rescheduling 重新调度
正如刚才提到的,不管你是想让1个或者1000个pod保持运行,ReplicationController 都会保证指定的pod数量存在,即使是node故障或者pod中断(比如由另一个控制代理的执行)
Scaling 扩容
ReplicationController 扩容副本很简单,通过更新replicas
字段更新。
Rolling updates 滚动升级
ReplicationController 通常替换一个又一个的pod来实现滚动升级。
如在#1353所述,推荐的方法是创建一个包含一个replica的新ReplicationController,新的扩容1个,旧的缩容1个,最后当旧的replicas为0的时候删除旧的controller 。无论是否有意外故障,都可以看到更新的状态。
理想情况下,滚动升级controller将会考虑应用的readiness,并且会保证在任何时候都有足够的pod服务。
两个ReplicationControllers 需要创建至少有一个差异化label的pod,比如pod的主容器的image标签,通常镜像更新会促发滚动更新。
滚动升级是通过 kubectl rolling-update
客户端工具实现的。查看 kubectl rolling-update task 来看更多的例子.
Multiple release tracks
除了在滚动更新正在进行时运行多个版本的应用,使用 multiple release tracks 在很长一段时间内运行多个版本是很常见的, tracks 将会按labels区分。
比如,一个service的所有pod标签是tier in (frontend), environment in (prod)
。现在假设你有10个复制的pods。但是你想启用一个新的版本组件叫"canary"。你可以创建一个ReplicationController 把replicas
设置成9,labels 是tier=frontend, environment=prod, track=stable
,并且其他的ReplicationController 会把canary的replicas
设置成1.现在service包含canary 和non-canary 两种pod。
Using ReplicationControllers with Services
多个ReplicationControllers 可以服务同一个service,以便把一些流量转到旧版本,一些流量转到新版本。
ReplicationController 不会终止他自己, 但是也不要预期它会和services一样长时间存活。Service 可能会由多个ReplicationControllers控制的pod组成,并且预计可以在service 的生命周期内创建和销毁许多ReplicationController,Service本身及其客户端都应该忽略维护服务pod的ReplicationControllers。
Writing programs for Replication
pod通过ReplicationController是为了可替代性和可复制性,虽然他们的配置随着时间的推移会变得复杂。这显然适合用来复制无状态服务,但是ReplicationControllers 也可以用来维护master可用性、分片和应用工作池。此类应用程序应使用动态工作分配机制,比如 RabbitMQ,而不是静态/一次性定制每个pod的配置,这被认为是一种反模式。任何pod自定义执行,比如垂直自动调整资源(比如,cpu或者内存),应该由另一个在线控制器进程执行,而不是ReplicationController 自己。
Responsibilities of the ReplicationController 职责
ReplicationController 只是保证期望的匹配他label的pods数量。目前,只有终止的pod才会从其计数中排除。未来, readiness
和其他有用的系统信息可能会被考虑进去,我们可能会对替换政策添加更多控制权,我们计划发布可供外部客户使用的事件,以实施任意复杂的替换和/或缩减策略。
ReplicationController 永远受限于这种缩小策略。它本身不会执行readiness 或liveness probes。
你修改replicas
字段后它会使用外部自动扩容控制 而不是自己本身自动扩容。我们不会为ReplicationController添加调度策略。它也不应该验证控制的pod与当前指定的模板匹配,因为这会阻碍自动调整大小和其他自动化过程。同样的,完成时间、顺序依赖、配置扩展,或其他的特性 也是一样的。我们甚至计划分解出批量pod创建的机制。
ReplicationController 尝试成为一个可组合的构建模块。我们希望未来高等级api或工具或其他东西能基于它构建 以方便用户使用。kubectl 当前支持macro
操作 是概念验证的例子。比如我们可以想象一些类似 Asgard 管理ReplicationControllers, auto-scalers, services, scheduling policies, canaries等等。
Alternatives to ReplicationController 备选方案
ReplicaSet
ReplicaSet 是下一代支持新set-based label selector的ReplicationController。它主要用于Deployment作为编排pod创建、删除和更新的机制.请注意我们推荐使用Deployments 而不是直接创建Replica ,除非你需要自定义编排或永远不需要更新。
Deployment(推荐用法)
Deployment是一个高级API 用来更新底层的Replica Sets 并且是他们的pod以类似 kubectl rolling-update
方式来更新的。如果你想用使用滚动升级功能,推荐使用Deployments
。和kubectl rolling-update
不同, 他们是声明性的、服务性的 并具有更多功能。
Bare Pods
与用户直接创建pod不同,ReplicationController 会替换 任何被删除或被终止的pod,比如node故障或破坏性node维护(比如内核升级) .出于这个原因,我们推荐你使用ReplicationController 即使你的应用只需要一个pod。想一想 与进程监控类似,他会监控多个node上的多个pod,而不是单个node上的独立的进程。ReplicationController 委托node上理的代理重启本地容器。比如Kubelet or Docker
Job
使用Job来替代pod,好让pod本身可以终止自己。比如执行批量任务。
DaemonSet
使用DaemonSet来替换ReplicationController 让pod可以提供机器级别的功能。比如机器监控或者机器日志。这些pod的生命周期和机器的生命周期有关:DaemonSet 的pod必须在其他pod启动前运行,当机器需要重启/关闭的时候他们会安全的终止。
Deployments
Deployment 控制器用来声明更新pod和ReplicaSets。
你可以在Deployment 对象声明一个预期的状态,并且Deployment 控制器以一定的速度修改当前的状态成预期的状态。 你可以定义Deployments 来创建新的ReplicaSets,或者移除已经存在的Deployments 并且让新的Deployments使用它的所有资源。 注意:你不应该手动管理Deployment所拥有的ReplicaSets , 应该使用Deployment 对象来维护。如果您的使用方式未在下面介绍,请考虑在Kubernetes代码仓库上创建一个issue
Use Case
以下是Deployments的通常用法;
- 创建一个新的Deployment来发布ReplicaSet。 ReplicaSet 会在后台创建Pods 。可以查看发布的状态来查看发布成功还是失败 。例子
- 通过更新Deployment的PodTemplateSpec 来生命pod的新状态。一个新的ReplicaSet会被创建并且Deployment会以一定的速度把pod从旧的ReplicaSet移到新的ReplicaSet里。每个新的ReplicaSet 都会更新成Deployment里修改的样子。例子
- 如果Deployment的当前状态不稳定,Deployment 会回滚到早一点的版本。例子
- 扩容Deployment 承接更多的负载。例子
- 暂停Deployment来修复PodTemplateSpec里声明的程序 并且在恢复后发布一个新版本。例子
- 使用Deployment的状态当做 发布卡住的指标。例子
- 清除不再需要的旧的ReplicaSets。例子
Creating a Deployment
以下是一个Deployment例子。它会创建一个ReplicaSet 来创建三个nginx pod, 以下的配置命名成 nginx-deployment.yaml文件:
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
字段定义。 - 创建3个复制的pod, 通过
replicas
定义。 -
selector
字段用来定义Deployment 应该发现哪些pod 来管理。在这个例子里你可以简单的搜索到定义在Pod template 里的label(app: nginx). 所以更复杂的搜索规则也是可以的,只要Pod template 满足这个搜索规则。
注意:matchLabels是一个{key,value} 键值对。 在matchLabels里单个 {key,value} 就相当于matchExpressions的元素,键字段是"key" ,操作字段是"in",值 的数组 只包含"value". and 操作
-
template
字段包含以下的子字段:- pod 使用
labels
字段来标记app: nginx
- pod template 的规范 或者
.template.spec
字段表示pod执行在一个容器里,nginx
表示执行在 Docker Hub里的1.7.9版本的 nginx镜像。 - 使用
name
字段创建一个容器并且命名成nginx
- 执行1.7.9的nginx镜像
- 打开80端口好让容器可以接收和发送流量
- pod 使用
使用以下的命令执行Deployment:kubectl create -f nginx-deployment.yaml
注意: 你可以指定 "--record" 标志来写命令 执行在"kubernetes.io/change-cause" annotation 里的资源,它对于自检很有用,比如查看命令在每个Deployment 里修改的内容
下一步,执行kubectl get deployments
,查看deployments状态:
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 0 0 0 1s
当你检查你集群里的Deployments 的时候,会显示以下字段:
-
NAME
:集群里的Deployments的名称列表 -
DESIRED
:展示你在创建Deployment的定义的replicas 数量。 这是在理想状态下的数量。 -
CURRENT
:展示当前有多少replicas在执行。 -
UP-TO-DATE
: 展示已更新到预期状态的replicas 数量。 -
AVAILABLE
: 展示有多少可用的应用可以给用户使用。 -
AGE
: 展示应用已经执行的时间
以下Deployment声明里值对应的意思:
- replicas 预期的值是3 ,对应的字段是
.spec.replicas
- 当前的replicas 是0 , 对应
.status.replicas
- replicas最新的值是0 对应
.status.updatedReplicas
- replicas 可用的值是0 对应
.status.availableReplicas
查看Deployment 发布状态,kubectl rollout status deployment.v1.apps/nginx-deployment
,这个命令会返回一下内容:
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment.apps/nginx-deployment successfully rolled out
一段时间后再执行kubectl get deployments
:
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 3 3 3 18s
Deployment 的所有3个replicas都创建了,并且所有replicas都是最新的并且是可用的。
查看deployment创建的ReplicaSet ,执行kubectl get rs
:
NAME DESIRED CURRENT READY AGE
nginx-deployment-75675f5897 3 3 3 18s
ReplicaSet 的格式是[DEPLOYMENT-NAME]-[RANDOM-STRING]
。RANDOM-STRING
是随机生成的并且使用pod-template-hash做随机种子。
查看每个pod自动生成的labels ,执行kubectl get pods --show-labels
。返回以下内容:
NAME READY STATUS RESTARTS AGE LABELS
nginx-deployment-75675f5897-7ci7o 1/1 Running 0 18s app=nginx,pod-template-hash=3123191453
nginx-deployment-75675f5897-kzszj 1/1 Running 0 18s app=nginx,pod-template-hash=3123191453
nginx-deployment-75675f5897-qqcnn 1/1 Running 0 18s app=nginx,pod-template-hash=3123191453
创建3个ReplicaSet 确保始终有三个nginx pod 在执行注意: 你必须在Deployment里指定合适的selector 和 Pod template labels (这个例子里是 "app: nginx").不要和其他controllers(包括其他Deployments 和StatefulSets)的labels 或selectors 重复。 Kubernetes 不会阻止这种重复。如果有多个controllers 有重复的selectors 那么这些controllers 可能会有冲突并且会有不可意料的问题。
Pod-template-hash label
注意:不要修改这个 label
pod-template-hash
是Deployment controller 在创建或采用Deployment的时候额外添加给每个ReplicaSet 的label。
这个label 确保Deployment的子ReplicaSets 不会重复。它通过哈希 ReplicaSet 的PodTemplate 生成 并且使用这个哈希当成label 的值 添加到ReplicaSet selector。 Pod template labels 在任何ReplicaSet 的pod都有可能存在。
Updating a Deployment
注意: Deployment 发布是一个触发过程 只有Deployment pod template(.spec.template) 修改的时候才会触发。比如template 的labels 或者template 的容器镜像更新。 其他的更新,比如 扩容Deployment 不会触发发布。
假如你现在想要更新nginx 从nginx:1.7.9 更新到nginx:1.9.1:
kubectl --record deployment.apps/nginx-deployment set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1
image updated
另外,你可以edit
Deployment 修改.spec.template.spec.containers[0].image 从nginx:1.7.9 修改成nginx:1.9.1:
kubectl edit deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment edited
查看发布状态,执行:
kubectl rollout status deployment.v1.apps/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment.apps/nginx-deployment successfully rolled out
当发布成功后,使用get 查看Deployment
kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 3 3 3 36s
当你检查你集群里的Deployments 的时候,会显示以下字段:
-
NAME
:集群里的Deployments的名称列表 -
DESIRED
:展示你在创建Deployment的定义的replicas 数量。 这是在理想状态下的数量。 -
CURRENT
:展示当前有多少replicas在执行。 -
UP-TO-DATE
: 展示已更新到预期状态的replicas 数量。 -
AVAILABLE
: 展示有多少可用的应用可以给用户使用。 -
AGE
: 展示应用已经执行的时间
以下Deployment声明里值对应的意思:
- replicas 预期的值是3 ,对应的字段是
.spec.replicas
- 当前的replicas 是0 , 对应
.status.replicas
- replicas最新的值是0 对应
.status.updatedReplicas
- replicas 可用的值是0 对应
.status.availableReplicas
查看Deployment 发布状态,kubectl rollout status deployment.v1.apps/nginx-deployment
,这个命令会返回一下内容:
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment.apps/nginx-deployment successfully rolled out
一段时间后再执行kubectl get deployments
:
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 3 3 3 18s
Deployment 的所有3个replicas都创建了,并且所有replicas都是最新的并且是可用的。
查看deployment创建的ReplicaSet ,执行kubectl get rs
:
NAME DESIRED CURRENT READY AGE
nginx-deployment-75675f5897 3 3 3 18s
ReplicaSet 的格式是[DEPLOYMENT-NAME]-[RANDOM-STRING]
。RANDOM-STRING
是随机生成的并且使用pod-template-hash做随机种子。
查看每个pod自动生成的labels ,执行kubectl get pods --show-labels
。返回以下内容:
NAME READY STATUS RESTARTS AGE LABELS
nginx-deployment-75675f5897-7ci7o 1/1 Running 0 18s app=nginx,pod-template-hash=3123191453
nginx-deployment-75675f5897-kzszj 1/1 Running 0 18s app=nginx,pod-template-hash=3123191453
nginx-deployment-75675f5897-qqcnn 1/1 Running 0 18s app=nginx,pod-template-hash=3123191453
创建3个ReplicaSet 确保始终有三个nginx pod 在执行注意: 你必须在Deployment里指定合适的selector 和 Pod template labels (这个例子里是 "app: nginx").不要和其他controllers(包括其他Deployments 和StatefulSets)的labels 或selectors 重复。 Kubernetes 不会阻止这种重复。如果有多个controllers 有重复的selectors 那么这些controllers 可能会有冲突并且会有不可意料的问题。
Pod-template-hash label
注意:不要修改这个 label
pod-template-hash
是Deployment controller 在创建或采用Deployment的时候额外添加给每个ReplicaSet 的label。
这个label 确保Deployment的子ReplicaSets 不会重复。它通过哈希 ReplicaSet 的PodTemplate 生成 并且使用这个哈希当成label 的值 添加到ReplicaSet selector。 Pod template labels 在任何ReplicaSet 的pod都有可能存在。
Updating a Deployment
注意: Deployment 发布是一个触发过程 只有Deployment pod template(.spec.template) 修改的时候才会触发。比如template 的labels 或者template 的容器镜像更新。 其他的更新,比如 扩容Deployment 不会触发发布。
假如你现在想要更新nginx 从nginx:1.7.9 更新到nginx:1.9.1:
kubectl --record deployment.apps/nginx-deployment set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1
image updated
另外,你可以edit
Deployment 修改.spec.template.spec.containers[0].image 从nginx:1.7.9 修改成nginx:1.9.1:
kubectl edit deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment edited
查看发布状态,执行:
kubectl rollout status deployment.v1.apps/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment.apps/nginx-deployment successfully rolled out
当发布成功后,使用get 查看Deployment
kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 3 3 3 36s
最新的 up-to-date replicas 数量 表示 Deployment 已经把replicas 更新到最新配置。current replicas 表示Deployment 管理的所有replicas 的总数。 available replicas 表明目前可用的replicas 数量。
你可以执行kubectl get rs
来查看Deployment 通过创建新的ReplicaSet 更新的pod 并且会扩容成3个replicas ,最终旧的ReplicaSet 会缩容成0。 `
kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-1564180365 3 3 3 6s
nginx-deployment-2035384211 0 0 0 36s
现在执行`get pods`获取到的应该只有新的pod:
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-1564180365-khku8 1/1 Running 0 14s
nginx-deployment-1564180365-nacti 1/1 Running 0 14s
nginx-deployment-1564180365-z9gth 1/1 Running 0 14s
下次当你想更新这些pod,你只需要更新Deployment的 pod template 配置。
Deployment 能保证 在pod更新的时候只有一定数量的pod会down。默认情况下,它保证会比所需pod的数量少25%(最多25%不可用)。
Deployment 也能保证 只会在所需的pod的数量以上的 一定数量的pod创建。默认情况下,保证最多高于25%(最多超出25%)。
例如,如果你仔细看看上面的部署,你会看到它先创建一个新的pod,然后删除一些旧的pod,然后在创建新的。它会在有足够数量的新的pod创建后,才会删除旧的pod, 并且会在足够的pod kill 后才会创建新的pod。在至少总共有4个pod的情况下,保证最少有2个pod可用。
kubectl describe deployments
Name: nginx-deployment
Namespace: default
CreationTimestamp: Thu, 30 Nov 2017 10:56:25 +0000
Labels: app=nginx
Annotations: deployment.kubernetes.io/revision=2
Selector: app=nginx
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.9.1
Port: 80/TCP
Environment: <none>
Mounts: <none>
Volumes:
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets:
NewReplicaSet: nginx-deployment-1564180365 (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 2m deployment-controller Scaled up replica set nginx-deployment-2035384211 to 3
Normal ScalingReplicaSet 24s deployment-controller Scaled up replica set nginx-deployment-1564180365 to 1
Normal ScalingReplicaSet 22s deployment-controller Scaled down replica set nginx-deployment-2035384211 to 2
Normal ScalingReplicaSet 22s deployment-controller Scaled up replica set nginx-deployment-1564180365 to 2
Normal ScalingReplicaSet 19s deployment-controller Scaled down replica set nginx-deployment-2035384211 to 1
Normal ScalingReplicaSet 19s deployment-controller Scaled up replica set nginx-deployment-1564180365 to 3
Normal ScalingReplicaSet 14s deployment-controller Scaled down replica set nginx-deployment-2035384211 to 0
这儿你可以看到你第一次创建Deployment的时间,它创建一个ReplicaSet (nginx-deployment-2035384211) 然后直接扩容成3个replicas 。当你更新Deployment,它会创建一个新的ReplicaSet (nginx-deployment-1564180365) 并且扩容成1 然后down掉一个旧的,ReplicaSet 变成2,这样在总共有4个pod的情况下始终至少有2个pod可用。它会使用相同的滚动升级策略继续扩容新的ReplicaSet和缩容旧的ReplicaSet。最终,在新的ReplicaSet里将会有3个可用的replicas ,并且旧的ReplicaSet 缩容成0。
#### [Rollover (aka multiple updates in-flight)](https://v1-13.docs.kubernetes.io/docs/concepts/workloads/controllers/deployment/#rollover-aka-multiple-updates-in-flight)
每一次一个新的deployment 对像被Deployment controller 观察到,如果没有ReplicaSet 那么一个ReplicaSet 会被创建 并且会创建预期数量的pod。现有的控制Pods 的ReplicaSet 会匹配 `.spec.selector` 来做扩容 但是不会匹配`.spec.template`。最终新的ReplicaSet 会被扩容成`.spec.replicas` 旧的ReplicaSets 会缩容成0。
如果你更新了Deployment 但是一个现有的rollout 正在处理,Deployment 将会根据更新情况创建一个新的ReplicaSet,然后进行扩容,并且放弃之前正在扩展的ReplicaSet,它会将其添加到旧的ReplicaSet列表中 并且开始扩容它。