上次写博客是n多年以前了,以前的几十篇在blogjava里面应该还能吵到。最近遭遇变故,忽然想把自己近期学的一点东西记录下来,日后查阅起来也是方便。

        话不多说,笔者最近在啃k8s,容器也好、k8s也罢,里面和服务相关的port概念五六个,索性整理一下,看看他们之间的关系。

首先看一下我们的测试环境:

一组三从的k8集群:

root@dev-4-control-plane# sh node.sh
|NODE                          |INTERNAL_IP
|dev-4-control-plane           |172.18.0.2
|dev-4-worker                  |172.18.0.5
|dev-4-worker2                 |172.18.0.4
|dev-4-worker3                 |172.18.0.3

三副本的前端服务:

root@dev-4-control-plane# sh pod.sh
|NAME                                |NODE                            |IP              |STATUS
|frontend-5b79c8694b-6hjrc           |dev-4-worker3/172.18.0.3        |10.244.1.9      |Running
|frontend-5b79c8694b-87xnh           |dev-4-worker2/172.18.0.4        |10.244.3.9      |Running
|frontend-5b79c8694b-lkbt5           |dev-4-worker/172.18.0.5         |10.244.2.9      |Running


root@dev-4-control-plane# sh svc.sh
|NAME           |CLUSTERIP       |PORT              |TARGETPORT  |ENDPOINTS
|frontend       |10.96.48.151    |NodePort%         |80/TCP      |10.244.1.9:80,10.244.2.9:80,10.244.3.9:80

接下来看看我们的service及deployment中的port定义:

root@dev-4-control-plane# kubectl describe svc frontend
Name:                     frontend
Namespace:                default
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.96.48.151
IPs:                      10.96.48.151
Port:                     <unset>  8088/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  30080/TCP
Endpoints:                10.244.1.9:80,10.244.2.9:80,10.244.3.9:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

root@dev-4-control-plane# kubectl describe deployment frontend
Name:                   frontend
Namespace:              default
CreationTimestamp:      Thu, 20 Apr 2023 03:11:12 +0000
Labels:                 <none>
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=guestbook
Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=guestbook
  Containers:
   php-redis:
    Image:      anjia0532/google_samples.gb-frontend:v5
    Port:       8081/TCP
    Host Port:  9088/TCP
    Environment:
      GET_HOSTS_FROM:  dns
    Mounts:            <none>
  Volumes:             <none>

好了,开始进入今天的主题。从上面的内容中可以获取如下信息:

	clusterIP: 10.96.48.151
	nodeIP: 172.18.0.3\172.18.0.4\172.18.0.5
	podIP: 10.244.1.9\10.244.2.9\10.244.3.9
	containerPort: 8081(这个端口就是deployment中的port,由于测试中使用的image的问题,apache使用的80端口不可修改,因此这个8081定义实际不起作用)
	hostPort:9088(这个端口就是deployment中的port)
	servicePort: 8088(service中的port)
	TargetPort: 80 (这个端口理论上需要和container保持一致,但因为咱们实验中的镜像使用了不可改的80,因此containerPort不生效,为了不影响测试,这个targetPort直接指向了实际listen的port)
	NodePort: 30080

        准备好两个客户端,分别为 dev-4-worker3(同集群客户端)、dev-2-worker(异集群客户端),看看他们分别可以通过哪些途径访问pod中的service。验证结果如下,其中同集群客户端有三种方式访问,分别为:podIP+containerPort、nodeIP+nodePort、clusterIP+servicePort,而集群外的客户端只有一种访问方式,即:nodeIP+nodePort。

    #dev-4-worker2:
    #1、podIP+containerPort: curl http://10.244.1.9:80
    #2、nodeIP+nodePort:  curl http://172.18.0.3:30080
    #3、clusterIP+serviePort: curl http://10.96.48.151:8088
    
    #dev-2-worker:
    #1、nodeIP+nodePort:  curl http://172.18.0.3:30080

        不过大家发现问题没有?我们定义了hostPort,理论上hostPort和nodePort是一致的,但为什么无论集群内外的客户端都无法通过nodeIP+hostPort方式访问呢?看一下iptable信息吧

	root@dev-4-worker3:/# iptables -t nat -nvL | grep 9088
    0     0 CNI-HOSTPORT-SETMARK  tcp  --  *      *       10.244.1.0/24        0.0.0.0/0            tcp dpt:9088
    0     0 CNI-HOSTPORT-SETMARK  tcp  --  *      *       127.0.0.1            0.0.0.0/0            tcp dpt:9088
    0     0 DNAT       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:9088 to:10.244.1.8:8081
    0     0 CNI-DN-ae37526a616fce7a60616  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* dnat name: "kindnet" id: "d5c4b7e0630bd6419d118eabcefe960976097792ac610eec265eecc691f9087a" */ multiport dports 9088
	

      看到了吗?9088端口的流量被转发给无效containerPort 8081了(说明deployment中的hostPort代理了外部到containerPort的流量),访问自然无法正常。我们修改一下containerPort,看看能否正常访问。
    
    看一下路由表,DNAT成功指向了80端口,再访问一下试试:

    root@dev-4-worker3:/# iptables -t nat -nvL | grep 9088
    0     0 CNI-HOSTPORT-SETMARK  tcp  --  *      *       10.244.1.0/24        0.0.0.0/0            tcp dpt:9088
    0     0 CNI-HOSTPORT-SETMARK  tcp  --  *      *       127.0.0.1            0.0.0.0/0            tcp dpt:9088
    0     0 DNAT       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:9088 to:10.244.1.8:80
    0

