Kubernetes 学习记录
-
一、`kubernetes` 入门学习
-
二 、`kubernetes` 的进阶学习
-
-
2.1 管理 `kubernetes` 对象
-
2.2 标签和选择器
-
2.3 如何使用标签和选择器
-
2.4 注解`annotation`
-
2.5 字段选择器
-
2.6 容器生命周期事件处理`postStart` 和 `preStop` 处理程序
-
2.7 容器组 – 生命周期
-
2.8 容器组 – 配置初始化容器
-
2.9 容器组 – `PodDisruptionBudget(PDB)`
-
2.10 控制器 – `ReplicaSet (RS)`
-
2.11 控制器 – `Deployment (deploy)`
-
2.12 控制器 – `StatefulSet`
-
2.13 控制器 – `DaemonSet`
-
2.14 `Service`
-
2.15 数据卷 `Volume`
-
2.16 `ConfigMap`
-
2.17 管理容器资源
-
2.18 污点和容忍
-
2.19 `Secret`
-
2.20 Security Context
-
-
三、`Kubernetes` 高级学习
-
四、`Kubernetes` 实战
一、
kubernetes
入门学习
kubernetes
1.1 部署第一个
Deployment
Deployment
-
创建
yaml
文件
nginx-deploy.yaml
apiVersion: apps/v1 #与k8s集群版本有关,使用 kubectl api-versions 即可查看当前集群支持的版本
kind: Deployment #该配置的类型,我们使用的是 Deployment
metadata: #译名为元数据,即 Deployment 的一些基本属性和信息
name: nginx-deployment #Deployment 的名称
labels: #标签,方便后面通过一些方式来选取得标记,key=value 得形式
app: nginx #为该Deployment设置key为app,value为nginx的标签
spec: #这是关于该Deployment的描述,可以理解为你期待该Deployment在k8s中如何使用
replicas: 1 #使用该Deployment创建一个应用程序实例
selector: #标签选择器,与上面的标签共同作用,用来选取标签为什么得选择器
matchLabels: #选择包含标签app:nginx的资源
app: nginx
template: #这是选择或创建的Pod的模板
metadata: #Pod的元数据
labels: #Pod的标签,上面的selector即选择包含标签app:nginx的Pod
app: nginx
spec: #期望Pod实现的功能(即在pod中部署)
containers: #生成container,与docker中的container是同一种
- name: nginx #container的名称
image: nginx:1.7.9 #使用镜像nginx:1.7.9创建container,该container默认80端口可访问
-
执行
yaml
文件
kubectl apply -f nginx-deploy.yaml
- 查询结果
# -n 是 namespace ,命名空间
kubectl get deploy -n default
1.2 查看资源的命令
-
查看
deployment
kubectl get deploy -A
- 显示资源详细信息
# 查看nginx deployment的详情
kubectl describe deploy nginx -n test
# 查看pod nginx 的详情
kubectl deploy pod -n test nginx-ds-585449566-hc5jv
-
查看pod日志(类似与
docker logs -f
)
kubectl logs -f -n test nginx-ds-585449566-hc5jv
- 进入pod中的容器内
kubectl exec -n test -it nginx-ds-585449566-hc5jv /bin/bash
# 该命令将会在未来被替代
kubectl exec -n test nginx-ds-585449566-hc5jv -- "curl localhost"
1.3 发布应用程序
1.3.1 暴漏服务的三种模式
-
ClusterIP
(默认)
在集群中的内部IP上公布服务,这种
Service
只在集群内布可以访问 -
NodePort
使用
NAT
在集群中每个
worker
节点同一个端口上公布服务。这种方式下,可以通过集群中任意节点+端口号的方式访问服务,此时
ClusterIP
的访问方式仍然可用。 -
LoadBalancer
在云环境中,创建一个集群外部的负载均衡器,使用该负载均衡器的IP作为服务的访问地址,此时
ClusterIP
和
NodePort
的访问方式仍然可用。
1.3.2 创建一个
nginx deployment
的
Service
nginx deployment
Service
apiVersion: v1
kind: Service
metadata:
name: nginx-service #Service 的名称
labels: #Service 自己的标签
app: nginx #为该 Service 设置 key 为 app,value 为 nginx 的标签
spec: #这是关于该 Service 的定义,描述了 Service 如何选择 Pod,如何被访问
selector: #标签选择器
app: nginx #选择包含标签 app:nginx 的 Pod
ports:
- name: nginx-port #端口的名字
protocol: TCP #协议类型 TCP/UDP
port: 80 #集群内的其他容器组可通过 80 端口访问 Service
nodePort: 32600 #通过任意节点的 32600 端口访问 Service
targetPort: 80 #将请求转发到匹配 Pod 的 80 端口
type: NodePort #Serive的类型,ClusterIP/NodePort/LoaderBalancer
-
执行
yaml
文件
kubectl apply -f nginx-svc.yaml -n test
1.4 设置命名空间偏好
kubectl config set-context --current --namespace=test
二 、
kubernetes
的进阶学习
kubernetes
2.1 管理
kubernetes
对象
kubernetes
2.1.1 指令性的命令行
当使用指令性的命令行(imperative commands)时,用户通过向
kubectl
命令提供参数的方式,直接操作集群中的 Kubernetes 对象。此时,用户无需编写或修改
.yaml
文件。
kubectl create deploy nginx --image nginx:latest -n test
2.1.2 指令性的对象配置
使用指令性的对象配置(imperative object configuration)时,需要向
kubectl
命令指定具体的操作(
create,replace,apply,delete
等),可选参数以及至少一个配置文件的名字。配置文件中必须包括一个完整的对象的定义,可以是
yaml
格式,也可以是
json
格式。
# 创建对象
kubectl create -f nginx.yaml
# 删除对象
kubectl delete -f nginx.yaml -f redis.yaml
# 替换对象
kubectl replace -f nginx.yaml
2.1.3 声明式的对象配置
当使用声明式的对象配置时,用户操作本地存储的Kubernetes对象配置文件,然而,在将文件传递给
kubectl
命令时,并不指定具体的操作,由
kubectl
自动检查每一个对象的状态并自行决定是创建、更新、还是删除该对象。使用这种方法时,可以直接针对一个或多个文件目录进行操作(对不同的对象可能需要执行不同的操作)
# 查看具体的变更
kubectl diff -f configs/
kubectl apply -f configs
# 递归处理目录中的内容
kubectl diff -R -f configs/
kubectl apply -R -f configs/
2.2 标签和选择器
2.2.1 标签(
Label
)
Label
label
是附加在Kubernetes对象上的一组名值对,其意图是按照对用户有意义的方式来标识Kubernetes对象,同时,又不对Kubernetes的核心逻辑产生影响。标签可以用来组织和选择一组Kubernetes对象。您可以在创建Kubernetes对象时为其添加标签,也可以在创建以后再为其添加标签。每个Kubernetes对象可以有多个标签,同一个对象的标签的
Key
必须唯一,例如:
metadata:
labels:
key1: value1
key2: value2
2.2.2 标签选择器
与
name
和
UID
不同,标签不一定是唯一的。通常来讲,会有多个Kubernetes对象包含相同的标签。通过使用标签选择器(
label selector
),用户/客户端可以选择一组对象。标签选择器(
label selector
)是 Kubernetes 中最主要的分类和筛选手段。
Kubernetes api server支持两种形式的标签选择器,
equality-based
基于等式的 和
set-based
基于集合的。标签选择器可以包含多个条件,并使用逗号分隔,此时只有满足所有条件的 Kubernetes 对象才会被选中。
- 基于等式的选择方式
envionment = prod
tier != frontend
也可以使用逗号分隔的两个等式
environment=production,tier!=frontend
,此时将选中所有
environment
为
prod
且
tier
不为
front
的对象。
以
Pod
的节点选择器 为例,下面的
Pod
可以被调度到包含标签
accelerator=nvidia-tesla-p100
的节点上:
apiVersion: v1
kind: Pod
metadata:
name: cuda-test
spec:
containers:
- name: cuda-test
image: "k8s.gcr.io/cuda-vector-add:v0.1"
resources:
limits:
nvidia.com/gpu: 1
nodeSelector:
accelerator: nvidia-tesla-p100
-
基于集合的选择方式
Set-based 标签选择器可以根据标签名的一组值进行筛选。支持的操作符有三种:
in
、
notin
、
exists
。例如:
# 选择所有的包含 `environment` 标签且值为 `production` 或 `qa` 的对象
environment in (production, qa)
# 选择所有的 `tier` 标签不为 `frontend` 和 `backend`的对象,或不含 `tier` 标签的对象
tier notin (frontend, backend)
# 选择所有包含 `partition` 标签的对象
partition
# 选择所有不包含 `partition` 标签的对象
!partition
基于集合的选择方式是一个更宽泛的基于等式的选择方式
例如,
environment=production
等价于
environment in (production)
;
environment!=production
等价于
environment notin (production)
。
基于集合的选择方式可以和基于等式的选择方式可以混合使用,例如:
partition in (customerA, customerB),environment!=qa
2.3 如何使用标签和选择器
2.3.1 查询条件
- 基于等式的选择方式
kubectl get pods -l environment=prod,tier=front
- 使用基于集合的选择方式
kubectl get pods -l ’environment in (prod),tier in (front)‘
2.3.2
Service
Service
Service
中 通过
spec.selector
字段来选择一组
Pod
,并将服务请求转发到Pod上
selector:
component: redis
2.3.3 有些对象支持基于集合的选择方式
Job
、
Deployment
、
ReplicaSet
和
DaemonSet
同时支持基于等式的选择方式和基于集合的选择方式。例如
selector:
matchLabels:
component: redis
matchExpressions:
- {key: tier, operator: In, values: [cache]}
- {key: environment, operator: NotIn, values: [dev]}
matchLabels
是一个
{key,value}
组成的
map
。
map
中的一个
{key,value}
条目相当于
matchExpressions
中的一个元素,其
key
为
map
的
key
,
operator
为
In
,
values
数组则只包含
value
一个元素。
matchExpression
等价于基于集合的选择方式,支持的
operator
有
In
、
NotIn
、
Exist
s 和
DoesNotExist
。当
operator
为
In
或
NotIn
时,
values
数组不能为空。所有的选择条件都以 AND 的形式合并计算,即所有的条件都满足才可以算是匹配。
2.4 注解
annotation
annotation
metadata:
annotations:
key1: value1
key2: value2
2.5 字段选择器
所有的对象都支持两个字段是
metadata.name
和
metadata.namespace
,在字段选择器中使用不支持的字段,将报错,可以指定多个字段选择器,用
,
隔开
kubectl get pods --field-selector status.phase=Running -n test
2.6 容器生命周期事件处理
postStart
和
preStop
处理程序
postStart
preStop
-
创建
lifecycle-demo.yaml
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"]
2.7 容器组 – 生命周期
2.7.1
Pod phase
Pod phase
pod phase
代表其所处生命周期的阶段。
|
描述 |
---|---|
|
Kubernetes 已经创建并确认该
。此时可能有两种情况: –
还未完成调度(例如没有合适的节点 – 正在从
下载镜像 |
|
该
已经被绑定到一个节点,并且该
所有的容器都已经成功创建。其中至少有一个容器正在运行,或者正在启动/重启 |
|
中的所有容器都已经成功终止,并且不会再被重启 |
|
中的所有容器都已经终止,至少一个容器终止于失败状态:容器的进程退出码不是 0,或者被系统 kill |
|
因为某些未知原因,不能确定
的状态,通常的原因是
与
所在节点之间的通信故障 |
2.7.2 何时使用健康检查/就绪检查
-
如果容器中的进程在碰到问题时可以自己 crash,您并不需要执行健康检查;
kubelet
可以自动的根据
Pod
的
restart policy
(重启策略)执行对应的动作 -
如果您希望在容器的进程无响应后,将容器 kill 掉并重启,则指定一个健康检查
liveness probe
,并同时指定
restart policy
(重启策略)为
Always
或者
OnFailure
-
如果您想在探测
Pod
确实就绪之后才向其分发服务请求,请指定一个就绪检查
readiness probe
。此时,就绪检查的内容可能和健康检查相同。就绪检查适合如下几类容器:- 初始化时需要加载大量的数据、配置文件
- 启动时需要执行迁移任务
- 其他
2.7.3 重启策略
定义
Pod
或工作负载时,可以指定
restartPolicy
,可选的值有:
-
Always
(默认值) -
OnFailure
-
Never
2.8 容器组 – 配置初始化容器
创建一个
Pod
,该
Pod
包含一个应用程序容器(工作容器)和一个初始化容器(
Init Container
)。初始化容器执行结束之后,应用程序容器(工作容器)才开始启动。
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
initContainers:
- name: install
image: nginx
command:
- "/bin/bash"
- "-c"
args:
[
"echo '<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body>
<h1>Hello World1</h1>
</body>
</html>' > /work-dir/index1.html;
echo '<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body>
<h1>Hello World2</h1>
</body>
</html>' > /work-dir/index2.html"
]
volumeMounts:
- name: workdir
mountPath: "/work-dir"
dnsPolicy: Default
volumes:
- name: workdir
emptyDir: {}
Pod
中初始化容器和应用程序共享了同一个数据卷,舒适化容器将该共享数据集挂在到
/work-dir
路径,应用程序容器将共享数据卷挂在到
/usr/share/nginx/html
路径。初始化容器执行
command
命令后就退出容器。
执行该命令时,初始化容器将结果写入了应用程序
nginx
服务器对应的html根路径下的
index.html
。
2.9 容器组 –
PodDisruptionBudget(PDB)
PodDisruptionBudget(PDB)
应用程序管理员可以为每一个应用程序创建
PodDisruptionBudget
对象(
PDB
)。
PDB
限制了多副本应用程序在自愿的毁坏情况发生时,最多有多少个副本可以同时停止。例如,一个 web 前端的程序需要确保可用的副本数不低于总副本数的一定比例。属于一种保护机制。
PodDisruptionBudget
包含三个字段:
-
标签选择器
.spec.selector
用于指定
PDB
适用的
Pod
。此字段为必填 -
.spec.minAvailable
:当完成驱逐时,最少仍然要保留多少个
Pod
可用。该字段可以是一个整数,也可以是一个百分比 -
.spec.maxUnavailable
: 当完成驱逐时,最多可以有多少个
Pod
被终止。该字段可以是一个整数,也可以是一个百分比
在一个
PodDisruptionBudget
中,只能指定
maxUnavailable
和
minAvailable
中的一个。
maxUnavailable
只能应用到那些有控制器的
Pod
上。
PodDisruptionBudget 并不能真正确保指定数量(或百分比)的Pod始终保持可用。
例如,当 Pod 数量已经为 PDB 中指定的最小数时,某一个节点可能意外宕机,
导致 Pod 数量低于 PDB 中指定的数量。 PodDisruptionBudget 只能保护应用避免受到 自愿毁坏 的影响,
而不是所有原因的毁坏。
使用
minAvailable
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: zk-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: zookeeper
使用
maxUnavailable
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: zk-pdb
spec:
maxUnavailable: 1
selector:
matchLabels:
app: zookeeper
查看
pdb
kubectl get pdb
2.10 控制器 –
ReplicaSet (RS)
ReplicaSet (RS)
kubernetes
中
ReplicaSet
用来维护一个数量稳定的
Pod
副本集合,可以保证某种定义一样的
Pod
始终有指定数量的副本数在运行。
2.10.1
ReplicaSet
的工作方式
ReplicaSet
-
selector
: 用于指定哪些
Pod
属于该
ReplicaSet
的管辖范围 -
replicas
: 副本数,用于指定该
ReplicaSet
应该维持多少个
Pod
副本 -
template
:
Pod
模板,在
ReplicaSet
使用
Pod
模板的定义创建新的
Pod
ReplicaSet
控制器将通过创建或删除
Pod
,以使得当前
Pod
数量达到
replicas
指定的期望值,通过
selector
字段的定义,识别哪些
Pod
应该由其管理。如果
Pod
没有
ownerReference
字段,或者
ownerReference
字段指向的对象不是一个控制器,且该
Pod
匹配了
ReplicaSet
的
selector
,则该
Pod
的
ownerReference
将被修改为 该
ReplicaSet
的引用(包括不是由该RS创建的
Pod
)。
2.10.2 何时使用
ReplicaSet
ReplicaSet
ReplicaSet
用来维护一个数量稳定的
Pod
副本集合。
Deployment
是一个更高级别的概念,可以管理
ReplicaSet
,并提供声明式的更新,以及其他的许多有用的特性。因此,推荐用户总是使用
Deployment
,而不是直接使用
ReplicaSet
,除非您需要一些自定义的更新应用程序的方式,或者您完全不更新应用。
示例:
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: nginx
image: nginx
2.10.3
ReplicaSet
的定义
ReplicaSet
与其他
Kubernetes
对象一样,
ReplicaSet
需要的字段有:
apiVersion
:apps/v1
kind
:始终为
ReplicaSet
metadata
spec
:
ReplicaSet
的详细定义
PodTemplate
PodTemplate
-
.spec.template
字段是一个 Pod Template,为必填字段,且其中必须定义 -
.spec.template.metadata.labels
字段。在前面的ReplicaSet例子中,定义了 label 为 tier: frontend。请小心该字段不要与其他控制器的
selector
重合,以免这些控制器尝试接管该
Pod
。 -
.spec.template.spec.restartPolicy
的默认值为
Always
Pod Selector
Pod Selector
-
.spec.selector
字段为一个 标签选择器,用于识别可以接管哪些
Pod
。在前面的例子中,标签选择器为
Replicas
Replicas
-
spec.replicas
字段用于指定同时运行的
Pod
的副本数。
ReplicaSet
将创建或者删除由其管理的
Pod
,以便使副本数与该字段指定的值匹配。
2.10.4 使用
ReplicaSet
ReplicaSet
删除
ReplicaSet
及
Pod
ReplicaSet
Pod
kubectl delete -f nginx-rs.yaml
只删除
ReplicaSet
ReplicaSet
kubectl delete nginx-rs --casade=false
将Pod 从 ReplicaSet 中隔离
修改
Pod
的标签,可以使
Pod
脱离
ReplicaSet
的管理
2.11 控制器 –
Deployment (deploy)
Deployment (deploy)
2.11.1 创建
Deployment
Deployment
下面的 yaml 文件定义了一个
Deployment
,该
Deployment
将创建一个有 3 个 nginx
Pod
副本的
ReplicaSet
(副本集):
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
-
-
执行命令以创建
Deployment
kubectl apply -f nginx-ds.yaml
可以为该命令增加
--record
选项,此时
kubectl
会将
kubectl apply -f nginx-ds.yaml --record
写入 Deployment 的
annotation
(注解)
kubernetes.io/change-cause
中。这样,您在将来就可以回顾某一个 Deployment 版本变化的原因
-
查看 Deployment 的发布状态(
rollout status
),执行命令
kubectl rollout status deploy nginx-ds
-
查看 Pod 的标签,执行命令
kubectl get pods --show-labels
2.11.2 更新
Deployment
Deployment
-
执行以下命令,将容器镜像从
nginx:1.7.9
更新到
nginx:1.9.1
kubectl set image deploy nginx-ds nginx=nginx:1.9.1 --record
或者也可以
edit
该 Deployment,并将
.spec.template.spec.containers[0].image
从
nginx:1.7.9
修改为
nginx:1.9.1
kubectl edit deployments.apps nginx-ds
- 查看发布更新(rollout)的状态:
kubectl rollout status deployment nginx-ds
2.11.3 回滚
Deployment
Deployment
检查
Deployment
的更新历史
Deployment
-
执行命令
kubectl rollout history deployment nginx-ds
检查 Deployment 的历史版本,输出结果如下所示:
deployment.apps/nginx-ds
REVISION CHANGE-CAUSE
2 image update to 1.9.1
3 kubectl apply --filename=nginx-ds.yaml --record=true
CHANGE-CAUSE
是该
revision
(版本)创建时从 Deployment 的 annotation
kubernetes.io/change-cause
拷贝而来。
可以通过如下方式制定 CHANGE-CAUSE 信息:
-
为 Deployment 增加注解,
kubectl annotate deploy nginx-ds kubernetes.io/change-cause="image update to 1.9.1"
-
执行 kubectl apply 命令时,增加
--record
选项 -
手动编辑 Deployment 的
.metadata.annotation
信息
-
执行命令
kubectl rollout history deploy nginx-ds --revision=2
查看revision(版本)的详细信息。输出结果如下:
deployment.apps/nginx-ds with revision #2
Pod Template:
Labels: app=nginx
pod-template-hash=694854bbcb
Annotations: kubernetes.io/change-cause: image update to 1.9.1
Containers:
nginx:
Image: nginx:1.9.1
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
回滚到前一个
revision
(版本)
revision
-
执行命令
kubectl rollout undo deployment nginx-ds
将当前版本回滚到前一个版本
2.11.4 伸缩 –
Deployment
Deployment
执行伸缩
-
执行命令
kubectl scale deploy nginx --replicas=3
实现手动伸缩 -
执行命令
kubectl autoscale deployment nginx --max=5 --min=3 --cpu-percent=80
cpu在百分之下最大5个 最小三个副本自动伸缩
-
查看
horizontalpodautoscalers.autoscaling (hpa)
kubectl get hpa
2.11.5 暂停和继续 –
Deployment
Deployment
-
执行命令
kubectl rollout pause deployment nginx
暂停
Deployment
, -
执行命令
kubectl rollout resume deployment nginx
继续改
Deployment
2.11.6 清理策略
通过 Deployment 中
.spec.revisionHistoryLimit
字段,可指定为该 Deployment 保留多少个旧的
ReplicaSet
。超出该数字的将被在后台进行垃圾回收。该字段的默认值是
10
。如果该字段被设为
0
,Kubernetes 将清理掉该 Deployment 的所有历史版本(revision),因此,您将无法对该 Deployment 执行回滚操作
kubectl rollout undo
2.11.7 部署策略
通过 Deployment 中
.spec.strategy
字段,可以指定使用
滚动更新 RollingUpdate
的部署策略还是使用
重新创建 Recreate
的部署策略
字段名称 | 可选值 | 字段描述 |
---|---|---|
类型 | 滚动更新,重新创建 |
如果选择重新创建,
将先删除原有副本集中的所有
,然后再创建新的副本集和新的
。如此,更新过程中将出现一段应用程序不可用的情况; |
最大超出副本数 | 数字或百分比 |
滚动更新过程中,可以超出期望副本数的最大值。该取值可以是一个绝对值(例如:5),也可以是一个相对于期望副本数的百分比(例如:10%);如果填写百分比,则以期望副本数乘以该百分比后
;当最大超出副本数
为 0 时,此数值不能为 0;默认值为 25%。例如:假设此值被设定为 30%,当滚动更新开始时,新的副本集(
)可以立刻扩容,但是旧
和新
的总数不超过
期待副本数(
)的 130%。一旦旧 Pod 被终止后,新的副本集可以进一步扩容,但是整个滚动更新过程中,新旧
的总数不超过
期待副本数(
)的 130%。 |
最大不可用副本数 | 数字或百分比 |
滚动更新过程中,不可用副本数的最大值。该取值可以是一个绝对值(例如:5),也可以是一个相对于期望副本数的百分比(例如:10%);如果填写百分比,则以期望副本数乘以该百分比后
;当最大超出副本数
为 0 时,此数值不能为 0;默认值为 25%;例如:假设此值被设定为 30%,当滚动更新开始时,旧的副本集(
)可以缩容到期望副本数的 70%;在新副本集扩容的过程中,一旦新的
已就绪,旧的副本集可以进一步缩容,整个滚动更新过程中,确保新旧就绪副本数之和不低于期望副本数的 70%。 |
2.11.8 金丝雀发布(灰度发布)
如果您想使用 Deployment 将最新的应用程序版本发布给一部分用户(或服务器),您可以为每个版本创建一个 Deployment,此时,应用程序的新旧两个版本都可以同时获得生产上的流量。
-
部署第一个版本
第一个版本的
Deployment
包含了 3 个
Pod
副本,
Service
通过
label selector
app: nginx 选择对应的 Pod,nginx 的标签为 1.7.9
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
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
labels:
app: nginx
spec:
selector:
app: nginx
ports:
- name: nginx-port
protocol: TCP
port: 80
nodePort: 32600
targetPort: 80
type: NodePort
-
假如此时想发布新版本nginx 1.8.0,可以创建第二个
Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-canary
labels:
app: nginx
track: canary
spec:
replicas: 1
selector:
matchLabels:
app: nginx
track: canary
template:
metadata:
labels:
app: nginx
track: canary
spec:
containers:
- name: nginx
image: nginx:1.8.0
局限性
按照 Kubernetes 默认支持的这种方式进行金丝雀发布,有一定的局限性:
-
不能根据用户注册时间、地区等请求中的内容属性进行流量分配
-
同一个用户如果多次调用该
Service
,有可能第一次请求到了旧版本的
Pod
,第二次请求到了新版本的
Pod
在
Kubernetes
中不能解决上述局限性的原因是:Kubernetes Service 只在 TCP 层面解决负载均衡的问题,并不对请求响应的消息内容做任何解析和识别。如果想要更完善地实现金丝雀发布,可以考虑如下三种选择:
- 业务代码编码实现
-
Spring Cloud
灰度发布 -
Istio
灰度发布
2.12 控制器 –
StatefulSet
StatefulSet
2.12.1
StatefulSet
使用场景
StatefulSet
StatefulSet
顾名思义,用于管理
Stateful
(有状态)的应用程序。
StatefulSet 管理 Pod 时,确保其 Pod 有一个按顺序增长的 ID。
与
Deployment
相似,
StatefulSet
基于一个 Pod 模板管理其 Pod。与
Deployment
最大的不同在于
StatefulSet
始终将一系列不变的名字分配给其 Pod。这些 Pod 从同一个模板创建,但是并不能相互替换:每个
Pod
都对应一个特有的持久化存储标识。
同其他所有控制器一样,
StatefulSet
也使用相同的模式运作:用户在
StatefulSet
中定义自己期望的结果,
StatefulSet
控制器执行需要的操作,以使得该结果被达成。
场景:
- 稳定、唯一的网络标识(dnsname)
-
每个Pod始终对应各自的存储路径(
PersistantVolumeClaimTemplate
) - 按顺序地增加副本、减少副本,并在减少副本时执行清理
- 按顺序自动地执行滚动更新
如果一个应用程序不需要稳定的网络标识,或者不需要按顺序部署、删除、增加副本,您应该考虑使用
Deployment
这类无状态(stateless)的控制器。
2.12.2
StatefulSet 的基本信息
StatefulSet 的基本信息
创建StatefulSet
-
一个名为 nginx 的
Headless Service
,用于控制网络域 -
一个名为 web 的
StatefulSet
,副本数为 3 -
volumeClaimTemplates
提供稳定的存储(每一个 Pod ID 对应自己的存储卷,且
Pod
重建后,仍然能找到对应的存储卷)
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
2.13 控制器 –
DaemonSet
DaemonSet
2.14
Service
Service
2.14.1
Service
概述
Service
-
为何需要
Service
?
Kubernetes
中
Pod
是随时可以消亡的(节点故障、容器内应用程序错误等原因)。如果使用
Deployment
运行您的应用程序,
Deployment
将会在
Pod
消亡后再创建一个新的
Pod
以维持所需要的副本数。每一个
Pod
有自己的 IP 地址,然而,对于
Deployment
而言,对应
Pod
集合是动态变化的。 -
Kubernetes Service
Kubernetes 中 Service 是一个 API 对象,通过 kubectl + YAML 或者 Kuboard,定义一个 Service,可以将符合 Service 指定条件的 Pod 作为可通过网络访问的服务提供给服务调用者。
Service 是 Kubernetes 中的一种服务发现机制:- Pod 有自己的 IP 地址
- Service 被赋予一个唯一的 dns name
- Service 通过 label selector 选定一组 Pod
- Service 实现负载均衡,可将请求均衡分发到选定这一组 Pod 中
2.14.2
Service
的详细描述
Service
创建
Service
Service
- 每个Pod都监听9376 TCP端口
-
每个Pod都有标签
app=MyApp
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
-
Kubernetes 将为该
Service
分配一个 IP 地址(
ClusterIP
或
集群内 IP
),供 Service Proxy 使用 -
Kubernetes 将不断扫描符合该
selector
的
Pod
,并将最新的结果更新到与
Service
同名 my-service 的
Endpoint
对象中。
TIP -
Pod
的定义中,
Port
可能被赋予了一个名字,您可以在
Service
的
targetPort
字段引用这些名字,而不是直接写端口号。这种做法可以使得您在将来修改后端程序监听的端口号,而无需影响到前端程序。 -
Service
的默认传输协议是
TCP
,您也可以使用其他 支持的传输协议。
Kubernetes Service 中,可以定义多个端口,不同的端口可以使用相同或不同的传输协议。
多端口得
Service
Service
可以在一个
Service
对象中定义多个端口,但必须为每个端口定义一个名字
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
nodePort: 32080
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
# nodeport 模式
type: NodePort
2.14.3
Ingress Nginx
暴露服务
Ingress Nginx
Ingress
Ingress
Ingress
是Kubernetes 的一种API对象,将集群内部的Service通过HTTP/HTTPS 方式暴露到集群外部,并通过规则定义HTTP/HTTPS 的路由。Ingress 具备如下特性:集群外部可访问URL,负载均衡,SSL Termination,按域路由。
Ingress Controller
Ingress Controller
Ingress Controller
(通常需要负载均衡器配合)负责实现Ingress API 对象所声明的能力。
使用
Ingress Nginx
实现
Ingress Controller
-
安装
Helm 3
(Kubernetes包管理工具)
wget https://get.helm.sh/helm-v3.4.1-linux-amd64.tar.gz
tar zxvf helm-v3.4.1-linux-amd64.tar.gz
mv linux-amd64/helm /usr/local/bin/helm
-
使用
Helm
安装
Ingress Nginx
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm pull ingress-nginx/ingress-nginx
tar -zxvf ingress-nginx-3.12.0.tgz
# 编辑valuse.yaml 文件
vim values.yaml
## nginx configuration
## Ref: https://github.com/kubernetes/ingress-nginx/blob/master/controllers/nginx/configuration.md
##
controller:
image:
repository: registry.cn-hangzhou.aliyuncs.com/k8s_gcr_io_allen/ingress-nginx-controller
tag: "v0.41.2"
pullPolicy: IfNotPresent
# www-data -> uid 101
runAsUser: 101
allowPrivilegeEscalation: true
# Configures the ports the nginx-controller listens on
containerPort:
http: 80
https: 443
# Will add custom configuration options to Nginx https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/
config: {}
## Annotations to be added to the controller config configuration configmap
##
configAnnotations: {}
# Will add custom headers before sending traffic to backends according to https://github.com/kubernetes/ingress-nginx/tree/master/docs/examples/customization/custom-headers
proxySetHeaders: {}
# Will add custom headers before sending response traffic to the client according to: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#add-headers
addHeaders: {}
# Optionally customize the pod dnsConfig.
dnsConfig: {}
# Optionally change this to ClusterFirstWithHostNet in case you have 'hostNetwork: true'.
# By default, while using host network, name resolution uses the host's DNS. If you wish nginx-controller
# to keep resolving names inside the k8s network, use ClusterFirstWithHostNet.
dnsPolicy: ClusterFirst
# Bare-metal considerations via the host network https://kubernetes.github.io/ingress-nginx/deploy/baremetal/#via-the-host-network
# Ingress status was blank because there is no Service exposing the NGINX Ingress controller in a configuration using the host network, the default --publish-service flag used in standard cloud setups does not apply
reportNodeInternalIp: false
# Required for use with CNI based kubernetes installations (such as ones set up by kubeadm),
# since CNI and hostport don't mix yet. Can be deprecated once https://github.com/kubernetes/kubernetes/issues/23920
# is merged
hostNetwork: false
## Use host ports 80 and 443
## Disabled by default
##
hostPort:
enabled: false
ports:
http: 80
https: 443
## Election ID to use for status update
##
electionID: ingress-controller-leader
## Name of the ingress class to route through this controller
##
ingressClass: nginx
# labels to add to the pod container metadata
podLabels: {}
# key: value
## Security Context policies for controller pods
##
podSecurityContext: {}
## See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for
## notes on enabling and using sysctls
###
sysctls: {}
# sysctls:
# "net.core.somaxconn": "8192"
## Allows customization of the source of the IP address or FQDN to report
## in the ingress status field. By default, it reads the information provided
## by the service. If disable, the status field reports the IP address of the
## node or nodes where an ingress controller pod is running.
publishService:
enabled: true
## Allows overriding of the publish service to bind to
## Must be <namespace>/<service_name>
##
pathOverride: ""
## Limit the scope of the controller
##
scope:
enabled: false
namespace: "" # defaults to .Release.Namespace
## Allows customization of the configmap / nginx-configmap namespace
##
configMapNamespace: "" # defaults to .Release.Namespace
## Allows customization of the tcp-services-configmap
##
tcp:
configMapNamespace: "" # defaults to .Release.Namespace
## Annotations to be added to the tcp config configmap
annotations: {}
## Allows customization of the udp-services-configmap
##
udp:
configMapNamespace: "" # defaults to .Release.Namespace
## Annotations to be added to the udp config configmap
annotations: {}
## Additional command line arguments to pass to nginx-ingress-controller
## E.g. to specify the default SSL certificate you can use
## extraArgs:
# default-ssl-certificate: "ailuoli-ingress-nginx-secret"
extraArgs: {}
## Additional environment variables to set
extraEnvs: []
# extraEnvs:
# - name: FOO
# valueFrom:
# secretKeyRef:
# key: FOO
# name: secret-resource
## DaemonSet or Deployment
##
kind: DaemonSet
## Annotations to be added to the controller Deployment or DaemonSet
##
annotations: {}
# keel.sh/pollSchedule: "@every 60m"
## Labels to be added to the controller Deployment or DaemonSet
##
labels: {}
# keel.sh/policy: patch
# keel.sh/trigger: poll
# The update strategy to apply to the Deployment or DaemonSet
##
updateStrategy: {}
# rollingUpdate:
# maxUnavailable: 1
# type: RollingUpdate
# minReadySeconds to avoid killing pods before we are ready
##
minReadySeconds: 0
## Node tolerations for server scheduling to nodes with taints
## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
##
tolerations: []
# - key: "key"
# operator: "Equal|Exists"
# value: "value"
# effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)"
## Affinity and anti-affinity
## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
##
affinity: {}
# # An example of preferred pod anti-affinity, weight is in the range 1-100
# podAntiAffinity:
# preferredDuringSchedulingIgnoredDuringExecution:
# - weight: 100
# podAffinityTerm:
# labelSelector:
# matchExpressions:
# - key: app.kubernetes.io/name
# operator: In
# values:
# - ingress-nginx
# - key: app.kubernetes.io/instance
# operator: In
# values:
# - ingress-nginx
# - key: app.kubernetes.io/component
# operator: In
# values:
# - controller
# topologyKey: kubernetes.io/hostname
# # An example of required pod anti-affinity
# podAntiAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# - labelSelector:
# matchExpressions:
# - key: app.kubernetes.io/name
# operator: In
# values:
# - ingress-nginx
# - key: app.kubernetes.io/instance
# operator: In
# values:
# - ingress-nginx
# - key: app.kubernetes.io/component
# operator: In
# values:
# - controller
# topologyKey: "kubernetes.io/hostname"
## Topology spread constraints rely on node labels to identify the topology domain(s) that each Node is in.
## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/
##
topologySpreadConstraints: []
# - maxSkew: 1
# topologyKey: failure-domain.beta.kubernetes.io/zone
# whenUnsatisfiable: DoNotSchedule
# labelSelector:
# matchLabels:
# app.kubernetes.io/instance: ingress-nginx-internal
## terminationGracePeriodSeconds
## wait up to five minutes for the drain of connections
##
terminationGracePeriodSeconds: 300
## Node labels for controller pod assignment
## Ref: https://kubernetes.io/docs/user-guide/node-selection/
##
nodeSelector:
kubernetes.io/os: linux
ingressApp: ingress
## Liveness and readiness probe values
## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes
##
livenessProbe:
failureThreshold: 5
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
port: 10254
readinessProbe:
failureThreshold: 3
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
port: 10254
# Path of the health check endpoint. All requests received on the port defined by
# the healthz-port parameter are forwarded internally to this path.
healthCheckPath: "/healthz"
## Annotations to be added to controller pods
##
podAnnotations: {}
replicaCount: 1
minAvailable: 1
# Define requests resources to avoid probe issues due to CPU utilization in busy nodes
# ref: https://github.com/kubernetes/ingress-nginx/issues/4735#issuecomment-551204903
# Ideally, there should be no limits.
# https://engineering.indeedblog.com/blog/2019/12/cpu-throttling-regression-fix/
resources:
# limits:
# cpu: 100m
# memory: 90Mi
requests:
cpu: 100m
memory: 90Mi
# Mutually exclusive with keda autoscaling
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 11
targetCPUUtilizationPercentage: 50
targetMemoryUtilizationPercentage: 50
autoscalingTemplate: []
# Custom or additional autoscaling metrics
# ref: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-custom-metrics
# - type: Pods
# pods:
# metric:
# name: nginx_ingress_controller_nginx_process_requests_total
# target:
# type: AverageValue
# averageValue: 10000m
# Mutually exclusive with hpa autoscaling
keda:
apiVersion: "keda.sh/v1alpha1"
# apiVersion changes with keda 1.x vs 2.x
# 2.x = keda.sh/v1alpha1
# 1.x = keda.k8s.io/v1alpha1
enabled: false
minReplicas: 1
maxReplicas: 11
pollingInterval: 30
cooldownPeriod: 300
restoreToOriginalReplicaCount: false
triggers: []
# - type: prometheus
# metadata:
# serverAddress: http://<prometheus-host>:9090
# metricName: http_requests_total
# threshold: '100'
# query: sum(rate(http_requests_total{deployment="my-deployment"}[2m]))
behavior: {}
# scaleDown:
# stabilizationWindowSeconds: 300
# policies:
# - type: Pods
# value: 1
# periodSeconds: 180
# scaleUp:
# stabilizationWindowSeconds: 300
# policies:
# - type: Pods
# value: 2
# periodSeconds: 60
## Enable mimalloc as a drop-in replacement for malloc.
## ref: https://github.com/microsoft/mimalloc
##
enableMimalloc: true
## Override NGINX template
customTemplate:
configMapName: ""
configMapKey: ""
service:
enabled: true
annotations: {}
labels: {}
# clusterIP: ""
## List of IP addresses at which the controller services are available
## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips
##
externalIPs: []
# loadBalancerIP: ""
loadBalancerSourceRanges: []
enableHttp: true
enableHttps: true
## Set external traffic policy to: "Local" to preserve source IP on
## providers supporting it
## Ref: https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typeloadbalancer
# externalTrafficPolicy: ""
# Must be either "None" or "ClientIP" if set. Kubernetes will default to "None".
# Ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies
# sessionAffinity: ""
# specifies the health check node port (numeric port number) for the service. If healthCheckNodePort isn’t specified,
# the service controller allocates a port from your cluster’s NodePort range.
# Ref: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip
# healthCheckNodePort: 0
ports:
http: 80
https: 443
targetPorts:
http: 80
https: 443
type: NodePort
# type: NodePort
# nodePorts:
# http: 32080
# https: 32443
# tcp:
# 8080: 32808
nodePorts:
http: "80"
https: "443"
tcp:
8080: 8080
udp: {}
## Enables an additional internal load balancer (besides the external one).
## Annotations are mandatory for the load balancer to come up. Varies with the cloud service.
internal:
enabled: false
annotations: {}
## Restrict access For LoadBalancer service. Defaults to 0.0.0.0/0.
loadBalancerSourceRanges: []
## Set external traffic policy to: "Local" to preserve source IP on
## providers supporting it
## Ref: https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typeloadbalancer
# externalTrafficPolicy: ""
extraContainers: []
## Additional containers to be added to the controller pod.
## See https://github.com/lemonldap-ng-controller/lemonldap-ng-controller as example.
# - name: my-sidecar
# image: nginx:latest
# - name: lemonldap-ng-controller
# image: lemonldapng/lemonldap-ng-controller:0.2.0
# args:
# - /lemonldap-ng-controller
# - --alsologtostderr
# - --configmap=$(POD_NAMESPACE)/lemonldap-ng-configuration
# env:
# - name: POD_NAME
# valueFrom:
# fieldRef:
# fieldPath: metadata.name
# - name: POD_NAMESPACE
# valueFrom:
# fieldRef:
# fieldPath: metadata.namespace
# volumeMounts:
# - name: copy-portal-skins
# mountPath: /srv/var/lib/lemonldap-ng/portal/skins
extraVolumeMounts: []
## Additional volumeMounts to the controller main container.
# - name: copy-portal-skins
# mountPath: /var/lib/lemonldap-ng/portal/skins
extraVolumes: []
## Additional volumes to the controller pod.
# - name: copy-portal-skins
# emptyDir: {}
extraInitContainers: []
## Containers, which are run before the app containers are started.
# - name: init-myservice
# image: busybox
# command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
admissionWebhooks:
annotations: {}
enabled: false
failurePolicy: Fail
# timeoutSeconds: 10
port: 8443
certificate: "/usr/local/certificates/cert"
key: "/usr/local/certificates/key"
namespaceSelector: {}
objectSelector: {}
service:
annotations: {}
# clusterIP: ""
externalIPs: []
# loadBalancerIP: ""
loadBalancerSourceRanges: []
servicePort: 443
type: ClusterIP
patch:
enabled: true
image:
repository: docker.io/jettech/kube-webhook-certgen
tag: v1.5.0
pullPolicy: IfNotPresent
## Provide a priority class name to the webhook patching job
##
priorityClassName: ""
podAnnotations: {}
nodeSelector: {}
tolerations: []
runAsUser: 2000
metrics:
port: 10254
# if this port is changed, change healthz-port: in extraArgs: accordingly
enabled: false
service:
annotations: {}
# prometheus.io/scrape: "true"
# prometheus.io/port: "10254"
# clusterIP: ""
## List of IP addresses at which the stats-exporter service is available
## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips
##
externalIPs: []
# loadBalancerIP: ""
loadBalancerSourceRanges: []
servicePort: 9913
type: ClusterIP
# externalTrafficPolicy: ""
# nodePort: ""
serviceMonitor:
enabled: false
additionalLabels: {}
namespace: ""
namespaceSelector: {}
# Default: scrape .Release.Namespace only
# To scrape all, use the following:
# namespaceSelector:
# any: true
scrapeInterval: 30s
# honorLabels: true
targetLabels: []
metricRelabelings: []
prometheusRule:
enabled: false
additionalLabels: {}
# namespace: ""
rules: []
# # These are just examples rules, please adapt them to your needs
# - alert: NGINXConfigFailed
# expr: count(nginx_ingress_controller_config_last_reload_successful == 0) > 0
# for: 1s
# labels:
# severity: critical
# annotations:
# description: bad ingress config - nginx config test failed
# summary: uninstall the latest ingress changes to allow config reloads to resume
# - alert: NGINXCertificateExpiry
# expr: (avg(nginx_ingress_controller_ssl_expire_time_seconds) by (host) - time()) < 604800
# for: 1s
# labels:
# severity: critical
# annotations:
# description: ssl certificate(s) will expire in less then a week
# summary: renew expiring certificates to avoid downtime
# - alert: NGINXTooMany500s
# expr: 100 * ( sum( nginx_ingress_controller_requests{status=~"5.+"} ) / sum(nginx_ingress_controller_requests) ) > 5
# for: 1m
# labels:
# severity: warning
# annotations:
# description: Too many 5XXs
# summary: More than 5% of all requests returned 5XX, this requires your attention
# - alert: NGINXTooMany400s
# expr: 100 * ( sum( nginx_ingress_controller_requests{status=~"4.+"} ) / sum(nginx_ingress_controller_requests) ) > 5
# for: 1m
# labels:
# severity: warning
# annotations:
# description: Too many 4XXs
# summary: More than 5% of all requests returned 4XX, this requires your attention
## Improve connection draining when ingress controller pod is deleted using a lifecycle hook:
## With this new hook, we increased the default terminationGracePeriodSeconds from 30 seconds
## to 300, allowing the draining of connections up to five minutes.
## If the active connections end before that, the pod will terminate gracefully at that time.
## To effectively take advantage of this feature, the Configmap feature
## worker-shutdown-timeout new value is 240s instead of 10s.
##
lifecycle:
preStop:
exec:
command:
- /wait-shutdown
priorityClassName: ""
## Rollback limit
##
revisionHistoryLimit: 10
# Maxmind license key to download GeoLite2 Databases
# https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-geolite2-databases
maxmindLicenseKey: ""
## Default 404 backend
##
defaultBackend:
##
enabled: false
image:
repository: k8s.gcr.io/defaultbackend-amd64
tag: "1.5"
pullPolicy: IfNotPresent
# nobody user -> uid 65534
runAsUser: 65534
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
extraArgs: {}
serviceAccount:
create: true
name:
## Additional environment variables to set for defaultBackend pods
extraEnvs: []
port: 8080
## Readiness and liveness probes for default backend
## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/
##
livenessProbe:
failureThreshold: 3
initialDelaySeconds: 30
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
readinessProbe:
failureThreshold: 6
initialDelaySeconds: 0
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 5
## Node tolerations for server scheduling to nodes with taints
## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
##
tolerations: []
# - key: "key"
# operator: "Equal|Exists"
# value: "value"
# effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)"
affinity: {}
## Security Context policies for controller pods
## See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for
## notes on enabling and using sysctls
##
podSecurityContext: {}
# labels to add to the pod container metadata
podLabels: {}
# key: value
## Node labels for default backend pod assignment
## Ref: https://kubernetes.io/docs/user-guide/node-selection/
##
nodeSelector: {}
## Annotations to be added to default backend pods
##
podAnnotations: {}
replicaCount: 1
minAvailable: 1
resources: {}
# limits:
# cpu: 10m
# memory: 20Mi
# requests:
# cpu: 10m
# memory: 20Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 2
targetCPUUtilizationPercentage: 50
targetMemoryUtilizationPercentage: 50
service:
annotations: {}
# clusterIP: ""
## List of IP addresses at which the default backend service is available
## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips
##
externalIPs: []
# loadBalancerIP: ""
loadBalancerSourceRanges: []
servicePort: 80
type: ClusterIP
priorityClassName: ""
## Enable RBAC as per https://github.com/kubernetes/ingress/tree/master/examples/rbac/nginx and https://github.com/kubernetes/ingress/issues/266
rbac:
create: true
scope: false
# If true, create & use Pod Security Policy resources
# https://kubernetes.io/docs/concepts/policy/pod-security-policy/
podSecurityPolicy:
enabled: false
serviceAccount:
create: true
name:
## Optional array of imagePullSecrets containing private registry credentials
## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
imagePullSecrets: []
# - name: secretName
# TCP service key:value pairs
# Ref: https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx/examples/tcp
##
tcp: {}
# 8080: "default/example-tcp-svc:9000"
# UDP service key:value pairs
# Ref: https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx/examples/udp
##
udp: {}
# 53: "kube-system/kube-dns:53"
这里选择是要
NodePort
得方式部署这个
ingress nginx
,默认方式是使用
LoadBlancer
得,所以需要修改一些配置
-
controller.image.repository
:
registry.cn-hangzhou.aliyuncs.com/k8s_gcr_io_allen/ingress-nginx-controller
image 得仓库要是可用得,默认的是谷歌得镜像,无法访问。 -
controller.kind
:
DaemonSet
这个apiVersion建议使用 DaemonSet -
controller.nodeSelector.ingressApp:
ingress
这里新增个label 选择部署得节点 -
controller.service.type
:
NodePort
这里得类型改为NodePort,并且将端口号暴漏出去,注意这里需要把集群得端口范围改为
1-65535
nodePorts:
http: "80"
https: "443"
tcp:
8080: 8080
udp: {}
-
测试部署一个应用并暴漏出去,并且配置https,云服务器要配置80 和 443 端口得开放
先创建个secret 用于配置 https。
kubectl create secret tls ailuoli-nginx-secret --cert=ailuoli.cn.crt --key=ailuoli.cn.key -n test
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
labels:
app: nginx
spec:
selector:
app: nginx
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
- name: https
protocol: TCP
port: 443
targetPort: 443
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: my-ingress-for-nginx # Ingress 的名字,仅用于标识
annotations:
# 路径匹配规则,匹配后路径rewrite 为 / ,例如访问 https://ailuoli.cn/nginx,实际得路径匹配到了/nginx规则,路由到真正得服务则是https://ailuoli.cn/
nginx.ingress.kubernetes.io/rewrite-target: /
kubernetes.io/ingress.class: nginx
# 设置http请求强制路由到https
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
rules: # Ingress 中定义 L7 路由规则
- host: ailuoli.cn # 根据 virtual hostname 进行路由(请使用您自己的域名)
http:
paths: # 按路径进行路由
- path: /nginx
backend:
serviceName: nginx-service # 指定后端的 Service 为之前创建的 nginx-service
servicePort: 80
tls:
- hosts:
- ailuoli.cn
secretName: ailuoli-ingress-nginx-secret
执行创建
Deployment
,
Service
,
Ingress
kubectl apply -f nginx-ingress.yaml
2.15 数据卷
Volume
Volume
kubernetes volume
主要解决得问题
- 数据持久性:通常情况下,容器运行起来之后,写入到其文件系统的文件暂时性的。当容器崩溃后,kubelet 将会重启该容器,此时原容器运行后写入的文件将丢失,因为容器将重新从镜像创建。
- 数据共享:同一个 Pod(容器组)中运行的容器之间,经常会存在共享文件/文件夹的需求
2.15.1 数据卷的类型
-
emptyDir
emptyDir
类型的数据卷在容器组被创建时分配给该容器组,并且直到容器组被移除,该数据卷才被释放。该数据卷初始分配时,始终是个空目录。同一容器组中的不通容器都可以对该目录执行读写操作,并且共享其中的数据,当容器组被删除时,
emptyDir
数据卷中的数据将被永久删除。 -
nfs
nfs
类型的数据卷可以加载NFS 到容器组/容器。容器组被移除时,将仅仅umount(写在)NFS数据卷,NFS中的数据仍将被保留。 -
cephfs
cephfs
数据卷可以挂载一个外部CephFS卷到容器组中。 -
hostPath
hostPath
类型的数据卷将Pod所在的节点文件系统上某一个文件或文件夹挂载进容器组。
除了为
hostPath
指定 path 字段以外,您还可以为其指定
type
字段,可选的
type
字段描述如下:
Type 字段取值 | 描述 |
---|---|
空字符串(default)用于向后兼容,此时,kubernetes 在挂载 hostPath 数据卷前不会执行任何检查 | |
|
如果指定的 hostPath 路径不存在,kubernetes 将在节点的该路径上创建一个空文件夹,权限设置为 0755,与 kubelet 进程具备相同的 group 和 ownership |
|
指定 hostPath 路径必须存在,且是一个文件夹 |
|
如果指定的 hostPath 路径不存在,kubernetes 将在节点的该路径上创建一个空的文件,权限设置为 0644,与 kubelet 进程具备相同的 group 和 ownership |
|
指定 hostPath 路径必须存在,且是一个文件 |
|
指定 hostPath 路径必须存在,且是一个 Unix Socket |
|
指定 hostPath 路径必须存在,且是一个 character device |
|
指定 hostPath 路径必须存在,且是一个 block device |
-
configMap
ConfigMap
提供了一种向容器组注入配置信息的途径。
ConfigMap
中的数据可以被Pod中的容器作为一个数据卷挂载。
在数据卷中引用ConfigMap时- 可以引用整个ConfigMap到数据卷,此时 ConfigMap 中的每一个 key 对应一个文件名,value 对应该文件的内容
- 只引用 ConfigMap 中的某一个名值对,此时可以将 key 映射成一个新的文件名
-
secret
secret
数据卷可以用来注入敏感信息(例如密码)到容器组。您可以将敏感信息存入 kubernetes secret 对象,并通过 Volume(数据卷)以文件的形式挂载到容器组(或容器)。
secret
数据卷使用 tmpfs(基于 RAM 的文件系统)挂载。 -
persistentVolumeClaim
persistentVolumeClaim
数据卷用来挂载 PersistentVolume 存储卷。PersistentVolume 存储卷为用户提供了一种在无需关心具体所在云环境的情况下”声明“ 所需持久化存储的方式。
2.15.2 数据卷挂载
数据卷内子路径
我们需要在同一个 Pod 的不同容器间共享数据卷。使用
volumeMounts.subPath
属性,可以使容器在挂载数据卷时指向数据卷内部的一个子路径,而不是直接指向数据卷的根路径。
下面的例子中,一个 LAMP(Linux Apache Mysql PHP)应用的 Pod 使用了一个共享数据卷,HTML 内容映射到数据卷的
html
目录,数据库的内容映射到了
mysql
目录
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
readOnly: false
- name: php
image: php:7.0-apache
volumeMounts:
- mountPath: /var/www/html
name: site-data
subPath: html
readOnly: false
volumes:
- name: site-data
persistentVolumeClaim:
claimName: my-lamp-site-data
通过环境变量指定数据卷内子路径
使用
volumeMounts.subPathExpr
字段,可以通过容器的环境变量指定容器内路径。使用此特性时,必须启用
VolumeSubpathEnvExpansion
如下面的例子,该 Pod 使用
subPathExpr
在
hostPath
数据卷
/var/log/pods
中创建了一个目录
pod1
(该参数来自于Pod的名字)。此时,宿主机目录
/var/log/pods/pod1
挂载到了容器的 /logs 路径:
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- name: container1
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
image: busybox
command: [ "sh", "-c", "while [ true ]; do echo 'Hello'; sleep 10; done | tee -a /logs/hello.txt" ]
volumeMounts:
- name: workdir1
mountPath: /logs
subPathExpr: $(POD_NAME)
readOnly: false
restartPolicy: Never
volumes:
- name: workdir1
hostPath:
path: /var/log/pods
挂载传播
数据卷的挂载传播(Mount Propagation)由 Pod 的
spec.containers[*].volumeMounts.mountPropagation
字段控制。可选的取值有:
-
None
: 默认值。在数据卷被挂载到容器之后,此数据卷不会再接受任何后续宿主机或其他容器挂载到该数据卷对应目录下的子目录的挂载。同样的,在容器中向该数据卷对应目录挂载新目录时,宿主机也不能看到。对应 Linux 的 private mount propagation 选项 。 -
HostToContainer
: 在数据卷被挂载到容器之后,宿主机向该数据卷对应目录添加挂载时,对容器是可见的。对应 Linux 的 rslave mount propagation 选项。 -
Bidirectional
: 在数据卷被挂载到容器之后,宿主机向该数据卷对应目录添加挂载时,对容器是可见的;同时,从容器中向该数据卷创建挂载,同样也对宿主机可见。对应 Linux 的 rshared mount propagation 选项
2.15.3
PersistentVolume
PersistentVolume
与管理计算资源相比,管理存储资源是一个完全不同的问题。为了更好的管理存储,Kubernetes 引入了
PersistentVolume
和
PersistentVolumeClaim
两个概念,将存储管理抽象成如何提供存储以及如何使用存储两个关注点。
-
PersistentVolume
(PV 存储卷)是集群中的一块存储空间,由集群管理员管理、或者由 Storage Class(存储类)自动管理。PV(存储卷)和 node(节点)一样,是集群中的资源(kubernetes 集群由存储资源和计算资源组成)。
PersistentVolumeClaim
(存储卷声明)是一种类型的 Volume(数据卷),
PersistentVolumeClaim
(存储卷声明)引用的
PersistentVolume
(存储卷)有自己的生命周期,该生命周期独立于任何使用它的容器组。
PersistentVolume
(存储卷)描述了如何提供存储的细节信息(NFS、cephfs等存储的具体参数)。 -
PersistentVolumeClaim
(PVC 存储卷声明)代表用户使用存储的请求。Pod 容器组消耗 node 计算资源,PVC 存储卷声明消耗
PersistentVolume
存储资源。Pod 容器组可以请求特定数量的计算资源(CPU / 内存);
PersistentVolumeClaim
可以请求特定大小/特定访问模式(只能被单节点读写/可被多节点只读/可被多节点读写)的存储资源。
存储卷和存储卷声明的关系
-
PersistentVolume
是集群中的存储资源,通常由集群管理员创建和管理 -
StorageClass
用于对
PersistentVolume
进行分类,如果正确配置,
StorageClass
也可以根据
PersistentVolumeClaim
的请求动态创建
Persistent Volume
-
PersistentVolumeClaim
是使用该资源的请求,通常由应用程序提出请求,并指定对应的
StorageClass
和需求的空间大小 -
PersistentVolumeClaim
可以做为数据卷的一种,被挂载到容器组/容器中使用
为PVC 提供 PV的两种方式
-
静态提供static
集群管理员实现创建好一系列
PersistentVolume
,它们包含了可供集群中应用程序使用的关于实际存储的具体信息。 -
动态提供Dynamic
在配置有合适的
StorageClass
(存储类)且
PersistentVolumeClaim
关联了该
StorageClass
的情况下,kubernetes 集群可以为应用程序动态创建
PersistentVolume
。
绑定Binding
如果一个
PersistentVolume
是动态提供给一个新的
PersistentVolumeClaim
,Kubernetes master 会始终将其绑定到该
PersistentVolumeClaim
。除此之外,应用程序将被绑定一个不小于(可能大于)其
PersistentVolumeClaim
中请求的存储空间大小的
PersistentVolume
。一旦绑定,
PersistentVolumeClaim
将拒绝其他
PersistentVolume
的绑定关系。PVC 与 PV 之间的绑定关系是一对一的映射。
使用 Using
对于 Pod 容器组来说,
PersistentVolumeClaim
存储卷声明是一种类型的 Volume 数据卷。Kubernetes 集群将
PersistentVolumeClaim
所绑定的
PersistentVolume
挂载到容器组供其使用。
Storage Object in Use Protection
-
使用中保护(
Storage Object in Use Protection
)的目的是确保正在被容器组使用的
PersistentVolumeClaim
以及其绑定的
PersistentVolume
不能被系统删除,以避免可能的数据丢失。 -
如果用户删除一个正在使用中的
PersistentVolumeClaim
,则该 PVC 不会立即被移除掉,而是推迟到该 PVC 不在被任何容器组使用时才移除;同样的如果管理员删除了一个已经绑定到 PVC 的
PersistentVolume
,则该 PV 也不会立刻被移除掉,而是推迟到其绑定的 PVC 被删除后才移除掉。
回收 Reclaiming
当用户不在需要其数据卷时,可以删除掉其
PersistentVolumeClaim
,此时其对应的
PersistentVolume
将被集群回收并再利用。Kubernetes 集群根据
PersistentVolume
中的 reclaim policy(回收策略)决定在其被回收时做对应的处理。当前支持的回收策略有:Retained(保留)、Recycled(重复利用)、Deleted(删除)
-
保留 Retain
保留策略需要集群管理员手工回收该资源。当绑定的 PersistentVolumeClaim 被删除后,PersistentVolume 仍然存在,并被认为是”已释放“。但是此时该存储卷仍然不能被其他 PersistentVolumeClaim 绑定,因为前一个绑定的 PersistentVolumeClaim 对应容器组的数据还在其中。集群管理员可以通过如下步骤回收该 PersistentVolume:- 删除该 PersistentVolume。PV 删除后,其数据仍然存在于对应的外部存储介质中(nfs、cefpfs、glusterfs 等)
- 手工删除对应存储介质上的数据
- 手工删除对应的存储介质,您也可以创建一个新的 PersistentVolume 并再次使用该存储介质
-
删除 Delete
删除策略将从 kubernete 集群移除
PersistentVolume
以及其关联的外部存储介质(云环境中的 AWA EBS、GCE PD、Azure Disk 或 Cinder volume)。 -
再利用 Recycle
再利用策略将在
PersistentVolume
回收时,执行一个基本的清除操作(rm -rf /thevolume/*),并使其可以再次被新的
PersistentVolumeClaim
绑定。
集群管理员也可以自定义一个 recycler pod template,用于执行清除操作
2.15.4 使用
nfs
做存储 创建
StorageClass
nfs
StorageClass
搭建
nfs server
nfs server
yum install -y nfs-utils
mkdir /data/nfsdata/
chmod 777 /data/nfsdata/
编辑
/etc/exports
文件,内容如下
/data/nfsdata/ *(rw,sync,insecure,no_subtree_check,no_root_squash)
其中
*
可以指定那些 ip 可以共享该文件夹
systemctl enable rpcbind
systemctl enable nfs-server
systemctl start rpcbind
systemctl start nfs-server
添加公网 ip 作为网卡
cat > /etc/sysconfig/network-scripts/ifcfg-eth0:1 <<EOF
BOOTPROTO=static
DEVICE=eth0:1
IPADDR=120.53.234.127
PREFIX=32
TYPE=Ethernet
USERCTL=no
ONBOOT=yes
EOF
客户端测试
nfs server
nfs server
yum install -y nfs-utils
# 检查服务端是否设有共享目录
showmount -e ailuoli.cn
执行命令挂载nfs服务器上的共享目录到本机路径
mkdir /data/share
mount -t nfs ailuoli.cn:/data/nfsdata /data/share
#写一个测试文件
echo "hello nfs server" > /data/share/test.txt
验证nfs server 有没有
cat /data/nfsdata/test.txt
取消挂载
umount /data/share
创建
Storage Class
来自动创建 PVC
Storage Class
使用
helm
部署
nfs-client-provisioner
helm repo add stable https://charts.helm.sh/stable
helm pull stable/nfs-client-provisioner
tar -zxvf nfs-client-provisioner-1.2.11.tgz
修改
values.yaml
nfs:
server: ailuoli.cn
path: /data/nfsdata
... 删除策略
reclaimPolicy: Retain
执行创建 nfs-client
helm install nfs-client .
# 查看创建好的 Storage Class
kubectl get sc
测试下是否可以正常创建 PVC,创建 pvc-test.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
执行文件
kubectl apply -f pvc-test.yaml
查看 pvc
kubectl get pvc
结果如下,status 为 Bound 就为正常
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
data-mysql-0 Bound pvc-be7fdb16-e579-4857-b2ef-e5015b56e6f8 8Gi RWO nfs-client 9h
k8s 1.20 以上版本遇到 PVC Pending 问题
编辑该文件
vim /etc/kubernetes/manifests/kube-apiserver.yaml
添加此配置
- --feature-gates=RemoveSelfLink=false
2.16
ConfigMap
ConfigMap
可以使用
kubectl create configmap
命令基于目录,文件,字面值来创建
kubectl create configmap <map-name> <data-source>
基于目录创建configMap
kubectl create configmap test-config --from-file=./dev/
查看
configmaps
kubectl get configmaps test-config -o yaml
apiVersion: v1
data:
ailuoli-dev.properties: from=git-dev-1.0-t
service1-dev.properties: profile=dev-1.0
kind: ConfigMap
metadata:
creationTimestamp: "2020-12-16T03:17:38Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:ailuoli-dev.properties: {}
f:service1-dev.properties: {}
manager: kubectl-create
operation: Update
time: "2020-12-16T03:17:38Z"
name: test-config
namespace: test
resourceVersion: "2387780"
selfLink: /api/v1/namespaces/test/configmaps/test-config
uid: 87f7843a-8230-4bb8-ad22-4085c0b108a9
基于文件创建configMap
kubectl create configmap test-config2 --from-file=./ailuoli-dev.properties
可以使用多个
--from-file
来创建
configMap
kubectl create configmap game-config-2 --from-file=configure-pod-container/configmap/game.properties --from-file=configure-pod-container/configmap/ui.properties
使用
--from-env-file
来创建
configMap
- Env 文件中的每一行必须为 VAR=VAL 格式。
- 以#开头的行(即注释)将被忽略。
- 空行将被忽略。
- 引号不会被特殊处理(即它们将成为 ConfigMap 值的一部分)。
kubectl create configmap test-env-config --from-env-file=./ailuoli-test.properties
apiVersion: v1
data:
from: git-test-1.0
kind: ConfigMap
metadata:
creationTimestamp: "2020-12-16T03:33:00Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:from: {}
manager: kubectl-create
operation: Update
time: "2020-12-16T03:33:00Z"
name: test-env-config
namespace: test
resourceVersion: "2389938"
selfLink: /api/v1/namespaces/test/configmaps/test-env-config
uid: 5bfba06b-7073-487f-80e3-a1e6384eeaeb
同样
--from-env-file
也可以使用多个
kubectl create configmap config-multi-env-files \
--from-env-file=configure-pod-container/configmap/game-env-file.properties \
--from-env-file=configure-pod-container/configmap/ui-env-file.properties
定义从文件创建
ConfigMap
时要使用的键
kubectl create configmap test-config3 --from-file=testailuoli=./ailuoli-dev.properties
apiVersion: v1
data:
testailuoli: from=Ailuoli-Ailuoli-Ailuoli
kind: ConfigMap
metadata:
creationTimestamp: "2020-12-16T03:43:05Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:testailuoli: {}
manager: kubectl-create
operation: Update
time: "2020-12-16T03:43:05Z"
name: test-config3
namespace: test
resourceVersion: "2391353"
selfLink: /api/v1/namespaces/test/configmaps/test-config3
uid: 9dafdda6-3085-451c-9e41-b02ffe81d367
根据字面值创建
ConfigMap
ConfigMap
--from-literal
根据命令行定义文字值:
kubectl create configmap test-config4 --from-literal=hello1=world1 --from-literal=hello2=world2
apiVersion: v1
data:
hello1: world1
hello2: world2
kind: ConfigMap
metadata:
creationTimestamp: "2020-12-16T06:28:51Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:hello1: {}
f:hello2: {}
manager: kubectl-create
operation: Update
time: "2020-12-16T06:28:51Z"
name: test-config4
namespace: test
resourceVersion: "2414579"
selfLink: /api/v1/namespaces/test/configmaps/test-config4
uid: d05fa08b-44ca-4bdf-aaf4-38b47477190a
基于生成器创建
ConfigMap
ConfigMap
自1.14 开始,kubectl 开始支持 kustomization.yaml。
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: test-config5
files:
- configmap/config-repos/ailuoli-dev.properties
# - test=configmap/config-repos/ailuoli-dev.properties
# 自定义key
EOF
kubectl apply -k .
apiVersion: v1
data:
ailuoli-dev.properties: from=Ailuoli-Ailuoli-Ailuoli
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"ailuoli-dev.properties":"from=Ailuoli-Ailuoli-Ailuoli"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test-config5-245dh9m2fd","namespace":"test"}}
creationTimestamp: "2020-12-16T09:40:42Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:ailuoli-dev.properties: {}
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
manager: kubectl-client-side-apply
operation: Update
time: "2020-12-16T09:40:42Z"
name: test-config5-245dh9m2fd
namespace: test
resourceVersion: "2441461"
selfLink: /api/v1/namespaces/test/configmaps/test-config5-245dh9m2fd
uid: 0f97cda8-0c6c-47db-9599-1287e3dea642
使用
ConfigMap
定义容器环境变量
ConfigMap
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
env:
- name: test-config4
valueFrom:
configMapKeyRef:
key: hello1
name: test-config4
将
ConfigMap
数据添加到一个
Volume
中
ConfigMap
Volume
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: k8s.gcr.io/busybox
command: [ "/bin/sh", "-c", "ls /etc/config/" ]
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
# Provide the name of the ConfigMap containing the files you want
# to add to the container
name: special-config
items:
#使用 path 字段为特定的 ConfigMap 项目指定预期的文件路径。 在这里,SPECIAL_LEVEL 将挂载在 config-volume 数据卷中 /etc/config/keys 目录下。
- key: SPECIAL_LEVEL
path: keys
restartPolicy: Never
2.17 管理容器资源
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: app
image: images.my-company.example/app:v4
env:
- name: MYSQL_ROOT_PASSWORD
value: "password"
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
- name: log-aggregator
image: images.my-company.example/log-aggregator:v6
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
2.18 污点和容忍
Pod 中存在属性
Node selector
/
Node affinity
用于将Pod指定到合适的节点。相对的,节点中存在 污点属性
taints
,使得节点可以排斥某些
Pod
污点和容忍(taints and )成对工作,以确保
Pod
不会被调度到不合适的节点上。
- 可以为节点增加污点(taints, 一个节点可有 多个污点)
-
可以为
Pod
增加容忍 (toleration, 一个节点可以有多个容忍) -
如果节点上存在污点,则该节点不会接受任何不能容忍该污点得
Pod
2.18.1 向节点添加污点
-
执行
kubectl taint
命令,可以向节点添加污点
kubectl taint nodes node1 key=value:NoSchedule
该命令向节点
node1
添加了一个污点。污点是一个键值对,污点得键为
key
,值为
value
,污点效果为
NoSchedule
。此污点意味着
kubernetes
不会向该节点调度任何
Pod
,除非该
Pod
有一个匹配得容忍(
toleration
)
- 执行以下命令去除污点
kubectl taint nodes node1 key:NoSchedule-
2.18.2 向
Pod
添加容忍
Pod
PodSpec
中有一个
tolerations
字段,可用于向Pod添加容忍。
- 容忍1:
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
- 容忍2:
tolerations:
- key: "key"
operator: "Exists"
effect: "NoSchedule"
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
tolerations:
- key: "example-key"
operator: "Exists"
effect: "NoSchedule"
2.18.3 污点和容忍匹配
当满足如下条件时,k8s 认为容忍和污点匹配:
-
键(
key
) 相同 -
效果(
effect
)相同 -
污点
operator
为:-
Exists
(此时污点中不该指定
value
) -
或者
Equal
(此时容忍的
value
应与污点的
value
相同)
如果不指定
operator
,则默认为
Equal
-
特殊情况:
-
容忍中未定义
key
但是定义了
operator
为
Exists
,Kubernetes 认为此容忍匹配所有的污点.
tolerations:
- operator: "Exists"
-
容忍中未定义
effect
但是定义了
key
,Kubernetes 认为此容忍匹配所有
effect
.
tolerations:
- key: "key"
operator: "Exists
effect
效果有
-
NoSchedule
-
PreferNoSchedule
比
NoSchedule
更宽容一些,Kubernetes将尽量避免将没有匹配容忍的
Pod
调度该节点上,但是并不是不可以 -
NoExecute
不能在节点上运行(如果已经运行,将被驱逐 )
示例:
给一个Node 添加三个污点
kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule
同时有一个Pod带有两个容忍:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
在这个案例中,Pod 上有两个容忍,匹配了节点的前两个污点,只有节点的第三个污点对该 Pod 来说不可忽略,该污点的效果为
NoSchedule
:
- Kubernetes 不会将此 Pod 调度到该节点上
-
如果 Kubernetes 先将 Pod 调度到了该节点,后向该节点添加了第三个污点,则 Pod 将继续在该节点上运行而不会被驱逐(节点上带有
NoExecute
效果的污点已被 Pod 上的第二个容忍匹配,因此被忽略)
可以设置 Pod在多久后被驱逐
tolerationSeconds
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
tolerationSeconds: 3600
kubernetes 的节点控制器在碰到某些特定的条件时,将自动为节点添加污点,默认启用。
-
node.kubernetes.io/not-ready
: 节点未就绪。对应着 NodeCondition Ready 为 False 的情况 -
node.kubernetes.io/unreachable
: 节点不可触达。对应着 NodeCondition 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 的容忍可能如下所示:
tolerations:
- key: "node.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 6000
如果 Pod 没有
node.kubernetes.io/not-ready
容忍, Kubernetes 将自动为 Pod 添加一个
tolerationSeconds=300
的
node.kubernetes.io/not-ready
容忍。同样的,如果 Pod 没有
node.kubernetes.io/unreachable
容忍,Kubernetes 将自动为 Pod 添加一个
tolerationSeconds=300
的
node.kubernetes.io/unreachable
容忍
这类自动添加的容忍确保了 Pod 在节点发生 not-ready 和 unreachable 问题时,仍然在节点上保留 5 分钟。
DaemonSet Pod
相对特殊一些,他们在创建时就添加了不带
tolerationSeconds
的
NoExecute
效果的容忍,适用的污点有:
-
node.kubernetes.io/unreachable
-
node.kubernetes.io/not-ready
这将确保
DaemonSet Pod
始终不会被驱逐
2.19
Secret
Secret
2.19.1 手动创建 Secret
和创建其他类型 API对象一样,可以现在
yaml
文件定义好Secret,然后通过
kubectl apply -f
命令创建。此时可以通过两种方式在
yaml
中定义Secret
-
data
: 使用 字段 时,取值的欸容必须是
base64
编码 -
stringDate
: 使用stringData时,更为方便,可以直接将取值以明文方式写在yaml文件中
-
在
yaml
中定义data
echo -n 'admin' | base64
echo -n '123456' | base64
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MTIzNDU2
- 在yaml中定义stringData
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
stringData:
username: admin
password: a1234567890
但是这种写法在
yaml
文件
annotation
中仍然可以看见铭文
apiVersion: v1
data:
password: YTEyMzQ1Njc4OTA=
username: YWRtaW4=
kind: Secret
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Secret","metadata":{"annotations":{},"name":"secret-test","namespace":"test"},"stringData":{"password":"a1234567890","username":"admin"},"type":"Opaque"}
creationTimestamp: "2020-12-24T06:45:47Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:password: {}
f:username: {}
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
f:type: {}
manager: kubectl-client-side-apply
operation: Update
time: "2020-12-24T06:45:47Z"
name: secret-test
namespace: test
resourceVersion: "4249564"
selfLink: /api/v1/namespaces/test/secrets/secret-test
uid: 6228d000-976f-49ca-8122-4940425b1c35
type: Opaque
-
同时定义了 data 和 stringData
如果同事定义了data 和 stringData,对于对象中key重复的字段,最终采纳 stringData 中的value
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
stringData:
username: administrator
2.19.2 解码和编辑 Secret
- 解码
echo 'YTEyMzQ1Njc4OTA=' | base64 --decode
- 编辑
kubectl edit secrets mysecret
2.20 Security Context
2.20.1 为 Pod 设置
Security Context
Security Context
在 Pod 的定义中增加
securityContext
字段,即可为 Pod 执行 Security 相关的设定。
securityContext
字段是一个
PodSecurityContext
对象。通过该字段指定的内容将对该 Pod 中所有的容器生效。
apiVersion: v1
kind: Pod
metadata:
name: security-context-demo
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
volumes:
- name: sec-ctx-vol
emptyDir: {}
containers:
- name: sec-ctx-demo
image: busybox
command: [ "sh", "-c", "sleep 1h" ]
volumeMounts:
- name: sec-ctx-vol
mountPath: /data/demo
securityContext:
allowPrivilegeEscalation: false
-
spec.securityContext.runAsUser
字段指定了该 Pod 中所有容器的进程都以UserID 1000 的身份运行,
spec.securityContext.runAsGroup
字段指定了该 Pod 中所有容器的进程都以GroupID 3000 的身份运行- 如果该字段被省略,容器进程的GroupID为 root(0)
- 容器中创建的文件,其所有者为 userID 1000,groupID 3000
-
spec.securityContext.fsGroup
字段指定了该 Pod 的 fsGroup 为 2000
数据卷 (本例中,对应挂载点
/data/demo
的数据卷为
sec-ctx-demo
) 的所有者以及在该数据卷下创建的任何文件,其 GroupID 为 2000
2.20.2 为容器设置
Linux Capabilities
Linux Capabilities
使用
Linux Capabilities
可以为容器内的进程授予某些特定的权限(而不是 root 用户的所有权限)。在容器定义的
securityContext
中添加
capabilities
字段,可以向容器添加或删除
Linux Capability
。
apiVersion: v1
kind: Pod
metadata:
name: security-context-demo-4
spec:
containers:
- name: sec-ctx-demo-4
image: busybox
command: [ "sh", "-c", "sleep 1h" ]
securityContext:
capabilities:
add: ["NET_ADMIN", "SYS_TIME"]
三、
Kubernetes
高级学习
Kubernetes
3.1 安全
3.1.1 用户认证
所有Kubernetes 集群都有两类用户,Kubernetes管理的
Service Account
和 普通用户
与普通用户相对,
Service Account
是通过 Kubernetes API 管理的用户。
Service Account
是名称空间级别的对象,可能由 ApiServer 自动创建,或者通过调用 API 接口创建。
Service Account
都绑定了一组
Secret
,
Secret
可以被挂载到 Pod 中,以便 Pod 中的进程可以获得调用 Kubernetes API 的权限。
集群内外的任何一个进程,在调用 API Server 的接口时,都必须认证其身份,或者被当做一个匿名用户。可能的场景有:
- 集群中某一个 Pod 调用 API Server 的接口查询集群的信息
- 用户通过 kubectl 执行指令,kubectl 调用 API Server 的接口完成用户的指令
- 用户通过 Kuboard 界面管理集群,Kuboard 调用 API Server 的接口实现界面上的功能
认证策略
Kubernetes 的认证策略(Authentication Strategies)是:通过
authentication plugin
认证发起 API 请求的用户身份,认证方式有 client certificates、bearer tokens、authenticating proxy、HTTP basic auth。当 API Server 接收到 HTTP 请求时,authentication plugin 会尝试将如下属性附加到请求:
-
Username
:唯一标识用户的一个字符串。例如 kube-admin 或者 jane@example.com -
UID
:唯一标识用户的一个字符串,相较于 username,提供更强的一致性和唯一性。(某些 Identity Provider 可能允许用户更改 username) -
Groups
:一组字符串,标识用户所属的用户组 -
额外字段
:Key,Value 都是 String 的 Map,包含一些 对 authorizer 可能有用的信息\
上述所有的字段对认证系统来说都是透明的,且只对 authorizer 有意义(authentication plugin 将认证结果中的某些字段赋值到上述字段中,认证系统只是按照自己的方式正常工作,并不知道上述这些字段的存在)。这使得 Kubernetes 可以同时支持多种认证方式,在实际工作中,您通常应该至少配置两种认证方式:
- Service account tokens 用于认证 Service Account,
- 至少另外一种认证方式用于认证普通用户。
3.1.2 管理 ServiceAccount
User accounts 和 service accounts
Kubernetes 明确地区分了 user account 和 service account 的概念,原因如下:
- User account 的使用者是用户(人),service account 的使用者是运行在 Pod 中的进程。
- User account 应该是全局的,用户名在集群范围内(跨名称空间)必须唯一。Service account 的名称在名称空间内唯一即可
- 通常,集群的 user account 可能是从企业的数据库同步过来,在那里,创建新的 user account 需要特殊的权限,并且受复杂的业务流程管控。Service account 的创建则更加轻量级,允许集群的用户为特定的任务创建 service account,(最小权限的原则)
- 对用户(人)和 service account 的审计过程可能会不一样
- 一个复杂系统中,可能为不同的组件配置不同的 service account。由于 service account 可以临时创建,并且在名称空间内唯一,这种配置信息是可以移植的
Service account admission controller
Admission Controller
(opens new window)是 apiserver 的一部分,它在 Pod 创建或者更新时,对 Pod 执行一些修改。此控制器激活时(默认处于激活状态),当 Pod 被创建或修改时,该控制器将执行如下动作:
-
如果 Pod 未设置
ServiceAccount
,将
ServiceAccount
设置为 default -
确保 Pod 引用的
ServiceAccount
存在,否则拒绝创建或者修改 Pod -
如果 Pod 不包含任何
ImagePullSecrets
,则
ServiceAccount
中的
ImagePullSecrets
将被添加到 Pod 上 -
为 Pod 添加一个
volume
(其中包含了访问 APIServer 的 token) -
为 Pod 中的每一个容器添加一个
volumeSource
,并挂载到路径
/var/run/secrets/kubernetes.io/serviceaccount
Token Controller
TokenController 作为 controller-manager 的一部分运行。以异步的方式执行如下动作:
-
监听
ServiceAccount
的创建,并创建一个对应的
Secret
以允许访问 APIServer -
监听
ServiceAccount
的删除,并删除所有对应的 ServiceAccountToken Secrets -
监听
Secret
的添加,确保其引用的
ServiceAccount
以存在,并在需要时向 Secret 添加 Token -
监听
Secret
的删除,并在需要的情况下将对应
ServiceAccount
中对 Secret 的引用也删除掉
。。。。 未完待续
四、
Kubernetes
实战
Kubernetes
4.1 使用 port-forward 访问集群中的应用程序
使用
kubectl port-forward
访问kubernetes集群中的Redis Server ,Debug 时很有效
- 创建 Redis 服务
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-master
labels:
app: redis
spec:
selector:
matchLabels:
app: redis
role: master
tier: backend
replicas: 1
template:
metadata:
labels:
app: redis
role: master
tier: backend
spec:
containers:
- name: master
image: redis
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: redis-master
labels:
app: redis
role: master
tier: backend
spec:
ports:
- port: 6379
targetPort: 6379
selector:
app: redis
role: master
tier: backend
这时redis 的pod就创建好了,
Service
使用的ClusterIP
- 转发本地端口到Pod的端口
# 这几个命令执行任意一个即可
kubectl port-forward redis-master-765d459796-258hz 7000:6379
kubectl port-forward pods/redis-master-765d459796-258hz 7000:6379
kubectl port-forward deployment/redis-master 7000:6379
kubectl port-forward rs/redis-master 7000:6379
kubectl port-forward svc/redis-master 7000:6379