Kubernetes 介紹與使用KIND簡單建置Cluster/Local Registry

Larry Lai Lv1

Kubernetes(K8S)

Kubernetes 是一個協助我們自動化部署、擴張以及管理容器應用程式(containerized applications)的系統。相較於需要手動部署每個容器化應用程式(containers)到每台機器上,

K8S 架構

k8sPod
k8sPod2

什麼是 K8S

Kubernetes 可以幫我們做到以下幾件事情:

  1. 同時部署多個 containers 到一台機器上,甚至多台機器。
  2. 管理各個 container 的狀態。如果提供某個服務的 container 不小心 crash 了,Kubernetes 會偵測到並重啟這個 container,確保持續提供服務
  3. 將一台機器上所有的 containers 轉移到另外一台機器上。
  4. 提供機器高度擴張性。Kubernetes cluster 可以從一台機器,延展到多台機器共同運行。
  • K8S 架構圖
    KinD_Arch

  • Master Node

    在 k8s 中,Master 扮演著管理叢集的角色,管理者可以透過 CLI (Command Line Interface) 或 APIs (Application Programming Interface) 或控制介面 (Dashboard) 等等不同方式與 Master 溝通進而存取、控制或修改叢集狀態例如配置資源、擴充 Pod 數量等等。

    • Scheduler
      • Scheduler 會知道目前 Worker Node 的狀況,當需要配置 Pod 時,Scheduler 會找出最合適的 Node 並配置 Pod。
    • API Server
      • 管理者會透過 REST command 把需要做的工作 (例如,新增或刪除物件) 傳送到 api-server , api-server 會驗證並處理管理者要求執行的工作,當工作執行完畢後,叢集的最新狀態便會儲存到 etcd 中。
    • Controller
      • Controller 會透過 api-server 了解目前叢集的狀態,並嘗試把目前狀態調整為管理者想要的狀態。
      • 管理者想要的狀態可能會因為種種原因無法達成,例如資源不夠 (cpu, memory) 等等。而 Controller 會持續嘗試著把叢集調整成管理者想要的狀態。
    • etcd
      • etcd 為鍵值儲存區 (key-value storage),在 k8s 中被用來儲存叢集的狀態。
      • etcd 可以是 Master 的一部分或者獨立被設置在外部。同樣的,當 etcd 被設置在外部時,Master 會連到 etcd 取得或更新叢集狀態。
  • Worker Node

    Worker Node 簡單來說就是一台機器,它可以是一台實體機器或虛擬機 (VMs)。而多個運行單位 Pod 會被配置到 Worker Node 中運行,而每個 Pod 中含有一到多個容器。

    • kubelet
      • kubelet 運行在 Worker Node,負責建立 Pod 中的容器。當 kubelet 收到來自 Master Node 送來 Pod 定義內容時,kubelet 會透過 container runtime 建立 Pod 需要的容器並確保容器狀態是可運行的。
    • kube-proxy
      • 當 Pod 運行在 Worker Node 之後,外部需要透過 Service 這個 k8s 物件來與 Pod 連線而非直接存取 Pod。
        kube-proxy 運行在 Worker Node 且持續監聽 api-server,並知道 Service 被建立或刪除。當 Service 被建立後,kube-proxy 會負責將 Request 導到對應的 Pod。
    • pod
      • k8s 基本運行單位。

使用 KinD 架設 K8S Cluster