路由结果正常了,我们再分别测试一下两个客户端的访问情况:

	同集群节点访问
    root@dev-4-worker3:/# wget http://172.18.0.3:9088
	--2023-04-20 03:13:13--  http://172.18.0.3:9088/
	Connecting to 172.18.0.3:9088... connected.
	HTTP request sent, awaiting response... 200 OK
	
	成功,再从异集群节点访问看看,
	
	root@dev-2-worker:/# wget http://172.18.0.3:9088
	--2023-04-20 03:15:30--  http://172.18.0.3:9088/
	Connecting to 172.18.0.3:9088... connected.
	HTTP request sent, awaiting response... 200 OK

为什么我们再deployment中定义了containerPort为8081,而实际侦听端口仍旧为80呢?检查一下image文件就可以发现端倪(ExposePorts已经写死了80为服务端口)。

	root@dev-4-worker2:/# crictl ps
	CONTAINER           IMAGE               CREATED             STATE               NAME                ATTEMPT             POD ID              POD
	8b328f18c15e7       4ea64ff11cebf       About an hour ago   Running             php-redis           0                   11329fb49ffe0       frontend-5b79c8694b-87xnh
	52a2ff4407dc2       2355926154447       5 hours ago         Running             leader              7                   d532eeac5a9ee       redis-leader-6b57b8f94f-598mp
	93d983ce50eee       eb3079d47a23a       5 hours ago         Running             kube-proxy          182                 c2c0ac4a7dbff       kube-proxy-sbrgc
	4106a715cae68       a329ae3c2c52f       5 hours ago         Running             kindnet-cni         7                   aa69fb8caeffe       kindnet-qvdcd
	
    root@dev-4-worker2:/# crictl inspecti 4ea64ff11cebf
	{
	  "status": {
		"id": "sha256:4ea64ff11cebf416e4a0629549d7f66275b8f1ba8e6dc714e44a1c47241b82d1",
		"repoTags": [
		  "docker.io/anjia0532/google_samples.gb-frontend:v5"
		],

		......

		"imageSpec": {
		  "created": "2021-12-31T18:54:06.462754392Z",
		  "architecture": "amd64",
		  "os": "linux",
		  "config": {
			"ExposedPorts": {
			  "80/tcp": {}
			},
			

好了,最后对几个port做一下简单的总结,如果错误不吝斧正:

container:containerPort容器中服务的listen port,当然这个port能否通过containerPort指定取决与image的写法,比如文中所提写死80的做法,此时containerPort怎么设置都不会生效。
service:port这个servicePort通常结合clusterIP使用的,用于集群内服务调用,当然集群内互访也可以通过podIP+containerPort、nodeIP+nodePort等方式访问,但这些方式没法做负载均衡和故障恢复透明。
service:targetport这个需要和container的containerPort保持一致,我们看到service的endpoint列表中使用的是这个端口,如果和container不一致,则数据无法正常理由
service:nodePort用于提供集群外的客户端访问,通过nodeIP + nodePort即可访问,但更常见的是用于前端的load balancer的服务端点
container:hostPort

这个和nodePort类似,早期k8s中nodePort会在node上起对应的nodePort侦听,而hostPort不会起侦听,不过现在的版本中无论hostPort还是nodePort都是通过ipvs实现端口映射,不会再启用侦听,不知道这两个port概念将来会不会删掉一个。



    

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