入門指南丨上手理解Deployment、Services和Ingress

在之前的文章中,我們瞭解了Kubernetes中的基本概念,其硬件結構,不同的軟件組件(例如Pod、Deployment、StatefulSet、Services、Ingress和Persistent Volumes),並瞭解瞭如何在服務之間與外部進行通信。

在本文中,我們將瞭解到:

  1. 使用MongoDB數據庫創建NodeJS後端

  2. 編寫Dockerfile來容器化我們的應用程序

  3. 創建Kubernetes Deployment腳本以啓動Pod

  4. 創建Kubernetes Service腳本以定義容器與外界之間的通信接

  5. 部署Ingress Controller以請求路由

  6. 編寫Kubernetes Ingress腳本來定義與外界的通信。

圖片

由於我們的代碼可以從一個節點重定向到另一個節點(例如,一個節點沒有足夠的內存,所以工作將重新調度到另一個具有足夠內存的節點上),因此保存在節點上的數據容易丟失 ,意味着MongoDB 數據不穩定。在下一篇文章中,我們將討論數據持久性問題以及如何使用Kubernetes持久卷安全地存儲我們的持久數據。

在本文中,我們將使用NGINX作爲Ingress Controller和Azure容器鏡像倉庫來存儲我們的自定義Docker鏡像。文中編寫所有腳本都可以在Stupid Simple Kubernetes git repo中找到,如有需要可訪問鏈接獲取:

http://GitHub - CzakoZoltan08/StupidSimpleKubernetes-AKS

請注意:這些腳本不限定於某個平臺,因此您可以使用其他類型的雲提供程序或帶有K3s的本地集羣來實踐本教程。我之所以建議使用K3s,因爲它非常輕量,所有依賴項都被打包在一個小於100MB的單個二進制文件中。更重要的是,它是一種高可用的、經過CNCF認證的Kubernetes發行版,專門用於資源受限的環境中的生產工作負載。有關更多信息,您可以訪問官方文檔:

https://docs.rancher.cn/k3s/

前期準備

在開始本教程之前,請確保您已安裝Docker。同時也要安裝kubectl。

Kubectl安裝鏈接:

https://kubernetes.io/docs/tasks/tools/#install-kubectl-on-windows

