Kubernetes使用者指南(二)–部署組合型的應用、連線應用到網路中

xiaohei.info發表於2015-10-10
版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/qq1010885678/article/details/49026825
一、部署組合型的應用

1、使用配置檔案啟動replicas集合

k8s通過Replication Controller來建立和管理各個不同的重複容器集合(實際上是重複的pods)。
Replication Controller會確保pod的數量在執行的時候會一直保持在一個特殊的數字,即replicas的設定。
這個功能類似於Google GCE的例項組管理和AWS的彈性伸縮。
在快速開始中,通過kubectl run以下的YAML檔案建立了一個rc執行著nginx:
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的YAML檔案相比,不同的只是kind的值為ReplicationController,replicas的值需要制定,pod的相關定義在template中,pod的名字不需要顯式地指定,因為它們會在rc中建立並賦予名字,點選檢視完整的rc定義欄位列表:
rc可以通過create命令像建立pod一樣來建立:
kubectl create -f ./nginx-rc.yaml
replicationcontrollers/my-nginx
和直接建立pod不一樣,rc將會替換因為任何原因而被刪除或者停止執行的Pod,比如說pod依賴的節點掛了。所以我們推薦使用rc來建立和管理複雜應用,即使你的應用只要使用到一個pod,在配置檔案中忽略replicas欄位的設定即可。
2、檢視Replication Controller的狀態
可以通過get命令來檢視你建立的rc:
kubectl get rc
CONTROLLER   CONTAINER(S)   IMAGE(S)   SELECTOR    REPLICAS
my-nginx     nginx          nginx      app=nginx   2
這個狀態表示,你建立的rc將會確保你一直有兩個nginx的副本。
也可以和直接創pPod一樣檢視建立的Pod狀態資訊:
kubectl get pods
NAME             READY     STATUS    RESTARTS   AGE
my-nginx-065jq   1/1       Running   0          51s
my-nginx-buaiq   1/1       Running   0          51s
3、刪除Replication Controller
當你想停止你的應用,刪除你的rc,可以使用:
kubectl delete rc my-nginx
replicationcontrollers/my-nginx
預設的,這將會刪除所有被這個rc管理的pod,如果pod的數量很大,將會花一些時間來完成整個刪除動作,如果你想使這些pod停止執行,請指定–cascade=false。
如果你在刪除rc之前嘗試刪除pod,rc將會立即啟動新的pod來替換被刪除的pod,就像它承諾要做的一樣。
4、Labels
k8s使用使用者自定義的key-value鍵值對來區分和標識資源集合(就像rc、pod等資源),這種鍵值對稱為label。
在上面的例子中,定義pod的template欄位包含了一個簡單定義的label,key的值為app,value的值為nginx。所有被建立的pod都會解除安裝這個label,可以通過-L引數來檢視:
kubectl get rc my-nginx -L app
CONTROLLER   CONTAINER(S)   IMAGE(S)   SELECTOR    REPLICAS   APP
my-nginx     nginx          nginx      app=nginx   2          nginx
預設情況下,pod的label會複製到rc的label,同樣地,k8s中的所有資源都支援攜帶label。
更重要的是,pod的label會被用來建立一個selector,用來匹配過濾攜帶這些label的pods。
你可以通過kubectl get請求這樣一個欄位來檢視template的格式化輸出:
kubectl get rc my-nginx -o template –template={{.spec.selector}}
map[app:nginx]
你也可以直接指定selector,比如你想在pod template中指明那些不想選中的label,但是你應該確保selector將會匹配這些從pod template中建立的pod的label,並且它將不會匹配其他rc建立的pod。
確保後者最簡單的方法是為rc建立一個唯一的label值,然後在pod template的label和selector中都指定這個label。
二、連線應用到網路中
1、k8s中連線容器的模型
現在,你已經有了組合的、多份副本的應用,你可以將它連線到一個網路上。在討論k8s聯網的方式之前,有必要和Docker中連線網路的普通方式進行一下比較。
預設情況下,Docker使用主機私有網路,所以容器之間可以互相互動,只要它們在同一臺機器上。
為了讓Docker容器可以進行跨節點的交流,必須在主機的IP地址上為容器分配埠號,之後通過主機IP和埠將資訊轉發到容器中。
這樣一來,很明顯地,容器之間必須謹慎地使用和協調埠號的分配,或者動態分配埠號。
在眾多開發者之間協調埠號的分配是十分困難的,會將叢集級別之外的複雜問題暴露給使用者來處理。
在k8s中,假設Pod之間可以互相交流,無論它們是在哪個宿主機上。
我們賦予每個Pod自己的叢集私有IP,如此一來你就不需要明確地在Pod之間建立連線,或者將容器的埠對映到主機的埠中。
這意味著,Pod中的容器可以在主機上使用任意彼此的埠,而且叢集中的Pods可以在不使用NAT的方式下連線到其他Pod。
本章將會詳細描述如何通過這樣一個網路連線模型來執行穩定的Services。
本指南使用一個簡單的nginx服務來驗證以上的概念,在Jenkins CI application有對相同概念的更加詳細的說明。
2、在叢集上將Pod連線到網路
我們之前做過這個例子,但是讓我們以網路連線的角度來重新做一次。
建立一個nginx Pod,注意,這個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的IPs:
kubectl get pods -l app=nginx -o json | grep podIP
                “podIP”: “10.245.0.15”,
                “podIP”: “10.245.0.14”,