kind is a tool for running local Kubernetes clusters using Docker container “nodes”.kind was primarily designed for testing Kubernetes itself, but may be used for local development or CI.

  • Kind 架構圖
    KinD_Arch
  1. 安裝 KinD

    1. wsl -l -v
    2. wsl –set-version Ubuntu-22.04 2
    3. docker 設定中調整 intergration 與 Ubuntu 整合 WSL2 的環境,讓 Ubuntu 可以直接調用 docker cli
    4. 下載 KinD 並安裝於 Ubuntu
      1
      2
      3
      curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-linux-amd64
      chmod +x ./kind
      sudo mv ./kind /usr/local/bin/kind
  2. 建立 K8S Cluster (要先開 Docker Engine Desktop)

    1. 先建立 kind.yaml 檔
      1
      2
      3
      4
      5
      6
      7
      8
      kind: Cluster
      apiVersion: kind.x-k8s.io/v1alpha4
      nodes:
      - role: control-plane
      extraPortMappings:
      - containerPort: 30000 #容器的port
      hostPort: 5432 #對外開啟的port
      - role: worker
    2. 使用 yaml 檔建立叢集 kind create cluster --config kind.yaml -n kind
  3. 將做好的 image 複製進去 kind 所建立的 cluster

    1. 進入路徑 ../src/dockerDemo,執行docker build -t dockerDemo:latest .

      執行好後可以先試試看 docker run -it dockerDemo -p 3000:3000,然後開啟瀏覽器進入 localhost:3000 看是否有看見 Hello World! 的訊息。

    2. Ubuntu 執行 kind load docker-image -n dockerDemo:latest

    3. 執行 docker exec -it kind-worker crictl image 查看是否有正確讀入 image。

  4. 建立一個 pod,這個 pod 將會直接在 worker 裡面做為一個單位運行,裡面則會包含使用方才的 image 所起的 Container。而建立好該 Pod 之後則需要建立 Service 來貫通整條路。

    1. 建立 00-pods-dockerdemo.yaml
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      apiVersion: v1 #Pod 使用的 Kubernetes API 是 v1 版本號
      kind: Pod #建立類型
      metadata:
      name: my-pod #該 Pod 的名稱
      labels: #標籤, 後續建立 Service 會使用, 所以要記得
      app: webserver
      spec: #該 Pod 的描述
      containers: #所包含的容器
      - name: pod-demo #容器名稱
      image: dockerdemo:latest #容器映像檔
      imagePullPolicy: Never #因為KinD本身已有複製本地的image,所以要設這個。
      ports:
      - containerPort: 3000 #該容器對外開放的 Port
    2. 建立 00-svc-dockerdemo.yaml
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      apiVersion: v1
      kind: Service
      metadata:
      name: dockerdemo-svc
      spec:
      type: NodePort
      ports:
      - port: 3000 #指定建立此Service的Cluster IP中是哪個port number要對應到targetPort
      nodePort: 30000 #指定這個node透過哪個port連接targetPort
      protocol: TCP
      targetPort: 3000 #指定的Pod的Port號, 也就是外部會透過port 30000對應到此pod的port 3000
      selector:
      app: webserver #將特定的port號收到的請求導向app:webserver標籤的pods
    3. 建立 Pods,執行 kubectl apply -f 00-pods-dockerdemo.yaml
    4. 有了 Pod 之後,需打通其橋梁,因此建立 Service。 執行 kubectl apply -f 00-svc-dockerdemo.yaml
    5. 可確認其有無完整建立,執行 kubectl get [Resource] 來查看。Resource 可改為 svc 或 pods
  5. 在 Host 主機打開瀏覽器,進入 127.0.0.1:5432 就可以看到 Hello World! 字樣。而這個 5432 Port 則是 Host 透過該 Port 進入到 Cluster,並由 Cluster 設定的 5432 進入到 30000,然後觸及到 service 的 3000 去對應到 Pod 的 3000。

其實在 Pod 建立好之後,就可以直接透過 Port Foward 的方式來將 Pod 整個暴露給外網的 Port。Port Forward 可以透過 Kubectl 建立起 Proxy 來將外部的流量導入你想進入的 Pod 的 Port。因為要建立起整個 Port 串通是一件不太方便的事情,除此之外在某些狀況也可能暴露整個服務。因此透過這樣的方式能夠更簡便的去除錯。只需要輸入:kubectl port-forward my-pod 8000:3000 。就能透過 Host 的瀏覽器進入 127.0.0.1:8000 就可以看到 **Hello World!**!

K8S Dashboard

因為 KinD 並沒有直接內建儀表板,所以我們得自己安裝回來。

1
2
3
4
5
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml

# 確認是否有Pod被建立
kubectl get pod -n kubernetes-dashboard -A -w

安裝好後需建立使用者來取得權限和建立 Token

1
2
3
4
5
6
7
kubectl create clusterrolebinding default-admin --clusterrole cluster-admin --serviceaccount=default:default

# Get Token
kubectl create token default

# Run Dashboard
kubectl proxy

網址列輸入 http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/#/login 進入 dashboard,貼入方才的 Token 就可以登入。

KinD Local Registry

