kubernetes集群常用的资源
版权声明:原创作品,谢绝转载!否则将追究法律责任。
当你的才华还撑不起你的野心的时候,你就应该静下心来学习。
当你的能力还驾驭不了你的目标的时候,你就应该沉下心来历练。
问问自己,想要怎样的人生。
一.POD资源管理
1.什么是pod
Pod是Kubernetes集群中最小部署单元,一个Pod由一个容器或多个容器组成,这些容器可以共享存储,网络等资源等。
Pod有以下特点:
(1)一个Pod可以理解为一个应用实例,提供服务;
(2)Pod中容器始终部署在同一个Node上;
(3)Pod中容器共享网络,存储资源;
(4)Kubernetes直接管理Pod,而不是容器;
2.Pod存在的意义
Pod主要用法:
运行单个容器: 这是最常见的用法,在这种情况下,可以将Pod看做是单个容器的抽象封装。
运行多个容器: 封装多个紧密耦合且需要共享资源的应用程序。
如果有这些需求,你可以运行多个容器:
(1)两个应用之间发生文件交互;
(2)两个应用需要通过127.0.0.1或者socket通信;
(3)两个应用需要发生频繁的调用;
pod场景:
众所周知,docker容器运行时需要后台守护进程,比如我们将"nginx"和"filebeat"应用部署在同一个容器内,无论我们选用哪个应用程序作为后台守护进程均可运行容器,但只要被选择用于后台守护进程的程序挂掉后,另一个程序可能无法使用。
为了解决上述的问题,我们生产环境中是建议将"nginx"和"filebeat"两个应用可以拆开,用两个容器部署,这样可以实现解耦,即一个应用程序挂掉不会影响到另一个容器的运行,因为各个容器底层资源是隔离的。
K8S的Pod设计:
K8S已经考虑到上述应用依赖性比较强的场景,我们可以将"nginx"单独作为一个容器运行,它会产生日志文件,而后我们单独运行一个"filebeat"容器,用于收集日志到ELK集群中。
你可能会疑问,为什么同一个Pod内多个容器共享同一个网络空间呢?
(1)节省IP地址的分配,因为一个较大的集群中,可能成千上万个容器,在刚刚每天甚至能达到上亿级别的容器运行,每周数十亿容器运行,因此同一个Pod内运行多个容器,我们没有必要维护多个IP,这样可以节省IP地址的分配;
(2)多个容器使用同一个网络空间,可以降低维护成本,无需运维人员同时维护同一个Pod内的多个网卡,降低运维成本,同时,也能降低开发的维护成本;
(3)依赖关系较高的多个容器可以通过127.0.0.1直接进行通信,但也会面临缺陷,比如在同一个Pod内运行多个Nginx版本会存在端口冲突的显现,需要我们注意这一点,因为它们使用的是同一个网卡;
3.运行单容器案例
(1)所有的node节点指定基础镜像(记得重启服务)
vim /etc/kubernetes/kubelet
...
# 指定pod基础设施容器(镜像需要在自己的仓库中存在)
KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=10.0.0.101:5000/pod-infrastructure"
...
systemctl restart kubelet.service
(2)创建资源清单
[root@10.0.0.101 /oldzhang/pods]# vim 01_nginx.yaml
[root@10.0.0.101 /oldzhang/pods]#
[root@10.0.0.101 /oldzhang/pods]# cat 01_nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: oldzhang-nginx
labels:
app: web01
spec:
containers:
- name: nginx
image: 10.0.0.101:5000/docker.io/nginx:1.13
ports:
- containerPort: 80
[root@10.0.0.101 /oldzhang/pods]#
(3)创建资源清单
[root@10.0.0.101 /oldzhang/pods]# kubectl create -f 01_nginx.yaml
pod "oldzhang-nginx" created
[root@10.0.0.101 /oldzhang/pods]#
4.运行多容器案例
[root@10.0.0.101 /oldzhang/pods]# vim 02_nginx_alpine.yaml
[root@10.0.0.101 /oldzhang/pods]#
[root@10.0.0.101 /oldzhang/pods]# cat 02_nginx_alpine.yaml
apiVersion: v1
kind: Pod
metadata:
name: oldzhang-nginx-2021
labels:
app: web01
name: oldzhang_linux75
spec:
nodeName: "172.200.1.102"
containers:
- name: nginx
image: 172.200.1.101:5000/nginx:1.13
ports:
- containerPort: 80
hostIP: "0.0.0.0"
hostPort: 8888
- name: alpine
image: alpine:latest
command: ["sleep","10"]
[root@10.0.0.101 /oldzhang/pods]#
[root@10.0.0.101 /oldzhang/pods]# kubectl apply -f 02_nginx_alpine.yaml
pod "oldzhang-nginx-alpine" created
[root@10.0.0.101 /oldzhang/pods]#
[root@10.0.0.101 /oldzhang/pods]# kubectl get pods -o wide --show-labels
温馨提示:
(1)command和args都是用来指定启动Pod的初始指令。其中command指令类似于dockerfile的entrypoint,而args指令类似于dockerfile的cmd。
(2)当args和command指令同时使用时,args会是command指令的参数。
5.Pod资源共享实现机制-验证同一个Pod默认共享网络
共享网络:
Kubernetes在启动Pod时会单独启动一个名为"Infrastructure Container"(基础设施容器)的容器用以实现网络共享,其它业务的容器的网络名称空间都会被共享。
(1)编写资源清单
[root@10.0.0.101 /oldzhang/pods]# cat 03-pod-net-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: oldzhang-nginx-2021
labels:
app: web01
name: oldzhang_linux75
spec:
nodeName: "172.200.1.102"
containers:
- name: oldzhang-nginx
image: 172.200.1.101:5000/nginx:1.13
ports:
- containerPort: 80
hostIP: "0.0.0.0"
hostPort: 8888
- name: oldzhang-alpine
image: alpine:latest
command: ["sleep","1000"]
[root@10.0.0.101 /oldzhang/pods]#
[root@10.0.0.101 /oldzhang/pods]# kubectl apply -f 03-pod-net-demo.yaml
pod "pod-net-demo" created
[root@10.0.0.101 /oldzhang/pods]#
(2)进入到mynginx容器修改数据
kubectl exec -it pod-net-demo -c mynginx -- sh
echo "<h1>oldzhang linux</h1>" > /usr/share/nginx/html/index.html
(3)进入到mylinux容器访问本地回环网卡可以访问到mynginx容器修改的数据
kubectl exec -it pod-net-demo -c mylinux -- sh
温馨提示:
1)当我们修改mynginx容器的首页文件内容后,使用mylinux容器登录是无法访问mynginx修改的内容的,说明默认情况下,同一个Pod其容器之间文件系统是相互隔离的;
2)当我们进入到mylinux容器后,访问本地回环网卡的80端口时,发现竟然能够拿到mynginx容器修改的数据,这说明两个容器共用了相同的网络空间;
综上所述,默认情况下,同一个Pod的所有容器共享的是同一个网络空间,但并不共享存储。
6.Pod资源共享实现机制-验证同一个Pod共享存储(
共享存储:
同一个Pod中的容器通过数据卷实现数据共享。
(1)编写资源清单
[root@10.0.0.101 /oldzhang/pods]# cat 04-pod-volume-demo.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
app: test
name: pod-volume-demo
spec:
containers:
- image: 10.0.0.101:5000/busybox:latest
name: mylinux
command: ["/bin/sh","-c","sleep 3600"]
volumeMounts:
- name: log
mountPath: /data
- image: 10.0.0.101:5000/nginx:1.17
name: mynginx
volumeMounts:
- name: log
mountPath: /data
volumes:
- name: log
emptyDir: {}
[root@10.0.0.101 /oldzhang/pods]#
[root@10.0.0.101 /oldzhang/pods]# kubectl apply -f 04-pod-volume-demo.yaml
pod "pod-volume-demo" created
[root@10.0.0.101 /oldzhang/pods]#
(2)进入到mylinux容器中修改数据
[root@10.0.0.101 /oldzhang/pods]# kubectl exec -it pod-volume-demo -c mylinux -- sh
...
/ # echo "https://www.cnblogs.com/oldzhang/" > /data/blog.txt
(3)进入到mynginx容器中查看数据
[root@10.0.0.101 /oldzhang/pods]# kubectl exec -it pod-volume-demo -c mynginx -- sh
...
# cat /data/blog.txt
https://www.cnblogs.com/oldzhang/
#
温馨提示:
很明显,我们可以基于数据卷实现共享存储。
7.kubernetes的Pod中的容器类型
Pod中的容器类型:
基础设施容器(Infrastructure Container):
维护整个Pod网络空间。
初始化容器(Init Containers):
先于业务容器开始执行。
业务容器(Containers):
并行启动。
什么是init containers
init containers:
顾名思义,用于初始化工作,执行完就结束,可以理解为一次性任务。
init containers特点:
(1)支持大部分应用容器配置,但不支持健康检查。
(2)优先应用容器执行。
参考连接:
https://kubernetes.io/zh/docs/concepts/workloads/pods/init-containers/
init containers的应用场景:
环境检查:
例如确保"应用容器"依赖的服务启动后再启动应用容器。
初始化配置:
例如给"应用容器"准备配置文件。
测试案例(kubernetes 1.8及以上版本才会支持初始化容器,基于Pod.Spec字段定义)
[root@10.0.0.101 /oldzhang/pods]# cat 05-pod-init-container-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
initContainers:
- name: download
image: busybox
command:
- wget
- "-O"
- "/opt/index.html"
- "https://www.cnblogs.com/oldzhang/"
volumeMounts:
- name: www
mountPath: "/opt"
containers:
- name: mynginx
image: nginx:1.17
ports:
- containerPort: 80
volumeMounts:
- name: www
mountPath: "/usr/share/nginx/html"
volumes:
- name: www
emptyDir: {}
[root@10.0.0.101 /oldzhang/pods]#
温馨提示;
(1)如下图所示,如果k8s版本较低,是不支持基于Pod.spec字段定义的哟~其 GitVersion:"v1.5.2"可以发现其版本有点过时了,我们生产环境中建议使用更新的版本。
(2)早期版本我们可以基于"annotations(注释)"实现初始化容器,但我本人并不推荐研究它们,因为它们在1.6和1.7中已弃用。在1.8中,注释不再受支持,并且必须转换为PodSpec字段。
8.Pod的生命周期
Pending(悬决) :
Pod已被Kubernetes系统接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待Pod被调度的时间和通过网络下载镜像的时间。
Running(运行中):
Pod已经绑定到了某个节点,Pod中所有的容器都已被创建。至少有一个容器仍在运行,或者正处于启动或重启状态。
Succeeded(成功):
Pod 中的所有容器都已成功终止,并且不会再重启。
Failed(失败):
Pod 中的所有容器都已终止,并且至少有一个容器是因为失败终止。也就是说,容器以非 0 状态退出或者被系统终止。
Unknown(未知):
因为某些原因无法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。
推荐阅读:
https://kubernetes.io/zh/docs/concepts/workloads/pods/pod-lifecycle/
9.关于pod、可能会遇到坑
(1)小马虎:
a)私有镜像名称写错,比如将"k8s101.oldzhang.com:5000/alpine:latest"写成"k8s101.oldzhang.com/alpine:latest";
b)搞不清楚"insecure-registries"的作用,它指定的基于http协议的私有仓库,严格意义上来说,应该是私有镜像的前缀,如果是IP,则镜像应该写IP,如果是主机名,则镜像应该写主机名;
c)本地测试镜像没问题,但是使用K8S启动POD始终有问题,最终发现未更新私有仓库的镜像;
d)集群的所有node节点KUBELET组件的"KUBELET_HOSTNAME"变量配置一致,导致启动一个pod,3个节点都存在;
e)将Pod类型的"nodeName"的主机写错了,本来在etcd存储的是IP地址,但手动调度时却用的是主机名,从而导致不断的pending(过一段时间就直接退出了,这里采用的是K8S1.5版本),Pod始终无法拉起;
(2)删除资源始终处于"Terminating"状态,学习环境超过1-5分钟以上依旧没有删除资源,这个时候等着他删除,结果1小时过去了,白白的浪费了时间。如果遇到这样问题,应该及时检查集群的工作状态是否正常,检查master节点和node节点是否正常工作:
a)检查master节点状态: "kubectl get cs",如果状态为"Healthy"正常,如果"Unhealthy"不正常;
b)检查node节点状态: "kubectl get no",如果状态为"Ready"代表正常,如果"NotReady"不正常;
(3)基础镜像命名错误,故意将"alpine:latest"重新打tag为"pod-infrastructure:latest",充当基础镜像,结果出现国所有在Pod无法正常启动且均处于"Created"状态,"pod-infrastructure:latest"创建的容器处于"Exitd"状态,通过"kubectl describe ..."命令分析查看"Event"得出"cannot join network of a non running container: ...."这样的报错信息。
解决方案: 使用我笔记中提供的基础镜像,咱们班的同学直接导入我给的基础包即可。
(4)Pod处于RUNING状态,宿主机相关端口也暴露了,且宿主机本地可以正常访问,容器内测试也可以正常访问(说明服务正常运行),但集群其他节点或者Windows主机无法访问容器的资源,归根结底是因为未能开启内核转发,这个时候需要我们手动开启一下。
解决方案: "net.ipv4.ip_forward = 1",重新加载相关配置文件即可.
10.补充内容
(1)查看Pod的IP地址以及被调度的node节点
kubectl get po -o wide
(2)获取pod资源的yaml配置文件(比如做k8s集群迁移,但没有yaml配置文件)
kubectl get po -o yaml
(3)返回json格式的数据,(比如返回给前端页面)
kubectl get po -o json
(4)基于custom-columns属性格式化输出:
案例一:
kubectl get po/oldzhang-linux76-nginx-alpine -o custom-columns=oldzhang-CONTAINER:.spec.containers[*].name,oldzhang-IMAGE:.spec.containers[*].image
案例二:
kubectl get po/oldzhang-linux76-nginx-alpine -o custom-columns=oldzhang-CONTAINER:.spec.containers[0].name,oldzhang-IMAGE:.spec.containers[0].image
kubectl get po/oldzhang-linux76-nginx-alpine -o custom-columns=oldzhang-CONTAINER:.spec.containers[1].name,oldzhang-IMAGE:.spec.containers[1].image
Pod的扩容内容案例:
[root@k8s110 pod]# cat 09-ports-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: oldzhang-linux76-nginx-09
labels:
school: oldzhang
class: linux76
spec:
# 使用宿主机的网络,默认值为false
# hostNetwork: true
# 为Pod指定主机名
# hostname: oldzhang-linux76
# 指定Pod内的容器重启策略,默认为Always,可以指定为"Always, OnFailure, Never"
restartPolicy: Never
# 指定Pod被调度到哪个节点上,值得注意的是,被调度的节点必须为Ready状态且etcd有记录该节点!
nodeName: k8s113.oldzhang.com
containers:
- name: oldzhang-nginx
image: k8s110.oldzhang.com:5000/nginx:1.20
ports:
- containerPort: 80 # 指定容器的IP
hostIP: "k8s113.oldzhang.com"
hostPort: 9999
protocol: TCP
- containerPort: 80
protocol: UDP
# resources使用"requests"和"limits"参数做资源限制
# requests:
# 代表需要运行Pod的期望资源,如果该node节点不符合期望的资源,则无法创建Pod.
# limits:
# 设置资源的使用上限.
resources:
requests:
memory: "100Mi" # 分配内存
cpu: "250m" # 分配CPU,1core = 1000m
limits:
memory: "200Mi"
cpu: "500m"
# command相当于dockerfile的ENTRYPOINT指令,而args相当于CMD指令
# 二者可以同时使用,只不过args会作为command的参数传递.
command: ["sleep"]
args: ["20"]
# 镜像的下载策略
# IfNotPresent:
# 如果本地存在镜像名称,则直接使用.如果本地不存在镜像,则去仓库拉取镜像.
# Always:(默认值)
# 无视本地镜像,直接去仓库拉取镜像.如果本地有同名镜像则删除后拉取新镜像.
# Never:
# 只是用本地镜像,不拉取镜像.
imagePullPolicy: Always
# 传递环境变量
env:
- name: USERNAME
value: oldzhang
- name: PASSWORD
value: "123456"
- name: ADDRESS
value: ShaHe
[root@k8s110 pod]#
11.有关pod小彩蛋-静态Pod(基于kubeadm方式部署集群经常使用)
/usr/bin/kubelet --logtostderr=true --v=0 --api-servers=http://10.0.0.110:8080 --address=0.0.0.0 --hostname-override=k8s112.oldzhang.com --allow-privileged=false --pod-infra-container-image=k8s110.oldzhang.com:5000/oldzhang-linux76/pod-infrastructure:latest --cluster_dns=10.254.254.254 --cluster_domain=cluster.local --pod-manifest-path /oldzhang/static/pods
温馨提示:
(1)通过"ps -ef | grep kubelet "获取启动kubelet的二进制命令,而后添加"--pod-manifest-path"参数指定本地的静态Pod路径;
(2)静态Pod路径仅对Pod资源的yaml文件生效,对于其他的资源yaml无效;
二.ReplicationController(简称”rc”,该资源在较新版本中已被废弃,不建议将其作为学习的重点)资源管理
1.什么是ReplicationController控制器(Controller)
replicationcontrollers(简称"rc")可以保证指定数量的pod始终存活,rc通过标签选择器来关联pod。
温馨提示:
ReplicationController是早期K8S版本的控制器的控制器(如K8S 1.7),现在都更新K8S都更新到K8S 1.22版本了,因此这种控制器早已被废弃.
2.基于ReplicationController创建Pod并删除容器测试
(1)编写rc的测试文件
[root@10.0.0.101 /oldzhang/pods]# vim 06-nginx-rc1.yaml
[root@10.0.0.101 /oldzhang/pods]#
[root@10.0.0.101 /oldzhang/pods]# cat 06-nginx-rc1.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: oldzhang-nginx
spec:
replicas: 5
selector:
app: oldzhang-web
template:
metadata:
labels:
app: oldzhang-web
spec:
containers:
- name: oldzhang-linux-web
image: 10.0.0.101:5000/docker.io/nginx:1.13
ports:
- containerPort: 80
[root@10.0.0.101 /oldzhang/pods]#
(2)应用资源
[root@10.0.0.101 /oldzhang/pods]# kubectl apply -f 06-nginx-rc1.yaml
(3)删除Pod测试RC控制器是否会自动拉起新的Pod(删除效果如下图所示。)
[root@10.0.0.101 /oldzhang/pods]# kubectl delete pod oldzhang-nginx-0qkqw
[root@10.0.0.101 /oldzhang/pods]# kubectl delete pod oldzhang-nginx-xwr7c
3.编写rc升级版本的配置文件
(1)编写rc文件实现滚动升级
[root@10.0.0.101 /oldzhang/pods]# cat 07-nginx-rc2.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: oldzhang-nginx-2021
spec:
replicas: 5
selector:
app: oldzhang-web-2021
template:
metadata:
labels:
app: oldzhang-web-2021
spec:
containers:
- name: oldzhang-linux-web-2021
image: 10.0.0.101:5000/docker.io/nginx:1.15
ports:
- containerPort: 80
[root@10.0.0.101 /oldzhang/pods]#
(2)滚动升级
kubectl rolling-update oldzhang-nginx -f 07-nginx-rc2.yaml --update-period=10s
(3)滚动回滚
kubectl rolling-update oldzhang-nginx-2021 -f 06-nginx-rc1.yaml --update-period=1s
4.使用rc的实现滚动升级
kubectl rolling-update oldzhang-nginx -f 07-nginx-rc2.yaml --update-period=10s
相关参数说明:
rolling-update:
表示进行滚动升级操作。
oldzhang-nginx:
指定滚动升级的对象。
-f 07-nginx-rc2.yaml:
指定基于哪个资源清单进行滚动升级。
--update-period=10s:
指定更新的间隔时间。
5.使用rc的实现滚动回滚
kubectl rolling-update oldzhang-nginx-2021 -f 06-nginx-rc1.yaml --update-period=2s
三.service资源
1.什么是service
如上图所示,Service是一组Pod提供负载均衡,对外提供统一访问入口。Pod和Service的关系如下所示:
(1)service通过标签关联一组Pod;
(2)service使用iptable或者ipvs为一组Pod提供负载均衡能力;
service的引入主要解决Pod的动态变化,提供统一的访问入口。service主要提供以下两个功能:
(1)防止Pod失联,准备找到提供同一个服务的Pod(服务发现);
(2)定义一组Pod的访问策略(负载均衡);
service的工作流程图如下图所示。我们大致总结以下service的工作流程:
(1)当client访问工作节点的service时,首先他会根据iptables/ipvs规则做相应的负载均衡策略找到后端的Pod以提供服务;
(2)其中iptables/ipvs的规则是由kube-proxy组件来实现的;
(3)kubelet会实时监控对应工作节点的Pod信息;
生产环境在使用iptables和ipvs的对比
iptables:
(1)灵活,功能强大;
(2)规则从上至下遍历匹配和更新,因此在集群规模过大时,可能缺点就越明显,因为每次都需要遍历大量的规则;
ipvs:
(1)工作在内核态,有更好的性能;
(2)调度算法丰富:"rr","wrr","lc","wlc","ip hash"等;
综上所述,推荐大家在生产环境中使用"ipvs"模式。k8s 1.8+版本中推荐使用lvs(四层负载均衡 传输层tcp,udp),在早期版本中,默认使用的是iptables规则哟。
2.创建service资源
(1)编写配置文件
[root@10.0.0.101 /oldzhang/service]# cat 01-nginx-service.yaml
apiVersion: v1
kind: Service # 简称svc
metadata:
name: oldzhang-nginx-svc
spec:
type: NodePort # 默认类型为ClusterIP
ports:
- port: 8888 # 指定service监听的端口
protocol: TCP # 指定协议
nodePort: 30000 # 指定基于NodePort类型对外暴露的端口,若不指定,则会在"30000-32767"端口访问内随机挑选一个未监听的端口暴露哟~
targetPort: 80 # 指定后端Pod服务监听的端口
selector:
app: oldzhang-web # 指定匹配的Pod对应的标签
[root@10.0.0.101 /oldzhang/service]#
(2)应用资源清单
[root@10.0.0.101 /oldzhang/service]# kubectl apply -f 01-nginx-service.yaml
service "oldzhang-nginx-svc" created
[root@10.0.0.101 /oldzhang/service]#
(3)修改"oldzhang-web"标签的副本数量,并观察对应service的Endpoints信息变化。
kubectl scale rc oldzhang-nginx --replicas=2
kubectl describe service oldzhang-nginx-svc
温馨提示:
(1)访问所有的node节点的30000端口是可以访问到nginx访问的,如下图所示,我们也可以访问service的VIP端口(本案例为"8888"),但是对于K8S 1.5有缺陷,访问VIP端口可能会经常访问不到,尽管可以访问后端Pod的IP地址对应的端口哟,顺便说一句,建议在node节点测试成功率较高些,在master节点成功率可能较弱。
(2)使用"kubectl exec -it pod_name /bin/bash"指令可以进入容器修改index.html的内容;
(3)我们也可以基于命令行创建service资源,这在平时测试很有用哟;
kubectl expose rc oldzhang-nginx --type=NodePort --port=80
(4)修改nodePort范围需要修改如下操作,并重启服务即可。
vim /etc/kubernetes/apiserver
KUBE_API_ARGS="--service-node-port-range=3000-60000"
3.service资源常用的类型
ClusterIP类型
默认的类型,分配一个稳定的IP地址,即VIP,只能在集群内部访问。换句话说,适用于K8S集群内部,这意味着k8s集群外部将无法访问到哟~
NodePort类型
适用于对K8S集群外部暴露应用,会在每个节点上启用一个端口来暴露服务,可以在集群外部访问。也会分配一个稳定内部集群IP地址。
配置后可以使用访问地址"<任意NodeIP>:<NodePort>",访问端口:"30000-32767"。
温馨提示:
如下图所示,NodePort类型会在每台K8S的Node上监听端口接收用户流量,在实际情况下,对用户暴露的只会有一个IP和端口,那这么多台Node该使用哪台让用户访问呢?
这时就需要在签名加一个公网负载均衡器为项目提供统一访问入口了。主流开源的负载均衡器有:Haproxy,Nginx,Lvs等,公有云也有类似于SLB的解决方案。
LoadBalancer类型
适合在公有云上对K8S集群外部暴露应用。
与NodePort类似,在每个节点上启用一个端口来暴露服务。除此之外,kubernetes会请求底层云平台(例如阿里云,腾讯云,AWS等)上的负载均衡器,将每个Node(<NodeIP>:<NodePort>)作为后端添加进去。
四.deployment资源
1.什么是deployment
有rc在滚动升级之后,会造成服务访问中断。这是因为基于rc滚动升级时其对应的Pod标签会发生变化,想要快速恢复业务,则需要人工手动修改"svc"资源管理的pod标签。这个过程尽管您的手速再快也是会在短时间内造成服务的中断哟。
综上所述,于是k8s引入了deployment资源。Deployment是最常见的控制器,用于更高级部署和管理Pod。
Deployment功能:
(1)管理ReplicaSet,通过RS资源管理Pod;
(2)具有上线部署,副本设定,滚动升级,回滚等功能;
(3)提供声明式更新(换句话说,就是告诉deployment资源更新什么内容),例如只更新一个新的Image;
应用场景:
网站,API,微服务等。
2.基于deployment部署Pod并进行资源限制
[root@10.0.0.101 ~]# vim 01-deploy.yaml
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# cat 01-deploy.yaml
apiVersion: extensions/v1beta1 # 注意观察这个API的版本编号哟~在后期的版本中其是有变化的!
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
template:
metadata:
labels:
app: oldzhang-nginx
spec:
containers:
- name: nginx
image: 10.0.0.101:5000/docker.io/nginx:1.13
ports:
- containerPort: 80
resources:
limits:
cpu: 100m
memory: 10M
requests:
cpu: 100m
memory: 10M
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# kubectl apply -f 01-deploy.yaml
deployment "nginx" created
[root@10.0.0.101 ~]#
3.基于命令行的方式对上一步创建的deplpyment资源进行滚动升级和回滚
(1)基于svc暴露服务,如上图所示访问任意Node节点暴露的NodePort对象的端口。
kubectl expose deployment nginx --port=80 --target-port=80 --type=NodePort
(2)如下图所示,基于命令行的方式升级nginx镜像版本,在此过程中服务始终不会中断。
kubectl edit deployment nginx
(3)如下图所示,我们也可以基于命令行的方式升级nginx镜像版本,在此过程中服务始终不会中断。
kubectl rollout undo deployment nginx
温馨提示:
除了使用上面的方式进行回滚外,我们还可以使用"kubectl set image deployment nginx nginx=10.0.0.101:5000/docker.io/nginx:1.15
"指令来升级或回滚相应的版本,只不过我们需要手动指定相应的版本而已哟~
4.Deployment指定升级策略
(1)编写资源清单
[root@10.0.0.101 ~]# vim 02-deploy.yaml
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# cat 02-deploy.yaml
apiVersion: extensions/v1beta1 # 注意观察这个API的版本编号哟~在后期的版本中其是有变化的!
kind: Deployment
metadata:
name: nginx
spec:
replicas: 5 # 指定启动Pod的副本数。
strategy: # 指定滚动升级策略
rollingUpdate:
maxSurge: 2 # 在原有的Pod基础上,多启动Pod的数量,假设原来待升级的Pod数量为5,如果我们该值设置为2,表示同时允许启动5 + 2个容器数量。 注意哈,我们也可以指定百分比哟~
maxUnavailable: 1 # 指定最大不可用的Pod数量,如果我们设置该值为1,很明显,副本数为5,表示最少要有4个Pod是可用的。
type: RollingUpdate # 滚动升级
minReadySeconds: 30 # 指定Pod升级的间隔时间,以秒为单位。
template:
metadata:
labels:
app: oldzhang-nginx
spec:
containers:
- name: nginx
image: 10.0.0.101:5000/docker.io/nginx:1.13
ports:
- containerPort: 80
resources:
limits:
cpu: 100m
memory: 10M
requests:
cpu: 100m
memory: 10M
[root@10.0.0.101 ~]#
(2)应用资源清单
[root@10.0.0.101 ~]# kubectl apply -f 02-deploy.yaml
deployment "nginx" created
[root@10.0.0.101 ~]#
(3)升级Pod
kubectl set image deployment nginx nginx=10.0.0.101:5000/docker.io/nginx:1.15
有关升级策略如下:
(1)本案例副本数为5,当我们设置maxSurge的值为2时,表示总的Pod数量不能超过5 + 2 = 7个,而由于我们将maxUnavailable数量设置为1时,表示最小的可用数量为 5 - 1 = 4个。
(2)结合第一步的前提,要求4个Pod是必须可用的,而总Pod数量为7,也就是说我们可用同时升级3个Pod;
(3)当上一步的3个Pod升级完成并能正常提供服务时,此时会干掉3个旧的Pod,用升级的Pod替代之,但不得不说还剩余1个旧的Pod依旧是需要保留的;
(4)接下来由于已经有3个新的和1个旧的Pod,我们只需要在升级2个Pod即可达到5个副本的需求;
(5)当所有的副本升级完成后,就会讲最后一个旧的Pod删除掉;
温馨提示:
注意哈,"maxSurge"的值可以是百分百哟~例如:"maxSurge: 10%"
5.ReplicationController和deployment对比
共同点:
(1)可以控制Pod数量;
(2)都可以滚动升级;
(3)通过标签选择器关联Pod;
不同点:
(1)rc升级需要yaml文件(如果在使用者修改了文件内容会造成不必要的麻烦),deployment修改配置文件可以实时生效(直接在命令行使用"kubectl edit deployment nginx"案例);
(2)rc升级会导致服务中断(需要手动修改service的标签),而deployment升级并不会中断服务访问;
(3)rc直接控制Pod,而deployment基于rs来控制Pod;
6.基于命令行实现deployment升级和回滚操作
(1)命令行创建deployment
kubectl run nginx --image=10.0.0.101:5000/docker.io/nginx:1.13 --replicas=3 --record
(2)命令行升级到指定镜像版本(常用)
kubectl set image deployment nginx nginx=10.0.0.101:5000/docker.io/nginx:1.15
(3)查看deployment所有历史版本
kubectl rollout history deployment nginx
(4)deployment回滚到上一个版本(不常用)
kubectl rollout undo deployment nginx
(5)deployment回滚到指定版本(不常用)
kubectl rollout undo deployment nginx --to-revision=2
温馨提示:
(1)基于"kubectl run"命令行方式启动的容器只能在启动的Pod中允许一个容器,且容器名称和deployment的名称一致;
(2)如果想要在同一个Pod中启动多个容器,请使用之前的yaml文件配置;
(3)上面使用"kubectl set image deployment nginx nginx=10.0.0.101:5000/docker.io/nginx:1.15"命令中,其中关于第二个nginx(即"nginx=...")的名称应该为容器的名称哟;
扩展命令:(猜猜看下面在命令在干啥)
kubectl run -it oldzhang-linux76 --image=k8s110.oldzhang.com:5000/oldzhang-nginx/alpine-80:1.16 --replicas=1 --port=80 --env="oldzhang_linux76_username=66666666666666666" --rm -- sh
7.改写wordpress案例
(1)将MySQL和wordpress部署在同一个Pod内耦合性过高
[root@10.0.0.101 ~]# cat 03-deploy-wordpress.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: wordpress
spec:
replicas: 1
template:
metadata:
labels:
app: wordpress
spec:
containers:
- name: mysql
image: 10.0.0.101:5000/mysql:5.7
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: somewordpress
- name: MYSQL_DATABASE
value: wordpress
- name: MYSQL_USER
value: wordpress
- name: MYSQL_PASSWORD
value: wordpress
- name: wordpress
image: 10.0.0.101:5000/wordpress:latest
ports:
- containerPort: 80
env:
- name: WORDPRESS_DB_HOST
value: 127.0.0.1
- name: WORDPRESS_DB_USER
value: wordpress
- name: WORDPRESS_DB_PASSWORD
value: wordpress
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# kubectl apply -f 03-deploy-wordpress.yaml
deployment "wordpress" created
[root@10.0.0.101 ~]#
(2)创建svc
kubectl expose deployment wordpress --port=80 --target-port=80 --type=NodePort
(3)对wordpress进行弹性伸缩,请再次访问wordpress,你会发现其中的问题。
kubectl scale deployment wordpress --replicas=2
综上所述,生产环境中我们不应该将多个容器放在同一个Pod,这样耦合性太高。在弹性伸缩时我们会发现很难基于同一个Pod的某个容器进行单独的扩容或缩容。因此今日作业就是将其拆开。
温馨提示:
(1)如果想要进入到一个wordpress容器,需要执行以下命令:("--container"可以简写为"-c")
kubectl exec -it wordpress-738700435-2g4h2 --container=wordpress /bin/bash
(2)如果一个Pod中只有一个容器的话,则无需进入到指定的容器。
8.讲解说明
(1)启动MySQL的Pod
[root@10.0.0.101 ~/wordpress]# cat 01-wordpress-mysql.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: oldzhang-wordpress-mysql
spec:
replicas: 1
template:
metadata:
labels:
app: oldzhang-wordpress-mysql
spec:
containers:
- name: mysql
image: 10.0.0.101:5000/mysql:5.7
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: somewordpress
- name: MYSQL_DATABASE
value: wordpress
- name: MYSQL_USER
value: wordpress
- name: MYSQL_PASSWORD
value: wordpress
[root@10.0.0.101 ~/wordpress]#
[root@10.0.0.101 ~/wordpress]# kubectl apply -f 01-wordpress-mysql.yaml
deployment "oldzhang-wordpress-mysql" configured
[root@10.0.0.101 ~/wordpress]#
(2)暴露mysql的service
[root@10.0.0.101 ~/wordpress]# cat 02-wordpress-mysql-svc.yml
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
ports:
- port: 3306
targetPort: 3306
selector:
app: oldzhang-wordpress-mysql
[root@10.0.0.101 ~/wordpress]#
[root@10.0.0.101 ~/wordpress]# kubectl apply -f 02-wordpress-mysql-svc.yml
service "mysql" created
[root@10.0.0.101 ~/wordpress]#
[root@10.0.0.101 ~/wordpress]# kubectl get svc -o wide
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes 10.254.0.1 <none> 443/TCP 16d <none>
mysql 10.254.250.92 <none> 3306/TCP 28s app=oldzhang-wordpress-mysql
[root@10.0.0.101 ~/wordpress]#
(3)使用MySQL的service暴露的地址
[root@10.0.0.101 ~/wordpress]# cat 03-wordpress-web.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: wordpress
spec:
replicas: 1
template:
metadata:
labels:
app: wordpress
spec:
containers:
- name: wordpress
image: 10.0.0.101:5000/wordpress:latest
ports:
- containerPort: 80
env:
- name: WORDPRESS_DB_HOST
value: 10.254.250.92 # 指定数据库的service地址哟~
- name: WORDPRESS_DB_USER
value: wordpress
- name: WORDPRESS_DB_PASSWORD
value: wordpress
[root@10.0.0.101 ~/wordpress]#
[root@10.0.0.101 ~/wordpress]# kubectl apply -f 03-wordpress-web.yaml
deployment "wordpress" created
[root@10.0.0.101 ~/wordpress]#
(4)编写wordpress的语法
[root@10.0.0.101 ~/wordpress]# cat 04-wordpress-svc.yml
apiVersion: v1
kind: Service
metadata:
name: wordpress
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
selector:
app: wordpress
[root@10.0.0.101 ~/wordpress]#
[root@10.0.0.101 ~/wordpress]# kubectl apply -f 04-wordpress-svc.yml
service "wordpress" configured
[root@10.0.0.101 ~/wordpress]#
温馨提示:
我们也可以基于命令行的方式暴露service资源哟,如:"kubectl expose deployment wordpress --port=80 --target-port=80 --type=NodePort"
五.tomcat+mysql练习
1.启动MySQL实例
[root@10.0.0.101 ~]# vim mysql-rc.yml
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# cat mysql-rc.yml
apiVersion: v1
kind: ReplicationController
metadata:
name: mysql
spec:
replicas: 1
selector:
app: oldzhang-mysql
template:
metadata:
labels:
app: oldzhang-mysql
spec:
containers:
- name: mysql
image: 10.0.0.101:5000/mysql:5.7
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: '123456'
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# kubectl apply -f mysql-rc.yml
2.启动MySQL实例的svc
[root@10.0.0.101 ~]# vim mysql-svc.yml
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# cat mysql-svc.yml
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
ports:
- port: 3306
targetPort: 3306
selector:
app: oldzhang-mysql
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# kubectl apply -f mysql-svc.yml
service "mysql" configured
[root@10.0.0.101 ~]#
3.启动tomcat实例
[root@10.0.0.101 ~]# cat tomcat-rc.yml
apiVersion: v1
kind: ReplicationController
metadata:
name: oldzhang-web
spec:
replicas: 1
selector:
app: oldzhang-tomcat-app
template:
metadata:
labels:
app: oldzhang-tomcat-app
spec:
containers:
- name: myweb
image: 10.0.0.101:5000/tomcat-app:v1
ports:
- containerPort: 8080
env:
- name: MYSQL_SERVICE_HOST
value: '10.254.87.39' # 指定MySQL的svc资源的VIP地址即可.
- name: MYSQL_SERVICE_PORT
value: '3306'
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# kubectl apply -f tomcat-rc.yml
replicationcontroller "oldzhang-web" configured
[root@10.0.0.101 ~]#
4.启动tomcat的实例的svc
[root@10.0.0.101 ~]# cat tomcat-svc.yml
apiVersion: v1
kind: Service
metadata:
name: myweb
spec:
type: NodePort
ports:
- port: 8080
nodePort: 30008
selector:
app: oldzhang-tomcat-app
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# kubectl apply -f tomcat-svc.yml
service "myweb" created
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# kubectl get svc
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.254.0.1 <none> 443/TCP 15d
mysql 10.254.87.39 <none> 3306/TCP 24m
myweb 10.254.43.201 <nodes> 8080:30008/TCP 8m
[root@10.0.0.101 ~]#
温馨提示:
在浏览器访问svc暴露的30008端口就能访问到tomcat,但请注意要访问"http://k8s102.oldzhang.com:30008/demo/index.jsp"才能进入到下面的界面哟。
5.扩容MySQL数据库实例,访问Pod并添加测试数据,观察其他Pod数据是否同步。
[root@10.0.0.101 ~]# kubectl get rc
NAME DESIRED CURRENT READY AGE
mysql 1 1 1 33m
oldzhang-web 1 1 1 10m
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# kubectl scale rc oldzhang-web --replicas=3
replicationcontroller "oldzhang-web" scaled
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# kubectl get rc
NAME DESIRED CURRENT READY AGE
mysql 1 1 1 33m
oldzhang-web 3 3 2 10m
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]#
温馨提示:
如下图所示,当我们扩容了多个数据库时,尽管访问不同的Pod,但其数据是相同的哟。
6.使用deployment改写上面的rc资源清单
(1)删除资源清单
[root@10.0.0.101 ~]# kubectl delete -f mysql-rc.yml
replicationcontroller "mysql" deleted
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# kubectl delete -f tomcat-rc.yml
replicationcontroller "oldzhang-web" deleted
[root@10.0.0.101 ~]#
(2)重命名当前的所有文件
[root@10.0.0.101 ~]# rename rc deploy *
(3)修改mysql-deploy.yml资源清单
[root@10.0.0.101 ~]# cat mysql-deploy.yml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: mysql
spec:
replicas: 1
template:
metadata:
labels:
app: oldzhang-mysql
spec:
containers:
- name: mysql
image: 10.0.0.101:5000/mysql:5.7
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: '123456'
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# kubectl apply -f mysql-deploy.yml
deployment "mysql" created
[root@10.0.0.101 ~]#
(4)修改tomcat-deploy.yml资源清单
[root@10.0.0.101 ~]# cat tomcat-deploy.yml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: oldzhang-web
spec:
replicas: 1
template:
metadata:
labels:
app: oldzhang-tomcat-app
spec:
containers:
- name: myweb
image: 10.0.0.101:5000/tomcat-app:v1
ports:
- containerPort: 8080
env:
- name: MYSQL_SERVICE_HOST
value: '10.254.87.39' # 指定MySQL的svc资源的VIP地址即可.
- name: MYSQL_SERVICE_PORT
value: '3306'
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# kubectl apply -f tomcat-deploy.yml
deployment "oldzhang-web" created
[root@10.0.0.101 ~]#
(5)访问之前的service资源,观察服务是否能够正常访问
如下图所示,仅需访问任何Node节点的30008端口,均能正常访问服务哟"http://k8s103.oldzhang.com:30008/demo/"
7.内存回收小妙招
echo 3 > /proc/sys/vm/drop_caches
drop_caches的值可以是0-3之间的数字,代表不同的含义
0:不释放(系统默认值)
1:释放页缓存
2:释放dentries和inodes
3:释放所有缓存
六.ReplicaSet资源
1.ReplicaSet概述
Replica Set控制器的用途:
(1)Pod副本数量管理,不断对比当前Pod数量和期望Pod数量。
(2)Deployment每次发布都会创建RS作为记录,用于实现回滚。
值得注意的是,Replica Set控制器Replication控制器的升级版,其工作原理如下图所示。
k8s 1.5测试案例:
[root@k8s110 replicasets]# cat 01-rs-nginx.yaml
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
name: oldzhang-linux76-rs
labels:
school: oldzhang
class: linux76
spec:
replicas: 3
template:
metadata:
labels:
apps: nginx-1-20
spec:
containers:
- name: oldzhang-linux76-nginx-alpine-1-20
image: k8s110.oldzhang.com:5000/oldzhang-nginx/alpine-80:1.20
ports:
- containerPort: 80
[root@k8s110 replicasets]#
参考链接:
https://kubernetes.io/zh/docs/concepts/workloads/controllers/replicaset/
2.部署deployment资源时会生成replicaset资源
[root@10.0.0.101 ~]# cat 01-deploy.yaml
apiVersion: extensions/v1beta1 # 注意观察这个API的版本编号哟~在后期的版本中其是有变化的!
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
template:
metadata:
labels:
app: oldzhang-nginx
spec:
containers:
- name: nginx
image: 10.0.0.101:5000/docker.io/nginx:1.13
ports:
- containerPort: 80
resources:
limits:
cpu: 100m
memory: 10M
requests:
cpu: 100m
memory: 10M
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# kubectl apply -f 01-deploy.yaml
deployment "nginx" created
[root@10.0.0.101 ~]#
温馨提示:
我们可以手动修改上面的yaml资源清单,并修改"image"字段信息。
七.DaemonSet资源
1.DaemonSet概述
DaemonSet功能概述:
(1)在每一个Node上运行一个pod;
(2)新加入的Node也同样会自动运行一个Pod;
应用场景:
(1)网络插件(例如kube-proxy,colico);
(2)zabbix agent;
(3)node_export;
(4)日志收集端,例如filebeat;
...
温馨提示:
(1)DaemonSet不支持副本设置哟,其特点是在所有不含污点的工作节点上只运行一个Pod,如果配置了污点容忍也可以在被打污点的工作节点上运行哟;
(2)DaemonSet也支持滚动更新哟~
2.使用DemonSet部署一个日志采集程序实战案例
[root@10.0.0.101 ~]# vim daemonset-install-demo.yaml
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# cat daemonset-install-demo.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: filebeat
namespace: kube-system
spec:
selector:
matchLabels:
name: filebeat
template:
metadata:
labels:
name: filebeat
spec:
tolerations: # 配置污点容忍,避免master节点不能被调度(因为我的环境中存在master和Node复用的情况)
- effect: NoSchedule
operator: Exists # 匹配"effect"的值为"NoSchedule"的所有污点,并没有精确匹配具体的KEY。
containers:
- name: log
image: elastic/filebeat:7.3.2
[root@10.0.0.101 ~]#
八.Job资源
1.Job概述
Job控制器:
一次性任务,用来管理非守护进程,指的是运行某次任务(即一次性作业)。
应用场景:
离线数据处理,视频解码,备份数据库等。
温馨提示:
由Job控制器创建的Pod在执行完任务后会转换为"Completed"状态,并不会删除。
2.Job控制器应用案例
[root@10.0.0.101 ~]# vim job-install-demo.yaml
[root@10.0.0.101 ~]#
[root@10.0.0.101 ~]# cat job-install-demo.yaml
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 # 无论Pod执行的结果如何,从不重启该Pod
backoffLimit: 4 # 重试次数
[root@10.0.0.101 ~]#
温馨提示:
查看Job创建的Pod产生的日志信息,计算的是PI的2000个数字。
我们可以使用"kubectl logs"命令查看容器的输出结果哟~。
九.CronJob资源
1.CronJob概述
CronJob控制器:
CronJob用于实现定时任务,像Linux的Crontab一样,多用于定时任务。
应用场景:
通知,备份等场景。
2.实战案例
[root@k8s103.oldzhang.com ~]# vim cronjob-install.yaml
[root@k8s103.oldzhang.com ~]#
[root@k8s103.oldzhang.com ~]# cat cronjob-install.yaml
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 www.cnblogs.com/oldzhang/
restartPolicy: OnFailure
[root@k8s103.oldzhang.com ~]#
温馨提示:
所有的Node工作节点上最终只会保留3个最新的Pod哟~
十.StatefulSet资源
1.无状态与有状态概述
Deployment控制器设计原则:
管理所有Pod一模一样,提供同一个服务,也不考虑各个Pod在哪台Node运行,可随意扩容和缩容。这种应用称为"无状态",例如Web服务。
在实际场景中,Deployment控制器并不能满足所有应用,尤其是分布式应用,会部署多个实例,这些实例之间往往会有依赖关系,例如主从关系,主备关系,这种应用称为"有状态",例如MySQL主从,Etcd集群等。
综上所述,为了要解决有状态应用,以MySQL主从为例,需要满足以下三个要求:
(1)启动顺序;
(2)实例的数据是独立存储;
(3)需要一个固定的IP或者主机名;
StatefulSet控制器设计原则:
用于部署有状态的应用,满足一些有状态应用的需求。
StatefulSet控制器的设计特点:
(1)Pod有序的部署,扩容,删除和停止;
(2)Pod分配一个稳定且唯一的网络标识;
(3)Pod分配一个独享的存储;
温馨提示:
在K8S1.5版本之前,StatefulSet的前身为"Pet Sets"
Deployment和StatefulSet控制器的区别
Deployment和StatefulSet控制器的区别主要在:域名,主机名,存储(PVC)。
网络标识概述:
使用Headless Service(相比普通Service只是将"Service.spec.clusterIP"定义为None)来维护Pod网络身份,会为每个Pod分配一个数字编号并且按照编号顺序部署。
还需要在StatefulSet添加"serviceName: Nginx"字段指定StatefulSet控制器要是用这个Headless Service。
稳定主要体现在主机名和Pod A记录:
主机名:
<statefulset名称>-<编号>
Pod DNS A记录:
<statefulset名称-编号>.<service-name>.<namespace>.svc.cluster.local
2.创建一个名为”headless-web”的Headless Service
[root@k8s103.oldzhang.com ~]# kubectl get svc # 注意观察,普通的service其"CLUSTER-IP"字段是一个固定的IP地址。
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11d
[root@k8s103.oldzhang.com ~]#
[root@k8s103.oldzhang.com ~]# kubectl get svc -o yaml | grep " clusterIP"
clusterIP: 10.96.0.1
[root@k8s103.oldzhang.com ~]#
[root@k8s103.oldzhang.com ~]# vim headless-Service-demo.yaml
[root@k8s103.oldzhang.com ~]#
[root@k8s103.oldzhang.com ~]# cat headless-Service-demo.yaml
apiVersion: v1
kind: Service
metadata:
name: headless-web
namespace: default
spec:
clusterIP: None # 我们将clusterIP设置为None,其就是一个headless,因为该service创建成后并不会分配一个固定的IP地址
ports:
- port: 80 # 指定service的端口
protocol: TCP # 指定service的协议
targetPort: 80 # 指定容器端口
selector: # 标签选择器
app: nginx # 指定关联Pod的标签
type: ClusterIP # 指定service的服务类型
[root@k8s103.oldzhang.com ~]#
[root@k8s103.oldzhang.com ~]# kubectl apply -f headless-Service-demo.yaml
service/headless-web created
[root@k8s103.oldzhang.com ~]#
[root@k8s103.oldzhang.com ~]# kubectl get svc -o wide # 注意观察,我们创建的Headless Service其"CLUSTER-IP"字段并不是一个固定的IP地址,其值被我们显式设置为"None"。
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
headless-web ClusterIP None <none> 80/TCP 12s app=nginx
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11d <none>
[root@k8s103.oldzhang.com ~]#
[root@k8s103.oldzhang.com ~]# kubectl get svc -o yaml | grep " clusterIP"
clusterIP: None
clusterIP: 10.96.0.1
[root@k8s103.oldzhang.com ~]#
3.使用StatefulSet控制器引用名为”headless-web”的Headless Service
[root@k8s103.oldzhang.com ~]# vim statefulset-web.yaml
[root@k8s103.oldzhang.com ~]#
[root@k8s103.oldzhang.com ~]# cat statefulset-web.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: statefulset-web
spec:
# 指定咱们已创建名为"headless-web"的Headless Service
serviceName: "headless-web"
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: mynginx
image: nginx:1.17
ports:
- containerPort: 80
name: myweb
[root@k8s103.oldzhang.com ~]#
[root@k8s103.oldzhang.com ~]# kubectl apply -f statefulset-web.yaml
statefulset.apps/statefulset-web created
[root@k8s103.oldzhang.com ~]#
温馨提示:
注意观察statefulset控制器产生的Pod名称,其名称都是有序(其顺序编号是从0开始)的哟~
此处我们只是对statefulset有个印象即可,后续有专门的章节讲解它,因为我们还欠缺一些知识点哟~
十一.可能会遇到的问题
1.No API token found for service account “default”, retry after the token is automatically created and added to the service account
报错原因:
在"default"名称空间缺少服务账户(service account)相关的凭据。
解决方案:
(1)生成密钥
openssl genrsa -out /etc/kubernetes/serviceaccount.key 2048
(2)修改apiserver的配置文件
vim /etc/kubernetes/apiserver
...
# 只需在最后一行添加如下内容
KUBE_API_ARGS="--service_account_key_file=/etc/kubernetes/serviceaccount.key"
(3)修改controller-manager的配置文件
vim /etc/kubernetes/controller-manager
...
# 只需在最后一行添加如下内容
KUBE_CONTROLLER_MANAGER_ARGS="--service_account_private_key_file=/etc/kubernetes/serviceaccount.key"
(4)重启服务
systemctl restart etcd kube-apiserver kube-controller-manager kube-scheduler
2. Invalid value: “oldzhang_nginx”: must match the regex a-z0-9?(.a-z0-9?)*
故障原因:
对于"metadata.name"字段其值为无效的。
解决方案:
请遵循命名规则的正则表达式: [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
3.Error syncing pod, skipping: failed to “StartContainer” for “POD” with ImagePullBackOff: “Back-off pulling image “registry.access.redhat.com/rhel7/pod-infrastructure:latest””
故障原因:
如下图所示,观察Error类型的错误,从输出中可以看到去"registry.access.redhat.com..."这个第三方仓库下载镜像失败了。
解决方案:
手动下载"pod-infrastructure:latest"镜像即可并上传到私有仓库,而后修改"/etc/kubernetes/kubelet"的配置文件的"KUBELET_POD_INFRA_CONTAINER"参数即可。
温馨提示:
"Infrastructure Container"(基础设施容器)的容器用以实现同一个Pod之间网络共享。
我们无需去红帽公司的镜像仓库下载"pod-infrastructure:latest"镜像,直接在docker官方的hub仓库就有该镜像哟~我已经将其上传到docker hub,直接执行"docker pull jasonyin2020/pod-infrastructure"即可。
4.cannot join network of a non running container: ….
报错原因:
基础镜像指定的不正确"pod-infrastructure:latest"。
解决方案:
使用我课上准备的镜像,或者直接"docker pull jasonyin2020/pod-infrastructure"
5.PodFitsHostPorts Predicate PodFitsHostPorts failed
问题原因:
大多数场景下是端口被占用.
解决方案:
请检查"kubectl explain pods.spec.containers.ports.hostPort",请将其注释!
本文来自博客园,作者:kirin(麒麟),转载请注明原文链接:https://www.cnblogs.com/kirin365/articles/16137847.html