1. 程式人生 > >Kubernetes(k8s)中文文件 管理應用:連線應用_Kubernetes中文社群

Kubernetes(k8s)中文文件 管理應用:連線應用_Kubernetes中文社群

譯者:it2afl0rd

Kubernetes容器連線模型

既然已經有了一個可持續執行的、可複製的應用,現在就可以在網路中將它暴露出來了。在討論Kubernetes的網路連線方式之前,很值得和Docker的常規網路連線方式做個對比。

Dokcer預設使用私有網路連線方式,所以只有在同一臺物理機器上的容器之前才可以通訊。為了能讓Docker容器可以跨節點通訊,必須要給機器的IP地址分配埠號,這個埠之後會被用來轉發或者路由給容器。很明顯,這意味著容器要麼很小心地協調使用埠,要麼有動態分配地埠。

在一定的規模下,為多個開發者協調埠號非常困難。這也會把叢集級別的問題暴露給使用者,這是在使用者的控制之外的。Kubernetes假定pods之間是可以通訊的,不管它們落到哪個主機上。我們給每個pod指定叢集私有的IP地址(cluster-private-IP address),所以不需要顯示地建立pod之間的連結,也不需要對映容器的埠到主機的埠。這意味著

pod裡的容器可以在本機(localhost)上訪問各自的埠,而且在沒有NAT的情況下,叢集中所有的pod也可以互相可見的。本文剩下的內容將會詳細闡述如何在這樣的網路模型中執行可靠的服務。

這個指南中用了一個簡單的nginx服務來演示驗證這個概念(proof of concept)。同樣的原理也在一個更完整的Jenkins CI 應用中體現了。

在叢集中暴露Pod

在前面的例子中已經演示過,讓我們把注意力集中在網路的視角在來一次。建立一個nginx的 Pod ,請注意它定義了容器的埠:

$ cat nginxrc.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: my-nginx
spec:
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80

這使得它從叢集中的任一節點都可以被訪問到。檢查一下供pod執行的節點:

$ kubectl create -f ./nginxrc.yaml
$ kubectl get pods -l app=nginx -o wide
my-nginx-6isf4 1/1 Running 0 2h e2e-test-beeps-minion-93ly
my-nginx-t26zt 1/1 Running 0 2h e2e-test-beeps-minion-93ly

檢查pod的IP地址:

$ kubectl get pods -l app=nginx -o json | grep podIP
"podIP": "10.245.0.15",
"podIP": "10.245.0.14",

你應該可以ssh到叢集裡的任何一個節點,而且用curl也能夠訪問這兩個IP。要注意的是容器並沒有用節點的80埠,也沒用任何特殊的會把流量路由到pod的NAT規則。這意味著你可以在同一個節點上用同樣的 containerPort 配置執行多個nginx pod,而且通過IP就可以在其他pod或者叢集裡的其他節點訪問它們。和Docker類似,埠也可以在節點的網路介面中釋出出來,但是在Kubernetes的這種網路模型下,這樣的需求從根本上減少了。

建立Service

現在我們有了執行態的nginx,它們執行在一個水平的,叢集範圍的地址空間內。理論上,我們已經可以和這些pod直接互動了,但是如果一個節點死掉了會發生什麼?它裡面的Pod也會死掉,然後Replication Controller會建立一個新的Pod,但是IP是不一樣的。這就是Service可以解決的問題。

Kubernetes Service是對在叢集中某處執行的一系列Pod的邏輯集合的抽象定義,這些Pod提供的功能是一樣的。每個Service被建立的時候會被分配一個唯一的IP地址(也叫 clusterIP )。這個地址和Service繫結,只要Service活著就不會改變。Pod可以配置成和Service互動,並且知道和Service的通訊會被自動地負載均衡到Service成員中的某個Pod。

可以用下面的yaml為兩個nginx副本建立一個Service:

$ cat nginxsvc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginxsvc
labels:
app: nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
app: nginx

這個定義會建立一個Service,這個Service會把帶有app=nginx Label的Pod的TCP 80埠暴露到Service的抽象埠(targetPort:是容器可以接收流量的埠,port:是Service的抽象埠,可以是除用來訪問Service的埠之外的任何埠)。在Service定義中支援的所有的域可以在Service API 物件中檢視。

檢視Service:

$ kubectl get svc
NAME      LABELS    SELECTOR   IP(S)         PORT(S)
nginxsvc  app=nginx app=nginx  10.0.116.146  80/TCP

在前面提到過,Service是由一組Pod支撐的。這些Pod通過endpoints暴露出來。Service會持續評估Selector,並把結果傳送給Endpoint物件(也叫做nginxsvc)。當一個Pod死了之後,它就會被自動地從Endpoint裡面刪掉,能夠匹配Service的Selector的新Pod會被自動加到Endpoint裡。檢查Endpoint的時候也會看到IP和前一步裡建立的Pod是一樣的:

