本文將介紹在 k8s 中向外界提供服務的幾種方法port-forward
、NodePort
,以及 更加常用的提供服務的資源ingress
。本文要求對service
資源有一定的了解,如果你還不清楚的話,可以點擊 k8s 中的服務如何溝通 簡單學習一下。
接下來的內容需要一些pod
作為實際的服務處理者,你可以通過下面命令新建一個rs
,這個rs
會新建三個名為kubia-xxxx
的 pod,其8080
端口會開放一個服務,請求時會返回其所在節點名稱:
kubectl create -f https://raw.githubusercontent.com/luksa/kubernetes-in-action/master/Chapter04/kubia-replicaset.yaml
port-forward 映射服務到端口
首先來看一下最簡單粗暴的方法,我們可以通過一條 k8s 命令來將指定的 pod 端口映射到本地的端口上,基本用法如下:
kubectl port-forward <資源類型>/<資源名> <本機端口>:<資源端口>
例如,我有個 pod 叫做kubia
,它通過80
端口對外提供服務,那么我就可以使用kubectl port-forward pod/kubia 8080:80
將其映射到本機的8080
端口上。你可以使用kubectl port-forward -h
查看更多用法。
啟用后會占用當前終端的標准輸出,可以在后面添加&
來指定后台運行:
root@master1:~# k port-forward kubia-5k2zr 8080:8080 & [1] 13949 Forwarding from 127.0.0.1:8080 -> 8080 Forwarding from [::1]:8080 -> 8080 root@master1:~# curl http://localhost:8080 Handling connection for 8080 You've hit kubia-5k2zr
port-forward
可以將 pod 臨時映射出來,一般用於測試資源是否可用,在生產環境並不會大規模應用。
NodePort 映射服務到節點端口
相對於上一種port-forward
來說,這一種要正式的多,NodePort
可以將其 轉發到所有 k8s 節點的指定端口上,並且不會像port-forward
一樣在前台運行。我們先來新建一個NodePort
,新建nodeport.yaml
文件並填寫如下內容,然后使用kubectl create -f nodeport.yaml
新建。:
apiVersion: v1
kind: Service
metadata:
name: kubia-nodeport
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30123
selector:
app: kubia
通過kind
屬性可以看到NodePort
本質上是一個svc
資源,他通過指定spec.ports.nodePort
來講服務映射到節點的端口上,接下來我們就可以訪問下試試,直接curl
訪問節點ip + 端口號
即可,你可以通過kubectl get nodes -o wide
找到所有 k8s 節點的 ip:
root@master1:~# curl http://192.168.56.11:30123
You've hit kubia-m68bq
root@master1:~# curl http://192.168.56.11:30123
You've hit kubia-flg8w
root@master1:~# curl http://192.168.56.11:30123
You've hit kubia-flg8w
root@master1:~# curl http://192.168.56.11:30123
You've hit kubia-8r2cg
root@master1:~# curl http://192.168.56.11:30123
You've hit kubia-m68bq
可以看到,使用了nodePort
之后,服務被正常的請求,並且也正常的被均衡到每一個 pod 上。
但是這里有個問題,假如我的 k8s 里運行了多個 web 應用服務器,我總不能讓用戶通過端口號http://domain:8081
、http://domain:8082
來訪問不同的 web 服務吧。能不能處理成http://domain/web1
、http://domain/web2
...這種形式呢。
當然可以,這個就是接下來要說的Ingress
。
通過 Ingress 暴露服務
ingress 是啥?其實,ingress 就是一個nginx
服務器。從本質上說,我們可以直接通過配置nginx
服務器來實現剛才說的訪問方式,但是這樣每次svc
發生變更了我們就要重新手動配置一遍,好麻煩的,於是就有聰明的人想出來了,為什么我們不把復雜的配置操作抽象成一個文件,這樣有新的變更的話我們直接修改文件,不就可以避免直接操作nginx
服務器了么?於是,ingress
誕生了。記住,ingress
本質上就是一個nginx
和許多個配置文件。
ingress 有兩部分構成,負責轉發服務的nginx-ingress-controller
和每個服務的配置文件ingress
資源,如下:

