阿里云ACK网络关系

Database and Ruby, Python, History


之前用阿里云的托管版 K8S(即 ACK),却一直不知道一个请求是怎么进入 pod 的。以前的项目是非容器化的,直接一条 DNS 记录指向 EC2 上的 Nginx,再用 Nginx 转发到背后的 Application。

K8S 版本的网络请求会是像下面这个图一样的。

阿里云K8S

进入 Pod 的请求

假设有app1.qas.abc.com app2.qas.abc.comapp3.qas.abc.com3 个域名,前两个是公网可以访问,最后一个是内网可以访问,就可以创建两个负载均衡,一个public-lb,一个private-lb,通过在private-lb上面加 ACL 来控制哪些 IP 可以访问。

DNS 记录显示,前两个域名都指向public-lb的公网 IP,最后一个域名指向private-lb的公网 IP。

负载均衡背后指向的是 K8S 的 ingress,中间的 ENI 就是 K8S 里面 ingress-controller 的内网地址(根据 VPC 划分的)。

进入 ingress 之后,就看配置的 service 规则,再通过集群主机解析 service name 得到一个虚拟服务 IP,这个 IP 通过 iptables/ipvs 跳转到 Pod 的内部 IP,最后进入到 Pod。这些可以在 K8S 的路由和服务下面找到。

Deep Dive

查看域名的 IP

dig app1.qas.abc.com 就可以查看到这个域名的公网 IP 地址。

也可以通过阿里云的云解析服务找到对应的 DNS 记录。

查看公网 IP 对应的 SLB

在阿里云的负载均衡里面,可以找到该 IP 对应的 LB,背后的虚拟服务器组,以及相应的访问控制。

查看虚拟服务器组,可以看到一个弹性网卡的 IP 地址,该 IP 其实是 K8S 集群其中一个节点的 IP。

ECS

访问 ECS,查找这个 IP,可以找到该 IP 对应的实例。 同样,在 Kubenetes 的集群节点内,也可以看到这个 IP 地址。

即这个 SLB 的 80 和 443 端口都被转发到了背后的 K8S 节点上的 80 和 443 端口了。

Ingress

访问 K8S 下面的 ingress-controller,进入 Pod,查看 nginx.conf 文件,可以找到 Nginx 的配置信息。其实这个信息就是在 K8S 里配置的 Ingress 信息。

## start server app1.qas.abc.com
	server {
		server_name app1.qas.abc.com ;

		listen 80  ;
		listen [::]:80  ;
		listen 443  ssl http2 ;
		listen [::]:443  ssl http2 ;

		set $proxy_upstream_name "-";

		ssl_certificate_by_lua_block {
			certificate.call()
		}

		location / {

			set $namespace      "app";
			set $ingress_name   "app1-service-ingress";
			set $service_name   "app1-service";
			set $service_port   "8080";
			set $location_path  "/";
			set $global_rate_limit_exceeding n;

			set $balancer_ewma_score -1;
			set $proxy_upstream_name "app1-svc-app-8080";
			set $proxy_host          $proxy_upstream_name;
			set $pass_access_scheme  $scheme;

			set $pass_server_port    $server_port;

			set $best_http_host      $http_host;
			set $pass_port           $pass_server_port;

			proxy_pass http://upstream_balancer;

			proxy_redirect                          off;

		}

	}
	## end server app1.qas.abc.com

上面的规则表示,如果访问请求的域名是app1.qas.abc.com,则会被转发到app1-service服务。

在 Ingress pod 内部,尝试下面两个命令,就可以访问到后端的 Pod。

curl -v http://app1-service.app.svc.cluster.local:8080
curl -v http://app1-service.app:8080

Service

上面的app1-service.app是通过 DNS 解析到 service 的虚拟 IP,可以在 K8S 的 service 下面找到这个 service,看到这个虚拟 IP 是 192.168.X.X。

Kube-proxy

ssh 到 K8S 的一个节点上,访问下面地址,得知 K8S 用的是 IPVS 模式。有些集群用的是 iptables,可以用 iptables 查看具体的转发链。

curl -v localhost:10249/proxyMode

再执行下面的命令查看转发规则。

ipvsadm -Ln

可以看到下面这样的信息。这表示,该 service 的虚拟 IP 会被转发到 10.17.10.91 的 8080 端口,而 10.17.10.91 其实就是 Pod 的内网地址。

TCP  192.168.195.85:8080 rr
  -> 10.17.10.91:8080             Masq    1      0          0

至此,一个完整的网络请求就这样进入了 Pod。

Pod 访问外网的请求

Pod 访问公网的请求走的是 NAT 公网,如图粉色线。所以,出口的 IP 就不是上面 DNS 解析后的 IP 地址。

Reference