$ kubectl describe svc nginxsvc
Name:        nginxsvc
Namespace:   default
Labels:      app=nginx
Selector:    app=nginx
Type:        ClusterIP
IP:          10.0.116.146
Port:        <unnamed> 80/TCP
Endpoints:  10.245.0.14:80,10.245.0.15:80
Session Affinity: None
No events.

$ kubectl get ep
NAME      ENDPOINTS
nginxsvc  10.245.0.14:80,10.245.0.15:80

現在你應該可以從叢集裡的任一節點上用curl命令訪問10.0.116.146:80上的nginx Service了。要注意的是Service的IP完全是虛擬的,跟物理網路沒有關係。

訪問Service

Kubernetes支援兩種主要的模式來發現Service:環境變數和DNS。環境變數在安裝之後就可以直接使用,DNS模式需要kube-dns 叢集外掛。

環境變數

當一個Pod在某個節點上執行的時候, kubelet 為每個活躍的Service新增一系列的環境變數。這會引入環境變數排序的問題。想知道為什麼,檢查一下執行中的nginx Pod的環境:

$ kubectl exec my-nginx-6isf4 -- printenv | grep SERVICE
KUBERNETES_SERVICE_HOST=10.0.0.1
KUBERNETES_SERVICE_PORT=443

注意這裡並沒有提到Service,這是因為這些副本是在Service之前建立的。這樣做的另一個缺點是,排程器也許會把兩個Pod放到相同的機器上,如果機器出問題,整個Service就不工作了。正確的方式是把這兩個Pod殺掉,然後等Replication Controller重新建立它們。現在Service是在Pod副本之前存在了,因此Service獲得了排程器級別的Pod擴散能力(只要所有的節點的容量是一樣的),而且環境變數也是正確的:

$ kubectl scale rc my-nginx --replicas=0; kubectl scale rc my-nginx --replicas=2;
$ kubectl get pods -l app=nginx -o wide
NAME           READY  STATUS   RESTARTS AGE NODE
my-nginx-5j8ok 1/1    Running  0        2m  node1
my-nginx-90vaf 1/1    Running  0        2m  node2

$ kubectl exec my-nginx-5j8ok -- printenv | grep SERVICE
KUBERNETES_SERVICE_PORT=443
NGINXSVC_SERVICE_HOST=10.0.116.146
KUBERNETES_SERVICE_HOST=10.0.0.1
NGINXSVC_SERVICE_PORT=80

DNS

Kubernetes提供了一個DNS叢集外掛Service,這個Service使用 skydns 自動給其他Service分配DNS。可以用下面的命令檢查它是否在叢集中執行:

$ kubectl get services kube-dns --namespace=kube-system
NAME      LABELS  SELECTOR           IP(S)      PORT(S)
kube-dns  <none>  k8s-app=kube-dns   10.0.0.10  53/UDP
                                                53/TCP

If it isn’t running, you can enable it. The rest of this sectionwill assume you have a Service with a long lived ip (nginxsvc), and a dns server that hasassigned a name to that ip (the kube-dns cluster addon), so you can talk to the Service fromany pod in your cluster using standard methods (e.g. gethostbyname). Let’s create anotherpod to test this:

$ cat curlpod.yaml
apiVersion: v1
kind: Pod
metadata:
name: curlpod
spec:
containers:
- image: radial/busyboxplus:curl
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
name: curlcontainer
restartPolicy: Always

And perform a lookup of the nginx Service

$ kubectl create -f ./curlpod.yaml
default/curlpod
$ kubectl get pods curlpod
NAME READY STATUS RESTARTS AGE
curlpod 1/1 Running 0 18s
$ kubectl exec curlpod -- nslookup nginxsvc
Server: 10.0.0.10
Address 1: 10.0.0.10
Name: nginxsvc
Address 1: 10.0.116.146

Securing the Service

Till now we have only accessed the nginx server from within the cluster. Before exposing theService to the internet, you want to make sure the communication channel is secure. Forthis, you will need:

  • Self signed certificates for https (unless you already have an identitiy certificate)
  • An nginx server configured to use the cretificates
  • A secret that makes the certificates accessible to pods

You can acquire all these from the nginx https example, in short:

$ make keys secret KEY=/tmp/nginx.key CERT=/tmp/nginx.crt SECRET=/tmp/secret.json
$ kubectl create -f /tmp/secret.json
secrets/nginxsecret
$ kubectl get secrets
NAME                TYPE                                 DATA
default-token-il9rc kubernetes.io/service-account-token  1
nginxsecret         Opaque

Now modify your nginx replicas to start a https server using the certificate in the secret, andthe Service, to expose both ports (80 and 443):

K8S中文社群微信公眾號