OpenShift 4.18 UPI on Proxmox — 部署操作 MOP

在 Proxmox VE 上以 UPI + platform: none 安装 OpenShift 4.18:自建 Helper(DNS/LB/引导)、PVE Kernel 引导 RHCOS、集群装完后通过 Ceph CSI 对接 PVE 上的外部 Ceph 存储。

架构一览

1
2
3
4
5
6
用户 → DNS(api/*.apps) → Helper 外网 IP → HAProxy
├─ 6443/22623 → Master(安装期含 Bootstrap)
└─ 80/443 → Worker

OCP 节点(192.168.100.x)→ PVE NAT → HTTP 代理 → quay.io 拉镜像
OCP PVC → Ceph CSI → PVE Ceph Monitor :6789 → ocp-pool
节点 生命周期 作用
Helper(RHEL) 永久 BIND、HAProxy、Nginx、安装器、出网代理
Bootstrap 临时 临时 control plane,装完删除
Master ×3 永久 API、etcd
Worker ×N 永久 业务 Pod、Ingress
PVE Ceph 永久 外部 RBD 存储(ocp-pool

OCP 节点只需内网 IP;容器镜像由各节点从 quay.io 拉取,不是 Bootstrap 分发。

参数模板(安装前填写)

变量 示例
CLUSTER_NAME lab-ocp
BASE_DOMAIN example.com
PVE_IP 10.0.0.41
HELPER_EXT 10.0.0.42(外网,双网卡)
HELPER_INT 192.168.100.2
PVE_INT_GW 192.168.100.1
HTTP_PROXY http://proxy.example.com:8080
CEPH_POOL ocp-pool
OCP 版本 4.18.19

域名规则api.<CLUSTER_NAME>.<BASE_DOMAIN>*.apps.<CLUSTER_NAME>.<BASE_DOMAIN> → 解析到 Helper 外网 IP

前置条件:PVE 上 Ceph 已部署,ocp-poolHEALTH_OK(参见 PVE 部署 MOP)。


Step 1 — 下载软件

从 Red Hat 官网获取(版本保持一致):

文件 用途
openshift-install-linux-4.18.19.tar.gz 安装器
openshift-client-linux-4.18.19.tar.gz oc 客户端
rhel-9.x-boot.iso Helper 安装
rhcos-live-kernel-x86_64 RHCOS 内核
rhcos-live-initramfs.x86_64.img RHCOS initrd
rhcos-live-rootfs.x86_64.img RHCOS rootfs

上传到 PVE:/var/lib/vz/template/iso/

验证

1
2
ls -lh /var/lib/vz/template/iso/
# 确认 rhcos 三件套和 rhel ISO 均在

Step 2 — PVE 准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 内网网桥(OCP 节点专用)
cat >> /etc/network/interfaces << 'EOF'
auto vmbr1
iface vmbr1 inet static
address 192.168.100.1/24
bridge_ports none
bridge_stp off
bridge_fd 0
EOF
ifup vmbr1

# NAT 出网(OCP 节点访问代理和 quay.io)
iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -o vmbr0 -j MASQUERADE
echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-ocp-nat.conf
sysctl -p /etc/sysctl.d/99-ocp-nat.conf

# 持久化 NAT(重启不丢)
apt install -y iptables-persistent 2>/dev/null || true

验证

1
2
3
ip addr show vmbr1          # → 192.168.100.1/24
sysctl net.ipv4.ip_forward # → 1
iptables -t nat -L POSTROUTING -n | grep 192.168.100

Step 3 — DNS

在公网/内网 DNS 注册(指向 Helper 外网 IP):

  • api.<CLUSTER_NAME>.<BASE_DOMAIN>
  • *.apps.<CLUSTER_NAME>.<BASE_DOMAIN>(或至少 console、oauth)

验证

1
2
dig +short api.<CLUSTER_NAME>.<BASE_DOMAIN>
# → <HELPER_EXT>

Step 4 — 创建 VM

VMID 角色 vCPU 内存 磁盘 网卡 内网 IP
100 Helper 4 16G 120G vmbr0+vmbr1 .2
110 Bootstrap 8 16G 100G vmbr1 .3
101-103 Master 8 16G 80G vmbr1 .101-.103
104+ Worker 16 32G 160G vmbr1 .104+
  • 存储:vm-storage
  • Helper 挂 RHEL ISO
  • Bootstrap/Master/Worker qm start(空白盘会 iPXE 循环)
  • SCSI 控制器选 VirtIO SCSI single,磁盘 SSD emulation + Discard

验证

1
2
3
4
qm list
# 共 N+2 台 VM,除 Helper 外均为 stopped

qm config 101 | grep -E 'scsi|net|memory|cores'

Step 5 — 安装 Helper(RHEL 9 Minimal)

外网(ens192 等) <HELPER_EXT>,网关、DNS
内网(ens224 等) 192.168.100.2/24无网关
SSH 公钥登录,禁用密码
1
2
3
4
5
# 装完后
tar xzf openshift-install-*.tar.gz -C /usr/local/bin/
tar xzf openshift-client-*.tar.gz -C /usr/local/bin/
mkdir -p /var/www/html/{rhcos,ignition}
# 上传 RHCOS 三件套到 /var/www/html/rhcos/

验证

1
2
3
4
5
openshift-install version
oc version --client
curl -I http://127.0.0.1/rhcos/rhcos-live-kernel-x86_64 # 配置 Nginx 后
ping -c 2 192.168.100.1 # 内网网关可达
curl -x <HTTP_PROXY> -I https://quay.io # 代理出网正常

Step 6 — Helper 服务

1
2
dnf install -y bind bind-utils haproxy nginx squid firewalld jq
systemctl enable --now firewalld

BIND(集群内 DNS)

zone 名:<CLUSTER_NAME>.<BASE_DOMAIN>

记录 类型
api A 192.168.100.2
*.apps A 192.168.100.2
api-int A 192.168.100.2
1
2
named-checkconf && named-checkzone <CLUSTER_NAME>.<BASE_DOMAIN> /var/named/<zone-file>
systemctl enable --now named

HAProxy

安装期 API/MCS 必须含 Bootstrap 192.168.100.3

1
2
3
4
5
6
7
8
frontend api
bind *:6443
default_backend api_back
backend api_back
server bootstrap 192.168.100.3:6443 check
server master101 192.168.100.101:6443 check
server master102 192.168.100.102:6443 check
server master103 192.168.100.103:6443 check

22623(MCS)后端同样包含 bootstrap + master。80/443 指向 worker(未启动时 DOWN 正常)。

1
systemctl enable --now haproxy

Nginx + Squid

  • Nginx 192.168.100.2:8080/var/www/html/rhcos//ignition/
  • Squid 192.168.100.2:3128 → 转发 HTTP_PROXY
  • 可选 Registry Proxy :5000(解决 Go 客户端经企业代理拉镜像问题)

防火墙

1
2
3
4
for p in 53/tcp 53/udp 6443 22623 80 443 8080 3128 5000; do
firewall-cmd --permanent --add-port=${p}/tcp 2>/dev/null
done
firewall-cmd --reload

验证

1
2
3
4
dig api.<CLUSTER_NAME>.<BASE_DOMAIN> @192.168.100.2 +short    # → 192.168.100.2
ss -tlnp | grep -E '6443|8080|3128'
firewall-cmd --list-ports
curl -I http://192.168.100.2:8080/rhcos/rhcos-live-kernel-x86_64 # → 200

将 RHCOS kernel/initrd 复制到 PVE:/var/lib/vz/template/iso/


Step 7 — install-config 与 Ignition

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
apiVersion: v1
baseDomain: <BASE_DOMAIN>
metadata:
name: <CLUSTER_NAME>
platform:
none: {}
controlPlane:
name: master
replicas: 3
compute:
- name: worker
replicas: <N>
networking:
clusterNetwork:
- cidr: 172.28.0.0/16
hostPrefix: 23
serviceNetwork:
- 172.29.0.0/16
machineNetwork:
- cidr: 192.168.100.0/24
networkType: OVNKubernetes
pullSecret: '<console.redhat.com 获取>'
sshKey: '<与 OCP 节点登录用的公钥>'
proxy:
httpProxy: <HTTP_PROXY>
httpsProxy: <HTTP_PROXY>
noProxy: .<BASE_DOMAIN>,192.168.100.0/24,<外网段>,172.28.0.0/16,172.29.0.0/16,.svc,.cluster.local
additionalTrustBundle: |
-----BEGIN CERTIFICATE-----
...(代理自签 CA,如有)...
-----END CERTIFICATE-----

不要写 publish: 字段。metadata.name + baseDomain 必须等于 DNS 中的集群域名。

1
2
3
4
5
6
mkdir -p /root/ocp-install && cd /root/ocp-install
vi install-config.yaml
cp install-config.yaml install-config.yaml.bak # create 会删除原文件
openshift-install create ignition-configs
cp *.ign /var/www/html/ignition/
chmod 644 /var/www/html/ignition/*.ign

验证

1
2
3
4
5
6
7
ls -la /root/ocp-install/
# 应有 bootstrap.ign master.ign worker.ign auth/kubeconfig auth/kubeadmin-password

curl -s -o /dev/null -w '%{http_code}' http://192.168.100.2:8080/ignition/bootstrap.ign
# → 200

grep -E 'baseDomain|name:' install-config.yaml.bak

Step 8 — Kernel 引导 RHCOS(PVE)

网卡名用 **ens18**;必须带 ip= 静态地址,否则 nm-wait-online 卡住。

1
2
3
4
5
6
7
8
9
10
KERNEL=/var/lib/vz/template/iso/rhcos-live-kernel-x86_64
INITRD=/var/lib/vz/template/iso/rhcos-live-initramfs.x86_64.img
IGN=http://192.168.100.2:8080/ignition
ROOTFS=http://192.168.100.2:8080/rhcos/rhcos-live-rootfs.x86_64.img
GW=192.168.100.1

# Bootstrap 110 → .3
APPEND="coreos.live.rootfs_url=${ROOTFS} ignition.config.url=${IGN}/bootstrap.ign ip=192.168.100.3::${GW}:255.255.255.0::ens18:none console=tty0"
qm set 110 --args "-kernel ${KERNEL} -initrd ${INITRD} -append \"${APPEND}\""
qm start 110

Bootstrap 上(企业网环境)

  • 配置 crio/bootkube 代理环境变量
  • registries.conf mirror 到 Registry Proxy :5000
  • 必要时在 bootkube.sh 硬编码 VERSION="4.18.19"

验证

1
2
3
4
5
6
7
8
# PVE Console 或 SSH 到 bootstrap
# 应见 ens18: 192.168.100.3

sudo crictl pods
# 应出现 etcd-kube-*、kube-apiserver-* 等 pod

curl -k https://192.168.100.3:6443/healthz
# 安装初期可能还未 ready,几分钟后应返回 ok

Step 9 — 启动 Master,等待 bootstrap-complete

1
2
3
4
5
6
7
8
9
# 101~103:ignition 换 master.ign,IP 改 .101/.102/.103
for id ip in "101 192.168.100.101" "102 192.168.100.102" "103 192.168.100.103"; do
set -- $id $ip
APPEND="coreos.live.rootfs_url=${ROOTFS} ignition.config.url=${IGN}/master.ign ip=$2::${GW}:255.255.255.0::ens18:none console=tty0"
qm set $1 --args "-kernel ${KERNEL} -initrd ${INITRD} -append \"${APPEND}\""
qm start $1
done

openshift-install wait-for bootstrap-complete --dir=/root/ocp-install --log-level=debug

验证

1
2
3
4
curl -k https://api.<CLUSTER_NAME>.<BASE_DOMAIN>:6443/healthz   # ok
export KUBECONFIG=/root/ocp-install/auth/kubeconfig
oc get nodes # 3 Master Ready
oc get clusteroperators | grep -v True # 应无大量 False

bootstrap-complete 后:从 haproxy.cfg 删除 server bootstrap 行 → systemctl reload haproxy

1
2
grep bootstrap /etc/haproxy/haproxy.cfg
# 应无 bootstrap 条目

Step 10 — 删 Bootstrap,启动 Worker

1
2
3
4
5
6
7
8
qm stop 110 && qm destroy 110

# Worker:worker.ign,IP .104+
# 示例 worker 104
APPEND="coreos.live.rootfs_url=${ROOTFS} ignition.config.url=${IGN}/worker.ign ip=192.168.100.104::${GW}:255.255.255.0::ens18:none console=tty0"
qm set 104 --args "-kernel ${KERNEL} -initrd ${INITRD} -append \"${APPEND}\""
qm start 104
# 其余 worker 同理
1
2
3
4
5
6
7
# 批准 CSR(循环直到无 pending)
while true; do
PENDING=$(oc get csr -o json | jq -r '.items[] | select(.status == {}) | .metadata.name')
[ -z "$PENDING" ] && break
echo "$PENDING" | xargs -r oc adm certificate approve
sleep 10
done

验证

1
2
3
4
5
oc get nodes
# 全部 Ready

oc get csr | grep Pending
# 应无 Pending

Step 11 — 完成安装

1
2
3
4
5
6
openshift-install wait-for install-complete --dir=/root/ocp-install

export KUBECONFIG=/root/ocp-install/auth/kubeconfig
oc get clusterversion
oc get clusteroperators
oc whoami

验证

  • ClusterVersion 状态 Completed,版本 4.18.19
  • 所有 ClusterOperatorAvailable=True
  • Console:https://console-openshift-console.apps.<CLUSTER_NAME>.<BASE_DOMAIN> 可访问
  • auth/kubeconfigkubeadmin-password 已备份

Step 12 — 配置 Ceph 存储(CSI)

PVE 上已有 Ceph(ocp-pool,Monitor 在 <PVE_IP>:6789)。OCP 通过 Ceph CSI RBD 动态供给 PVC。

12.1 网络连通性

OCP Worker 经 PVE NAT 需能访问 Ceph Monitor:

1
2
3
# 在任意 Worker 上
nc -zv <PVE_IP> 6789
# 预期:Connection succeeded

若不通,在 PVE 放行 6789,或确认 192.168.100.0/24 到 PVE 管理网路由/NAT 正常。

12.2 在 PVE 上创建 Ceph 用户

1
2
3
4
5
6
7
8
# PVE 上执行
ceph auth get-or-create client.openshift \
mon 'profile rbd' \
osd 'profile rbd pool=<CEPH_POOL>' \
-o /etc/pve/ceph/openshift.client.openshift.keyring

ceph auth get client.openshift
ceph fsid

记录 keyfsid

验证

1
2
ceph auth get client.openshift | grep caps
# 应含 mon 'profile rbd' 和 osd 'profile rbd pool=<CEPH_POOL>'

12.3 创建 Secret 与 ConfigMap

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
# Helper 上执行
export KUBECONFIG=/root/ocp-install/auth/kubeconfig

CEPH_KEY="<client.openshift 的 key>"
FSID="<ceph fsid>"

cat <<EOF | oc apply -f -
apiVersion: v1
kind: Secret
metadata:
name: csi-ceph-secret
namespace: default
stringData:
userID: openshift
userKey: ${CEPH_KEY}
EOF

cat <<EOF | oc apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: ceph-csi-config
namespace: default
data:
config.json: |
[
{
"clusterID": "${FSID}",
"monitors": ["<PVE_IP>:6789"],
"cephFS": {},
"rbd": {}
}
]
EOF

验证

1
2
oc get secret csi-ceph-secret -o jsonpath='{.data.userID}' | base64 -d
# → openshift

12.4 安装 Ceph CSI

选用与集群 K8s 版本兼容的 ceph-csi(OCP 4.18 对应 K8s 1.31,示例 v3.13.0):

1
2
3
4
5
6
7
8
9
CEPH_CSI_VER=v3.13.0
for f in csi-provisioner-rbac csi-nodeplugin-rbac csi-rbdplugin csi-rbdplugin-provisioner; do
curl -LO https://raw.githubusercontent.com/ceph/ceph-csi/${CEPH_CSI_VER}/deploy/rbd/kubernetes/${f}.yaml
done

oc apply -f csi-provisioner-rbac.yaml
oc apply -f csi-nodeplugin-rbac.yaml
oc apply -f csi-rbdplugin.yaml
oc apply -f csi-rbdplugin-provisioner.yaml

验证

1
2
3
4
5
oc get pods -A | grep csi-rbd
# provisioner 和 node plugin 均应 Running

oc logs -l app=csi-rbdplugin-provisioner -c csi-provisioner --tail=20
# 无 auth / monitor 连接错误

12.5 创建 StorageClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cat <<EOF | oc apply -f -
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ceph-rbd
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: rbd.csi.ceph.com
parameters:
clusterID: ${FSID}
pool: <CEPH_POOL>
imageFeatures: layering
csi.storage.k8s.io/provisioner-secret-name: csi-ceph-secret
csi.storage.k8s.io/provisioner-secret-namespace: default
csi.storage.k8s.io/controller-expand-secret-name: csi-ceph-secret
csi.storage.k8s.io/controller-expand-secret-namespace: default
csi.storage.k8s.io/node-stage-secret-name: csi-ceph-secret
csi.storage.k8s.io/node-stage-secret-namespace: default
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: Immediate
EOF

验证

1
2
oc get sc
# ceph-rbd (default)

12.6 PVC 功能测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat <<EOF | oc apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 5Gi
storageClassName: ceph-rbd
EOF

oc get pvc test-pvc -w
# 预期:Bound

oc get pv
# 应出现对应 PV,STATUS Bound

在 PVE 上交叉验证:

1
2
rbd ls -p <CEPH_POOL>
# 应出现 CSI 创建的 image

测试完成后清理:

1
oc delete pvc test-pvc

验证清单

  • CSI provisioner / node plugin 全部 Running
  • test-pvc 状态 Bound
  • PVE 上 rbd ls -p <CEPH_POOL> 可见对应 image
  • 删除 PVC 后 image 被回收(reclaimPolicy: Delete

SSH 登录提示

Helper 与 OCP 节点可能使用不同 SSH 密钥:

1
2
3
ssh -i <ocp-key.pem> -o IdentitiesOnly=yes \
-o ProxyCommand="ssh -i <helper-key.pem> -o IdentitiesOnly=yes -W %h:%p root@<HELPER_EXT>" \
core@192.168.100.101

-i + -J 单命令要求两跳同一密钥;两把密钥时先登 Helper 再 ssh core@...


节点调试(API 不可用时)

1
2
3
sudo crictl pods && sudo crictl ps -a
sudo crictl logs <container-id>
sudo journalctl -u kubelet -u bootkube -u release-image -n 50

踩坑速查

现象 处理
iPXE 无限重启 用 kernel --args 引导,勿直接 qm start
nm-wait-online 卡住 ip=...ens18:none
curl :8080 No route to host Helper 防火墙放行 8080
bootstrap 超时 HAProxy 含 bootstrap 后端;核对 API 域名
Permission denied SSH install-config sshKey 与登录私钥不一致
镜像 403/timeout 真实 pullSecret;Registry Proxy + registries.conf
PVC Pending Worker 到 <PVE_IP>:6789 不通;检查 Ceph 用户权限和 fsid
CSI pod CrashLoop oc logs 查 monitor 地址、secret key、pool 名称
Notice: 正常情况下,这里会有一个基于utteranc.es的留言系统,如果看不到,可能要想想办法才能看到。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2012 - 2026 tiaobug.com All Rights Reserved.

鲁ICP备2024124237号-1