在學習 Kubernetes 的時候,單一節點的 Kubernetes 測試環境已經可以讓你理解許多物件的建立與操作,對於「應用程式的開發人員」來說已經足夠。由於 Kubernetes 提供不同層級的「抽象」,照理說在不特別理解 Kubernetes 配置的情況下,應用程式都應該能正常運行才對。但如果想測試一些只有多台節點才能測試的功能,就需要相對完整的叢集才能測試了。本篇文章將分享 KinD (Kubernetes in Docker) 這套工具,可以幫助你在幾秒內成功架設擁有多節點的 Kubernetes 叢集環境。
前置條件 (Prerequisites)
要進行本篇文章的設定步驟時,必須符合以下條件:
調整 WSL 2 (Ubuntu 20.04 LTS) 系統設定
-
進入 Ubuntu-20.04
的 bash 環境
wsl -d Ubuntu-20.04
cd ~
-
更新 Ubuntu 到最新版
sudo apt update
sudo apt upgrade -y
-
設定更清楚的命令提示字串(Prompt) (請加入到 .profile
檔案中)
export PS1="\[\033[33m\]\D{%Y-%m-%d %I:%M%p}\[\033[00m\] \[\033[32m\]\u@$WSL_DISTRO_NAME\[\033[00m\]:\[\033[36m\]\w\[\033[00m\]$ "
-
建議調整 bash 環境參數 (請加入到 .profile
檔案中)
shopt -u progcomp
shopt -s no_empty_cmd_completion
-
我個人偏好使用 vim
當成預設編輯器
sudo update-alternatives --set editor /usr/bin/vim.basic
-
安裝 NERDTree 檔案總管套件 (Vim)
git clone https://github.com/preservim/nerdtree.git ~/.vim/pack/vendor/start/nerdtree
vim -u NONE -c "helptags ~/.vim/pack/vendor/start/nerdtree/doc" -c q
-
新增 ~/.vimrc
檔案
set background=dark
syntax enable
"set number
set noruler
set ignorecase
set smartcase
"set cursorline
"set cursorcolumn
let &t_ti.="\e[1 q"
let &t_SI.="\e[5 q"
let &t_EI.="\e[1 q"
let &t_te.="\e[0 q"
map <A-Left> <Esc>:b#<CR>
map <A-Right> <Esc>:bn<CR>
map <C-W> <Esc>:q<CR>
"------------Plugin: nerdtree------------
map <C-p> :NERDTreeToggle<CR>
map <C-l> :tabn<CR>
map <C-h> :tabp<CR>
" Map Ctrl-Left & Ctrl-Right to switching tabs Ctrl-n to open new tab
map <C-Left> <Esc>:tabprev<CR>
map <C-Right> <Esc>:tabnext<CR>
map <C-n> <Esc>:tabnew<CR>
-
安裝 kubectx 與 kubens 工具
sudo apt install pkg-config -y
wget https://github.com/ahmetb/kubectx/releases/download/v0.9.1/kubectx_v0.9.1_linux_x86_64.tar.gz
wget https://github.com/ahmetb/kubectx/releases/download/v0.9.1/kubens_v0.9.1_linux_x86_64.tar.gz
sudo tar zxvf kubectx_v0.9.1_linux_x86_64.tar.gz -C /usr/local/bin kubectx
sudo tar zxvf kubens_v0.9.1_linux_x86_64.tar.gz -C /usr/local/bin kubens
sudo git clone https://github.com/ahmetb/kubectx.git /etc/kubectx
COMPDIR=$(pkg-config --variable=completionsdir bash-completion)
sudo ln -sf /etc/kubectx/completion/kubens.bash $COMPDIR/kubens
sudo ln -sf /etc/kubectx/completion/kubectx.bash $COMPDIR/kubectx
rm kubectx_v0.9.1_linux_x86_64.tar.gz kubens_v0.9.1_linux_x86_64.tar.gz
-
安裝 k9s 工具
sudo wget https://github.com/derailed/k9s/releases/download/v0.21.7/k9s_Linux_x86_64.tar.gz
sudo tar -zxvf k9s_Linux_x86_64.tar.gz -C /usr/local/bin k9s
k9s info
-
開啟 Docker Desktop for Windows 的 Settings 設定視窗
請務必切換到 Linux container 模式!
-
調整以下兩個設定
General > 勾選 Use the WSL 2 based engine
Resources > WSL INTEGRATION > 啟用 Ubuntu-20.04
檢查 docker 與 kubectl 版本
-
檢查 Docker 版本資訊
$ docker version
Client: Docker Engine - Community
Version: 19.03.12
API version: 1.40
Go version: go1.13.10
Git commit: 48a66213fe
Built: Mon Jun 22 15:45:36 2020
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.12
API version: 1.40 (minimum version 1.12)
Go version: go1.13.10
Git commit: 48a66213fe
Built: Mon Jun 22 15:49:27 2020
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: v1.2.13
GitCommit: 7ad184331fa3e55e52b890ea95e65ba581ae3429
runc:
Version: 1.0.0-rc10
GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd
docker-init:
Version: 0.18.0
GitCommit: fec3683
如果你無法成功執行 docker version
的話,請嘗試在 Windows PowerShell 中執行 Restart-Service LxssManager
命令重啟 LxssManager
服務,並 Restart Docker 應用程式。
-
檢查 kubectl 版本資訊
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"16+", GitVersion:"v1.16.6-beta.0", GitCommit:"e7f962ba86f4ce7033828210ca3556393c377bcc", GitTreeState:"clean", BuildDate:"2020-01-15T08:26:26Z", GoVersion:"go1.13.5", Compiler:"gc", Platform:"linux/amd64"}
The connection to the server localhost:8080 was refused - did you specify the right host or port?
$ kubectl version --client --short
Client Version: v1.16.6-beta.0
此時連不上 Kubernetes 是正常的!
安裝 KinD 工具並建立 k8s 叢集
-
安裝 KinD 工具
curl -Lo ./kind https://kind.sigs.k8s.io/dl/$(curl -sSL 'https://api.github.com/repos/kubernetes-sigs/kind/releases/latest' | jq '.tag_name' -r)/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/
-
建立單一節點的 K8S 叢集
kind create cluster --name myk8s
安裝過程大概只要 35 秒左右即可完成!
如果不加上 --name myk8s
參數,也可以成功建立叢集,只是叢集名稱預設為 kind
如果要安裝舊版的 k8s 叢集,kube可以嘗試以下命令:
kind create cluster --name myk8s --image kindest/node:v1.17.5
完整可用的 node image 清單在此: https://hub.docker.com/r/kindest/node/tags
若要自訂 node image 可參考 Base Image 的 Dockerfile 進行調整。
建立完成後,可以透過以下命令查詢目前已經透過 kind 建立幾套叢集:
kind get clusters
-
驗證安裝結果
安裝好叢集之後,預設會在 $HOME/.kube
目錄下建立好一個 config
設定檔,你可以透過以下命令查詢叢集資訊:
$ kubectl cluster-info
Kubernetes master is running at https://127.0.0.1:43663
KubeDNS is running at https://127.0.0.1:43663/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
如果你有超過一套叢集被建立,則必須指定 context name 才能正確取得叢集資訊:
kubectl cluster-info --context kind-kind
kubectl cluster-info --context kind-kind-2
取得節點清單 (預設 kind 只會建立一個節點同時肩負 Control Plane Node 與 Worker Node 任務)
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
myk8s-control-plane Ready master 10m v1.18.2
注意:早期的 Master Node 都已經改名為 Control Plane Node 了!(#BlackLivesMatter)
取得所有命名空間下的所有物件清單
請注意:在我的部落格可以按下鍵盤的 F
鍵切換瀏覽模式為全螢幕。
$ kubectl get all --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system pod/coredns-66bff467f8-4nf59 1/1 Running 0 11m
kube-system pod/coredns-66bff467f8-k625z 1/1 Running 0 11m
kube-system pod/etcd-myk8s-control-plane 1/1 Running 0 11m
kube-system pod/kindnet-z4wx9 1/1 Running 0 11m
kube-system pod/kube-apiserver-myk8s-control-plane 1/1 Running 0 11m
kube-system pod/kube-controller-manager-myk8s-control-plane 1/1 Running 0 11m
kube-system pod/kube-proxy-2s2m7 1/1 Running 0 11m
kube-system pod/kube-scheduler-myk8s-control-plane 1/1 Running 0 11m
local-path-storage pod/local-path-provisioner-bd4bb6b75-bglr2 1/1 Running 0 11m
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11m
kube-system service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 11m
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
kube-system daemonset.apps/kindnet 1 1 1 1 1 <none> 11m
kube-system daemonset.apps/kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 11m
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
kube-system deployment.apps/coredns 2/2 2 2 11m
local-path-storage deployment.apps/local-path-provisioner 1/1 1 1 11m
NAMESPACE NAME DESIRED CURRENT READY AGE
kube-system replicaset.apps/coredns-66bff467f8 2 2 2 11m
local-path-storage replicaset.apps/local-path-provisioner-bd4bb6b75 1 1 1 11m
也可以透過以下命令查詢 Client 與 Server 版本:
$ kubectl version --short
Client Version: v1.16.6-beta.0
Server Version: v1.18.2
當預設 Client 與 Server 版本不同時,建議你安裝跟 Server 相同版本的 Client 工具 (kubectl
)
curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.18.2/bin/linux/amd64/kubectl
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl
請注意上述網址中有個 v1.18.2
版本號碼,請換成跟你目前的 Server 版本一致。
再檢查一次 kubectl
版本:
$ kubectl version --short
Client Version: v1.18.2
Server Version: v1.18.2
參考連結: Install and Set Up kubectl
除此之外,也建議設定 kubectl 的 bash 自動完成設定:
kubectl completion bash | sudo tee /etc/bash_completion.d/kubectl > /dev/null
echo 'alias k=kubectl' >>~/.profile
echo 'complete -F __start_kubectl k' >>~/.profile
-
刪除 K8S 叢集
kind delete cluster --name myk8s
刪除過程大概只要 1 秒左右即可完成!
建立多節點的 k8s 叢集
如果你想建立一個有 3 個 control-plane nodes 與 2 個 worker nodes 的叢集,那就可以先建立好一個設定檔:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: control-plane
- role: control-plane
- role: worker
- role: worker
你也可以用以下命令快速產生 kind-5nodes.yaml
檔案:
# Create a config file for a 3 nodes cluster
cat << EOF > kind-5nodes.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: control-plane
- role: control-plane
- role: worker
- role: worker
EOF
然後透過以下命令載入設定檔並建立叢集,過程大約 2 ~ 3 分鐘即可完成,相當便利!
kind create cluster --name myk8s --config ./kind-5nodes.yaml
剛裝好之後,還需要等十幾秒左右的時候,讓每個 Node 的 STATUS 進入 Ready 狀態!
$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k8s-control-plane Ready master 2m3s v1.18.2 172.18.0.3 <none> Ubuntu 19.10 4.19.104-microsoft-standard containerd://1.3.3-14-g449e9269
k8s-control-plane2 Ready master 96s v1.18.2 172.18.0.6 <none> Ubuntu 19.10 4.19.104-microsoft-standard containerd://1.3.3-14-g449e9269
k8s-control-plane3 Ready master 51s v1.18.2 172.18.0.2 <none> Ubuntu 19.10 4.19.104-microsoft-standard containerd://1.3.3-14-g449e9269
k8s-worker Ready <none> 32s v1.18.2 172.18.0.5 <none> Ubuntu 19.10 4.19.104-microsoft-standard containerd://1.3.3-14-g449e9269
k8s-worker2 Ready <none> 32s v1.18.2 172.18.0.4 <none> Ubuntu 19.10 4.19.104-microsoft-standard containerd://1.3.3-14-g449e9269
建立多節點的 k8s 叢集並設定對應 PORTS 到 Host 主機
預設透過 kind 建立的多節點叢集環境會有點小問題,那就是只因為 kind 是將每個「節點」跑在一個「容器」中,而預設執行容器時,只有將每個容器的 api-server
服務對外,你可以從以下命令看出 PORTS
的設定狀況,預設 control-plane
節點有對外提供連線,但 worker
則是都沒有辦法直接從本機連線過去!
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
db64eb84d956 kindest/haproxy:2.1.1-alpine "/docker-entrypoint.…" 23 minutes ago Up 23 minutes 127.0.0.1:37527->6443/tcp k8s-external-load-balancer
30259d7adfaf kindest/node:v1.18.2 "/usr/local/bin/entr…" 23 minutes ago Up 23 minutes k8s-worker2
492ef5c6a816 kindest/node:v1.18.2 "/usr/local/bin/entr…" 23 minutes ago Up 23 minutes k8s-worker
68e329d37aca kindest/node:v1.18.2 "/usr/local/bin/entr…" 23 minutes ago Up 23 minutes 127.0.0.1:43579->6443/tcp k8s-control-plane3
f60e32459b9a kindest/node:v1.18.2 "/usr/local/bin/entr…" 23 minutes ago Up 23 minutes 127.0.0.1:35647->6443/tcp k8s-control-plane2
28793e631168 kindest/node:v1.18.2 "/usr/local/bin/entr…" 23 minutes ago Up 23 minutes 127.0.0.1:39199->6443/tcp k8s-control-plane
另一方面,在建立 Service 物件的時候,也沒辦法使用 type: LoadBalancer
取得服務對外的 IP,只能用 type: NodePort
才能對外提供連線。
我們也知道 Kubernetes 的 Service 物件當設定為 type: LoadBalancer
時,指定的 Port 埠號只能介於 30000
~ 32767
而已,且每個 Node 的這個 Ports 範圍都可以接聽要求。所以我們現在可以先用以下命令快速產生 kind-5nodes-mapping-ports.yaml
檔案,隨便指定其中一個 Node 的 extraPortMappings
額外設定,並將這個 Node 容器的對外 IP 30080
對應到本機的 80
Port,如此一來你從 Windows 10 本機電腦就可以透過 http://localhost 連線到某個節點的 Port 30080,然後就可以直接存取叢集中的服務了!
注意:kind 的每個節點都是跑在 container 下!
# Create a config file for a 3 nodes cluster
cat << EOF > kind-5nodes-mapping-ports.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: control-plane
- role: control-plane
- role: worker
- role: worker
extraPortMappings:
- containerPort: 30080
hostPort: 80
- containerPort: 30443
hostPort: 443
EOF
然後透過以下命令載入設定檔並建立叢集:
kind create cluster --name myk8s --config ./kind-5nodes-mapping-ports.yaml
建立完成後,我們可以測試一下網路連線是否會通!
-
建立 Pod 與 Service 物件所需的 YAML 檔 (mynginx.yaml
)
cat <<EOF > mynginx.yaml
apiVersion: v1
kind: Service
metadata:
name: web-nginx
labels:
app.kubernetes.io/name: nginx
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: http
nodePort: 30080
selector:
app.kubernetes.io/name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-nginx
labels:
app.kubernetes.io/name: nginx
spec:
selector:
matchLabels:
app.kubernetes.io/name: nginx
replicas: 2
template:
metadata:
labels:
app.kubernetes.io/name: nginx
spec:
containers:
- name: nginx
image: docker.io/bitnami/nginx:1.19.1-debian-10-r23
ports:
- name: http
containerPort: 8080
EOF
-
套用 YAML 設定建立 Deployment 與 Service 物件
kubectl apply -f mynginx.yaml
$ kubectl get all -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/web-nginx-57ff6845c-2m28n 1/1 Running 0 5m32s 10.244.4.4 myk8s-worker2 <none> <none>
pod/web-nginx-57ff6845c-gds75 1/1 Running 0 5m32s 10.244.3.5 myk8s-worker <none> <none>
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 24m <none>
service/web-nginx NodePort 10.100.138.97 <none> 80:30080/TCP 5m32s app.kubernetes.io/name=nginx
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
deployment.apps/web-nginx 2/2 2 2 5m32s nginx docker.io/bitnami/nginx:1.19.1-debian-10-r23 app.kubernetes.io/name=nginx
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
replicaset.apps/web-nginx-57ff6845c 2 2 2 5m32s nginx docker.io/bitnami/nginx:1.19.1-debian-10-r23 app.kubernetes.io/name=nginx,pod-temp
-
開啟瀏覽器並打開 http://localhost 網頁
如果這時你看見 Nginx 預設首頁畫面,就代表網路設定一切正常!
-
刪除 YAML 中定義的所有物件
kubectl delete -f mynginx.yaml
使用 Kubernetes Dashboard 管理主控台
-
安裝 Kubernetes Dashboard
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/$(curl -s https://api.github.com/repos/kubernetes/dashboard/r
eleases/latest | jq -r '.name')/aio/deploy/recommended.yaml
kubectl get all -n kubernetes-dashboard
-
設定 Dashboard 權限
建立 k8s 服務帳戶 (ServiceAccount
)
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
EOF
替上述 ServiceAccount 建立 ClusterRoleBinding 權限
kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard
EOF
-
取得登入 Token
kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk '{print $1}') --template={{.data.token}} | base64 -d
由於 WSL 2 跟 Windows 10 無縫整合,你可以透過以下命令,直接將 Token 儲存到 Windows 剪貼簿中:
kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk '{print $1}') --template={{.data.token}} | base64 -d | clip.exe
-
啟動 Proxy 連接介面
kubectl proxy
-
連接到 Kubernetes Dashboard 登入頁面並輸入 Token
http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
由於 Kubernetes Dashboard 支援多國語系顯示,他會自動判斷你的瀏覽器可接受語言來進行自動顯示,而且沒有可以切換語言的地方。如果想以「英文」為主要語系,建議安裝 Advanced Page Language Switcher 擴充套件 (Google Chrome),並切換到 English (en
) 語系。設定完成後,再回到 Kubernetes Dashboard 按下 Ctrl+F5
重新整理,如果還是看到「中文」介面,請清空瀏覽器快取後再試一次即可!
使用 Istio 作為服務對外的閘道
-
下載 Istio
curl -L https://istio.io/downloadIstio | sh -
cd istio-1.*
export PATH=$PWD/bin:$PATH
-
安裝 Istio
測試環境可以用 demo
這個 Profile 來進行安裝,但正式環境不建議使用 demo
Profile 喔!
istioctl install --set profile=demo
Installation Configuration Profiles
-
設定 default
這個 namespace 啟用自動注入 Sidecar 的 Label
kubectl label namespace default istio-injection=enabled
列出目前所有 namespace 的 istio-injection
狀態
kubectl get namespace -L istio-injection
-
部署範例應用程式
kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
kubectl get services
kubectl get pods
kubectl exec -it $(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}') -c ratings -- curl productpage:9080/productpage | grep -o "<title>.*</title>"
-
開放應用程式給外部網路連線
kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml
驗證 Istio 設定是否「正確」(因為太常設定錯誤了,特別寫了一個工具專門用來檢查)
istioctl analyze
-
決定 ingress 的 IP 與 Ports
先取得 istio-system
負責對外的 istio-ingressgateway
服務資訊
kubectl get svc istio-ingressgateway -n istio-system
由於 KinD 內的服務沒辦法透過 type: LoadBalancer
取得 IP 地址,因此必須手動改成 type: NodePort
才行!
請修改 spec.type
為 NodePort
,並調整 .spec.ports[*].nodePort
的 Port 埠號。
name: http2
的 nodePort
調整為 30080
name: https
的 nodePort
調整為 30443
Service | Kubernetes > Type NodePort
-
在 Windows 10 開啟瀏覽器,透過 Istio Gateway 瀏覽網頁!
當你連到 http://localhost/ 將會看到 HTTP 404 錯誤頁面。
當你連到 http://localhost/productpage 就會看到 BookInfo Sample 的首頁!
-
開啟 Kiali dashboard 查看 Istio 的所有流量資訊
透過以下命令可以啟動一個 Proxy 端點,讓你可以透過本機進行瀏覽
請注意:每次執行 istioctl dashboard kiali
都會產生不同的 Ports
$ istioctl dashboard kiali
http://localhost:41515/kiali
Failed to open browser; open http://localhost:41515/kiali in your browser.
由於我們使用 demo
Profile 安裝 Istio,所以預設已經啟用 Kiali 擴充套件,預設的登入帳號密碼為 admin
/ admin
參考下圖進行報表顯示設定,對網站進行壓力測試後,就會看見流量的即時動畫!
其他 kind 設定
-
啟用 Feature Gates 功能
Kubernetes 每當推出新版功能時,都會事先加上 Feature Gates 選項,讓你在安裝叢集的時候可以依據想啟用的功能進行開啟,這部分也可以直接透過 kind 的設定檔進行設定,設定方式如下:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
FeatureGateName: true
完整的 FeatureGateName
可以參考 Feature Gates 文件說明。
-
匯出叢集所有節點的完整紀錄 (Export Cluster Logs)
kind export logs --name myk8s
預設會輸出到 /tmp/XXXXXX
暫存目錄下!
.
├── docker-info.txt
└── kind-control-plane/
├── containers
├── docker.log
├── inspect.json
├── journal.log
├── kubelet.log
├── kubernetes-version.txt
└── pods/
相關連結