Rancher with K8S cluster 之旅 (3) - Offline installation of Rancher and K8S cluster

Posted by Jimmy's Blog on February 18, 2021

前言

前面介紹了如何在有網路的環境底下安裝 Rancher 以及佈署一個 K8S cluster。接下來,這篇文章將會針對如何離線安裝來做說明。其中最需要大家注意的就是 Private registry 的部分。另外,上一篇有些安裝步驟都是需要使用者進到網頁去設置一些東西,包含創建 K8S cluster 的動作。因此,今天這篇也會講述我如何使用 Rancher 以及 K8S 原生的 API 去做設置,就完全不需要登入 UI 去操作了。可以操作 API 的話,自動化安裝當然就會水到渠成,所以自動化安裝即將是下一篇的重點之一,有利於整進 CI/CD 的環境當中。最終的架構會是一個 shell script 串起整個安裝的核心以及操作的工具。好了! 那就開始吧!

準備離線資源

安裝必要套件

既然是屬於 On-premise 的環境要去佈署,代表我們必須要把必要的 packages 打包好才能夠安裝。主機上有些必要的套件需要安裝:

  • Master node: dockercurljqkubectl

  • Worker node: docker

這邊由於大家所使用的 OS 環境都不全然相同,這邊就不列出以上套件的安裝包了,大家可以自己去抓。像是如果是使用 Centos 的話,你就可以抓取 rpm 包來進行安裝。 kubectl 的安裝方式在 K8S 的官網也都有詳細的說明囉! 安裝也十分簡單 :

1
2
3
curl -LO https://dl.k8s.io/release/v1.20.0/bin/linux/amd64/kubectl
chmod +x kubectl
cp kubectl /usr/local/bin/

安裝完之後,可以查看一下 version

1
kubectl version

就會看到 Client 的 version。Server 的部分無法顯示沒關西,要等到架好 K8S cluster,進行完 config 就可以了。

準備必要 Image

這邊要提及的部分是準備 Docker 的 Images。這些 Images 是安裝 Rancher 以及 K8S 所必備的元件。Rancher 的官方已經很貼心地把必要的 Images 都 list 出來了,還給你了一些 shell 方便你打包,真是棒棒Der ! 連結在這裡,如附圖:

點擊最下面的 Assets 就可以看到囉! 要是你不想用 2.5.5 的版本,一樣的道理就挑選其他 tag 就可以了。

官方文件有教你如何使用上面那些來 Download 並打包這些東西! 找一個有網路的環境也有 docker 的機器 (對公司來說就是 build machine),執行:

1
./rancher-save-images.sh --image-list ./rancher-images.txt

就這麼簡單,在同個 folder 會產生出 rancher-images.tar.gz,這裡面包含了全部 K8S 所需要的套件映像檔。還沒結束,有了這一包之後,我們需要創建一個自己的 registry 來儲存這個包好的 Images。還不熟悉 docker registry的大家可以先去 Docker 官網閱讀一下唷! 前面有說到我們是要準備離線的資源,所以 registry 它自己本身的 Image 也要先打包喔! 我這邊使用的是 registry:2.7 的版本。

1
2
docker pull registry:2.7
docker save -o registry.tar registry:2.7

至此,我們所有需要的離線資源都準備好了,可以進行下一階段。

開始安裝

Deploy registry

首先,我們要佈署好我們的 private registry,先做一下 docker 的 config。將我們等等要使用的 insecure-registries 加進 /etc/docker/daemon.json。假設我們等等要使用的是本機端的 5000 port:

1
2
jq '."insecure-registries" += ["localhost:5000"]' /etc/docker/daemon.json > "tmp" && mv "tmp" daemon.json
cp -f daemon.json /etc/docker/

config 好之後,Reload docker 就可以囉。

1
2
systemctl daemon-reload
systemctl restart docker

那我們先把 registry 帶起來:

1
2
docker load -i registry.tar
docker run -d -p 5000:5000 --restart=always --name registry registry:2.7

完成之後,接著就是要把我們打包好的 Rancher Images 全部 push 到 registry 當中,一樣利用 Rancher 官網建議的方式:

1
./rancher-load-images.sh --image-list ./rancher-images.txt --images ./rancher-images.tar.gz  --registry localhost:5000

這邊可能會花一點時間,因為它會將所有的 Image 先 load 出來之後再去 re-tag 每個 Image,最後 push 至 registry 裡面。完成之後,我們多做一個動作把 load 出來的 Image 砍掉,確保等一下他都是從 registry 裡面拉出來,否則你 local 有了它就不會去找 registry 裡面的了。

1
docker rmi -f $(docker images -q)

查看一下,應該只會看到 registry 他自己本身的 image,不會存在其他的。

1
docker images

Deploy Rancher Server

