在 Home Lab 建置 OpenShift 4.18

這篇是我在 HomelabRHEL 10.1 + KVM 切虛擬機,從零開始走完一遍 OpenShift 4.18 UPI(User Provisioned Infrastructure)c安裝的紀錄。UPI 相對於 IPI 的差別在於所有底層基礎設施:DNSLoad BalancerHTTP Web Server節點 都要自己準備,在雲端可以透過 Cloud Provider 自動搞定,安裝不難,但是需要耐心和細心。

首先先在宿主機安裝 RHEL10.1,然後把 cockpit裝起來,就可以得到一個蠻精美的 RHEL 管理介面。

再來把虛擬化的相關套件裝起來,就可以用 virt 新增和管理虛擬機。

1
2
3
4
5
# 安裝虛擬化主機相關套件
sudo dnf module install virt

# 或者安裝特定工具
sudo dnf install qemu-kvm libvirt virt-install

叢集設計規劃

整座叢集域名為 ocp4.hazel.com,Bastion 機(rhel9)身兼 DNSHAProxyHTTP Server,所有對外 URL 都打進 Bastion 機上面的 HAProxy 再轉發。

節點 IP 角色 資源配置
rhel9(Bastion) 192.168.0.33 DNS / HAProxy / HTTP Server 2 vCPU / 3 GB Memory (超極限壓榨)
bootstrap 192.168.0.100 臨時安裝節點(安裝完銷毀) 4 vCPU / 8 GB Memory
master0~2 192.168.0.101~103 Control Plane(etcd、API Server) 4 vCPU / 16 GB Memory (後來我調整成 24 GB Memory )
worker0~1 192.168.0.104~105 Worker(Ingress、App Pods) 4 vCPU / 16 GB Memory

常用組件存取 URL:

用途 URL
API https://api.ocp4.hazel.com:6443
Web Console https://console-openshift-console.apps.ocp4.hazel.com
OAuth https://oauth-openshift.apps.ocp4.hazel.com

階段一:基礎設施準備

切割磁碟、DNS、Load Balancer (HA Proxy)、HTTP Server 都會需要先準備好

Bastion 機的建立過程可以參考 這篇。本篇不就 KVM 的操作多加說明。

磁碟分割

  1. 列出宿主機上面可以用的磁碟,那顆 2TB 的磁碟就是給 OpenShift 和其他應用程式用的資料碟
1
2
3
4
5
6
7
8
9
10
hazel@hazel-host:/var/lib/libvirt/images$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nvme0n1 259:0 0 1.9T 0 disk
nvme1n1 259:1 0 476.9G 0 disk
├─nvme1n1p1 259:2 0 600M 0 part /boot/efi
├─nvme1n1p2 259:3 0 1G 0 part /boot
└─nvme1n1p3 259:4 0 475.4G 0 part
├─rhel-root 253:0 0 70G 0 lvm /
├─rhel-swap 253:1 0 32G 0 lvm [SWAP]
└─rhel-home 253:2 0 373.4G 0 lvm /home
  1. 執行以下步驟,劃分出給虛擬機用的磁碟空間
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 建立 GPT 分割表並切出 1TB 的空間 (nvme0n1p1)
sudo parted -s /dev/nvme0n1 mklabel gpt
sudo parted -s /dev/nvme0n1 mkpart primary xfs 0% 1TB

# 2. 【只對這個 1TB 的分割區】進行格式化
sudo mkfs.xfs /dev/nvme0n1p1

# 3. 建立目錄並掛載這個分割區
sudo mkdir -p /mnt/ocp-data
sudo mount /dev/nvme0n1p1 /mnt/ocp-data

# 4. 寫入 fstab 與設定 KVM 權限
echo '/dev/nvme0n1p1 /mnt/ocp-data xfs defaults 0 0' | sudo tee -a /etc/fstab
sudo chown qemu:qemu /mnt/ocp-data
sudo chmod 750 /mnt/ocp-data
  1. 用 lsblk 確認有看到 nvme0n1p1 掛載在 /mnt/ocp-data 上
1
2
3
4
5
6
7
8
9
10
11
hazel@hazel-host:/var/lib/libvirt/images$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nvme0n1 259:0 0 1.9T 0 disk
└─nvme0n1p1 259:5 0 931.3G 0 part /mnt/ocp-data
nvme1n1 259:1 0 476.9G 0 disk
├─nvme1n1p1 259:2 0 600M 0 part /boot/efi
├─nvme1n1p2 259:3 0 1G 0 part /boot
└─nvme1n1p3 259:4 0 475.4G 0 part
├─rhel-root 253:0 0 70G 0 lvm /
├─rhel-swap 253:1 0 32G 0 lvm [SWAP]
└─rhel-home 253:2 0 373.4G 0 lvm /home