這時,你應該能夠通過ssh登入任意節點然後使用curl來連線任一IP(如果節點之間沒有處於同一個子網段,無法使用私有IP進行連線的話,就只能在對應節點上使用對應的IP進行連線測試)。
注意,容器並不是真的在節點上使用80埠,也沒有任何的NAT規則來路由流量到Pod中。
這意味著你可以在同樣的節點上使用同樣的containerPort來執行多個nginx Pod,並且可以在叢集上的任何Pod或者節點通過這個IP來連線它們。
和Docker一樣,埠仍然可以釋出到宿主機的介面上,但是因為這個網路連線模型,這個需求就變得很少了。
如果你好奇的話,可以通過這裡檢視我們是如何做到的:
3、建立Service
現在,在叢集上我們有了一個執行著niginx並且有分配IP地址空間的的Pod。
理論上,你可以直接和這些Pod進行互動,但是當節點掛掉之後會發生什麼?
這些Pod會跟著節點掛掉,然後RC會在另外一個健康的節點上重新建立新的Pod來代替,而這些Pod分配的IP地址都會發生變化,對於Service型別的服務來說這是一個難題。
k8s上的Service是抽象的,其定義了一組執行在叢集之上的Pod的邏輯集合,這些Pod是重複的,複製出來的,所以提供相同的功能。
當Service被建立,會被分配一個唯一的IP地址(也稱為叢集IP)。這個地址和Service的生命週期相關聯,並且當Service是執行的時候,這個IP不會發生改變。
Pods進行配置來和這個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
這個YAML定義建立建立一個Service,帶有Label為app=nginx的Pod都將會開放80埠,並將其關聯到一個抽象的Service埠。
(targetPort欄位是指容器內開放的埠Service通過這個埠連線Pod,port欄位是指抽象Service的埠,nodePort為節點上暴露的埠號,不指定的話為隨機。)
點選這裡檢視完整的Service欄位列表:
現在檢視你建立的Service:
kubectl get svc
NAME         CLUSTER_IP       EXTERNAL_IP       PORT(S)                SELECTOR     AGE
kubernetes   10.179.240.1     <none>            443/TCP                <none>       8d
nginxsvc     10.179.252.126   122.222.183.144   80/TCP,81/TCP,82/TCP   run=nginx2   11m
和之前提到的一樣,Service是以一組Pod為基礎的。
這些Pod通過endpoints欄位開放出來。
Service的selector將會不斷地進行Pod的Label匹配,結果將會通知一個Endpoints Object,這裡建立的也叫做nginxsvc。
當Pod掛掉之後,將會自動從Endpoints中移除,當有新的Pod被Service的selector匹配到之後將會自動加入這個Endpoints。
你可以檢視這個Endpoint,注意,這些IP和第一步中建立Pods的時候是一樣的:
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
你現在應該可以通過10.0.116.146:80這個IP從叢集上的任何一個節點使用curl命令來連線到nginx的Service。
注意,Service的IP是完全虛擬的,如果你想知道它是怎麼工作的,請點選:
4、Pod發現並加入到Service中
k8s提供了兩種基本的方式來發現Service:環境變數和DNS。
環境變數是立即可以使用的,DNS則需要kube-dns叢集外掛。
5、環境變數
當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相關的東西,因為這個Pod是在Service之前建立的,這種做法另外一個缺點是,
Scheduler可能會將兩個Pod都在同一個機器上啟動,這樣一來,當節點掛掉之後整個Service也會掛掉。     
這裡正確的做法是殺死兩個Pod,等待RC重新啟動新的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

6、DNS
k8s提供了一個DNS叢集服務外掛,使用skydns來自動分配DNS給其他的Service。如果你的叢集上有執行的話,你可以檢視它的狀態:
kubectl get services kube-dns –namespace=kube-system
NAME       CLUSTER_IP      EXTERNAL_IP   PORT(S)         SELECTOR           AGE
kube-dns   10.179.240.10   <none>        53/UDP,53/TCP   k8s-app=kube-dns   8d
如果叢集上沒有執行,那麼你可以啟用它:enable
it
本節剩下的內容是在假設你擁有一個長時間可以使用IP的Service(nginxsvc)並且DNS域名已經通過dns服務(DNS叢集服務外掛)分配給這個IP的前提下。
所以你可以在叢集上的任一節點中使用標準的請求方法來連線Service,現在來建立另外一個Pod進行測試:
$ 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
執行查詢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