佈署完 registry 之後,就進入重頭戲囉。跟前一篇文章提到的作法差不多,我們先安裝 Rancher 的 Server,唯一不一樣就是它需要從 registry 裡面拉出來:

1
docker run -d --restart=unless-stopped  --privileged  -p 8080:80 -p 8443:443 localhost:5000/rancher/rancher:v2.5.5

等待他跑起來之後,接下來一連串就是去使用 Rancher 的 API 去做事情囉。

1
2
3
4
LOGINRESPONSE=`curl -s 'https://localhost:8443/v3-public/localProviders/local?action=login' -H 'content-type: application/json' --data-binary '{"username":"admin","password":"admin"}' --insecure`
LOGINTOKEN=`echo $LOGINRESPONSE | jq -r .token`
# Change password
curl -s 'https://localhost:8443/v3/users?action=changepassword' -H 'content-type: application/json' -H "Authorization: Bearer $LOGINTOKEN" --data-binary '{"currentPassword":"admin","newPassword":"'$rancher_admin_password'"}' --insecure

做到這邊,就是前一篇文章設置完密碼的階段。$rancher_admin_password 記得要替換成你要的密碼字串喔! 接著我們往下做:

1
2
3
4
5
6
# Create API key
APIRESPONSE=`curl -s 'https://localhost:8443'/v3/token' -H 'content-type: application/json' -H "Authorization: Bearer $LOGINTOKEN" --data-binary '{"type":"token","description":"Just for testing"}' --insecure`
# Extract and store token
APITOKEN=`echo $APIRESPONSE | jq -r .token`
# Set server-url
curl -s 'https://localhost:8443/v3/settings/server-url' -H 'content-type: application/json' -H "Authorization: Bearer $APITOKEN" -X PUT --data-binary '{"name":"server-url","value":"https://localhost:8443"}' --insecure > /dev/null

做到這邊,就是設置完 Server URL 的階段。這時候你可以登進 UI 查看一下,至此,Rancher server 的部分就完成囉。讀者可以跟前一篇文章交叉比對目前的步驟。

Deploy a K8S cluster

接下來,我們就要來創建我們 K8S 的 cluster 囉! 這邊我附上一個 json 範例,用於創建 cluster 的時候的參數設定。

cluster-sample.json

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
{
    "dockerRootDir": "/var/lib/docker",
    "enableClusterAlerting": false,
    "enableClusterMonitoring": false,
    "enableNetworkPolicy": false,
    "windowsPreferedCluster": false,
    "type": "cluster",
    "name": "k8s-demo",
    "rancherKubernetesEngineConfig": {
      "addonJobTimeout": 45,
      "ignoreDockerVersion": true,
      "rotateEncryptionKey": false,
      "sshAgentAuth": false,
      "type": "rancherKubernetesEngineConfig",
      "kubernetesVersion": "v1.19.6-rancher1-1",
      "authentication": {
        "strategy": "x509",
        "type": "authnConfig"
      },
      "dns": {
        "type": "dnsConfig",
        "nodelocal": {
          "type": "nodelocal",
          "ip_address": "",
          "node_selector": null,
          "update_strategy": {}
        }
      },
      "network": {
        "mtu": 0,
        "plugin": "flannel",
        "type": "networkConfig",
        "options": {
          "flannel_backend_type": "vxlan"
        }
      },
      "ingress": {
        "defaultBackend": false,
        "httpPort": 0,
        "httpsPort": 0,
        "provider": "nginx",
        "type": "ingressConfig"
      },
      "monitoring": {
        "provider": "metrics-server",
        "replicas": 1,
        "type": "monitoringConfig"
      },
      "services": {
        "type": "rkeConfigServices",
        "kubeApi": {
          "alwaysPullImages": false,
          "podSecurityPolicy": false,
          "serviceNodePortRange": "30000-32767",
          "type": "kubeAPIService",
          "secretsEncryptionConfig": {
            "enabled": false,
            "type": "secretsEncryptionConfig"
          }
        },
        "etcd": {
          "creation": "12h",
          "extraArgs": {
            "heartbeat-interval": 500,
            "election-timeout": 5000
          },
          "gid": 0,
          "retention": "72h",
          "snapshot": false,
          "uid": 0,
          "type": "etcdService",
          "backupConfig": {
            "enabled": true,
            "intervalHours": 12,
            "retention": 6,
            "safeTimestamp": false,
            "type": "backupConfig"
          }
        }
      },
      "upgradeStrategy": {
        "maxUnavailableControlplane": "1",
        "maxUnavailableWorker": "10%",
        "drain": "false",
        "nodeDrainInput": {
          "deleteLocalData": false,
          "force": false,
          "gracePeriod": -1,
          "ignoreDaemonSets": true,
          "timeout": 120,
          "type": "nodeDrainInput"
        },
        "maxUnavailableUnit": "percentage"
      },
      "privateRegistries": [
        {
          "isDefault": true,
          "type": "privateRegistry",
          "url": "localhost:5000"
        }
      ]
    },
    "localClusterAuthEndpoint": {
      "enabled": true,
      "type": "localClusterAuthEndpoint"
    },
    "labels": {},
    "scheduledClusterScan": {
      "enabled": false,
      "scheduleConfig": null,
      "scanConfig": null
    }
  }