每個想要對外暴露的svc
都需要有一個對應的ingress
資源(配置文件)才能對外提供服務,ingress
資源並不負責實際的流量轉發,它只是告訴nginx-ingress-controller
應該把流量轉發到哪個svc
。
接下來,我們實踐一下,從頭部署一個可用的ingress
。
安裝 ingress
k8s 並不自帶 ingress,所以我們需要重新安裝,幸好安裝比較簡單,直接執行如下命令即可,k8s 會根據其內容安裝所有需要的資源。
kubectl apply -f https://raw.githubusercontent.com/StudyXX/google-containers/v1.10.2/install/ingress-nginx/ingress-nginx-controller.yaml
如果出現了鏡像拉取失敗的情況可以先手動將命令中的配置文件下載下來,然后將其的鏡像地址從quay.io/
替換成quay-mirror.qiniu.com/
,重新kubectl apply
即可。文件地址為 github - StudyXX/ingress-nginx-controller.yaml 。
安裝成功之后可以執行如下操作查看 ingress 是否安裝完成,輸入kubectl get namespaces
,查看ingress-nginx
(ingress相關的pod組) 的狀態是否為Active
。輸入kubectl get pod -n ingress-nginx
,查看nginx-ingress-controller
(負責實際轉發流量的nginx) 的狀態是否為Running
。
root@master1:~# kubectl get namespaces NAME STATUS AGE default Active 34d ingress-nginx Active 27d kube-node-lease Active 34d kube-public Active 34d kube-system Active 34d root@master1:~# kubectl get pod -n ingress-nginx NAME READY STATUS RESTARTS AGE default-http-backend-7vg6q 1/1 Running 1 26d nginx-ingress-controller-5c4964c449-9ccbm 1/1 Running 2 26d
這兩者正常基本可以斷定ingress
安裝成功了,接下來我們來創建一個對外服務。
使用 ingress 創建對外服務
在"通過 ingress 配置 service 訪問"圖里我們可以看出,使用 ingress 暴露服務需要先新建一個svc
,所以我們先使用如下內容創建一個,它會把 pod kubia
端口8080
上的服務轉發到自己的8080
上,他控制的 pod 就是我們文章開頭時新建的那些。
kubia-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
ports:
- port: 8080
targetPort: 8080
selector:
app: kubia
然后使用kubectl create -f kubia-svc.yaml
創建該svc
,接下來我們創建一個ingress
。ingress 支持創建HTTP
和HTTPS
的服務,接下來我們先來創建一個HTTP
的:
創建 HTTP 協議的訪問
http
協議的訪問比較簡單,直接創建如下配置文件即可,通過這個ingress
配置文件,nginx-ingress-controller
就知道如何對外開發服務了。
kubia-http-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: kubia
spec:
rules:
# 將服務映射到該域名
- host: kubia.example.com
http:
paths:
# 通過 / 路徑就可以訪問該服務
- path: /
# 該服務后端 svc 的名稱及端口號
backend:
serviceName: kubia
servicePort: 8080
然后使用kubectl create -f kubia-http-ingress.yaml
就可以創建ingress
了。我們可以使用kubectl describe ingress kubia
來查看他的介紹:
root@master1:~# k describe ingress kubia Name: kubia Namespace: default Address: Default backend: default-http-backend:80 (<none>) Rules: Host Path Backends ---- ---- -------- kubia.example.com / kubia:8080 (10.244.1.59:8080,10.244.2.31:8080,10.244.3.30:8080) Annotations: Events: <none>
ok,現在我們就可以通過http://kubia.example.com/
來訪問目標svc
啦,這時你可能會疑惑,我沒這個域名啊?沒關系,在系統的hosts
文件里配置一下,將這個域名映射到nginx-ingress-controller
的 ip 地址就可以了,詳情見下方:
配置域名到 ip 地址
首先獲取
nginx-ingress-controller
的 ip 地址,執行如下命令后按Tab
補全名稱,然后在IP
列就可以找到對應的 ip 了。kubectl describe pod -n ingress-nginx nginx-ingress-controller-
我的 ip 地址為
192.168.56.22
,執行vi /etc/hosts
,輸入以下內容即可:192.168.56.22 kubia.example.com
修改完了之后,我們就可以進行訪問了,執行curl http://kubia.example.com/
,就可以看到來自 svc kubia
的響應了。
root@master1:~# curl http://kubia.example.com/
You've hit kubia-m68bq
root@master1:~# curl http://kubia.example.com/
You've hit kubia-8r2cg
root@master1:~# curl http://kubia.example.com/
You've hit kubia-flg8w
創建 HTTPS 協議的訪問
這里的 https 訪問是指從客戶端到 ingress 控制器之間的連接是加密的,而控制器與后端svc
及pod
之間的連接則還是 http,如下所示:

