k8s可以通過三種方式將集群內服務暴露到外網,分別是NodePort、LoadBalancer、Ingress,其中NodePort作為基礎通信形式我們在《k8s網絡模型與集群通信》中進行了介紹,這里我們主要關注LoadBalancer和Ingress
LoadBalancer
loadbalancer是服務暴露到因特網的標准形式,和nodeport一樣我們只需在創建service是指定type為loadbalancer即可,接着Service 的通過status.loadBalancer
字段將需要創建的負載均衡器信息發布供負載均衡服務創建。不過loadbalancer是雲服務商”專屬“,像騰訊雲CLB、阿里雲SLB,這樣在創建service時會自動幫我們創建一個負載均衡器。
大多數雲上負載均衡也是基於nodeport,他們的結構如下:
如果要在本地創建一個負載均衡器如何實現呢?
MetalLB,一個CNCF沙箱項目,使用標准路由協議(ARP/BGP),實現裸機K8s集群的負載均衡器。
安裝方式可參考官方文檔:installation
L2(子網)模式的結構,圖源
安裝后我們獲得如下兩個組件:
metallb-system/controller
deployment。用於處理IP分配的控制器。metallb-system/speaker
daemonset。集群中每個節點啟動一個協議服務守護進程。
接着添加一個configmap配置metallb IP池。
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.1.240-192.168.1.250
這樣當我們創建一個loadbalancer類型的service時,EXTERNAL-IP將會從地址池中獲取一個用於外部訪問的IP 192.168.1.243 當外部流量進入時,ARP將我們的請求地址廣播獲取所屬的service,接着k8s內部 通過iptables
規則和 kube-proxy
,將流量從服務端點引導到后端。
#nginx_deployment_service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: metallb-system
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
namespace: metallb-system
name: nginx
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: LoadBalancer
查看service kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx LoadBalancer 10.96.243.159 192.168.1.243 80:31052/TCP 40h
測試訪問:curl 192.168.1.243
# curl 192.168.1.243
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
負載均衡可以建立在 OSI 網絡模型的不同級別上,主要是在 L4(傳輸層,例如 TCP/UDP)和 L7(應用層,例如 HTTP)上。在 Kubernetes 中,Services
是 L4 的抽象,LoadBalancer類型負載均衡依然有局限性,同時我們看到每創建一個service對應的負載均衡器都會消耗一個靜態IP,這並不合理。當然k8s中的另一種資源對象ingress可工作在 L7 層實現應用程序協議(HTTP/HTTPS)的負載均衡。
Ingress
Ingress 公開了從集群外部到集群內服務的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 資源上定義的規則控制。我們可以將 Ingress 配置為服務提供外部可訪問的 URL、負載均衡流量、終止 SSL/TLS,以及提供基於名稱的虛擬主機等能力。
我們所說的Ingress包含兩個部分:
- ingress k8s資源對象:流量路由規則的控制
- ingress-controller控制器:控制器的實現有非常多,可參考官方文檔中列表Ingress 控制器,這里我們使用k8s官方維護的控制器NGINX Ingress Controller
外部流量進入集群時先經過ingress-controller,然后根據ingress配置的路由規則將請求轉發到后端service。
ingress-controller
ingress-controller其實就是守護進程加一個反向代理的應用,守護進程不斷監聽集群中資源的變化,將ingress中的配置信息生成反向代理配置。在nginx-ingress controller中即生成nginx.conf
的配置文件。
在本文中因為我們上面已經配置好了loadbalancer的服務,這樣我們創建一個type為LoadBalancer的service關聯這組pod,再把域名解析指向該地址,就實現了集群服務的對外暴露。當然你也可以使用NodePort
、Hostnetwork
的方式,感興趣的小伙伴可以進行測試。
ingress-controller不是k8s內部組件,可以通過helm或資源清單方式安裝,可查看ingress-nginx deploy
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.0/deploy/static/provider/cloud/deploy.yaml
然后我們編輯service
kubectl edit service/ingress-nginx-controller -n ingress-nginx
修改spec.type為LoadBalancer即可。
這樣我們創建好了nginx-ingress controller,下一步就要配置ingress路由規則。
ingress規則
host:k8s.com
基於url的路由:
- /api/v1
- /api/v2
這兩個url分別路由到不同的service中
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test
namespace: training
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: k8s.com
http:
paths:
- path: /api/v1
backend:
serviceName: service-apiv1
servicePort: 80
- path: /api/v2
backend:
serviceName: service-apiv2
servicePort: 80
ingress.kubernetes.io/rewrite-target
是nginx-ingress controller的一個注解,當后端服務中暴露的 URL 與 Ingress 規則中指定的路徑不同時可以通過此重定向。
查看svc可以看到此時控制器已經獲得了一個EXTERNAL-IP
#kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.96.87.23 192.168.1.245 80:32603/TCP,443:31906/TCP 621d
ingress-nginx-controller-admission ClusterIP 10.96.109.70 <none> 443/TCP 621d
現在nginx-ingress controller和ingress路由規則都有了。
我們可以進入到nginx-ingress controller pod中查看nginx.conf可以看到此時我們的ingress配置已經被生成為路由規則。
接下來就是指定我們的backend,即上面的server-apiv1/2
我們添加兩個用於暴露的service和deployment,和loadbalancer中測試清單一樣,我們稍稍修改一下名稱即可。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-apiv1
namespace: training
spec:
selector:
matchLabels:
app: nginx-apiv1
template:
metadata:
labels:
app: nginx-apiv1
spec:
containers:
- name: nginx-apiv1
image: nginx:latest
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
namespace: training
name: service-apiv1
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx-apiv1
type: NodePort
將nginx-apiv1換成nginx-apiv2創建出另一個service和deployment。
最后修改hosts解析k8s.com
192.168.1.245 k8s.com
使用curl命令測試url路由(記得在pod中添加測試文件,否則雖然url進行了路由但會出現404)。
# curl k8s.com/api/v1/index.html
api v1
# curl k8s.com/api/v2/index.html
api v2
這樣我們對ingress有了初步了解,ingress的路由規則可自定項較多也比較繁雜,可通過官方文檔進一步學習。
希望小作文對你有些許幫助,如果內容有誤請指正。
您可以隨意轉載、修改、發布本文,無需經過本人同意。