7、使用加密連線的Service
到目前為止,我們僅僅是在叢集中連線了nginx服務,在將服務開放到Internet上之前,你需要確認雙方交流的通道是安全的。
你將會需要以下的東西來確保安全性:
  • HTTPS自簽名證書(或者你已經有了一個證書)
  • 一個nginx服務配置好了使用這個證書
  • 一個加密措施使得證書在Pod之間交流使用
你可以通過這裡
來獲得完整的配置方式,簡要地說,方式如下:
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                                2
現在修改你的nginx副本來啟動一個使用這個證書的HTTPS服務和Service,並且都開放80和443埠:
$ cat nginxapp.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginxsvc
  labels:
    app: nginx
spec:
  type: NodePort
  ports:
  – port: 8080
    targetPort: 80
    protocol: TCP
    name: http
  – port: 443
    protocol: TCP
    name: https
  selector:
    app: nginx

apiVersion: v1
kind: ReplicationController
metadata:
  name: my-nginx
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      volumes:
      – name: secret-volume
        secret:
          secretName: nginxsecret
      containers:
      – name: nginxhttps
        image: bprashanth/nginxhttps:1.0
        ports:
        – containerPort: 443
        – containerPort: 80
        volumeMounts:
        – mountPath: /etc/nginx/ssl
          name: secret-volume
nginx-app中值得注意的點有:
  • 其包括了RC和SVC的定義在同一個檔案中
  • nginx服務的HTTP通過80埠,HTTPS通過443埠
  • 每個容器都通過掛載在/etc/nginx/ssl卷中的keys來連線。這步是在nginx服務啟動之前完成的
kubectl delete rc,svc -l app=nginx; kubectl create -f ./nginx-app.yaml
replicationcontrollers/my-nginx
services/nginxsvc
services/nginxsvc
replicationcontrollers/my-nginx
此時,你可以在任意節點上訪問nginx服務
kubectl get pods -o json | grep -i podip
    “podIP”: “10.1.0.80”,
node $ curl -k https://10.1.0.80
<h1>Welcome to nginx!</h1>
注意在最後一步中,我們是如何使用-k這個引數的,因為我們不知道任何有關nginx服務執行的Pod的證書生成時刻,所以我們要告訴curl忽略別名不匹配。
通過建立一個Service,在Pod發現Service的時候我們將證書中使用的別名和真實地DNS域名關聯起來,讓我們通過一個Pod來測試(這裡會重用這個證書,Pod連線Service只需要nginx.crt):
cat curlpod.yaml
vapiVersion: v1
kind: ReplicationController
metadata:
  name: curlrc
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: curlpod
    spec:
      volumes:
      – name: secret-volume
        secret:
          secretName: nginxsecret
      containers:
      – name: curlpod
        command:
        – sh
        – -c
        – while true; do sleep 1; done
        image: radial/busyboxplus:curl
        volumeMounts:
        – mountPath: /etc/nginx/ssl
          name: secret-volume

kubectl create -f ./curlpod.yaml
kubectl get pods
NAME             READY     STATUS    RESTARTS   AGE
curlpod          1/1       Running   0          2m
my-nginx-7006w   1/1       Running   0          24m

kubectl exec curlpod — curl https://nginxsvc –cacert /etc/nginx/ssl/nginx.crt

<title>Welcome to nginx!</title>

8、開放Service
在你的應用中可能需要將其開放到一個外部的IP中。
k8s提供了兩種方式:NodePorts和LoadBalancers。
在上面的最後一部分中,我們已經通過NodePorts方式建立了Service,所以如果你有公網IP,你的nginx https副本就可以在Internet上開放了。
kubectl get svc nginxsvc -o json | grep -i nodeport -C 5
            {
                “name”: “http”,
                “protocol”: “TCP”,
                “port”: 80,
                “targetPort”: 80,
                “nodePort”: 32188
            },
            {
                “name”: “https”,
                “protocol”: “TCP”,
                “port”: 443,
                “targetPort”: 443,
                “nodePort”: 30645
            }
 
kubectl get nodes -o json | grep ExternalIP -C 2
                    {
                        “type”: “ExternalIP”,
                        “address”: “104.197.63.17”
                    }

                    },
                    {
                        “type”: “ExternalIP”,
                        “address”: “104.154.89.170”
                    }
curl https://104.197.63.17:30645 -k

<h1>Welcome to nginx!</h1>
現在,我們將通過負載均衡器重新建立一個Service,只需要將nginx-app.yaml檔案中的type欄位從NodePort改為LoadBalancer即可:
kubectl delete rc, svc -l app=nginx
kubectl create -f ./nginx-app.yaml
kubectl get svc nginxsvc
NAME      CLUSTER_IP       EXTERNAL_IP       PORT(S)                SELECTOR     AGE
nginxsvc  10.179.252.126   162.222.184.144   80/TCP,81/TCP,82/TCP   run=nginx2   13m

curl https://162.22.184.144 -k

<title>Welcome to nginx!</title>

EXTERNAL_IP這列的IP即是可以在公網上訪問的IP,CLUSTER_IP只能在自己的叢集上訪問到。


相關文章