在本教程中使用的Kubectl命令可以在Kubectl cheat sheet(https://kubernetes.io/docs/reference/kubectl/cheatsheet/)中找到。

在本教程中,我們將使用Visual Studio Code,但這不是必要的,你也可以使用其他的編輯器。

創建可用於生產的微服務架構

將應用程序容器化

第一步,創建NodeJS後端的Docker鏡像。創建鏡像後,我們會將其推送到容器鏡像倉庫中,在該鏡像倉庫中可以訪問它,並且可以通過Kubernetes服務(在本例中爲Azure Kubernetes Service)拉取。

The Docker file for NodeJS:  
FROM node:13.10.1  
WORKDIR /usr/src/app  
COPY package*.json ./  
RUN npm install  
# Bundle app source  
COPY . .  
EXPOSE 3000  
CMD [ "node", "index.js" ]

在第一行中,我們需要根據要創建後端服務的鏡像進行定義。在這種情況下,我們將使用Docker Hub中13.10.1版的官方節點鏡像。

在第3行中,我們創建一個目錄來將應用程序代碼保存在鏡像中。這將是您的應用程序的工作目錄。

該鏡像已經安裝了Node.js和NPM,因此下一步我們需要使用npm命令安裝您的應用程序依賴項。

請注意,要安裝必需的依賴項,我們不用複製整個目錄,而只需複製package.json,這使我們可以利用緩存的Docker層。

有關高效Dockerfile的更多信息,請訪問以下鏈接:

http://bitjudo.com/blog/2014/03/13/building-efficient-dockerfiles-node-dot-js/

在第9行中,我們將源代碼複製到工作目錄中,在第11行中,將其暴露在端口3000上(如果需要,您可以選擇另一個端口,但請確保同步更改Kubernetes Service腳本。)

最後,在第13行,我們定義了運行應用程序的命令(在Docker容器內部)。請注意,每個Dockerfile中應該只有一個CMD指令。如果包含多個,則只有最後一個纔會生效。

現在,我們已經定義了Dockerfile,我們將使用以下Docker命令從該Dockerfile中構建鏡像(使用Visual Studio Code的Terminal或在Windows上使用CMD):

docker build -t node-user-service:dev .

請注意Docker命令末尾的小圓點,這意味着我們正在從當前目錄構建鏡像,因此請確保您位於Dockerfile所在的同一文件夾中(在本例中,是repo的根文件夾)。

要在本地運行鏡像,我們可以使用以下命令:

docker run -p 3000:3000 node-user-service:dev  

若要將此鏡像推送到我們的Azure容器鏡像倉庫,我們必須使用以下格式標記它/::,在本例中如下所示:

docker tag node-user-service:dev stupidsimplekubernetescontainerregistry.azurecr.io/node-user-service:dev

最後一步是使用以下Docker命令將其推送到我們的容器鏡像倉庫中:

docker push stupidsimplekubernetescontainerregistry.azurecr.io/node-user-service:dev

使用部署腳本創建Pod

NodeJs後端

接下來,定義Kubernetes Deployment腳本,該腳本將自動爲我們管理Pod。

apiVersion: apps/v1  
kind: Deployment  
metadata:  
  name: node-user-service-deployment  
spec:  
  selector:  
    matchLabels:  
      app: node-user-service-pod  
  replicas: 3  
  template:  
    metadata:  
      labels:  
        app: node-user-service-pod  
    spec:  
      containers:  
        - name: node-user-service-container  
          image: stupidsimplekubernetescontainerregistry.azurecr.io/node-user-service:dev  
          resources:  
            limits:  
              memory: "256Mi"  
              cpu: "500m"  
          imagePullPolicy: Always  
          ports:  
            - containerPort: 3000

Kubernetes API可以查詢和操作Kubernetes集羣中對象的狀態(例如Pod、命名空間、ConfigMap等)。如第一行中所指定,這個API的當前穩定版本爲1。

在每個Kubernetes .yml腳本中,我們必須使用kind關鍵字定義Kubernetes資源類型(Pods、Deployments、Service等)。因此,你可以看到,我們在第2行中定義了我們想使用Deployment資源。

Kubernetes允許您向資源中添加一些元數據。這樣一來,您就可以更輕鬆地識別、過濾和參考資源。

在第5行中,我們定義了該資源的規範。在第8行中,我們指定此Deployment應僅應用於標籤爲app:node-user-service-pod的資源中,在第9行中可以看出我們想要創建同一Pod的3個副本。

Template(從第10行開始)定義了Pod。在這裏,我們將標籤app:node-user-service-pod添加到每個Pod。這樣,Deployment將識別它們。在第16和17行中,我們定義了應在pod內部運行哪種Docker容器。如您在第17行中看到的那樣,我們將使用Azure容器鏡像倉庫中的Docker鏡像,該鏡像是在上一節中構建並推送的。

我們還可以爲Pod定義資源限制,避免Pod資源不足(當其中一個Pod使用所有資源而其他Pod無法使用它們時)。此外,當您爲Pod中的容器指定資源請求時,調度程序將使用此信息來決定將Pod放置在哪個節點上。當您爲容器指定資源限制時,kubelet會強制執行這些限制,從而不允許運行中的容器使用超出您設置的資源限制。kubelet還至少保留該系統資源的“請求”量。請注意,如果您沒有足夠的硬件資源(例如CPU或內存),則永遠無法調度pod。

最後一步是定義用於通信的端口。在本例中,我們使用端口3000。此端口號應與Dockerfile中暴露的端口號相同。

MongoDB

MongoDB數據庫的Deployment腳本非常相似。唯一的區別是我們必須指定卷掛載(數據會被保存到節點上的文件夾中)。

apiVersion: apps/v1  
kind: Deployment  
metadata:  
  name: user-db-deployment  
spec:  
  selector:  
    matchLabels:  
      app: user-db-app  
  replicas: 1  
  template:  
    metadata:  
      labels:  
        app: user-db-app  
    spec:  
      containers:  
        - name: mongo  
          image: mongo:3.6.4  
          command:  
            - mongod  
            - "--bind_ip_all"  
            - "--directoryperdb"  
          ports:  
            - containerPort: 27017  
          volumeMounts:  
            - name: data  
              mountPath: /data/db  
          resources:  
            limits:  
              memory: "256Mi"  
              cpu: "500m"  
      volumes:  
        - name: data  
          persistentVolumeClaim:  
            claimName: static-persistence-volume-claim-mongo

在本例中,我們直接從DockerHub使用了官方MongoDB鏡像(第17行)。在第24行中定義了卷安裝。在討論Kubernetes持久卷時,我們將在下一篇文章中解釋最後四行。

創建用於網絡訪問的服務

現在我們已經啓動了Pod,並開始定義容器之間以及與外部世界的通信。爲此,我們需要定義一個服務。Service與Deployment之間的關係是一對一的,因此對於每個Deployment,我們都應該有一個Service。Deployment還可以管理Pod的生命週期,並且負責監控它們,而Service負責啓用對一組Pod的網絡訪問。

apiVersion: v1  
kind: Service  
metadata:  
  name: node-user-service  
spec:  
  type: ClusterIP  
  selector:  
    app: node-user-service-pod  
  ports:  
    - port: 3000  
      targetPort: 3000

這個.yml腳本的重要部分是selector,它定義瞭如何識別要從此Service引用的Pod(由Deployment創建)。在第8行中我們可以看到的,Selector 爲app:node-user-service-pod,因爲先前定義的Deployment中的Pod被標記爲這樣。另一個重要的事情是定義容器端口和服務端口之間的映射。在這種情況下,傳入請求將使用第10行中定義的3000端口,並將它們路由到第11行中定義的端口。

MongoDB pod的Kubernetes Service腳本非常相似。我們只需要更新Selector和端口。

apiVersion: v1  
kind: Service  
metadata:  
  name: user-db-service  
spec:  
  clusterIP: None  
  selector:  
    app: user-db-app  
  ports:  
    - port: 27017  
      targetPort: 27017

配置外部流量

爲了與外界通信,我們需要定義一個Ingress Controller並使用Ingress Kubernetes資源指定路由規則。

要配置NGINX ingress controller,我們將使用可以以下鏈接中的腳本:

https://github.com/CzakoZoltan08/StupidSimpleKubernetes-AKS/blob/master/manifest/ingress-controller/nginx-ingress-controller-deployment.yml

這是一個通用腳本,無需修改即可應用(詳細解釋NGINX Ingress Controller不在本文討論範圍之內)。

下一步是定義“負載均衡器”,該負載均衡器將用於使用公共IP地址路由外部流量(雲提供商提供負載均衡器)。

kind: Service  
apiVersion: v1  
metadata:  
  name: ingress-nginx  
  namespace: ingress-nginx  
  labels:  
    app.kubernetes.io/name: ingress-nginx  
    app.kubernetes.io/part-of: ingress-nginx  
spec:  
  externalTrafficPolicy: Local  
  type: LoadBalancer  
  selector:  
    app.kubernetes.io/name: ingress-nginx  
    app.kubernetes.io/part-of: ingress-nginx  
  ports:  
    - name: http  
      port: 80  
      targetPort: http  
    - name: https  
      port: 443  
      targetPort: https

現在我們已經啓動並運行了Ingress controller和負載均衡器,於是我們可以定義Ingress Kubernetes資源來指定路由規則。

apiVersion: extensions/v1beta1  
kind: Ingress  
metadata:  
  name: node-user-service-ingress  
  annotations:  
    kubernetes.io/ingress.class: "nginx"  
    nginx.ingress.kubernetes.io/rewrite-target: /$2  
spec:  
  rules:  
    - host: stupid-simple-kubernetes.eastus2.cloudapp.azure.com  
      http:  
        paths:  
          - backend:  
              serviceName: node-user-service  
              servicePort: 3000  
            path: /user-api(/|$)(.*)  
          # - backend:  
          #     serviceName: nestjs-i-consultant-service  
          #     servicePort: 3001  
          #   path: /i-consultant-api(/|$)(.*)

在第6行中,我們定義了Ingress Controller類型(這是Kubernetes的預定義值;Kubernetes當前支持和維護GCE和nginx controller)。

在第7行中,我們定義了重寫目標規則,在第10行中,我們定義了主機名。

對於應該從外部訪問的每個服務,我們應該在路徑列表中添加一個條目(從第13行開始)。在此示例中,我們僅爲NodeJS用戶服務後端添加了一個條目,可通過端口3000對其進行訪問。/ user-api唯一標識我們的服務,因此任何以stupid-simple-kubernetes.eastus2.cloudapp azure.com/user-api開頭的請求將被路由到此NodeJS後端。如果要添加其他服務,則必須更新此腳本(請參見注釋掉的代碼)。

應用.yml腳本

要應用這些腳本,我們將使用kubectl。應用文件的kubectl命令如下:

kubectl apply -f

在本例中,如果你在Stupid Simple Kubernetes repo的根文件夾中,您需要執行以下命令:

kubectl apply -f .\manifest\kubernetes\deployment.yml  
kubectl apply -f .\manifest\kubernetes\service.yml  
kubectl apply -f .\manifest\kubernetes\ingress.yml  
kubectl apply -f .\manifest\ingress-controller\nginx-ingress-controller-deployment.yml  
kubectl apply -f .\manifest\ingress-controller\ngnix-load-balancer-setup.yml  

應用這些腳本後,一切準備就緒,進而我們可以從外部調用後端(如使用Postman)。

總結

在本教程中,我們學習瞭如何在Kubernetes中創建各種資源,例如Pod、Deployment、Services、Ingress和Ingress Controller。我們使用MongoDB數據庫創建了一個NodeJS後端,並使用3個pod的副本容器化並部署了NodeJS和MongoDB容器。

在下一篇文章中,我們將瞭解持久保存數據的問題,並將介紹Kubernetes中的持久卷。

作者簡介

Czako Zoltan,一位經驗豐富的全棧開發人員,在前端,後端,DevOps,物聯網和人工智能等多個領域都擁有豐富的經驗。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章