想要讓ingress
可以提供https
服務,我們首先需要有證書和私鑰,這里我們先來創建他們倆:
openssl genrsa -out tls.key 2048 openssl req -new -x509 \ -key tls.key \ -out tls.cert \ -days 360 \ -subj /CN=kubia.example.com
執行完之后你就可以在當前文件夾下發現兩個文件,分別為tls.cert
和tls.key
。因為這種秘鑰比較敏感,不適合直接掛載到 pod 上,所以可以使用 k8s 提供的專門用於提供敏感數據的資源secret
來存放它,我們先來新建一個名為tls-secret
的secret
資源:
kubectl create secret tls tls-secret --cert=tls.cert --key=tls.key
什么是
secret
資源?
secret
用於存放一些敏感的配置信息,如密碼、密鑰等,你可以把他理解成一個更安全的ConfigMap
資源,k8s 提供了很多類型的secret
,例如上文的kubectl create secret tls
就是一個常用的secret
。
然后我們就可以修改上文中的kubia-http-ingress.yaml
,將這個secret
掛載上去:
kubia-https-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: kubia
spec:
# 添加 tls 字段來啟用 https
tls:
- hosts:
# 啟用 https 的域名
- kubia.example.com
# 給其分配的證書及密鑰,要求 tls 類型的 k8s secret 資源
secretName: tls-secret
rules:
- host: kubia.example.com
http:
paths:
- path: /
backend:
serviceName: kubia
servicePort: 8080
然后我們就可以通過以下命令來講剛才創建的http
服務提升至https
,kubectl apply
依靠kind
及metadata
字段中的數據來尋找要修改的資源,所以改名字並不會造成什么影響:
kubectl apply -f kubia-https-ingress.yaml
然后輸入curl -k -v https://kubia.example.com
,就可以發現我們的服務已經啟用的https
連接:
root@master1:~# curl -k -v https://kubia.example.com ... * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt ... * Server certificate: * subject: CN=kubia.example.com ... You've hit kubia-flg8w
使用 ingress 暴露多個服務
在kubia-ingress.yaml
文件中可以看到,rules
和paths
都是數組,所以我們可以通過其暴露多個服務,如下:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: kubia
spec:
rules:
- host: foo.example.com
http:
paths:
# 通過指定不同的路徑來訪問不同的服務
- path: /foo
backend:
serviceName: foo-svc
servicePort: 8080
- path: /bar
backend:
serviceName: bar-svc
servicePort: 8080
# 也可以通過指定多個 host 來配置不同的主機
- host: foo.example.com
http:
paths:
- path: /kubia
backend:
serviceName: kubia
servicePort: 8080
但是哪怕在一個文件中可以配置多個規則,但是依舊推薦為每一個svc
都創建一個自己專屬的ingress
,這樣條理會比較清晰,也方便日后的管理。
總結
本文介紹了如何將服務暴露出去,最簡單的是port-forward
命令,他可以將指定資源的端口轉發到本機,但是一般都用於測試。
其次是NodePort
模式,NodePort
本質上也是個svc
,只不過他在完成svc
本質工作的同時還將服務開放到了集群中所有節點的指定端口上。
但是用戶更傾向於通過子路徑訪問服務,而不是端口。為了實現這個目標,我們可以使用ingress
資源,通過發布ingress
配置文件,nginx-ingress-controller
就可以將指定的服務發布到指定域名的指定路徑上,ingress
支持發布HTTP
和HTTPS
連接。
作者:HoPGoldy
鏈接:https://www.jianshu.com/p/58976bf0a8a4
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。