DNS:安裝 BIND

⚠️ 這個階段會需要細心配置,DNS 沒解析好後面安裝過程有可能出錯。

1
sudo dnf install bind bind-utils -y

修改 /etc/named.confoptions 區塊:

1
2
3
listen-on port 53 { any; };
allow-query { any; };
recursion yes;

在設定檔最下方加入 zone 定義:

1
2
3
4
5
zone "ocp4.hazel.com" IN {
type master;
file "ocp4.hazel.com.zone";
allow-update { none; };
};

新增 Zone File /var/named/ocp4.hazel.com.zone

⚠️ 每次修改後 Serial 必須 +1,不然 DNS 不會更新。

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
$TTL 1W
@ IN SOA ns1.ocp4.hazel.com. root.ocp4.hazel.com. (
2026021901 ; Serial(格式 YYYYMMDDxx)
1D ; Refresh
1H ; Retry
1W ; Expire
1D ) ; Minimum TTL

@ IN NS ns1.ocp4.hazel.com.
ns1 IN A 192.168.0.33

; 核心端點(全指向 Bastion / HAProxy)
api IN A 192.168.0.33
api-int IN A 192.168.0.33
*.apps IN A 192.168.0.33

; 節點
bootstrap IN A 192.168.0.100
master0 IN A 192.168.0.101
master1 IN A 192.168.0.102
master2 IN A 192.168.0.103
worker0 IN A 192.168.0.104
worker1 IN A 192.168.0.105

; etcd SRV(Master 節點叢集建立用)
_etcd-server-ssl._tcp IN SRV 0 10 2380 master0.ocp4.hazel.com.
_etcd-server-ssl._tcp IN SRV 0 10 2380 master1.ocp4.hazel.com.
_etcd-server-ssl._tcp IN SRV 0 10 2380 master2.ocp4.hazel.com.

啟動並讓 Bastion 優先查自己的 DNS:

1
2
3
4
5
sudo systemctl enable --now named
sudo sed -i '1i nameserver 127.0.0.1' /etc/resolv.conf

# 測試(應回傳 192.168.0.33)
dig +short api.ocp4.hazel.com @192.168.0.33

HAProxy:Load Balancer

⚠️ SELinux 預設會擋住 HAProxy 綁定 6443、22623 這類非標準 Port,沒開這個boolean 值 HAProxy 啟動可能導致失敗。

1
2
sudo dnf install haproxy -y
sudo setsebool -P haproxy_connect_any 1

清空 /etc/haproxy/haproxy.cfg 後貼入以下設定:

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
global
log 127.0.0.1 local2
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon
stats socket /var/lib/haproxy/stats

defaults
mode tcp
log global
option tcplog
option dontlognull
retries 3
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout check 10s
maxconn 3000

# 監控儀表板(Port 9000)
listen stats
bind :9000
mode http
stats enable
stats uri /

# API Server
frontend ocp_api
bind :6443
default_backend ocp_api_backend

backend ocp_api_backend
balance source
option tcp-check
# ⚠️ 安裝完成後將 bootstrap 這行註解掉
server bootstrap 192.168.0.100:6443 check
server master0 192.168.0.101:6443 check
server master1 192.168.0.102:6443 check
server master2 192.168.0.103:6443 check

# Machine Config Server(節點開機抓 Ignition 用)
frontend ocp_mcs
bind :22623
default_backend ocp_mcs_backend

backend ocp_mcs_backend
balance source
option tcp-check
# ⚠️ 安裝完成後將 bootstrap 這行註解掉
server bootstrap 192.168.0.100:22623 check
server master0 192.168.0.101:22623 check
server master1 192.168.0.102:22623 check
server master2 192.168.0.103:22623 check

# Ingress HTTP
frontend ocp_http
bind :80
default_backend ocp_http_backend

backend ocp_http_backend
balance source
option tcp-check
server worker0 192.168.0.104:80 check
server worker1 192.168.0.105:80 check

# Ingress HTTPS
frontend ocp_https
bind :443
default_backend ocp_https_backend

backend ocp_https_backend
balance source
option tcp-check
server worker0 192.168.0.104:443 check
server worker1 192.168.0.105:443 check

開防火牆並啟動:

1
2
3
4
5
6
7
8
sudo firewall-cmd --add-port=6443/tcp --permanent
sudo firewall-cmd --add-port=22623/tcp --permanent
sudo firewall-cmd --add-port=80/tcp --permanent
sudo firewall-cmd --add-port=443/tcp --permanent
sudo firewall-cmd --add-port=9000/tcp --permanent
sudo firewall-cmd --reload

