前言
重点学习 k8s 对象 和 工作负载里的控制器
知识来源:
官网:https://kubernetes.io/zh/docs/concepts
Kubernetes(k8s)中文教程(半兽人):https://www.orchome.com/kubernetes/index
k8s 概念
k8s 组件
k8s 对象
k8s 对象的概念
在 Kubernetes 系统中,Kubernetes 对象 是持久化的实体。 Kubernetes 使用这些实体去表示整个集群的状态,k8s 对象一般描述了以下信息:
1、哪些容器化应用在运行(以及在哪些节点上)
2、可以被应用使用的资源
3、关于应用运行时表现的策略,比如重启策略、升级策略,以及容错策略
一旦创建对象,Kubernetes 系统将持续工作以确保对象存在,通过创建对象,让 k8s 保持 对象 处于 对象所定义的 期望状态(Desired State)。
k8s 对象的增删改查 需要 使用 Kubernetes API,可以通过 kubectl 命令 调用,也可以在程序中通过 工具库调用。
k8s 对象的 Spec 和 Status
几乎每个 Kubernetes 对象包含两个嵌套的对象字段,它们负责管理对象的配置: 对象 spec(规约) 和 对象 status(状态) 。
status :对象的实际状态;
spec :对象的期望状态;
工作方式:不断将 status 与 spec 匹配,来满足 spec 的指定。
描述 Kubernetes 对象
在 .yaml 格式的文件里描述对象,其中 apiVersion、kind、metadata、spec 是必须参数;
对象 spec 的精确格式对每个Kubernetes 对象来说是不同的;
集群的每个对象 都有一个名称来表示在同类资源中的唯一性,也有一个uuid 来表示 整个集群的唯一性;
下面是一个模板:
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
创建命令:
kubectl apply -f https://k8s.io/examples/application/deployment.yaml --record
# 输出 deployment.apps/nginx-deployment created
Kubernetes 对象的 创建 与 管理
1、命令式命令
这是开始或者在集群中运行一次性任务的最简单方法。因为这个技术直接在活动对象上操作,所以它不提供以前配置的历史记录。
kubectl run nginx --image nginx
# or
kubectl create deployment nginx --image nginx
2、命令式对象配置
创建配置文件定义的对象
kubectl create -f nginx.yaml
删除两个配置文件中定义的对象
kubectl delete -f nginx.yaml -f redis.yaml
通过覆盖活动配置来更新配置文件中定义的对象
kubectl replace -f nginx.yaml
优点:对象的配置可以存在 git 里,与流程集成;提供了创建对象的记录信息,提供了创建对象的模板。
命名空间
1、概念 与 作用
Kubernetes 支持多个虚拟集群,它们底层依赖于同一个物理集群。 这些虚拟集群被称为命名空间;
适用于存在很多跨多个团队或项目的用户的场景;
命名空间是在多个用户之间划分集群资源的一种方法(通过资源配额)。
2、默认的命名空间
default:没有指明使用其它名字空间的对象所使用的默认命名空间
kube-system:系统创建的对象
kube-public:所有用户都可以读取
kube-node-lease
3、命令
kubectl get namespace
kubectl run nginx --image=nginx --namespace=<命名空间>
kubectl get pods --namespace=<命名空间>
kubectl config set-context --current --namespace=<名字空间名称>
# 验证之
kubectl config view | grep namespace:
并不是所有对象都在命名空间中,比如 节点、持久化卷、命名空间本身 都不在其中。
kubectl api-resources --namespaced=true
kubectl api-resources --namespaced=false
标签
附加到 Kubernetes 对象(比如 Pods)上的键值对,用于 选择、查询、监听。
"metadata": {
"labels": {
"key1" : "value1",
"key2" : "value2"
}
}
常用标签:
"release" : "stable", "release" : "canary"
"environment" : "dev", "environment" : "qa", "environment" : "production"
"tier" : "frontend", "tier" : "backend", "tier" : "cache"
"partition" : "customerA", "partition" : "customerB"
"track" : "daily", "track" : "weekly"
推荐的标签格式:
app.kubernetes.io/name 应用程序的名称 mysql 字符串
app.kubernetes.io/instance 用于唯一确定应用实例的名称 mysql-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 字符串
举例:
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app.kubernetes.io/name: mysql
app.kubernetes.io/instance: mysql-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
注解:
常用来记录对象的相关信息,比如:
负责人联系方式;
镜像信息;
部署说明;
"metadata": {
"annotations": {
"key1" : "value1",
"key2" : "value2",
imageregistry: "https://hub.docker.com/"
}
}
字段筛选器
可以根据一个或多个资源对象的字段筛选
kubectl get pods --field-selector status.phase=Running
kubectl get ingress --field-selector foo.bar=baz
kubectl get services --all-namespaces --field-selector metadata.namespace!=default
kubectl get pods --field-selector=status.phase!=Running,spec.restartPolicy=Always
kubectl get statefulsets,services --all-namespaces --field-selector metadata.namespace!=default
k8s 架构
Node 节点
1、概念与组成
Kubernetes 通过将容器放入在节点(Node)上运行的 Pod 中来执行你的工作负载。
节点上的组件包括 kubelet、 容器运行时以及 kube-proxy。
2、注册节点
自行注册一个 Node,注意,Node 对象的名称必须是合法的 DNS 子域名。
{
"kind": "Node",
"apiVersion": "v1",
"metadata": {
"name": "10.240.79.157",
"labels": {
"name": "my-first-k8s-node"
}
}
}
3、节点相关命令
# 阻止节点被调度
kubectl cordon $NODENAME
# 查看节点详情
kubectl describe node <node-name>
k8s 容器
镜像名称
可包含仓库主机名、端口号,还应该添加一个标签(tag),例子如下:
fictional.registry.example/imagename
fictional.registry.example:10443/imagename
fictional.registry.example:10443/python:3.9
拉取镜像策略
默认情况,镜像在本地存在时,kubelet 不会拉取,当然可以自己定义策略来执行强制拉取:
1、imagePullPolicy 设置为 Always
2、不设置 imagePullPolicy,把镜像标签的tag 写为 latest
3、不设置 imagePullPolicy 与 镜像的 tag
4、启用 AlwaysPullImages
在 Pod 上指定 ImagePullSecrets
1、 使用 Docker Config 创建 Secret
kubectl create secret docker-registry <名称> \
--docker-server=DOCKER_REGISTRY_SERVER \
--docker-username=DOCKER_USER \
--docker-password=DOCKER_PASSWORD \
--docker-email=DOCKER_EMAIL
2、在 Pod 中引用 ImagePullSecrets
apiVersion: v1
kind: Pod
metadata:
name: foo
namespace: awesomeapps
spec:
containers:
- name: foo
image: janedoe/awesomeapp:v1
imagePullSecrets:
- name: myregistrykey
工作负载(Workloads)
工作负载是在 Kubernetes 上运行的应用程序,这些应用程序可以在一组 Pods 中运行;
可以使用 工作负载资源 来管理 Pods,比如 Deployment 和 ReplicaSet 、StatefulSet、 DaemonSet、Job 和 CronJob ,垃圾收集机制。
Pod
Pod 类似于共享 命名空间 和 文件系统卷 和 网络 的一组 Docker 容器。
创建Pod:一般不需要单独创建 Pod,一般使用比如 Deployment 、Job、 StatefulSet 等资源创建 并 管理维护 Pod。
k8s 集群中的 Pod 一般两种用法:
- 一个 Pod 一个 容器:最常用,将 Pod 看作容器的包装;
- 一个Pod 多个容器 协同工作:使用场景较少,容器之间紧密关联时才应该使用这种模式。
一个 Job 创建 Pod 的模板:
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
template:
# 这里是 Pod 模版
spec:
containers:
- name: hello
image: busybox
command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
restartPolicy: OnFailure
# 以上为 Pod 模版
静态 Pod
静态 Pod(Static Pod) 直接由特定节点上的 kubelet 守护进程管理, 不需要API 服务器看到它们。
Pod 生命期
Pod 在其生命周期中只会被调度一次。 一旦 Pod 被调度(分派)到某个节点,Pod 会一直在该节点运行,直到 Pod 停止或者 被终止。
Pod 自身不具有自愈能力。如果 Pod 被调度到某节点 而该节点之后失效,或者调度操作本身失效,Pod 会被删除;与此类似,Pod 无法在节点资源 耗尽或者节点维护期间继续存活。Kubernetes 使用一种高级抽象,称作 控制器,来管理这些相对而言 可随时丢弃的 Pod 实例。
Pod 的 status
status 的 phase 的值:
Pending(悬决) Pod 已被 Kubernetes 系统接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间,
Running(运行中) Pod 已经绑定到了某个节点,Pod 中所有的容器都已被创建。至少有一个容器仍在运行,或者正处于启动或重启状态。
Succeeded(成功) Pod 中的所有容器都已成功终止,并且不会再重启。
Failed(失败) Pod 中的所有容器都已终止,并且至少有一个容器是因为失败终止。也就是说,容器以非 0 状态退出或者被系统终止。
Unknown(未知) 因为某些原因无法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。
容器的 状态
可以通过如下命令查看:
kubectl describe pod <podname>
容器重启策略
Pod 的 spec 中包含一个 restartPolicy 字段,其可能取值包括 Always、OnFailure 和 Never。默认值是 Always。
Pod 状态
Pod 有一个 PodStatus 对象,其中包含一个 PodConditions 数组。
-
PodScheduled:Pod 已经被调度到某节点;
-
ContainersReady:Pod 中所有容器都已就绪;
-
Initialized:所有的 Init 容器 都已成功启动;
-
Ready:Pod 可以为请求提供服务,并且应该被添加到对应服务的负载均衡池中
type Pod 状况的名称
status 表明该状况是否适用,可能的取值有 "True", "False" 或 "Unknown"
lastProbeTime 上次探测 Pod 状况时的时间戳
lastTransitionTime Pod 上次从一种状态转换到另一种状态时的时间戳
reason 机器可读的、驼峰编码(UpperCamelCase)的文字,表述上次状况变化的原因
message 人类可读的消息,给出上次状态转换的详细信息
容器的三种探针
livenessProbe:存活态探针
readinessProbe:就绪态探针
startupProbe:启动探针
Init 容器
1、概念
在 Pod 的 spec 中添加 initContainers字段,该字段 和 containers 数组同级相邻;
Init 容器的状态 在 status.initContainerStatuses 中;
如果为一个 Pod 指定了多个 Init 容器,这些容器会按顺序逐个运行。 每个 Init 容器必须运行成功,下一个才能够运行;当所有的 Init 容器运行完成时, Kubernetes 才会为 Pod 初始化应用容器并像平常一样运行。
2、应用方式与场景
- 可以作为 容器启动的前置条件;
- 可以访问应用容器不能访问的 Secret 权限;
- 等待一个 service 创建
- 启动应用容器前等待一段时间;
- 动态生成配置文件
- 克隆 Git 仓库到卷中
3、示例
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.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
# 启动
kubectl apply -f myapp.yaml
# 查看 init 状态
kubectl get -f myapp.yaml
kubectl describe -f myapp.yaml
# 查看 init 容器内的日志
kubectl logs myapp-pod -c init-myservice # 查看第一个 Init 容器
kubectl logs myapp-pod -c init-mydb # 查看第二个 Init 容器
Pod 拓扑分布约束
1、先决条件:需要给每个 Node 标签,来标明每个节点所在的拓扑域,比如:
NAME STATUS ROLES AGE VERSION LABELS
node1 Ready <none> 4m26s v1.16.0 node=node1,zone=zoneA
node2 Ready <none> 3m58s v1.16.0 node=node2,zone=zoneA
node3 Ready <none> 3m17s v1.16.0 node=node3,zone=zoneB
node4 Ready <none> 2m43s v1.16.0 node=node4,zone=zoneB
此时表明 node1 和 node 2 在一个拓扑(zoneA),node3 和 node4 在另一个拓扑 (zoneB)
2、在 yaml 文件中定义 topologySpreadConstraints (拓扑分布约束)
kind: Pod
apiVersion: v1
metadata:
name: mypod
labels:
foo: bar
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
foo: bar
containers:
- name: pause
image: k8s.gcr.io/pause:3.1
maxSkew :描述了两个拓扑的最大 Pod 差值;
topologyKey: 节点标签的键
whenUnsatisfiable :如果 Pod 不满足分布约束 如何处理,可选 DoNotSchedule 和 ScheduleAnyway
labelSelector :查找并匹配 Pod
更多信息 可执行命令查看: kubectl explain Pod.spec.topologySpreadConstraints
PodAffinity 与 PodAntiAffinity
更细粒度的控制 Pod 的分布。
Pod Preset
1、概念
在 Pod 创建时,用户可以使用 PodPreset 对象将特定信息注入 Pod 中,这些信息可以包括 Secret、卷、卷挂载和环境变量。
Pod Preset 是一种 API 资源,在 Pod 创建时,用户可以用它将额外的运行时需求信息注入 Pod。 使用标签选择算符 来指定 Pod Preset 所适用的 Pod。
使用 Pod Preset 使得 Pod 模板编写者不必显式地为每个 Pod 设置信息。 这样,使用特定服务的 Pod 模板编写者不需要了解该服务的所有细节。
在你的集群中启用 Pod
2、工作方式
当创建一个Pod 时:
1、检索所有可用 PodPresets 。
2、检查 PodPreset 的标签选择器与要创建的 Pod 的标签是否匹配。
3、尝试合并 PodPreset 中定义的各种资源,并注入要创建的 Pod。
4、发生错误时抛出事件,该事件记录了 pod 信息合并错误,同时在 不注入 PodPreset 信息的情况下创建 Pod。
5、为改动的 Pod spec 添加注解,来表明它被 PodPreset 所修改。 注解形如: podpreset.admission.kubernetes.io/podpreset-<pod-preset 名称>": “<资源版本>”。
一个 Pod 可能不与任何 Pod Preset 匹配,也可能匹配多个 Pod Preset。
同时,一个 PodPreset 可能不应用于任何 Pod,也可能应用于多个 Pod。
当 PodPreset 应用于一个或多个 Pod 时,Kubernetes 修改 pod spec。
对于 Env、 EnvFrom 和 VolumeMounts 的改动, Kubernetes 修改 pod 中所有容器 spec;
对于卷的改动,Kubernetes 会修改 Pod spec。
Disruptions
1、Voluntary and involuntary disruptions (自愿中断 和 非资源中断)
非自愿中断:硬件故障、误删虚拟机、内核错误、节点资源不足、网断了;
自愿中断:删除了管理 Pod 的控制器(比如 Deployment)、更新了 Pod 模板、误删了 Pod;
2、 非自愿中断的处理
- 确保 Pod 在请求中给出所需资源
- 增加副本,保证高可用性
- 请跨机架(使用 反亲和性)或跨区域 (如果使用多区域集群)扩展应用程序,保证高可用性
3、自愿中断的处理
Disruption Budget(中断 预算)
应用程序所有者可以为每个应用程序创建 PodDisruptionBudget 对象(PDB)。 PDB 将限制在同一时间因自愿干扰导致的复制应用程序中宕机的 pod 数量。
工作负载资源
ReplicaSet
Deployments
StatefulSets
DaemonSet
Jobs
垃圾收集
已完成资源的 TTL 控制器
CronJob
ReplicationController
ReplicaSet
1、概念与原理
关键词:Pod 副本的稳定集合
ReplicaSet 的目的是维护一组在任何时候都处于运行状态的 Pod 副本的稳定集合。 因此,它通常用来保证给定数量的、完全相同的 Pod 的可用性。
建议用 Deployment 从而实现 ReplicaSet
ReplicaSet 由 识别 Pod 集合的选择算符 、副本个数 、 Pod 模板 组成。
ReplicaSet 和 Pod 建立对应关系是通过 Pod 上的 metadata.ownerReferences 字段;
ReplicaSet 使用 selector 来辨识要获得的 Pod 集合。如果某个 Pod 没有 OwnerReference 或者其 OwnerReference 不是一个 控制器,且这个Pod 的 labels 匹配到 某 ReplicaSet 的selector,则该 Pod 立即被此 ReplicaSet 获得。
2、例子
Deployment 可以管理 ReplicaSet,并向 Pod 提供声明式的更新以及许多其他有用的功能,因此,操作 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
kubectl apply -f https://kubernetes.io/examples/controllers/frontend.yaml
kubectl get rs
kubectl describe rs/frontend
kubectl get pods
kubectl get pods frontend-b2zdv -o yaml
3、不通过 ReplicaSet 直接创建 Pod
如果直接创建 Pod,当 Pod 的 labels 和 ReplicaSet 的 .spec.selector.matchLabels 匹配时,会被自动关联;
如果 ReplicaSet 已经部署,则 裸创建的 Pod 可能无法启动,因为 ReplicaSet 的 Pod 已经达到期望的副本数,因此新的 Pod 刚启动就会被 ReplicaSet 终止;
如果先创建 Pod 再 创建 ReplicaSet,ReplicaSet 便可以正常关联 先创建的 Pod,并部署期望的副本数。
apiVersion: v1
kind: Pod
metadata:
name: pod1
labels:
tier: frontend
spec:
containers:
- name: hello1
image: gcr.io/google-samples/hello-app:2.0
kubectl apply -f https://kubernetes.io/examples/pods/pod-rs.yaml
kubectl get pods
4、ReplicaSet 的 spec
apiVersion、kind、metadata 、.spec;
Pod 的重启策略 .spec.template.spec.restartPolicy 必须是 Always ,这也是默认值,不需要修改;
.spec.template.metadata.labels 的值必须与 spec.selector 值 相匹配;
Replicas :副本数,默认是1;
5、删除 Replicas
删除 Replicas 及关联的 Pods:
kubectl delete
# 当使用 REST API 或 client-go 库时,必须在删除选项中将 propagationPolicy 设置为 Background 或 Foreground
kubectl proxy --port=8080
curl -X DELETE 'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
-d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
-H "Content-Type: application/json"
只删除 ReplicaSet:
kubectl delete --cascade=false
# 当使用 REST API 或 client-go 库时,你必须将 propagationPolicy 设置为 Orphan
kubectl proxy --port=8080
curl -X DELETE 'localhost:8080/apis/apps/v1/namespaces/default/replicasets/frontend' \
-d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
-H "Content-Type: application/json"
6、将 Pod 从 ReplicaSet 中隔离:修改 Pod 的标签
7、扩容与缩容
修改 .spec.replicas 字段;
如果 ReplicaSet 作为 水平的 Pod 缩放器 (HPA) 的目标 ,则可以被自动扩容缩容
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: frontend-scaler
spec:
scaleTargetRef:
kind: ReplicaSet
name: frontend
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 50
kubectl apply -f https://k8s.io/examples/controllers/hpa-rs.yaml
kubectl autoscale rs frontend --max=10 --min=3 --cpu-percent=50
Deployments
1、概念
关键词:声明式的 回滚 与 更新
一个 Deployment 控制器为 Pods 和 ReplicaSets 提供声明式的更新能力。
2、使用场景
创建 Deployment 以将 ReplicaSet 上线;
通过更新 Deployment 的 PodTemplateSpec,声明 Pod 的新状态 ;
回滚到较早的 Deployment 版本;
扩大 Deployment 规模;
暂停 Deployment 以应用对 PodTemplateSpec 所作的多项修改, 然后恢复其执行以启动新的上线版本;
使用 Deployment 状态 来判定上线过程是否出现停滞;
清理较旧的不再需要的 ReplicaSet ;
3、例子
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.14.2
ports:
- containerPort: 80
kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml
# 是否创建
kubectl get deployments
# 上线状态
kubectl rollout status deployment.v1.apps/nginx-deployment
kubectl get rs
kubectl get pods --show-labels # 可以看到每个 Pod 都有一样的 Pod-template-hash
Pod-template-hash 是 Deployment 为每个 ReplicaSet 设置的,用来确保 每个 ReplicaSet 不重复,Pod-template-hash 会写到 ReplicaSet 的 selector 和 Pod 的 labels;
4、Deployment 的 Spec
apiVersion,kind 、 metadata、Spec
Pod 的 .spec.template.spec.restartPolicy 必须是 Always(默认)
.spec.replicas 默认是 1
.spec.strategy:Recreate(创建新 Pod 前,旧的都会被kill) 或 RollingUpdate(滚动更新,可以通过maxUnavailable 和 maxSurge 控制)
.spec.strategy.rollingUpdate.maxUnavailable:更新过程中不可用的 Pod 的个数上限,默认为 25%,可以是数字或百分比;
.spec.strategy.rollingUpdate.maxSurge:超出 期望 Pod 个数的数量,默认 25%,可以是数字或百分比;
.spec.progressDeadlineSeconds:
.spec.minReadySeconds:必须超过这个时间, Pod 才被视为可用,默认为0,即就绪(被探针检测到)后立即可用;
.spec.revisionHistoryLimit:保留的历史版本数;
.spec.paused:暂停的,PodTemplateSpec 的任何修改不会出发更新上线;
5、更新 Deployment
以 更新 Pod 的镜像为例:
kubectl --record deployment.apps/nginx-deployment set image \
deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1
# 或者
kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1 --record
# 或者 edit Deployment 将 .spec.template.spec.containers[0].image 从 nginx:1.14.2 更改至 nginx:1.16.1
kubectl edit deployment.v1.apps/nginx-deployment
# 查看上线状态
kubectl rollout status deployment.v1.apps/nginx-deployment
kubectl get deployments
kubectl get rs
kubectl get pods
kubectl describe deployments
更新策略:确保至少所需 Pods 75% 处于运行状态(最大不可用比例为 25%);
创建 Pods 的数量可能会比 期望的 多一些,最多多 25%;
6、回滚 Deployment
通过设置 Deployment 的 .spec.revisionHistoryLimit 可以设置集群保留的 Deployment 的版本数,默认是10。
以 不小心拼错了镜像名为例:
# anginx 拼错了!
kubectl set image deployment.v1.apps/nginx-deployment anginx=nginx:1.161 --record=true
# 上线后会停滞,验证一下
kubectl rollout status deployment.v1.apps/nginx-deployment
kubectl get rs
kubectl get pods
kubectl describe deployment
解决方案:
# 检查历史版本,通过查看 REVISION 决定要回滚的版本编号
kubectl rollout history deployment.v1.apps/nginx-deployment
# 查看某个版本的详情
kubectl rollout history deployment.v1.apps/nginx-deployment --revision=2
# 执行回滚
kubectl rollout undo deployment.v1.apps/nginx-deployment
kubectl rollout undo deployment.v1.apps/nginx-deployment --to-revision=2
# 检查回滚是否成功
kubectl get deployment nginx-deployment
kubectl describe deployment nginx-deployment
7、Deployment 的扩容与缩容
kubectl scale deployment.v1.apps/nginx-deployment --replicas=10
如果设置了Pod 的水平自动缩放:
kubectl autoscale deployment.v1.apps/nginx-deployment --min=10 --max=15 --cpu-percent=80
8、Deployment 的 暂停 与 恢复
可以在触发一个或多个更新之前暂停 Deployment,然后再恢复其执行;
这样做使得你能够在暂停和恢复执行之间应用多个修补程序,而不会触发不必要的上线操作。
kubectl get deploy
kubectl get rs
# 暂停 deployment 运行
kubectl rollout pause deployment.v1.apps/nginx-deployment
# 更改镜像
kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
# 查看是否真的暂停了。。。会看到 REVISION CHANGE-CAUSE
kubectl rollout history deployment.v1.apps/nginx-deployment
kubectl get rs
kubectl set resources deployment.v1.apps/nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi
# 恢复 deployment 运行
kubectl rollout resume deployment.v1.apps/nginx-deployment
kubectl get rs -w
kubectl get rs
9、Deployment 状态
查看 状态:
kubectl rollout status deployment.v1.apps/nginx-deployment
退出码为0 表示 Complete,为1 表示 Failed;
Progressing:正在创建新的 RS、正在为旧的 RS 缩容扩容、新的 Pods 已就绪(持续了MinReadySeconds);
Complete:Deployment 关联的所有副本都已更新、都已可用、未运行 Deployment 的旧副本;
Failed:配额(Quota)不足、就绪探测(Readiness Probe)失败、镜像拉取错误、权限问题、限制范围(Limit Ranges)、应用程序本身的问题;
检测方法:设置 progressDeadlineSeconds,到时间就会报错,错误信息在 Deployment 的 .status.conditions 中
# 设置 progressDeadlineSeconds
kubectl patch deployment.v1.apps/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":600}}'
# 检查 deployment
kubectl describe deployment nginx-deployment
# 输出
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing False ProgressDeadlineExceeded
ReplicaFailure True FailedCreate
# more
kubectl get deployment nginx-deployment -o yaml
Type=Available 加上 Status=True :Deployment 是可用的;
Type=Progressing 加上 Status=True:Deployment 正在上线;
StatefulSets
1、概念
关键词:有状态应用、管理 Deployment、为这些 Pod 提供序号和唯一性
StatefulSet 是用来管理有状态应用的 工作负载 API 对象( workload API object;
StatefulSet 用来管理 Deployment 和扩展一组 Pod,并且能为这些 Pod 提供序号和唯一性保证;
StatefulSet 和其他控制器使用相同的工作模式。你在 StatefulSet 对象 中定义你期望的状态,然后 StatefulSet 的 控制器 就会通过各种更新来达到那种你想要的状态。
2、场景
Pod 需要稳定的、唯一的网络标识符;
Pod 需要 稳定的、持久的存储;
Pod需要 有序的、优雅的部署和缩放;
Pod 需要 有序的、自动的滚动更新;
3、限制
Pod 的存储 必须由 storage class指定的 PersistentVolume 驱动 提供,或管理员事先提供;
删除或缩容 StatefulSets 不会删掉 关联的 Pods,必须手动删除 (保证数据安全性);
需要创建一个 headless-service 来标识 Pods 的网络;
删除 StatefulSets 时需要先将 Pods 缩容到0;
4、StatefulSets 的 Spec
.spec.template.metadata.labels 与 .spec.selector 需要匹配;
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: k8s.gcr.io/nginx-slim:0.8
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
5、Pod 标识
StatefulSet 会为 每个 Pod 分配一个编号,从 0 到 N-1 ;
每个Pod 的主机名:StatefulSet 名称-Pod编号;
服务的格式:服务名称.命名空间.svc.cluster.local,cluster.local 是集群域;
Kubernetes 为每个 VolumeClaimTemplate 创建一个 PersistentVolumes;
StatefulSet 控制器 创建 Pod 时,会给 Pod 设置一个 标签:statefulset.kubernetes.io/pod-name,用来为 特定 Pod 绑定 Service。
6、StatefulSet 的部署、扩容缩容、滚动更新
部署 Pods 的顺序为 从 0 到 N-1,前面一个 Pod 部署成功,后面一个才会部署;
缩容或删除 Pods 的顺序为 从 N-1 到 0,前面一个 Pod 删除成功,后面的才会删除,且 被删除的 Pod 后面的 Pod 必须是正常运行的;
滚动更新(RollingUpdate )的顺序也是从 N-1 到 0,前一个更新成功,才会进行后一个更新;
回滚的话需要人工干预。
DaemonSet
1、概念
关键词:每个节点运行、长期运行、守护进程、日志、监控
DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。 当有节点加入集群时, 也会为他们新增一个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。
2、场景
在每个节点上运行集群守护进程
在每个节点上运行日志收集守护进程
在每个节点上运行监控守护进程
3、 DaemonSet 的 Spec
apiVersion、kind 、 metadata、spec;
Pod 的 RestartPolicy 必须是 Always;
.spec.template.spec.nodeSelector: 可以指定想要部署的某个 Node;
.spec.template.spec.affinity:想要部署的亲和的 Node;
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
4、DaemonSet 的 调度策略
亲和性与反亲和性
污点和容忍度
5、删除与更新
kubectl delete
kubectl delete --cascade=false
支持滚动更新
Jobs
1、概念
关键词:一次性的任务、一个 Job 里可并行运行多个 Pod
Job 会创建一个或者多个 Pods,并确保指定数量的 Pods 成功终止。 随着 Pods 成功结束,Job 跟踪记录成功完成的 Pods 个数。 当数量达到指定的成功个数阈值时,任务(即 Job)结束。 删除 Job 的操作会清除所创建的全部 Pods。
2、例子
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
backoffLimit: 4
kubectl apply -f https://kubernetes.io/examples/controllers/job.yaml
kubectl describe jobs/pi
# 列举某个 Job 下的全部 Pods
pods=$(kubectl get pods --selector=job-name=pi --output=jsonpath='{.items[*].metadata.name}')
echo $pods
kubectl logs $pods
3、Job 的 Spec
apiVersion、kind 、 metadata、spec;
Pod 的 RestartPolicy 只能设置为 Never 或 OnFailure;
非并行 Job:一般只启动一个 Pod,.spec.completions 和 .spec.parallelism 都无需设置,默认都是1 ;
具有 确定完成计数 的并行 Job:.spec.completions 设置为 大于0,这个数值的 Pod 都成功后,即任务完成;
带 工作队列 的并行 Job:不设置 spec.completions,需要设置 .spec.parallelism(并行数),多个 Pod 一起合作从队列取任务,至少一个 Pod 成功完成,且其余Pod 也已经终止,表示该 Job 已完成;一个Pod 退出后,其他Pod也不可以再做操作。
.spec.backoffLimit:Job 失败后重试次数的上限。
4、当 Pod 和 容器 失效
根据 .spec.template.spec.restartPolicy 决定失败后是否重启;
5、清理已完成的 Job
activeDeadlineSeconds
设置 .spec.activeDeadlineSeconds:表示 Job 运行的最长时间限制,一旦达到这个时间长度,Job立即停止,关联的所有 Pod 也会停止,并且 Job 的状态更新为 type: Failed 及 reason: DeadlineExceeded;
.spec.activeDeadlineSeconds 优先级高于其 .spec.backoffLimit
手动删除
kubectl delete jobs/pi 或者 kubectl delete -f ./job.yaml
自动清理:
设置 Job的 .spec.ttlSecondsAfterFinished,单位是秒,比如 设置为 100,表示 Job 完成后的 100秒,会删除 该 Job 和所有关联的 Pods 对象,如果设置为0,则Job完成后会马上删除 Job 及关联的 Pods,如果不设置则Job完成后不会做任何删除;
设置ttlSecondsAfterFinished 应该保证 k8s 集群的 时间偏差非常小。
6、Job 与 RS
RS 与 Job 是互补的:
RS 管理的是 不希望被终止的 Pod (例如,Web 服务器);
Job 管理的是 希望被终止的 Pod(例如,批处理作业)。
垃圾收集
1、主人和附属
对象的关联关系,比如一个 ReplicaSet 是一组 Pod 的主人,这些 Pod 是 这个 RS 的 附属,每个 Pod 都有 metadata.ownerReferences 字段指向这个 RS 主人。
k8s 1.8版本之后,ReplicaSet、StatefulSet、DaemonSet、Deployment、 Job 和 CronJob 创建的 Pod 会自动设置ownerReference 的值,也可以手动设置来改变 主人 和 附属的关系。
当然 主人 和 附属 不能跨命名空间。
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-repset
spec:
replicas: 3
selector:
matchLabels:
pod-is-for: garbage-collection-example
template:
metadata:
labels:
pod-is-for: garbage-collection-example
spec:
containers:
- name: nginx
image: nginx
kubectl apply -f https://k8s.io/examples/controllers/replicaset.yaml
kubectl get pods --output=yaml
输出:
apiVersion: v1
kind: Pod
metadata:
...
ownerReferences:
- apiVersion: apps/v1
controller: true
blockOwnerDeletion: true
kind: ReplicaSet
name: my-repset
uid: d9607e19-f88f-11e6-a518-42010a800195
2、级联删除
删除对象时,对象的附属也自动被删除;
后台模式(Background):执行删除 主人后,垃圾回收器会在后台删除关联的 附属;
前台模式(Foreground):
附属设置 ownerReference.blockOwnerDeletion 会阻止删除人,但如果主人的 blockOwnerDeletion 的值为 True,就可以强行删除他们;
如果主人被删除,附属还在,这些附属就是 孤立对象(Orphaned);
3、级联删除策略
deleteOptions.propagationPolicy :可选 Orphan、Foreground 或者 Background;
后台删除附属对象:
kubectl proxy --port=8080
curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/replicasets/my-repset \
-d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Background"}' \
-H "Content-Type: application/json"
前台删除附属对象:
kubectl proxy --port=8080
curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/replicasets/my-repset \
-d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
-H "Content-Type: application/json"
让附属对象孤立:
kubectl proxy --port=8080
curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/replicasets/my-repset \
-d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
-H "Content-Type: application/json"
# 后台级联删除
kubectl delete replicaset my-repset --cascade=true
# 让附属对象孤立
kubectl delete replicaset my-repset --cascade=false
CronJob
1、概念
关键词:自动化任务、周期性任务、重复任务、备份任务、发送邮件任务
Cron Job 创建基于时间调度的 Jobs。
一个 CronJob 对象就像 crontab (cron table) 文件中的一行。 它用 Cron 格式进行编写, 并周期性地在给定的调度时间执行 Job。
2、使用场景
CronJobs 对于创建周期性的、反复重复的任务很有用,例如执行数据备份或者发送邮件。 CronJobs 也可以用来计划在指定时间来执行的独立任务,例如计划当集群看起来很空闲时 执行某个 Job。
3、例子
startingDeadlineSeconds 设置为很大的数值或未设置(默认),并且 concurrencyPolicy 设置为 Allow,则作业将始终至少运行一次。
CronJob 仅负责创建与其调度时间相匹配的 Job,而 Job 又负责管理其关联的 Pod。
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
服务、负载均衡 和 联网
Kubernetes 网络解决四方面的问题:
1、一个 Pod 中的容器之间通过本地回路(loopback)通信。
2、集群网络在不同 pod 之间提供通信。
3、Service 资源允许你对外暴露 Pods 中运行的应用程序,以支持来自于集群外部的访问。
4、可以使用 Services 来发布仅供集群内部使用的服务。
Service 拓扑
1、概念
Service 拓扑可以让一个服务基于集群的 Node 拓扑进行流量路由。
例如,一个服务可指定流量优先路由到一个和客户端在同一个 Node 或者在同一可用区域的端点。
默认情况,访问 ClusterIP 或者 NodePort 的流量可能被路由到任意一个后端服务,从 k8s 1.17 版本之后,Service 创建者可以根据源 Node 和目的 Node 的标签来定义流量路由策略,Service 拓扑特性实现了服务流量的路由。
2、使用案例
对于在公有云上的运营者来说,更偏向于把流量控制在同一区域内,因为区域间的流量是有费用成本的,而区域内的流量没有;
把流量路由到由 DaemonSet 管理的本地 Pod 上,或者把保持流量在连接同一机架交换机的 Node 上,以获得低延时。
3、使用方法
前提:
- Kubernetes 的版本不低于 1.17
- Kube-proxy(负载均衡) 运行在 iptables 模式或者 IPVS 模式,calico 目前二者都支持
- 启用 端点切片功能
给 kube-apiserver 和 kube-proxy 启用 ServiceTopology 功能:
--feature-gates="ServiceTopology=true"
控制 Service 的流量路由:
在 Service 配置中指定 topologyKeys 字段,流量会被定向到第一个标签值和源 Node 标签值相匹配的 Node。
如果 Node 的标签被打为其主机名,区域名和地区名,则有三种配置 topologyKeys 的策略:
- 只定向到同一个 Node 上的端点,否则就失败:[“kubernetes.io/hostname”]
- 偏向定向到同一个 Node 上的端点,其次是同一区域的端点上,然后是同一地区的端点,最后就匹配失败:[“kubernetes.io/hostname”, “topology.kubernetes.io/zone”, “topology.kubernetes.io/region”]
- 偏向于同一区域,如果没有,就随意分配:[“topology.kubernetes.io/zone”, “*”]
目前的 拓扑键只有如下三种:kubernetes.io/hostname,topology.kubernetes.io/zone 和 topology.kubernetes.io/region,通配符 * 用的话只可以放在最后。
Service
1、概念
将运行在一组 Pods 上的应用程序公开为网络服务的抽象方法;
使用 Kubernetes,你无需修改应用程序即可使用不熟悉的服务发现机制; Kubernetes 为 Pods 提供自己的 IP 地址,并为一组 Pod 提供相同的 DNS 名, 并且可以在它们之间进行负载均衡。
2、解决的问题
Deployment 是 动态 创建和销毁 Pod 的,Pod是有生命期的,且每个 Pod 的 IP不同,比如 后端 Pod 为 前端 Pod 提供服务,前端Pod 需要跟踪确定后端 Pod 的 IP,但是 IP 动态变化怎么办?
3、解决方法
Service 是一种服务,它定义了一组 逻辑上的 Pod 和 一种访问这组 Pod 的策略,Service 访问 Pod 的方式是 Service 的 selector;
服务发现的实现方式是查询 API 服务器 的 Endpoints 资源,服务中的 Pod 集合发生更改,Endpoints 就会被更新;
4、定义 Service
下面是一个 名称为 my-service 的 Service 对象,访问这个 Service 时,Service 会把请求转发到使用了端口 9376 且具有标签 app=MyApp 的 Pod 上。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
5、虚拟 IP 和 Service 代理
在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。 kube-proxy 负责为 Service 实现了一种 虚拟IP 的形式。
6、代理模式
userspace 代理模式
iptables 代理模式
IPVS 代理模式
7、选择自己的 IP 地址
在 Service 创建的请求中,可以通过设置 spec.clusterIP 字段来指定自己的集群 IP 地址。 比如,希望替换一个已经已存在的 DNS 条目,或者遗留系统已经配置了一个固定的 IP 且很难重新配置。
用户选择的 IP 地址必须合法,并且这个 IP 地址在 service-cluster-ip-range CIDR 范围内, 这对 API 服务器来说是通过一个标识来指定的。 如果 IP 地址不合法,API 服务器会返回 HTTP 状态码 422,表示值不合法。
8、服务发现
环境变量 :当 Pod 运行在 Node 上,kubelet 会为每个活跃的 Service 添加一组环境变量。
DNS:支持集群的 DNS 服务器(例如 CoreDNS)监视 Kubernetes API 中的新服务,并为每个服务创建一组 DNS 记录。 如果在整个集群中都启用了 DNS,则所有 Pod 都应该能够通过其 DNS 名称自动解析服务。
9、发布服务
ServiceTypes 默认是 ClusterIP 类型,可选值如下:
- ClusterIP:服务只在集群内部暴露,就通过集群的内部IP暴露服务;
- NodePort:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务,这种方式允许从集群外部访问服务;
- LoadBalancer:用云服务商提供的负载均衡器向外部提供服务;
NodePort:对外暴露的端口范围是 30000-32767:
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
Pod 与 Service 的 DNS
Kubernetes DNS 在群集上调度 DNS Pod 和服务,并配置 kubelet 以告知各个容器使用 DNS 服务的 IP 来解析 DNS 名称。