如果已實作一個 Cluster 的話,那麼要使用 Local Registry 需要重建一個新的 Cluster。因為 KinD 沒辦法針對叢集的設定去做調整,因此在建立時就必須決定好。

通常在 CI/CD 架構中會有個地方存放產出物(Artifact),無論是 Nuget、Npm 或是 Image,因此我們想透過 KinD 去模擬未來的 CI/CD 架構的話,就必須建立一個可以存放產出物的地方。但在初次練習沒有那麼多經驗可能會苦惱究竟要選擇什麼樣的工具或怎麼架。

Nexus Oss Registry、Proget 等等的都可以是未來的解決方案,或者是單純要架設私有的 Image Registry 也可以選擇 Habor。但在這裡我們會選擇最容易架設的 KinD Local Registry。

KinD Local Registry 架設方式只有兩步,到官網 複製執行語法,修改成自己要的。然後在 WSL2 裡面執行就能完成。

以下是我建立時所使用的執行語法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/bin/sh
set -o errexit

# create registry container unless it already exists
reg_name='kind-registry'
reg_port='5001'
if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" != 'true' ]; then
docker run \
-d --restart=always -p "127.0.0.1:${reg_port}:5000" --name "${reg_name}" \
registry:2
fi

# create a cluster with the local registry enabled in containerd
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30000
- role: worker
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:${reg_port}"]
endpoint = ["http://${reg_name}:5000"]
EOF

# connect the registry to the cluster network if not already connected
if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${reg_name}")" = 'null' ]; then
docker network connect "kind" "${reg_name}"
fi

# Document the local registry
# https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/generic/1755-communicating-a-local-registry
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: local-registry-hosting
namespace: kube-public
data:
localRegistryHosting.v1: |
host: "localhost:${reg_port}"
help: "https://kind.sigs.k8s.io/docs/user/local-registry/"
EOF

建立好 Local Registry 然後呢?

  1. 建立 Docker Image
  2. 將 Image Push 上去 Registry
  3. 透過建立 Deployment 來部署
  4. 透過建立 Service 讓主機能通到 Node 裡 Pod 的 Container

建立 Docker Image

進入路徑 ../src/dockerDemo,執行`docker build -t dockerDemo:latest .`

Tag Image Local Registry

docker tag dockerDemo localhost:5001/dockerDemo:latest

Push Image To Local Registry

docker push localhost:5001/dockerDemo

執行後可以看見像這樣的訊息:

1
2
3
4
5
6
7
8
9
Using default tag: latest
The push refers to repository [localhost:5001/dockerDemo]
9992941a9f7b: Pushed
e9f76b9eead8: Pushed
e40a27ff8335: Pushed
ea0f922ba68b: Pushed
1f6b17cd478d: Pushed
17bec77d7fdc: Pushed
latest: digest: sha256:05e38376828b2d2279517c04e93c2a0b072abbaf0fcbc8e32422c047e9a2c03d size: 1578

建立 Deployment 部署,在這裡會向 Local Registry Pull Image

kubectl create deployment test-dockerDemo --image=localhost:5001/dockerDemo

可以檢查 Pod 是否正常運作中

kubectl get pods -o wide

建立 Service 打通 Host 和 Node

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Service
metadata:
name: dockerdemo-svc
spec:
type: NodePort
ports:
- port: 3000
nodePort: 30000
protocol: TCP
targetPort: 3000
selector:
app: test-dockerdemo

透過 docker ps 可以看見 K8S 的 Container 的 30000 port 是對應到 Host 的哪個 Port 對外開放。得知之後只要在 Host 的 Browser 進入 127.0.0.1:[Port] 就能看見 Hello World!。

常用指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# get image list in kind (cluster1 叢集名稱)
docker exec -it cluster1-worker crictl image

# load image to k8s cluster
kind load docker-image redis:latest

# get login token
kubectl create token default

# get cluster info
kubectl cluster-info

# get cluster config
kubectl config view

  • 標題: Kubernetes 介紹與使用KIND簡單建置Cluster/Local Registry
  • 作者: Larry Lai
  • 撰寫于 : 2023-11-24 14:40:56
  • 更新于 : 2024-07-17 17:01:39
  • 連結: https://redefine.ohevan.com/2023/11/24/k8s-introduce-1/
  • 版權宣告: 本作品采用 CC BY-NC-SA 4.0 进行许可。
 留言