怎样从公网访问k8s服务

默认情况下,k8s创建的服务是无法对公网提供访问的,如果要从公网访问k8s服务,一般有三种方式,NodePort、LoadBalancer、Ingress,下面简单介绍NodePort、LoadBalancer,着重讲解Ingress

本文使用的deployment同官方教程nginx-deployment,图片是从ingress-nginx官方文档拷贝过来借用,如侵必删

NodePort

nodeport

如上图所示,客户端访问集群内任意机器端口30100,k8s自动将请求转发到对应的服务

  1. 创建NodePort类型的service(不指定nodeport的情况下,默认从30000-32767中挑选一个端口使用)
1
kubectl expose deployment nginx-deployment --type="NodePort" --port 80
  1. 查看service kubectl get svc,输出如下
1
2
3
NAME               TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 12d
nginx-deployment NodePort 10.110.255.135 <none> 80:31402/TCP 4s
  1. 验证

外部客户端先访问集群内任意节点端口31402,然后节点会转发到与端口关联的k8s内部服务nginx-deployment。

在物理机上访问服务curl http://vm1:31402,对比在虚拟机如vm1上执行curl http://10.110.255.135,两者的输出是一致的

  1. 删除service
1
kubectl delete svc nginx-deployment

注意:考虑到线上一般是直接通过域名访问,如果不想在域名后还要添加端口访问服务,如www.noname.io:31402,那么还需要使用nginx/haproxy建立一个反向代理的网关进行管理

LoadBalancer

metallb

如上图所示,客户端的请求抵达LoadBalancer指定的IP(这里的IP有点奇怪,应该是与节点IP同一个CIDR但被保留的IP才对,看下面metallb),然后机器将流量转发到对应的服务

一般情况下,各个云服务厂商有提供专门支持k8s的LoadBalancer,本文依赖的k8s是自建的裸集群(bare-metal),默认不支持LoadBalancer,因此需要先安装metallb

metallb安装

  1. 下载配置文件,修改image地址,然后使用kubectl执行
1
2
3
kubectl apply -f ./metallb-native.yaml
# 如果你的网络正常
# kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml
  1. 查看pod运行状态
1
kubectl get pods -n metallb-system -o wide

输出如下

1
2
3
4
5
NAME                        READY   STATUS    RESTARTS   AGE   IP                NODE   NOMINATED NODE   READINESS GATES
controller-f6d7bd7b-7kg47 1/1 Running 0 51s 10.0.2.19 vm3 <none> <none>
speaker-8fgzx 1/1 Running 0 51s 192.168.122.235 vm3 <none> <none>
speaker-bfbtk 1/1 Running 0 51s 192.168.122.11 vm1 <none> <none>
speaker-t4lv8 1/1 Running 0 51s 192.168.122.126 vm2 <none> <none>
  1. 配置IP池

配置IP池用于LoadBalancer分配,注意,要确保这部分IP不要被dhcp分配给其他任何机器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# vi ip-address-pool.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: default
namespace: metallb-system
spec:
addresses:
- 192.168.122.20-192.168.122.25
autoAssign: true
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: default
namespace: metallb-system
spec:
ipAddressPools:
- default

执行命令kubectl apply -f ip-address-pool.yaml

到这里就创建好了

实践

  1. 创建LoadBalancer类型的service
1
kubectl expose deployment nginx-deployment --type="LoadBalancer" --port 80
  1. 查看service kubectl get svc,输出如下

192.168.122.20这个地址不是节点IP,而是从IP池里挑选的未被使用的

1
2
3
NAME               TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)        AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 12d
nginx-deployment LoadBalancer 10.111.110.163 192.168.122.20 80:30804/TCP 8s
  1. 验证

在物理机上访问服务curl -D- http://192.168.122.20 -H 'Host: www.noname.io',对比在虚拟机上执行curl -D- http://10.111.110.163,两者的输出是一致的

  1. 删除service
1
kubectl delete svc nginx-deployment

Ingress

