/nodejs-docker-k8s-auto-scaling-example

手把手帶你實作 Node.js X Docker X Kubernetes 超簡單範例&在 Local 端模擬 K8s Auto Scaling

Primary LanguageDockerfile

帶你將 Node.js Web App 給 Docker 化,並用 Kubernetes 部署的超簡單範例

公司很早就導入了 Kubernetes(K8s),主要是為了自動化部署以及管理、監測多台機器上的 Container,在遇到高併發情境時可以自動擴展。過去我都是以輔助者的角色跟 DevOps 說我希望有哪些功能,因此對這個技術在懂與不懂之間;所以便藉著寫這篇文章的機會來初探這個已經成為業界潮流的技術。

本篇文章是在 Mac 的作業系統下操作的

一、安裝 Docker、kubectl、minikube 等工具

  • 安裝 Docker
    前往官網下載 Docker 安裝檔,安裝過程就是狂按下一步,這裡就不再多做介紹,下載後要把 App 打開依照步驟執行才算是完成安裝喔! image

    這邊建議順便註冊一組 Docker 帳號,方便你日後的操作。

  • 安裝 kubectl
    安裝他才能操作 K8s 喔!
    # 安裝 kubectl
    brew install kubectl
    # 確認是否安裝成功
    kubectl version --client
    # 查看目前 cluster 運行狀態
    kubectl get all
  • 安裝 minikube
    這個工具可以讓你在本機(Local)架設 K8s 叢集(Cluster)喔!
    # 安裝 minikube
    brew install minikube
    # 啟動 minikube,啟動 k8s cluster
    minikube start
    # 查看 minikube 狀態
    minikube status
    # 取得目前 minikube 的 IP address
    minikube ip

二、建立 Node.js Web App

你也可以直接去筆者的 github,直接將專案 Clone 到本機來做測試。

  • 建立package.json

    {
      "name": "docker_web_app",
      "version": "1.0.0",
      "description": "Node.js on Docker",
      "author": "Dean Lin",
      "main": "server.js",
      "scripts": {
        "start": "node server.js"
      },
      "dependencies": {
        "express": "^4.17.2"
      }
    }
  • 建立server.js

    "use strict";
    
    const express = require("express");
    
    // Constants
    const PORT = 8080;
    const HOST = "0.0.0.0";
    
    // App
    const app = express();
    app.get("/", (req, res) => {
      res.send("Hello World");
    });
    
    app.listen(PORT, HOST);
    console.log(`Running on http://${HOST}:${PORT}`);
  • 在終端機先輸入yarn將套件安裝,再輸入yarn start確認專案可以運行 image image

三、將 Node.js Web App 給 Docker 化

  • 建立Dockerfile
    填寫製作 Docker Image 時需要參考的環境&指令。

    FROM node:14
    
    # Create app directory
    WORKDIR /usr/src/app
    
    # Install app dependencies
    # A wildcard is used to ensure both package.json AND package-lock.json are copied
    # where available (npm@5+)
    COPY package*.json ./
    
    RUN npm install
    # If you are building your code for production
    # RUN npm ci --only=production
    
    # Bundle app source
    COPY . .
    
    EXPOSE 8080
    CMD [ "node", "server.js" ]
  • 建立.dockerignore
    填寫製作 Docker Image 時不想要複製的檔案。

    node_modules
    npm-debug.log
    
  • 製作 Docker Image

    # 登入 Docker
    docker login
    # !如果 Docker app沒有開啟就執行下面步驟會報錯!
    # 建立 Docker Image
    docker build . -t <your username>/node-web-app
    # 筆者範例指令
    docker build . -t babydragon9703111/node-web-app

    image

  • 印出建立的 Images

    docker images

    image

  • 將建立好的 Docker Image 跑起來

    # 裡面填寫的 port 是本地端要跑的(對外 port)
    docker run -p [port]:8080 -d <your username>/node-web-app
    # 筆者範例指令
    docker run -p 8080:8080 -d babydragon9703111/node-web-app
  • 確認 Docker Image 運行狀態,並進入網址確認畫面如預期

    # Get container ID
    docker ps
    
    # Print app output
    docker logs <container id>

    image image

四、撰寫 K8s 使用的 yaml 檔&部署 K8s

  • K8s 的 yaml 檔基礎說明

    • apiVersion:API 版本,需要依照 kind 的物件來決定 version。
    • kind:建立的物件。
    • metadata:描述物件及標籤。
    • spec:物件要的功能。
  • 建立nodetest-k8s.yaml來做測試範例,裡面的 image 等相關資訊記得算成自己的

    apiVersion: v1
    kind: Service
    metadata:
      name: node-app-service
    spec:
      selector:
        app: node-web-app
      ports:
        - protocol: "TCP"
          port: 8080
          targetPort: 8080
      type: LoadBalancer
    
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: node-web-app
    spec:
      selector:
        matchLabels:
          app: node-web-app
      replicas: 3
      template:
        metadata:
          labels:
            app: node-web-app
        spec:
          containers:
            - name: node-web-app
              image: babydragon9703111/node-web-app:latest
              imagePullPolicy: Always
              ports:
                - containerPort: 8080
  • K8s 部署

    # 啟動 k8s cluster
    minikube start
    
    # 執行部署
    kubectl apply -f nodetest-k8s.yaml

    image

  • 查看部署後 pods 狀態

    # 此指令會查 default namespace 的資源
    kubectl get pods

    因為我們在 spec 的 replicas 的值設為 3,所以會產生 3 個 pods。 image

  • 取得 deployment 的外部 ip,並點擊確認是否會顯示網頁

    minikube service node-app-service --url

    image

  • 如果網頁有正確顯示就大功告成啦~後續想將 K8s 刪除執行如下指令即可

    # 指定刪除剛剛建立的 K8s
    kubectl delete -f nodetest-k8s.yaml

    剛下達指令刪除時,在 STATUS 的部分會顯示 Terminating,過一段時間就會消失嚕~ image

  • 關閉 minikube

    # 如果沒關閉的話,你會發現電腦燙燙的,CPU 居高不下
    minikube delete

    image