這個 json 就是我們前篇文章在創建 cluster 的時候,在 UI 選擇很多有的沒的設定,這邊都寫在這個 json 檔裡面 (注意: Rancher 的版本日新月異,這個 json 範例是筆者自己產生出來的,符合 Rancher v2.5.5 的版本,若是其他版本不一定適用) 。 記得要改一些參數設定,像是 cluster 名稱、K8S version 以及你想使用的 CNI plugin。還有最重要的 registry 的設定: rancherKubernetesEngineConfig.privateRegistries 這個欄位,設置成你剛剛佈署好的 registry,以我剛剛上面的例子就是 localhost:5000。

再來就是利用這個 json 檔來創建 cluster,底下 command 有些參數(e.g. $APITOKEN)在上面已經有了 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Create cluster
CLUSTERRESPONSE=`curl -s 'https://localhost:8443/v3/cluster' -H 'content-type: application/json' -H "Authorization: Bearer $APITOKEN" --data @cluster.json --insecure`
# Extract clusterid to use for generating the docker run command
CLUSTERID=`echo $CLUSTERRESPONSE | jq -r .id`
# Create token
curl -s 'https://localhost:8443/v3/clusterregistrationtoken' -H 'content-type: application/json' -H "Authorization: Bearer $APITOKEN" --data-binary '{"type":"clusterRegistrationToken","clusterId":"'$CLUSTERID'"}' --insecure > /dev/null
# Set role flags
ROLEFLAGS="--etcd --controlplane --worker"
# Generate nodecommand
AGENTCMD=`curl -s 'https://localhost:8443/v3/clusterregistrationtoken?id="'$CLUSTERID'"' -H 'content-type: application/json' -H "Authorization: Bearer $APITOKEN" --insecure | jq -r '.data[].nodeCommand' | head -1`
# Concat commands
DOCKERRUNCMD="$AGENTCMD $ROLEFLAGS"
# Echo command
echo $DOCKERRUNCMD

把最後的 $DOCKERRUNCMD copy 到 host 上面執行就可以囉! 我這邊範例是包含etcd、controlplane 以及 worker。等待一下子,登進 UI 查看就可以看到你剛剛創建的 cluster 了喔! (想看圖可以參考前一篇的文章)。

Config for kubectl

安裝完 K8S cluster 之後,為了可以使用 kubectl 在 host 上面,要進行一些 config。首先我們要取得目前這個 cluster 的 config,再放到 ~/.kube/config 就可以囉!

1
2
mkdir -p ~/.kube
curl -s -k -X POST -H 'Accept: application/json' -H 'Content-Type: application/json' -H 'Authorization:Bearer '$APITOKEN'' 'https://localhost:8443/v3/clusters/'$CLUSTERID'?action=generateKubeconfig' | jq -r .config > ~/.kube/config

筆者這邊 command 都是已經切換到 root 的帳號才執行唷! 若讀者不是的話要注意有沒有權限問題。或是 kubectl 可能因為帳號不同所以 ~/.kube/config 沒有吃到要注意。最後我們可以檢查:

1
2
3
4
kubectl get nodes

NAME     STATUS   ROLES                      AGE    VERSION
master   Ready    controlplane,etcd,worker   126m   v1.19.6

有看到節點資訊了! OKAY! kubectl 的 command 也都能夠使用囉!

總結

不知道讀者到這邊有沒有充分理解整個步驟呢 ? 我相信一定沒有吧 XD,對任何步驟有問題都可以再寄信問我,非常歡迎。下一篇文章的重點就會是我把上面這些繁瑣的動作都包起來,形成一個 shell script。只要一行 command 加上一些參數就可以一鍵安裝到好,並會附帶一些功能讓使用者方便使用,像是一鍵安裝 registry,取得添加 worker node 的 command 等等的功能。另外,我也會分享很多我跌進去又爬出來的坑 XD。洋洋灑灑的 Rancher 之旅,在下一篇文章應該就會結束囉!

Contact me

有任何疑問或是建議歡迎聯絡 Jimmy!

Gmail: jimmyw86878@gmail.com

Reference

https://gist.github.com/superseb/c363247c879e96c982495daea1125276

https://rancher.com/docs/rancher/v2.x/en/installation/other-installation-methods/air-gap/populate-private-registry/