只使用 Pod, 将会面临如下需求:
控制器又称工作负载是用于实现管理 pod 的中间层,确保 pod 资源符合预期的状态,pod 的资源出现故障时,会尝试 进行重启,当根据重启策略无效,则会重新新建 pod 的资源。
[root@k8s-master deployment]# cat deploy-mysql.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
namespace: demo
spec:
replicas: 1 #指定Pod副本数
selector: #指定Pod的选择器
matchLabels:
app: mysql
template:
metadata:
labels: #给Pod打label
app: mysql
spec:
hostNetwork: true # 声明pod的网络模式为host模式,效果通docker run --net=host
volumes:
- name: mysql-data
hostPath:
path: /data/mysql/data
nodeSelector: # 使用节点选择器将Pod调度到指定label的节点
component: mysql
containers:
- name: mysql
image: 192.168.51.209:5000/mysql:5.7-utf8
args:
- "--character-set-server=utf8" #
- "--collation-server=utf8_general_ci" # 指定字符编码
ports:
- containerPort: 3306
env:
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: myblog
key: MYSQL_USER
- name: MYSQL_PASSWD
valueFrom:
secretKeyRef:
name: myblog
key: MYSQL_PASSWD
- name: MYSQL_DATABASE
value: "myblog"
resources:
requests:
memory: 100Mi
cpu: 50m
limits:
memory: 500Mi
cpu: 100m
readinessProbe:
tcpSocket:
port: 3306
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 3306
initialDelaySeconds: 15
periodSeconds: 20
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
[root@k8s-master deployment]# cat deploy-myblog.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myblog
namespace: demo
spec:
replicas: 1 #指定Pod副本数
selector: #指定Pod的选择器
matchLabels:
app: myblog
template:
metadata:
labels: #给Pod打label
app: myblog
spec:
containers:
- name: myblog
image: 192.168.51.209:5000/myblog
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_HOST
valueFrom:
configMapKeyRef:
name: myblog
key: MYSQL_HOST
- name: MYSQL_PORT
valueFrom:
configMapKeyRef:
name: myblog
key: MYSQL_PORT
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: myblog
key: MYSQL_USER
- name: MYSQL_PASSWD
valueFrom:
secretKeyRef:
name: myblog
key: MYSQL_PASSWD
ports:
- containerPort: 8002
resources:
requests:
memory: 100Mi
cpu: 50m
limits:
memory: 500Mi
cpu: 100m
livenessProbe:
httpGet:
path: /blog/index/
port: 8002
scheme: HTTP
initialDelaySeconds: 10 # 容器启动后第一次执行探测是需要等待多少秒
periodSeconds: 15 # 执行探测的频率
timeoutSeconds: 2 # 探测超时时间
readinessProbe:
httpGet:
path: /blog/index/
port: 8002
scheme: HTTP
initialDelaySeconds: 10
timeoutSeconds: 2
periodSeconds: 15
[root@k8s-master deployment]# cat configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myblog
namespace: demo
data:
MYSQL_HOST: "192.168.51.210"
MYSQL_PORT: "3306"
[root@k8s-master deployment]# cat secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: myblog
namespace: demo
type: Opaque
data:
MYSQL_USER: cm9vdA==
MYSQL_PASSWD: MTIzNDU2
创建 Deployment
[root@k8s-master deployment]# kubectl create -f deploy-mysql.yaml
deployment.apps/mysql created
[root@k8s-master deployment]# kubectl create -f configmap.yaml
configmap/myblog created
[root@k8s-master deployment]# kubectl create -f secret.yaml
secret/myblog created
[root@k8s-master deployment]# kubectl create -f deploy-myblog.yaml
deployment.apps/myblog created
[root@k8s-master deployment]# kubectl api-resources
[root@k8s-master deployment]# kubectl -n demo get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
myblog 1/1 1 1 3m9s
mysql 1/1 1 1 28h
NAME
列出了集群中 Deployments 的名称。READY
显示当前正在运行的副本数/期望的副本数。UP-TO-DATE
显示已更新以实现期望状态的副本数。AVAILABLE
显示应用程序可供用户使用的副本数。AGE
显示应用程序运行的时间量。[root@k8s-master deployment]# kubectl -n demo get po
NAME READY STATUS RESTARTS AGE
myblog-b66554b87-rms8b 1/1 Running 0 8m53s
mysql-8fcc5595f-dj4gb 1/1 Running 0 9m45s
命令指示:
kubectl delete -n demo deploy myblog
或者 kubectl delete -f deploy-mysql.yaml
kubectl describe -n demo pod mysql-55745b9899-dpzrj
controller 实时检测 pod 状态,并保障副本数一直处于期望的值。
[root@k8s-master deployment]# kubectl -n demo get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myblog-b66554b87-zp9nf 1/1 Running 0 4m31s 10.244.2.12 k8s-slave2 <none> <none>
mysql-8fcc5595f-trnx9 1/1 Running 0 28h 192.168.51.210 k8s-slave1 <none> <none>
[root@k8s-master deployment]#
[root@k8s-master deployment]# kubectl -n demo delete pod myblog-b66554b87-zp9nf
pod "myblog-b66554b87-zp9nf" deleted
[root@k8s-master deployment]# kubectl -n demo get pods -o wide -w
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myblog-b66554b87-phv4x 1/1 Running 0 30s 10.244.2.13 k8s-slave2 <none> <none>
myblog-b66554b87-zp9nf 1/1 Terminating 0 5m28s 10.244.2.12 k8s-slave2 <none> <none>
mysql-8fcc5595f-trnx9 1/1 Running 0 28h 192.168.51.210 k8s-slave1 <none> <none>
myblog-b66554b87-zp9nf 0/1 Terminating 0 5m30s <none> k8s-slave2 <none> <none>
myblog-b66554b87-zp9nf 0/1 Terminating 0 5m30s <none> k8s-slave2 <none> <none>
myblog-b66554b87-zp9nf 0/1 Terminating 0 5m42s <none> k8s-slave2 <none> <none>
myblog-b66554b87-zp9nf 0/1 Terminating 0 5m42s <none> k8s-slave2 <none> <none>
[root@k8s-master deployment]# kubectl -n demo get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myblog-b66554b87-phv4x 1/1 Running 0 73s 10.244.2.13 k8s-slave2 <none> <none>
mysql-8fcc5595f-trnx9 1/1 Running 0 28h 192.168.51.210 k8s-slave1 <none> <none>
kubectl -n demo edit deploy myblog
的方式,最好通过修改文件,然后 apply 的方式,这样 YAML 文件可以保持同步[root@k8s-master deployment]# kubectl -n demo scale deploy myblog --replicas=2
deployment.apps/myblog scaled
观察 pods
[root@k8s-master deployment]# kubectl -n demo get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myblog-b66554b87-jg9k8 1/1 Running 0 49s 10.244.1.25 k8s-slave1 <none> <none>
myblog-b66554b87-phv4x 1/1 Running 0 4m17s 10.244.2.13 k8s-slave2 <none> <none>
mysql-8fcc5595f-trnx9 1/1 Running 0 28h 192.168.51.210 k8s-slave1 <none> <none>
K8S 有个特色功能叫 pod eviction,它在某些场景下如节点 NotReady,或者资源不足时,把 pod 驱逐至其它节点,这也是出于业务保护的角度去考虑的。
pod-eviction-timeout
:NotReady 状态节点超过该时间后,执行驱逐,默认 5 minmemory.available
:节点可用内存nodefs.available
:节点根盘可用存储空间nodefs.inodesFree
:节点 inodes 可用数量imagefs.available
:镜像存储盘的可用空间imagefs.inodesFree
:镜像存储盘的 inodes 可用数量修改 dockerfile,重新打 tag 模拟服务更新。
更新方式:
kubectl -n demo apply -f deploy-myblog.yaml
来应用更新kubectl -n demo edit deploy myblog
在线更新kubectl set image deploy myblog myblog=192.168.51.209:5000/myblog:v2 --record
$ vi mybolg/blog/template/index.html
$ docker build . -t 192.168.51.209:5000/myblog:v2 -f Dockerfile_optimized
$ docker push 192.168.51.209:5000/myblog:v2
...
spec:
replicas: 2 #指定Pod副本数
selector: #指定Pod的选择器
matchLabels:
app: myblog
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate #指定更新方式为滚动更新,默认策略,通过get deploy yaml查看
...
策略控制:
在 Deployment rollout 时,需要保证 Available(Ready) Pods 数不低于 desired pods number - maxUnavailable; 保证所有的非异常状态 Pods 数不多于 desired pods number + maxSurge。
以 myblog 为例,使用默认的策略,更新过程:
kubectl -n demo describe deploy myblog
通过滚动升级的策略可以平滑的升级 Deployment,若升级出现问题,需要最快且最好的方式回退到上一次能够提供正常工作的版本。为此 K8S 提供了回滚机制。
revision:更新应用时,K8S 都会记录当前的版本号,即为 revision,当升级出现问题时,可通过回滚到某个特定的 revision,默认配置下,K8S 只会保留最近的几个 revision,可以通过 Deployment 配置文件中的 spec.revisionHistoryLimit 属性增加 revision 数量,默认是 10。
查看当前:
$ kubectl -n demo rollout history deploy myblog ##CHANGE-CAUSE为空
$ kubectl delete -f deploy-myblog.yaml ## 方便演示到具体效果,删掉已有deployment
记录回滚:
$ kubectl create -f deploy-myblog.yaml --record
$ kubectl -n demo set image deploy myblog myblog=192.168.51.209:5000/myblog:v2 --record=true
查看 deployment 更新历史:
kubectl -n demo rollout history deploy myblog
回滚到具体的 REVISION:
kubectl -n demo rollout undo deploy myblog --to-revision=1
label
是 kubernetes
中一个非常重要的概念,用户可以非常灵活的利用 label 来管理集群中的资源,POD 的调度可以根据节点的 label 进行特定的部署。
查看节点的 label:
kubectl get nodes --show-labels
为节点打 label:
kubectl label node k8s-master disktype=ssd
当 node 被打上了相关标签后,在调度的时候就可以使用这些标签了,只需要在 spec 字段中添加 nodeSelector
字段,里面是我们需要被调度的节点的 label。
...
spec:
hostNetwork: true # 声明pod的网络模式为host模式,效果通docker run --net=host
volumes:
- name: mysql-data
hostPath:
path: /opt/mysql/data
nodeSelector: # 使用节点选择器将Pod调度到指定label的节点
component: mysql
containers:
- name: mysql
image: 192.168.51.209:5000/demo/mysql:5.7
...
节点亲和性 , 比上面的 nodeSelector
更加灵活,它可以进行一些简单的逻辑组合,不只是简单的相等匹配 。分为两种,软策略和硬策略。
preferredDuringSchedulingIgnoredDuringExecution:软策略,如果你没有满足调度要求的节点的话,Pod 就会忽略这条规则,继续完成调度过程,说白了就是满足条件最好了,没有满足就忽略掉的策略。
requiredDuringSchedulingIgnoredDuringExecution : 硬策略,如果没有满足条件的节点的话,就不断重试直到满足条件为止,简单说就是你必须满足我的要求,不然我就不会调度 Pod。
要求 Pod 不能运行在 128 和 132 两个节点上,如果有个节点满足 disktype=ssd 的话就优先调度到这个节点上
...
spec:
containers:
- name: demo
image: 172.21.32.6:5000/demo/myblog
ports:
- containerPort: 8002
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- 192.168.136.128
- 192.168.136.132
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: disktype
operator: In
values:
- ssd
- sas
...
这里的匹配逻辑是 label 的值在某个列表中,现在 Kubernetes
提供的操作符有下面的几种:
如果 nodeSelectorTerms 下面有多个选项的话,满足任何一个条件就可以了;如果 matchExpressions 有多个选项的话,则必须同时满足这些条件才能正常调度 Pod
对于 nodeAffinity
无论是硬策略还是软策略方式,都是调度 Pod 到预期节点上,而 Taints
恰好与之相反,如果一个节点标记为 Taints ,除非 Pod 也被标识为可以容忍污点节点,否则该 Taints 节点不会被调度 Pod。
Taints(污点)是 Node 的一个属性,设置了 Taints(污点)后,因为有了污点,所以 Kubernetes 是不会将 Pod 调度到这个 Node 上的。于是 Kubernetes 就给 Pod 设置了个属性 Tolerations(容忍),只要 Pod 能够容忍 Node 上的污点,那么 Kubernetes 就会忽略 Node 上的污点,就能够(不是必须)把 Pod 调度过去。
比如用户希望把 Master 节点保留给 Kubernetes 系统组件使用,或者把一组具有特殊资源预留给某些 Pod,则污点就很有用了,Pod 不会再被调度到 taint 标记过的节点。taint 标记节点举例如下:
设置污点:
$ kubectl taint node [node_name] key=value:[effect]
其中[effect] 可取值: [ NoSchedule | PreferNoSchedule | NoExecute ]
NoSchedule:一定不能被调度。
PreferNoSchedule:尽量不要调度。
NoExecute:不仅不会调度,还会驱逐 Node 上已有的 Pod。
示例:kubectl taint node k8s-master smoke=true:NoSchedule 去除污点:
去除指定 key 及其 effect:
kubectl taint nodes [node_name] key:[effect]- #这里的 key 不用指定 value
去除指定 key 所有的 effect:
kubectl taint nodes node_name key-
示例:
kubectl taint node k8s-master smoke=true:NoSchedule
kubectl taint node k8s-master smoke:NoExecute-
kubectl taint node k8s-master smoke-污点演示:
给 k8s-slave1 打上污点,smoke=true:NoSchedule
$ kubectl taint node k8s-slave1 smoke=true:NoSchedule
$ kubectl taint node k8s-slave2 drunk=true:NoSchedule
扩容 myblog 的 Pod,观察新 Pod 的调度情况
$ kuebctl -n demo scale deploy myblog --replicas=3
$ kubectl -n demo get po -w ## pending
Pod 容忍污点示例:myblog/deployment/deploy-myblog-taint.yaml
[root@k8s-master deployment]# cat deploy-myblog-taint.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myblog
namespace: demo
spec:
replicas: 1 #指定Pod副本数
selector: #指定Pod的选择器
matchLabels:
app: myblog
template:
metadata:
labels: #给Pod打label
app: myblog
spec:
tolerations: #设置容忍性
- key: "smoke"
operator: "Equal" #如果操作符为Exists,那么value属性可省略,不指定operator,默认为Equal
value: "true"
effect: "NoSchedule"
- key: "drunk"
operator: "Equal" #如果操作符为Exists,那么value属性可省略,不指定operator,默认为Equal
value: "true"
effect: "NoSchedule"
containers:
- name: myblog
image: 192.168.51.209:5000/myblog
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_HOST
valueFrom:
configMapKeyRef:
name: myblog
key: MYSQL_HOST
- name: MYSQL_PORT
valueFrom:
configMapKeyRef:
name: myblog
key: MYSQL_PORT
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: myblog
key: MYSQL_USER
- name: MYSQL_PASSWD
valueFrom:
secretKeyRef:
name: myblog
key: MYSQL_PASSWD
ports:
- containerPort: 8002
resources:
requests:
memory: 100Mi
cpu: 50m
limits:
memory: 500Mi
cpu: 100m
livenessProbe:
httpGet:
path: /blog/index/
port: 8002
scheme: HTTP
initialDelaySeconds: 10 # 容器启动后第一次执行探测是需要等待多少秒
periodSeconds: 15 # 执行探测的频率
timeoutSeconds: 2 # 探测超时时间
readinessProbe:
httpGet:
path: /blog/index/
port: 8002
scheme: HTTP
initialDelaySeconds: 10
timeoutSeconds: 2
periodSeconds: 15
$ kubectl apply -f deploy-myblog-taint.yaml
spec:
containers:
- name: demo
image: 192.168.51.209:5000/demo/myblog
tolerations:
- operator: "Exists"