模擬 K8s Auto Scaling(可在 Local 端測試)

K8s 最吸引人的其中一個功能應該就是用 Auto Scaling 來應付高併發(High concurrency)情境,今天就用一個水平自動擴展(HPA)的範例來讓大家了解他的實際運作模式。

一、撰寫 Auto Scaling 的 yaml 檔

你也可以直接去筆者的 github,直接將專案 Clone 到本機來做測試。

  • HorizontalPodAutoscaler(水平自動擴展)參數說明

    • scaleTargetRef:填寫你想要針對哪個 ReplicaSet 或 Deployment 或 Replication Controller 來做 Scaling。
    • minReplicas、maxReplicas:Pod 擴展範圍。
    • targetCPUUtilizationPercentage:Auto Scaling 是根據 CPU 用量來做擴展,判定的依據為 Deployment resources 的 requests 設定值(如果沒填寫基本上就不會動 XD)。
  • 建立nodetest-k8s-auto.yaml來做 Auto Scaling 測試

    apiVersion: autoscaling/v1
    kind: HorizontalPodAutoscaler
    metadata:
      name: node-web-app
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: node-web-app
      minReplicas: 1
      maxReplicas: 5
      targetCPUUtilizationPercentage: 10

二、調整 Service、Deployment 參數並加入 yaml 檔

其實這裡的 Service、Deployment 都是從先前這篇文章改良過來的。

  • 調整 Service 參數

    • nodePort:這裡建議指定一個 port,如果沒有設定的話,最後對外 ip 的 port 為隨機產生。
  • 調整 Deployment 參數

    • resources 的 requests:如果 cpu 設定為 100m,而 targetCPUUtilizationPercentage 設定為 10;當 Pod 的 CPU 使用率為 1m(100m x 10%) 的時候就要進行 Auto Scaling。
    • replicas:特別設定為 1 方便觀察,原則上他的值要跟 minReplicas 相同。
  • 將如下參數加入剛剛建立的nodetest-k8s-auto.yaml

    apiVersion: v1
    kind: Service
    metadata:
      name: node-app-service
    spec:
      selector:
        app: node-web-app
      ports:
        - protocol: "TCP"
          port: 8080
          targetPort: 8080
          nodePort: 30390
      type: LoadBalancer
    
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: node-web-app
    spec:
      selector:
        matchLabels:
          app: node-web-app
      replicas: 1
      template:
        metadata:
          labels:
            app: node-web-app
        spec:
          containers:
            - name: node-web-app
              image: babydragon9703111/node-web-app:latest
              imagePullPolicy: Always
              ports:
                - containerPort: 8080
              resources:
                requests:
                  cpu: 100m
                  memory: 50Mi
                limits:
                  cpu: 200m
                  memory: 100Mi

三、部署&測試 pod 是否會依據流量的增加自動擴展

  • 開啟流量監控、部署 K8s

    # 將 minikube 監控流量的 addons 打開
    minikube addons enable metrics-server
    
    # 部署剛剛建立的 nodetest-k8s-auto.yaml
    kubectl apply -f nodetest-k8s-auto.yaml
    
    # 查看 Auto Scaler 狀態
    kubectl get hpa

    image

    一開始 TARGETS 會顯示 <unknown>,這是因為還在判斷流量,過一陣子就會顯示百分比了

  • 取得 Deployment 的外部 ip,然後瘋狂存取來測試 Auto Scaling

    # 取得 Deployment 的外部 ip(此時會看到 port 與設定的 nodePort 相同)
    minikube service node-app-service --url
    
    # 開始瘋狂存取剛剛輸出的 url 來確認 pod 是否會自動成長
    while true
    do curl <Deployment 的外部 ip>
    done

    image

  • 確認 pod(看 REPLICAS 這個欄位)是否會依據流量的增加自動擴展到設定的 maxReplicas image

  • 將上面的存取 K8s 外部 ip 的 while 迴圈關掉後,pod 會自動減少到設定的 minReplicas image

  • 測試完成後將建立的 K8s 服務刪除、關閉 minikube

    kubectl delete -f nodetest-k8s-auto.yaml
    # 如果沒關閉的話,你會發現電腦燙燙的,CPU 居高不下
    minikube delete

如果覺得我寫的文章對你有幫助,千萬不要吝嗇給我一個 ⭐STAR⭐ 喔~

參考資料