K8s笔记之二:在K8s集群里部署应用和服务
部署环境
在上一篇里,我已经在三台N1上组了一个集群,其中KUBESVR1是控制平面,SVR2和SVR3是节点。后来因为上了PVE,又把N1集群换下来了,现在这三台是放在PVE里跑的,实际使用没什么区别。总体性能差不多(并发N1可能还好一些,毕竟4核),J1900的耗电还大些。但用来学习已经可以了。
基本配置是这样的:
- 2G RAM
- 8G SSD
- 1 or 2 Cores(考虑到J1900僝弱的性能,只给Control Plane双核,因为它至少需要双核,其它节点都是单核)
对于一个相对完整的高可用K8s环境,会需要上十台服务器。但如果只是简单部署个测试环境就简单得多了:
- 一台Control Plane
- 一台工作节点(这里用了两台,毕竟咱有这个条件)
应用部署
基于上述概念来定义一个最基本的应用部署大致是这样:
- 先把应用打包成一个容器镜像
- 再把这个镜像在k8s集群里跑起来,方式有两类:
- 直接把镜像作为一个Pod运行起来
- 通过配置文件运行Pod(Pod配置或Deployment配置)
- 通过Pod的内部IP访问应用
现在就来实现这样一个部署吧。
直接部署
在所有节点都Ready的状态下,在Control Plane上运行:
kubectl run pyhttp --image=python:3.7-buster --command -- python -m http.server
即可在节点中启动一个pod,其中运行了一个python的web服务。其中pyhttp
是这个pod的名字,--image
参数指定了镜像的名字,--command --
之后的部分是容器的运行命令。
查看pod的状态:
kubectl get pods
或者查看更详细的信息,特别是当Pod启动失败时,可以通过这个命令查看到错误原因:
kubectl describe pod pyhttp
python自带的这个httpserver默认是监听8000端口的,但问题是它的IP是什么呢?在k8s里,每个pod都会有一个虚拟的IP,这可以通过查看pod详细取得。如果pod运行在多个nodes上,还会有多个虚拟IP。所以我们现在可以这样访问:
curl http://<virtual_ip>:8000
如果我们现在不需要这个Pod,可以把它删除:
kubectl delete pod pyhttp --now
其它Pod操作:
# 查看容器日志
kubectl logs pod pyhttp
# 进入容器
kubectl exec -it pyhttp -- /bin/bash
通过配置文件部署
因为命令直接部署相对比较麻烦,特别是需要多次部署或自动化操作的情况,所以更好的方法是通过配置文件部署。
这也有两种方式,一个是直接配置Pod,更好的方法是配置Deployment。
直接配置Pod是这样,写一个pyhttppod.yml
的配置文件:
apiVersion: v1
kind: Pod
metadata:
name: pyhttppod
spec:
containers:
- name: pyhttppod
image: python:3.7-buster
command: ['python', '-m', 'http.server']
然后运行命令部署:
kubectl apply -f pyhttppod.yml
简单的Pod配置部署功能还是弱了一点,更好的方法是用Deployment方式pyhttpdeploy.yml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: pyhttpdeploy
spec:
replicas: 2
selector:
matchLabels:
app: pyhttp
template:
metadata:
labels:
app: pyhttp
spec:
containers:
- name: pyhttpdeploy
image: python:3.7-buster
command: ['python', '-m', 'http.server']
改动了几个地方:
- apiVersion改为apps/v1
- kind由Pod改为Deployment
- 增加了replicas,指定副本数量,在有多个节点服务器的集群里,Pod的多个副本可以运行在不同的节点上
- 增加selector用于标签匹配,只有完全匹配标签的容器模板才会被部署
- containers配置移到template下,即原来的容器配置变成容器模板配置,可以增加部署的灵活性
同样运行命令kubectl apply -f pyhttpdeploy.yml
部署。
现在用kubectl get pods
查看就可以看到这个Pod有两个实例:
pyhttpdeploy-5897d584b7-8dg6l 1/1 Running 0 52s
pyhttpdeploy-5897d584b7-v6fqc 1/1 Running 0 52s
查看其中一个kubectl describe pod pyhttpdeploy-5897d584b7-8dg6l
取得IP,即可通过http://<virtual_ip>:8000
访问页面。
Deployment的相关命令:
# 查看deployment
kubectl get deployments
# 删除deployment
kubectl delete deployment pyhttpdeploy
# 查看deployment版本历史
kubectl rollout history deployment pyhttpdeploy
# 回滚deployment到上一版本
kubectl rollout undo deployment pyhttpdeploy
# 回滚到指定版本
kubectl rollout undo deployment pyhttpdeploy --to-revision=1
# 副本数量调整
kubectl scale deployment pyhttpdeploy --replicas=3
通过服务配置文件部署
如前面所说,我们部署完Pod以后都需要先查看一下Pod的虚拟IP才能连接,这很不科学:
因为这个虚拟IP是动态分配的,如果Pod因为某些原因(比如节点服务器故障)导致被干掉,然后重启(可能在不同的节点上),它的IP必然会被重新分配,那么我们就连不上这个Pod了。
另外,如果我们部署了Pod的多个副本,但每个副本有独立的虚拟IP,所以客户端连哪个副本也是问题,除非自己在前面做一个负载均衡。
为了解决这个问题,K8s提供了服务的概念。
服务是Pod之上的一层抽象,相当于一个虚拟的负载均衡IP:当客户端访问这个服务虚拟IP时,会自动被映射到实际的Pod虚拟IP上——不论这个Pod是否被重新部署或者有多个副本,服务的虚拟IP都会自动更新到最新可用的Pod虚拟IP上。
注意:服务是相对Pod独立的应用,即需要有相应的Pod(实际是Deployment)配置才能使用服务部署,服务通过选择标签进行匹配(所以需要Deployment,Pod配置不带标签)。
创建一个服务pyhttpsvc.yml
:
apiVersion: v1
kind: Service
metadata:
name: pyhttpsvc
spec:
selector:
app: pyhttp # 匹配相应的Pod
type: ClusterIP
ports:
- port: 8000
targetPort: 8000
用kubectl apply -f pyhttpsvc.yml
启动服务后,即可用kubectl get svc
查看服务的虚拟IP:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
...
pyhttpsvc ClusterIP 10.104.83.181 <none> 8000/TCP 7m59s
然后就可以通过http://10.104.83.181:8000
访问之前用Deployment部署的Pod了,不论它是否重启或有多个副本。
但问题在于,这个IP还是集群内的虚拟IP,从外部还是无法访问,虽然可以用kubectl port-forward service/pyhttpsvc 8000:8000
把端口映射出去,但不推荐。
正确的做法是改用NodePort
:
apiVersion: v1
kind: Service
metadata:
name: pyhttpsvc
spec:
selector:
app: pyhttp
type: NodePort # 改为NodePort
ports:
- port: 8000
targetPort: 8000
nodePort: 30800 # 增加端口号,限30000-32767
这样就可以通过任一节点服务器的IP的30800端口访问到服务了。现在看kubectl get svc
就会显示映射端口了:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
...
pyhttpsvc NodePort 10.104.83.181 <none> 8000:30800/TCP 38m
如果有很多节点服务器的话,这样其实仍然不太方便,毕竟任何一个节点都可能下线,对外用哪个IP都不合适,除非在前面再加一个负载均衡。
K8s其实有自带这么个LoadBalancer,跟NodePort一样,这也是一个服务类型,会创建一个虚拟的外网IP,自动映射到所有可用的节点IP。不过现在它已经被Ingress取代了,这里就不多说了。
推送到[go4pro.org]