Ingress在整个k8s架构中扮演着集群网关的角色,提供了如负载均衡、SSL终结和基于名称的虚拟托管功能,当然,你可以用在a/b测试、版本升级这种用途,目前支持的协议是http,后续的继任者Gateway提供了更强大的功能,如支持更多协议、流量管理、更灵活的配置等,但这里暂不做过多介绍

Ingress的运行方式有好几种,其中比较常见的是NodePort以及LoadBalancer。其工作原理是:客户端通过NodePort或者LoadBalancer的方式访问ingress,ingress再通过路由转发到指定的k8s内部服务

本文使用的ingress是ingress-nginx,当然,你也可以选择envoy或higress等

ingress-nginx

将repo下载到本地,地址:https://github.com/kubernetes/ingress-nginx,k8s依赖的部署文件放在`deploy/static/provider/`目录下,NodePort方式的文件放在`baremetal`,而LoadBalancer方式的文件放在`cloud`

  1. 拷贝文件夹baremetalcloud,修改deploy.yaml中的image地址

  2. 执行命令kubectl apply -k .

  3. 查看pod运行状况

1
kubectl get pods -o wide -n ingress-nginx

输出如下,前两个是一次性的job,忽略

1
2
3
4
NAME                                        READY   STATUS      RESTARTS   AGE     IP           NODE   NOMINATED NODE   READINESS GATES
ingress-nginx-admission-create-6mmd6 0/1 Completed 0 2m33s 10.0.2.178 vm3 <none> <none>
ingress-nginx-admission-patch-clnr4 0/1 Completed 0 2m33s 10.0.1.235 vm2 <none> <none>
ingress-nginx-controller-5989548bf5-tjv5t 1/1 Running 0 2m33s 10.0.2.12 vm3 <none> <none>
  1. 查看controller运行状况
1
kubectl get svc ingress-nginx-controller -n ingress-nginx

如果你执行的是cloud配置,也就是LoadBalancer方式,输出如下

1
2
NAME                       TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)                      AGE
ingress-nginx-controller LoadBalancer 10.109.74.112 192.168.122.20 80:31608/TCP,443:30939/TCP 77m

如果你执行的是baremetal配置,也就是NodePort方式,输出如下

1
2
NAME                       TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller NodePort 10.104.202.43 <none> 80:31880/TCP,443:30184/TCP 3m27s
  1. 验证

访问ingress地址

LoadBalancer方式如下

1
2
3
4
5
# LoadBalancer如下
curl -D- http://192.168.122.20 -H 'Host: www.noname.io'

# NodePort如下
curl -D- http://vm1:31880 -H 'Host: www.noname.io'

输出如下

1
2
3
4
5
6
7
8
9
10
11
12
13
HTTP/1.1 404 Not Found
Date: Wed, 28 Aug 2024 04:09:19 GMT
Content-Type: text/html
Content-Length: 146
Connection: keep-alive

<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>

到这里,ingress安装完成

实践

  1. 创建默认类型为ClusterIP的服务(该服务无法在集群外部访问,适合验证我们创建的ingress)
1
kubectl expose deployment nginx-deployment
  1. 查看service kubectl get svc,输出如下
1
2
3
NAME               TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 12d
nginx-deployment ClusterIP 10.106.19.73 <none> 80/TCP 12m
  1. 配置ingress规则

该规则指定www.noname.io的所有流量都转发到nginx-deployment服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# example-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
spec:
rules:
- host: www.noname.io
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: nginx-deployment
port:
number: 80
  1. 验证

访问ingress地址,仅展示LoadBalancer方式

1
curl -D- http://192.168.122.20 -H 'Host: www.noname.io'

done

参考文档

3 Ways to Expose Applications Running in Kubernetes Cluster to Public Access
Ingress
在 Minikube 环境中使用 NGINX Ingress 控制器配置 Ingress
Bare-metal considerations
Using Nginx Ingress Controller in Kubernetes bare-metal setup
Using Metal LB on a bare-metal(OnPrem) Kubernetes Setup