本文將介紹在 k8s 中向外界提供服務(wù)的幾種方法port-forward、NodePort,以及 更加常用的提供服務(wù)的資源ingress。本文要求對(duì)service資源有一定的了解,如果你還不清楚的話,可以點(diǎn)擊 k8s 中的服務(wù)如何溝通 簡單學(xué)習(xí)一下。
接下來的內(nèi)容需要一些pod作為實(shí)際的服務(wù)處理者,你可以通過下面命令新建一個(gè)rs,這個(gè)rs會(huì)新建三個(gè)名為kubia-xxxx的 pod,其8080端口會(huì)開放一個(gè)服務(wù),請(qǐng)求時(shí)會(huì)返回其所在節(jié)點(diǎn)名稱:
kubectl create -f https://raw.githubusercontent.com/luksa/kubernetes-in-action/master/Chapter04/kubia-replicaset.yaml
port-forward 映射服務(wù)到端口
首先來看一下最簡單粗暴的方法,我們可以通過一條 k8s 命令來將指定的 pod 端口映射到本地的端口上,基本用法如下:
kubectl port-forward <資源類型>/<資源名> <本機(jī)端口>:<資源端口>
例如,我有個(gè) pod 叫做kubia,它通過80端口對(duì)外提供服務(wù),那么我就可以使用kubectl port-forward pod/kubia 8080:80將其映射到本機(jī)的8080端口上。你可以使用kubectl port-forward -h查看更多用法。
啟用后會(huì)占用當(dāng)前終端的標(biāo)準(zhǔn)輸出,可以在后面添加&來指定后臺(tái)運(yùn)行:
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 臨時(shí)映射出來,一般用于測(cè)試資源是否可用,在生產(chǎn)環(huán)境并不會(huì)大規(guī)模應(yīng)用。
NodePort 映射服務(wù)到節(jié)點(diǎn)端口
相對(duì)于上一種port-forward來說,這一種要正式的多,NodePort可以將其 轉(zhuǎn)發(fā)到所有 k8s 節(jié)點(diǎn)的指定端口上,并且不會(huì)像port-forward一樣在前臺(tái)運(yùn)行。我們先來新建一個(gè)NodePort,新建nodeport.yaml文件并填寫如下內(nèi)容,然后使用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本質(zhì)上是一個(gè)svc資源,他通過指定spec.ports.nodePort來講服務(wù)映射到節(jié)點(diǎn)的端口上,接下來我們就可以訪問下試試,直接curl訪問節(jié)點(diǎn)ip + 端口號(hào)即可,你可以通過kubectl get nodes -o wide找到所有 k8s 節(jié)點(diǎn)的 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之后,服務(wù)被正常的請(qǐng)求,并且也正常的被均衡到每一個(gè) pod 上。
但是這里有個(gè)問題,假如我的 k8s 里運(yùn)行了多個(gè) web 應(yīng)用服務(wù)器,我總不能讓用戶通過端口號(hào)http://domain:8081、http://domain:8082來訪問不同的 web 服務(wù)吧。能不能處理成http://domain/web1、http://domain/web2 ...這種形式呢。
當(dāng)然可以,這個(gè)就是接下來要說的Ingress。
通過 Ingress 暴露服務(wù)
ingress 是啥?其實(shí),ingress 就是一個(gè)nginx服務(wù)器。從本質(zhì)上說,我們可以直接通過配置nginx服務(wù)器來實(shí)現(xiàn)剛才說的訪問方式,但是這樣每次svc發(fā)生變更了我們就要重新手動(dòng)配置一遍,好麻煩的,于是就有聰明的人想出來了,為什么我們不把復(fù)雜的配置操作抽象成一個(gè)文件,這樣有新的變更的話我們直接修改文件,不就可以避免直接操作nginx服務(wù)器了么?于是,ingress誕生了。記住,ingress本質(zhì)上就是一個(gè)nginx和許多個(gè)配置文件。
ingress 有兩部分構(gòu)成,負(fù)責(zé)轉(zhuǎn)發(fā)服務(wù)的nginx-ingress-controller和每個(gè)服務(wù)的配置文件ingress資源,如下:

每個(gè)想要對(duì)外暴露的svc都需要有一個(gè)對(duì)應(yīng)的ingress資源(配置文件)才能對(duì)外提供服務(wù),ingress資源并不負(fù)責(zé)實(shí)際的流量轉(zhuǎn)發(fā),它只是告訴nginx-ingress-controller應(yīng)該把流量轉(zhuǎn)發(fā)到哪個(gè)svc。
接下來,我們實(shí)踐一下,從頭部署一個(gè)可用的ingress。
安裝 ingress
k8s 并不自帶 ingress,所以我們需要重新安裝,幸好安裝比較簡單,直接執(zhí)行如下命令即可,k8s 會(huì)根據(jù)其內(nèi)容安裝所有需要的資源。
kubectl apply -f https://raw.githubusercontent.com/StudyXX/google-containers/v1.10.2/install/ingress-nginx/ingress-nginx-controller.yaml
如果出現(xiàn)了鏡像拉取失敗的情況可以先手動(dòng)將命令中的配置文件下載下來,然后將其的鏡像地址從quay.io/替換成quay-mirror.qiniu.com/,重新kubectl apply即可。文件地址為 github - StudyXX/ingress-nginx-controller.yaml 。
安裝成功之后可以執(zhí)行如下操作查看 ingress 是否安裝完成,輸入kubectl get namespaces,查看ingress-nginx(ingress相關(guān)的pod組) 的狀態(tài)是否為Active。輸入kubectl get pod -n ingress-nginx,查看nginx-ingress-controller(負(fù)責(zé)實(shí)際轉(zhuǎn)發(fā)流量的nginx) 的狀態(tài)是否為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
這兩者正?;究梢詳喽?code>ingress安裝成功了,接下來我們來創(chuàng)建一個(gè)對(duì)外服務(wù)。
使用 ingress 創(chuàng)建對(duì)外服務(wù)
在"通過 ingress 配置 service 訪問"圖里我們可以看出,使用 ingress 暴露服務(wù)需要先新建一個(gè)svc,所以我們先使用如下內(nèi)容創(chuàng)建一個(gè),它會(huì)把 pod kubia端口8080上的服務(wù)轉(zhuǎn)發(fā)到自己的8080上,他控制的 pod 就是我們文章開頭時(shí)新建的那些。
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創(chuàng)建該svc,接下來我們創(chuàng)建一個(gè)ingress。ingress 支持創(chuàng)建HTTP和HTTPS的服務(wù),接下來我們先來創(chuàng)建一個(gè)HTTP的:
創(chuàng)建 HTTP 協(xié)議的訪問
http協(xié)議的訪問比較簡單,直接創(chuàng)建如下配置文件即可,通過這個(gè)ingress配置文件,nginx-ingress-controller就知道如何對(duì)外開發(fā)服務(wù)了。
kubia-http-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: kubia
spec:
rules:
# 將服務(wù)映射到該域名
- host: kubia.example.com
http:
paths:
# 通過 / 路徑就可以訪問該服務(wù)
- path: /
# 該服務(wù)后端 svc 的名稱及端口號(hào)
backend:
serviceName: kubia
servicePort: 8080
然后使用kubectl create -f kubia-http-ingress.yaml就可以創(chuàng)建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,現(xiàn)在我們就可以通過http://kubia.example.com/來訪問目標(biāo)svc啦,這時(shí)你可能會(huì)疑惑,我沒這個(gè)域名?。繘]關(guān)系,在系統(tǒng)的hosts文件里配置一下,將這個(gè)域名映射到nginx-ingress-controller的 ip 地址就可以了,詳情見下方:
配置域名到 ip 地址
首先獲取
nginx-ingress-controller的 ip 地址,執(zhí)行如下命令后按Tab補(bǔ)全名稱,然后在IP列就可以找到對(duì)應(yīng)的 ip 了。kubectl describe pod -n ingress-nginx nginx-ingress-controller-我的 ip 地址為
192.168.56.22,執(zhí)行vi /etc/hosts,輸入以下內(nèi)容即可:192.168.56.22 kubia.example.com
修改完了之后,我們就可以進(jìn)行訪問了,執(zhí)行curl http://kubia.example.com/,就可以看到來自 svc kubia的響應(yīng)了。
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
創(chuàng)建 HTTPS 協(xié)議的訪問
這里的 https 訪問是指從客戶端到 ingress 控制器之間的連接是加密的,而控制器與后端svc及pod之間的連接則還是 http,如下所示:

想要讓ingress可以提供https服務(wù),我們首先需要有證書和私鑰,這里我們先來創(chuàng)建他們倆:
openssl genrsa -out tls.key 2048
openssl req -new -x509 \
-key tls.key \
-out tls.cert \
-days 360 \
-subj /CN=kubia.example.com
執(zhí)行完之后你就可以在當(dāng)前文件夾下發(fā)現(xiàn)兩個(gè)文件,分別為tls.cert和tls.key。因?yàn)檫@種秘鑰比較敏感,不適合直接掛載到 pod 上,所以可以使用 k8s 提供的專門用于提供敏感數(shù)據(jù)的資源secret來存放它,我們先來新建一個(gè)名為tls-secret的secret資源:
kubectl create secret tls tls-secret --cert=tls.cert --key=tls.key
什么是
secret資源?
secret用于存放一些敏感的配置信息,如密碼、密鑰等,你可以把他理解成一個(gè)更安全的ConfigMap資源,k8s 提供了很多類型的secret,例如上文的kubectl create secret tls就是一個(gè)常用的secret。
然后我們就可以修改上文中的kubia-http-ingress.yaml,將這個(gè)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
然后我們就可以通過以下命令來講剛才創(chuàng)建的http服務(wù)提升至https,kubectl apply依靠kind及metadata字段中的數(shù)據(jù)來尋找要修改的資源,所以改名字并不會(huì)造成什么影響:
kubectl apply -f kubia-https-ingress.yaml
然后輸入curl -k -v https://kubia.example.com,就可以發(fā)現(xiàn)我們的服務(wù)已經(jīng)啟用的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 暴露多個(gè)服務(wù)
在kubia-ingress.yaml文件中可以看到,rules和paths都是數(shù)組,所以我們可以通過其暴露多個(gè)服務(wù),如下:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: kubia
spec:
rules:
- host: foo.example.com
http:
paths:
# 通過指定不同的路徑來訪問不同的服務(wù)
- path: /foo
backend:
serviceName: foo-svc
servicePort: 8080
- path: /bar
backend:
serviceName: bar-svc
servicePort: 8080
# 也可以通過指定多個(gè) host 來配置不同的主機(jī)
- host: foo.example.com
http:
paths:
- path: /kubia
backend:
serviceName: kubia
servicePort: 8080
但是哪怕在一個(gè)文件中可以配置多個(gè)規(guī)則,但是依舊推薦為每一個(gè)svc都創(chuàng)建一個(gè)自己專屬的ingress,這樣條理會(huì)比較清晰,也方便日后的管理。
總結(jié)
本文介紹了如何將服務(wù)暴露出去,最簡單的是port-forward命令,他可以將指定資源的端口轉(zhuǎn)發(fā)到本機(jī),但是一般都用于測(cè)試。
其次是NodePort模式,NodePort本質(zhì)上也是個(gè)svc,只不過他在完成svc本質(zhì)工作的同時(shí)還將服務(wù)開放到了集群中所有節(jié)點(diǎn)的指定端口上。
但是用戶更傾向于通過子路徑訪問服務(wù),而不是端口。為了實(shí)現(xiàn)這個(gè)目標(biāo),我們可以使用ingress資源,通過發(fā)布ingress配置文件,nginx-ingress-controller就可以將指定的服務(wù)發(fā)布到指定域名的指定路徑上,ingress支持發(fā)布HTTP和HTTPS連接。