sudo systemctl enable --now haproxy

HTTP Server:放置 Ignition 檔案

Bootstrap、Master、Worker 開機時會透過 HTTP 拉 .ign 檔,這台 Server 就是用來放那些檔案的。Port 80 已被 HAProxy 佔用,改用 8080。

1
2
3
4
5
6
7
sudo dnf install httpd -y
sudo sed -i 's/^Listen 80/Listen 8080/' /etc/httpd/conf/httpd.conf

sudo firewall-cmd --add-port=8080/tcp --permanent
sudo firewall-cmd --reload

sudo systemctl enable --now httpd

安裝 OpenShift 4.18 工具

1
2
3
4
5
6
7
8
9
10
11
12
export OCP_VERSION="stable-4.18"

wget https://mirror.openshift.com/pub/openshift-v4/clients/ocp/$OCP_VERSION/openshift-install-linux.tar.gz
wget https://mirror.openshift.com/pub/openshift-v4/clients/ocp/$OCP_VERSION/openshift-client-linux.tar.gz

tar -zxvf openshift-install-linux.tar.gz openshift-install
tar -zxvf openshift-client-linux.tar.gz oc kubectl

sudo mv openshift-install oc kubectl /usr/local/bin/
rm -f openshift-install-linux.tar.gz openshift-client-linux.tar.gz

openshift-install version

生成 Ignition 設定檔

1. 準備 SSH 公鑰

1
2
ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519
cat ~/.ssh/id_ed25519.pub

2. 取得 Pull Secret

前往 Red Hat Hybrid Cloud Console 複製 JSON 格式的 Pull Secret。

3. 建立 install-config.yaml

⚠️ Ignition 檔案有效期限只有 24 小時,生成後要盡快完成安裝。

1
mkdir -p ~/ocp-install && cd ~/ocp-install
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: v1
baseDomain: hazel.com
compute:
- hyperthreading: Enabled
name: worker
replicas: 0
controlPlane:
hyperthreading: Enabled
name: master
replicas: 3
metadata:
name: ocp4
networking:
clusterNetwork:
- cidr: 10.128.0.0/14
hostPrefix: 23
networkType: OVNKubernetes
serviceNetwork:
- 172.30.0.0/16
platform:
none: {}
pullSecret: '...貼上完整的 Pull Secret...'
sshKey: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... hazel@rhel9'

4. 備份安裝設定檔案並生成對應節點 ign 檔案

⚠️ openshift-install 執行後會自動刪除 install-config.yaml,沒備份就只能重頭來過。

1
2
cp install-config.yaml install-config.yaml.bak
openshift-install create ignition-configs --dir=./

執行後產出:

檔案 用途
bootstrap.ign Bootstrap 節點開機設定
master.ign Master 節點開機設定
worker.ign Worker 節點開機設定
auth/kubeconfig oc 指令用憑證
auth/kubeadmin-password ⚠️ Web Console 登入密碼,請妥善保管

5. 上傳 ign 檔案至 HTTP Server,並開通防火牆

