之前用阿里云的托管版 K8S(即 ACK),却一直不知道一个请求是怎么进入 pod 的。以前的项目是非容器化的,直接一条 DNS 记录指向 EC2 上的 Nginx,再用 Nginx 转发到背后的 Application。
K8S 版本的网络请求会是像下面这个图一样的。
进入 Pod 的请求
假设有app1.qas.abc.com
app2.qas.abc.com
和app3.qas.abc.com
3 个域名,前两个是公网可以访问,最后一个是内网可以访问,就可以创建两个负载均衡,一个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 地址。