1. 程式人生 > 實用技巧 >k8s 如何對外提供服務

k8s 如何對外提供服務

本文將介紹在 k8s 中向外界提供服務的幾種方法port-forwardNodePort,以及 更加常用的提供服務的資源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:8081http://domain:8082來訪問不同的 web 服務吧。能不能處理成http://domain/web1http://domain/web2 ...這種形式呢。

當然可以,這個就是接下來要說的Ingress

通過 Ingress 暴露服務

ingress 是啥?其實,ingress 就是一個nginx伺服器。從本質上說,我們可以直接通過配置nginx伺服器來實現剛才說的訪問方式,但是這樣每次svc發生變更了我們就要重新手動配置一遍,好麻煩的,於是就有聰明的人想出來了,為什麼我們不把複雜的配置操作抽象成一個檔案,這樣有新的變更的話我們直接修改檔案,不就可以避免直接操作nginx伺服器了麼?於是,ingress誕生了。記住,ingress本質上就是一個nginx和許多個配置檔案

ingress 有兩部分構成,負責轉發服務的nginx-ingress-controller和每個服務的配置檔案ingress資源,如下:

通過 ingress 配置 service 訪問

每個想要對外暴露的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 kubia8080上的服務轉發到自己的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 支援建立HTTPHTTPS的服務,接下來我們先來建立一個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 控制器之間的連線是加密的,而控制器與後端svcpod之間的連線則還是 http,如下所示:

https 請求鏈路

想要讓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.certtls.key。因為這種祕鑰比較敏感,不適合直接掛載到 pod 上,所以可以使用 k8s 提供的專門用於提供敏感資料的資源secret來存放它,我們先來新建一個名為tls-secretsecret資源:

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服務提升至httpskubectl apply依靠kindmetadata欄位中的資料來尋找要修改的資源,所以改名字並不會造成什麼影響

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檔案中可以看到,rulespaths都是陣列,所以我們可以通過其暴露多個服務,如下:

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支援釋出HTTPHTTPS連線。



作者:HoPGoldy
連結:https://www.jianshu.com/p/58976bf0a8a4
來源:簡書
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。