1
2
sudo cp ~/ocp-install/*.ign /var/www/html/
sudo chmod 644 /var/www/html/*.ign
1
2
3
4
5
6
7
8
9
sudo firewall-cmd --add-service=dns --permanent
sudo firewall-cmd --add-port=53/udp --permanent
sudo firewall-cmd --add-port=53/tcp --permanent
sudo firewall-cmd --add-port=22623/tcp --permanent
sudo firewall-cmd --add-port=6443/tcp --permanent
sudo firewall-cmd --add-port=8080/tcp --permanent
sudo firewall-cmd --add-port=80/tcp --permanent
sudo firewall-cmd --add-port=443/tcp --permanent
sudo firewall-cmd --reload

階段二:節點開機與安裝(OS 層級)

下載 RHCOS Live ISO

1
2
3
wget https://mirror.openshift.com/pub/openshift-v4/dependencies/rhcos/4.18/latest/rhcos-live.x86_64.iso
sudo mv ~/Downloads/rhcos-live.x86_64.iso /var/lib/libvirt/images/
sudo chown qemu:qemu /var/lib/libvirt/images/rhcos-live.x86_64.iso

建立 KVM 虛擬機並安裝 RHCOS

以 Bootstrap 為例:

1
2
3
4
5
6
7
8
9
10
sudo virt-install \
--name bootstrap \
--vcpus 4 \
--memory 16384 \
--disk path=/mnt/ocp-data/bootstrap.qcow2,size=120,format=qcow2,bus=virtio \
--network bridge=br-vm,model=virtio \
--cdrom /var/lib/libvirt/images/rhcos-live.x86_64.iso \
--os-variant rhel9.0 \
--graphics vnc,listen=0.0.0.0 \
--noautoconsole

查出 VNC Port 後用 VNC 連進去,在 Live 環境執行安裝:

1
sudo virsh vncdisplay bootstrap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Bootstrap
sudo coreos-installer install /dev/vda \
--ignition-url http://192.168.0.33:8080/bootstrap.ign \
--insecure-ignition \
--append-karg "ip=192.168.0.100::192.168.0.1:255.255.255.0:bootstrap.ocp4.hazel.com::none nameserver=192.168.0.33"

# master0(其餘 master1/2 同理,改 IP 和 hostname)
sudo coreos-installer install /dev/vda \
--ignition-url http://192.168.0.33:8080/master.ign \
--insecure-ignition \
--append-karg "ip=192.168.0.101::192.168.0.1:255.255.255.0:master0.ocp4.hazel.com::none nameserver=192.168.0.33"

# worker0(worker1 同理)
sudo coreos-installer install /dev/vda \
--ignition-url http://192.168.0.33:8080/worker.ign \
--insecure-ignition \
--append-karg "ip=192.168.0.104::192.168.0.1:255.255.255.0:worker0.ocp4.hazel.com::none nameserver=192.168.0.33"

安裝完後重新開機,記得把開機順序改為從硬碟啟動,不要繼續從 ISO 開。(我當時沒有特別改開機順序,RHCOS 新版好像會自己選擇硬碟啟動)

階段三:Bootstrap 過程(自動化)

這個階段基本上就是Bootstrap 節點會先把自己起成一個暫時的 Control PlaneMaster 節點向它報到並接手。

Bastion 監控進度:

1
openshift-install wait-for bootstrap-complete --dir=~/ocp-install --log-level=info

看到 Bootstrap complete 之後:

  1. 把 HAProxy 設定裡 ocp_api_backendocp_mcs_backend 的 bootstrap 那行註解掉並 reload
  2. 可以關閉或銷毀 Bootstrap 虛擬機
1
sudo systemctl reload haproxy

階段四:收尾與 Worker 節點加入

Worker 節點啟動後會發出 CSR (Certificate Signing Request),預設是 Pending 狀態,要手動 approve

1
2
3
4
5
6
7
8
export KUBECONFIG=~/ocp-install/auth/kubeconfig

# 查看 CSR
oc get csr

# 批准全部 Pending 的 CSR
oc get csr -o go-template='{{range .items}}{{if not .status.certificate}}{{.metadata.name}}{{"\n"}}{{end}}{{end}}' \
| xargs oc adm certificate approve

等待安裝完全結束:

1
openshift-install wait-for install-complete --dir=~/ocp-install

安裝過程可以從瀏覽器看到 haproxy 上列出的節點狀態,順利的話每個節點都會是 UP 狀態:

完成後用 kubeadmin 搭配 auth/kubeadmin-password 的密碼登入 Web Console。

我後來有更新到 4.19,所以版本號不太一樣。

最後收尾

macOS Split DNS(讓 Mac 直接解析實驗室域名)

我想要讓我的 Macbook 可以解析我自訂的域名,但是不想把整台 MacbookDNS 都綁到 bastion 機器上,只要建一個 resolver 設定檔,讓 ocp4.hazel.com 的流量轉到 BastionBIND 就好,一般上網完全不影響:

1
2
sudo mkdir -p /etc/resolver
echo "nameserver 192.168.0.33" | sudo tee /etc/resolver/ocp4.hazel.com

Debug:Router Pod 跑到 Master 節點上

router pod 亂跑的話,haproxy 上面的節點會呈現啟動失敗的畫面。

剛裝完發現 Ingress Router Pod 全跑到 Master 上,原因是 Ingress Operator 預設沒有指定 node selector。不要手動刪 Pod,要改 IngressController CR:

1
2
oc patch ingresscontroller default -n openshift-ingress-operator --type=merge \
-p '{"spec":{"nodePlacement":{"nodeSelector":{"matchLabels":{"node-role.kubernetes.io/worker":""}}}}}'

套用後 Operator 會自動做 Rolling Update,把 Pod 遷移到 Worker 節點,不中斷服務。確認結果:

1
2
oc get pods -n openshift-ingress -o wide
# NODE 欄位應顯示 worker0.ocp4.hazel.com 或 worker1.ocp4.hazel.com