Network是Kubernetes体系中的重头戏,相比于其它组件来说也比较难,因为Kubernetes中所有的Pod或者服务之间一定是需要进行网络通信的,如果不能解决网络通信的问题,那可以说整个Kubernetes体系中的Pod是没有灵魂的。
所以必须要把Network这个组件搞清楚,那Network这个组件该从哪里开始入手呢?
首先,为了更好的了解Kubernetes中的Network组件,我们先回想下,在Docker中我们的网络是如何做的?
在Docker中,无非是划分成单机和多机,单机中Docker的网络可以由下图所示:
两个容器在同一个Centos中,为什么彼此能够ping通?是因为有docker0这样的网卡,我们都通过veth这样的一个pair对去匹配,这是最简单的一种场景,是容器之间的通信。
如果一个容器搬到了一个机器中,一个容器搬到了另外一个容器中,我们该如何解决呢?这就涉及到了Docker Swarm多机集群中的通信,可以使用overlay的网络实现,如下图所示:
如果没有overlay网络,两个container之间是不能进行通信的,那怎么样让两个container进行通信呢?
我们将整个container的信息,通过veth0这样一个跟互联网能够交互的网卡,把整个数据包通过overlay这样的网络发送给我们另外一端,另外一端接收到信息之后再去进行解包, 这样就能进行通信了。这是overlay网络帮我们做的实现,保证了两个容器在多机之间可以进行通信。
但是现在的问题是,我们讨论的并不是container,而讨论的是Pod,因为Pod是Kubernetes中最小的操作单元,所以Kubernetes的网络组件一定是从Pod开始入手。
我们可以先思考一个问题,在同一个pod中的两个container之间可以通信吗?答案是肯定的,因为同一个Pod中的container肯定会在同一个node上,可以参考单机Docker中container之间的通信。在Docker体系中,container之间通过docker0网卡进行通信;在Kubernetes中,当我们创建一个pod的时候,pod中一定会创建一个名字为pause container的容器,pod中所有的container都会连接到整个container中,这样就可以实现pod中container之间的通信
了解了同一个pod中container之间的通信之后,我们要思考的问题是同一个集群中pod之间如何通信。Pod之间的通信又可以划分为两个维度:
- 同一个Node中Pod之间的通信
- 不同Node中Pod之间的通信
我们通过一个例子展示:准备一个nginx-pod,一个busybox-pod
nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx
ports:
- containerPort: 80
busybox-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
将两个pod运行起来,并且查看运行情况
发现
:nginx-pod的ip为192.168.14.11,运行在work01节点;busybox-pod的ip为192.168.221.74,运行在worker02节点。
上面两个ip和我们虚拟机的IP是没有任何关系的,那这个IP是谁帮忙生成的呢?答案是网络插件帮忙生成的,比如calico等。
经测试发现,我们可以在任意Node上去访问Pod。
在worker02节点访问运行在01节点中的nginx
在worker01节点上ping busybox pod的ip
结论:通过网络插件(calico),集群中任意Node中的Pod之间可以彼此通信。
官网
:https://kubernetes.io/docs/concepts/cluster-administration/networking/#the-kubernetes-network-model
另外,Pod其实是很不稳定的,因为deployment随时会对Pod进行扩缩容,随时会有Pod被删除和创建(IP也会变化),也就意味着我们不能通过IP稳定的访问Pod内的服务,那要如何解决呢?能不能有一个固定的IP能够让我们去访问特定的Pod呢?答案是肯定的,我们可以通过service组件来控制,也就是在Pod的外面再添加一层service,我们只要访问service的IP,然后service帮忙去负载均衡到servic对应的Pod,如下图所示:
案例
- 创建whoami-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami-deployment
labels:
app: whoami
spec:
replicas: 3
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: jwilder/whoami
ports:
- containerPort: 8000
- 创建Pod
- 在任意一台机器都可以通过ip和port访问成功
- 添加service
先查看默认namespace下有哪些service
通过下面命令给whoami-deployment这个deployment创建service
kubectl expose deployment whoami-deployment
可以看到我们为whoami-deployment创建了一个ip为10.100.145.244的service。在任意节点通过这个ip去访问whoami,发现都可以访问成功,并且可以做负载均衡。
注意:这个IP只能在集群内访问,集群外访问不了。
service组件是如何实现这个功能的呢?可以通过下面命令查看service的详细信息
kubectl describe svc whoami-deployment
可以看到所有Pod的IP信息都在service得到endpoints中。
结论: Cluster IP类型的service的IP可以供集群内访问,并且对很多deployment进行负载均衡。
上面了解了集群内各个Node中Pod之间的通信,那么集群内和集群外面是如何通信的呢?
集群外面访问集群内Pod
方案一:NodePort类型的service
- 根据whoami-deployment.yaml创建Pod
此时,我们只能在集群内通过IP去访问。 - 创建NodePort类型的service
kubectl expose deployment whoami-deployment --type=NodePort
可以看到,创建了一个IP为10.104.253.245,类型为NodePort的service,并且把8000端口映射到宿主机上的30448端口。现在我们在集群外面访问30448端口:
这种类型的service会有一个潜在的问题,那就是端口可能会被占用,随着我们的service越来越多,宿主机的端口可能不够用,所以这种方式不建议在生产环境中使用。
方案二:Loadbalance 类型的service
这种service需要结合第三方供应商帮忙提供域名,并且有一定的约束性,所以也不常用。
方案三:Ingress 类型的网络
官网
:https://kubernetes.io/docs/concepts/services-networking/ingress/
通过官网可以发现,Ingress就是帮助我们访问集群内的服务的。不过在看Ingress之前,我们还是先以一个案例出发。很简单,在K8S集群中部署tomcat
浏览器想要访问这个tomcat,也就是外部要访问该tomcat,用之前的Service-NodePort的方式是可以的,比如暴露一个32008端口,只需要访问192.168.0.61:32008即可。显然,Service-NodePort的方式生产环境不推荐使用,那接下来就基于上述需求,使用Ingress实现访问tomcat的需求。
官网Ingress
:https://kubernetes.io/docs/concepts/services-networking/ingress/
GitHub Ingress Nginx
:https://github.com/kubernetes/ingress-nginx
Nginx Ingress Controller
:<https://kubernetes.github.io/ingress-nginx/
- 创建tomcat-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deployment
labels:
app: tomcat
spec:
replicas: 1
selector:
matchLabels:
app: tomcat
template:
metadata:
labels:
app: tomcat
spec:
containers:
- name: tomcat
image: tomcat
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
ports:
- port: 80
protocol: TCP
targetPort: 8080
selector:
app: tomcat
- 通过yaml创建pod
- 该Pod为Ingress Nginx Controller,要想让外界访问,可以通过Service的NodePort或者HostPort方式,这里选择HostPort,比如指定worker01运行
#确保nginx-controller运行到w1节点上
kubectl label node w1 name=ingress
#mandatory.yaml下载地址
https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.26.2/deploy/static/mandatory.yaml
修改mandatory.yaml
spec:
# wait up to five minutes for the drain of connections
terminationGracePeriodSeconds: 300
serviceAccountName: nginx-ingress-serviceaccount
hostNetwork: true
nodeSelector:
name: ingress
kubernetes.io/os: linux
containers:
#使用HostPort方式运行,需要增加配置
hostNetwork: true
#搜索nodeSelector,并且要确保w1节点上的80和443端口没有被占用,镜像拉取需要较长的时间,这块注意一下哦
kubectl apply -f mandatory.yaml
kubectl get all -n ingress-nginx
- 创建Ingress以及定义转发规则nginx-ingress.yaml
#ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-ingress
spec:
rules:
- host: tomcat.demo.com
http:
paths:
- path: /
backend:
serviceName: tomcat-service
servicePort: 80
kubectl apply -f nginx-ingress.yaml
kubectl get ingress
kubectl describe ingress nginx-ingress
5. 修改win的hosts文件,添加dns解析
192.168.0.61 tomcat.demo.com
- 打开浏览器,访问tomcat.demo.com
总结
:如果以后想要使用Ingress网络,其实只要定义ingress,service和pod即可,前提是要保证nginx ingress controller已经配置好了。