在裸机内网环境中部署 Kubernetes 时,最容易卡住的通常不是 Pod 本身,而是入口流量、LoadBalancer 地址分配、证书、Service 与 Gateway 之间的关系。本文记录一套两节点开发环境的完整落地路径:从 kubeadm 初始化集群,到安装 Calico、MetalLB、Envoy Gateway,再到通过独立域名暴露 Kite 管理界面。

适用环境

本文示例基于以下环境:

  • Ubuntu Server 26.04
  • Kubernetes v1.36.x
  • kubeadm
  • containerd
  • Calico CNI
  • Envoy Gateway
  • MetalLB Layer 2
  • Kite Kubernetes Web UI
  • 1 个控制平面节点和 1 个工作节点
  • 裸机内网,节点位于同一个二层网段

示例拓扑如下:

角色 主机名 IP
控制平面 k8s-master-1 192.168.110.200
工作节点 k8s-node-1 192.168.110.201

MetalLB 地址池建议预留:

1
192.168.110.220-192.168.110.230

这些 IP 必须满足以下条件:

  • 不与任何主机冲突
  • 不在 DHCP 地址池内
  • 与 Kubernetes 节点处于同一个 VLAN 或二层网络
  • 已经和网络管理员确认可以使用

总体架构

开发环境推荐链路如下:

1
2
3
4
5
6
7
8
9
10
11
Calico
|
MetalLB Layer 2
|
Envoy Gateway
|
HTTPRoute / TLSRoute
|
Kubernetes Service
|
Deployment

组件选择:

  • Gateway API 使用标准 CRD 通道。
  • Gateway Controller 使用 Envoy Gateway。
  • 裸机 LoadBalancer 使用 MetalLB Layer 2。
  • TLS 在开发环境使用自签名证书,测试和生产环境使用 cert-manager 或企业 CA。
  • Kite 使用单独域名访问,不建议挂在 /kite 子路径下。

当前场景不建议仅为了 Gateway API 更换 Calico。只有当项目计划同时引入 eBPF、Cilium NetworkPolicy、Cilium Service LB 和 Cilium Gateway API 时,再考虑整体迁移到 Cilium。

安装 Kubernetes 基础环境

如果集群已经初始化并且节点已经加入,可以跳过本节,直接从安装 Kite、Envoy Gateway 和 MetalLB 开始。

设置主机名和 hosts

192.168.110.200 上执行:

1
hostnamectl set-hostname k8s-master-1

192.168.110.201 上执行:

1
hostnamectl set-hostname k8s-node-1

两台机器都执行:

1
2
3
4
5
6
7
8
sed -i '/k8s-master-1/d;/k8s-node-1/d' /etc/hosts

cat >> /etc/hosts <<'EOF'
192.168.110.200 k8s-master-1
192.168.110.201 k8s-node-1
EOF

getent hosts k8s-master-1 k8s-node-1

互相测试网络:

1
2
ping -c 3 192.168.110.200
ping -c 3 192.168.110.201

安装基础工具

两台机器都执行:

1
2
3
4
5
6
7
8
9
10
11
12
apt-get update
apt-get full-upgrade -y
apt-get install -y \
ca-certificates \
curl \
gpg \
chrony \
conntrack \
socat \
openssl

systemctl enable --now chrony

如存在重启标记,应重启后继续:

1
test -f /var/run/reboot-required && echo "请重启后继续"

关闭 swap

两台机器都执行:

1
2
3
swapoff -a
sed -ri '/\sswap\s/ s/^/#/' /etc/fstab
swapon --show

swapon --show 必须没有输出。

配置内核模块和 sysctl

两台机器都执行:

1
2
3
4
5
6
7
cat > /etc/modules-load.d/k8s.conf <<'EOF'
overlay
br_netfilter
EOF

modprobe overlay
modprobe br_netfilter

配置网络转发:

1
2
3
4
5
6
7
cat > /etc/sysctl.d/99-kubernetes-cri.conf <<'EOF'
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF

sysctl --system

确认:

1
2
sysctl net.ipv4.ip_forward
lsmod | grep -E 'overlay|br_netfilter'

配置防火墙

两台节点之间至少需要允许:

方向 协议/端口 用途
管理端到控制平面 TCP 6443 Kubernetes API
控制平面自身 TCP 2379-2380 etcd
控制平面与节点 TCP 10250 kubelet
节点之间 UDP 4789 Calico VXLAN
节点之间 TCP/UDP 7946 MetalLB memberlist
外部到工作节点 TCP/UDP 30000-32767 NodePort,可选

如果这是隔离的开发内网,可以暂时关闭 UFW:

1
2
3
if command -v ufw >/dev/null 2>&1; then
ufw disable
fi

生产环境不要无条件关闭防火墙,应在上游防火墙精确放行端口。

安装 containerd

两台机器都执行:

1
2
3
4
apt-get install -y containerd runc

mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml

配置 systemd cgroup:

1
2
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' \
/etc/containerd/config.toml

确认 CRI 没有被禁用:

1
2
grep -nE 'disabled_plugins|SystemdCgroup' \
/etc/containerd/config.toml

如果看到:

1
disabled_plugins = ["cri"]

执行:

1
2
sed -i '/disabled_plugins.*cri/s/^/#/' \
/etc/containerd/config.toml

启动 containerd:

1
2
3
4
5
6
systemctl daemon-reload
systemctl enable --now containerd
systemctl restart containerd

systemctl status containerd --no-pager
ctr plugins ls | grep -E 'cri|io.containerd'

Kubernetes v1.36 要求 CRI v1;containerd 和 kubelet 的 cgroup 驱动必须一致,推荐使用 systemd。

安装 kubeadm、kubelet 和 kubectl

以下使用 Kubernetes v1.36 小版本仓库。官方仓库使用 pkgs.k8s.io,不要使用已经废弃的 apt.kubernetes.io

两台机器都执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
K8S_MINOR='v1.36'

mkdir -p -m 755 /etc/apt/keyrings

curl -fsSL \
"https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/deb/Release.key" \
| gpg --dearmor --yes \
-o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/deb/ /" \
> /etc/apt/sources.list.d/kubernetes.list

apt-get update
apt-cache madison kubeadm

apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl

systemctl enable kubelet

kubeadm version -o short
kubelet --version
kubectl version --client

两台机器的 kubeadmkubeletkubectl 主版本必须一致。

初始化控制平面

只在 192.168.110.200 执行。

先拉取 Kubernetes 核心镜像:

1
2
kubeadm config images pull \
--cri-socket unix:///run/containerd/containerd.sock

如果访问 registry.k8s.io 超时,应先配置 containerd 代理或使用内部镜像仓库,不要直接使用 --ignore-preflight-errors=all

1
2
3
4
5
6
7
8
9
10
11
12
13
mkdir -p /etc/systemd/system/containerd.service.d

cat > /etc/systemd/system/containerd.service.d/proxy.conf <<'EOF'
[Service]
Environment="HTTP_PROXY=http://PROXY_HOST:PROXY_PORT"
Environment="HTTPS_PROXY=http://PROXY_HOST:PROXY_PORT"
Environment="NO_PROXY=127.0.0.1,localhost,192.168.110.0/24,192.168.0.0/16,10.96.0.0/12,.svc,.cluster.local"
EOF

systemctl daemon-reload
systemctl restart containerd

systemctl show containerd -p Environment

初始化集群:

1
2
3
4
5
6
7
kubeadm init \
--apiserver-advertise-address=192.168.110.200 \
--control-plane-endpoint=192.168.110.200:6443 \
--pod-network-cidr=192.168.0.0/16 \
--service-cidr=10.96.0.0/12 \
--cri-socket=unix:///run/containerd/containerd.sock \
--kubernetes-version="$(kubeadm version -o short)"

保存命令输出的 kubeadm join 命令,后续 worker 加入集群需要使用。

配置 kubectl

1
2
3
4
5
6
export KUBECONFIG=/etc/kubernetes/admin.conf
kubectl get nodes

mkdir -p $HOME/.kube
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

此时节点显示 NotReady 是正常的,因为 CNI 尚未安装。

注意:/etc/kubernetes/admin.conf 具有集群管理员权限,不要复制给不可信用户。

安装 Calico CNI

仍然在控制平面执行。本文固定使用 Calico v3.32.1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mkdir -p /root/calico-v3.32.1
cd /root/calico-v3.32.1

curl -fsSLo operator-crds.yaml \
https://raw.githubusercontent.com/projectcalico/calico/v3.32.1/manifests/operator-crds.yaml

curl -fsSLo tigera-operator.yaml \
https://raw.githubusercontent.com/projectcalico/calico/v3.32.1/manifests/tigera-operator.yaml

curl -fsSLo custom-resources.yaml \
https://raw.githubusercontent.com/projectcalico/calico/v3.32.1/manifests/custom-resources.yaml

kubectl apply -f operator-crds.yaml
kubectl apply -f tigera-operator.yaml

kubectl -n tigera-operator rollout status \
deployment/tigera-operator \
--timeout=5m

kubectl apply -f custom-resources.yaml

等待组件就绪:

1
2
kubectl get tigerastatus
watch kubectl get pods -A -o wide

Calico 使用的 Pod 网段必须与物理网络不重叠。本文示例使用:

1
192.168.0.0/16

加入工作节点

在控制平面生成新的 join 命令:

1
2
3
kubeadm token create \
--ttl 24h \
--print-join-command

把输出复制到 192.168.110.201,并追加 containerd 参数:

1
2
3
4
kubeadm join 192.168.110.200:6443 \
--token <TOKEN> \
--discovery-token-ca-cert-hash sha256:<HASH> \
--cri-socket unix:///run/containerd/containerd.sock

回到控制平面验证:

1
2
watch kubectl get nodes -o wide
watch kubectl get pods -A -o wide

最终应看到两台节点都处于 Ready

NAME STATUS ROLES INTERNAL-IP CONTAINER-RUNTIME
k8s-master-1 Ready control-plane 192.168.110.200 containerd
k8s-node-1 Ready <none> 192.168.110.201 containerd

Kubernetes 基础验收

1
2
3
4
kubectl cluster-info
kubectl get --raw='/readyz?verbose'
kubectl get nodes -o wide
kubectl get pods -A

部署一个简单测试应用:

1
2
3
kubectl create deployment nginx --image=nginx:stable
kubectl expose deployment nginx --port=80
kubectl get deployment,pod,svc -o wide

测试集群内 DNS 和 Service:

1
2
3
4
5
kubectl run nettest \
--rm -it \
--restart=Never \
--image=busybox:1.36 \
-- wget -qO- http://nginx

清理测试应用:

1
2
kubectl delete deployment nginx
kubectl delete service nginx

安装 Kite

Kite 是一个 Kubernetes Web UI。本文建议通过独立域名访问 Kite,而不是挂在某个已有域名的子路径下。
根据官方文档安装

先确认 Helm:

1
helm version

如果没有 Helm,先按 Helm 官方文档安装。Kite 的安装方式以官方文档为准,完成安装后重点确认 Service:

1
2
kubectl get svc kite -n kite-system
kubectl get pods -n kite-system -l app=kite -o wide

本文后续假设 Kite Service 位于 kite-system 命名空间,并监听 8080

1
kite   ClusterIP   ...   8080/TCP

Gateway API 与 Envoy Gateway

开发环境推荐:

1
2
3
4
Envoy Gateway
+ port-forward 或 MetalLB
+ 自签名证书
+ HTTPRoute

测试环境推荐:

1
2
3
4
5
Envoy Gateway
+ MetalLB Layer 2
+ cert-manager
+ 内网 DNS
+ 与生产一致的 Gateway API 和 Envoy Gateway 版本

生产环境推荐:

1
2
3
4
5
Envoy Gateway
+ MetalLB BGP 或外部负载均衡器
+ cert-manager 或企业 CA
+ GitOps
+ Prometheus、日志和告警

当前 1 个 control-plane + 1 个 worker 只能作为开发或验证环境,不是高可用生产拓扑。

安装 Gateway API 与 Envoy Gateway CRD

下面固定使用 Envoy Gateway v1.9.0。如果更换版本,Helm Chart 和 quickstart YAML 必须使用相同版本。

1
2
3
4
5
6
7
helm template eg-crds \
oci://docker.io/envoyproxy/gateway-crds-helm \
--version v1.9.0 \
--set crds.gatewayAPI.enabled=true \
--set crds.gatewayAPI.channel=standard \
--set crds.envoyGateway.enabled=true \
| kubectl apply --server-side -f -

如果出现:

1
apiVersion not set, kind not set

将输出保存到文件并使用:

1
2
3
4
5
6
7
8
9
helm template eg-crds \
oci://docker.io/envoyproxy/gateway-crds-helm \
--version v1.9.0 \
--set crds.gatewayAPI.enabled=true \
--set crds.gatewayAPI.channel=standard \
--set crds.envoyGateway.enabled=true \
> /tmp/eg-crds.yaml

kubectl apply --server-side --validate=false -f /tmp/eg-crds.yaml

如果当前集群没有由其他组件管理 Gateway API CRD,也可以直接让主 chart 管理 CRD:

1
2
3
4
5
helm install eg \
oci://docker.io/envoyproxy/gateway-helm \
--version v1.9.0 \
-n envoy-gateway-system \
--create-namespace

两种 CRD 管理方式不要混用。

安装 Envoy Gateway Controller

如果已经单独安装 CRD:

1
2
3
4
5
6
helm upgrade --install eg \
oci://docker.io/envoyproxy/gateway-helm \
--version v1.9.0 \
-n envoy-gateway-system \
--create-namespace \
--set crds.enabled=false

等待就绪:

1
2
3
4
5
6
7
kubectl wait \
--timeout=5m \
-n envoy-gateway-system \
deployment/envoy-gateway \
--for=condition=Available

kubectl get pods -n envoy-gateway-system -o wide

部署官方示例

1
2
3
kubectl apply -f \
https://github.com/envoyproxy/gateway/releases/download/v1.9.0/quickstart.yaml \
-n default

检查资源状态:

1
2
3
kubectl get gatewayclass
kubectl get gateway,httproute -A
kubectl get pods -A -o wide

重点看这些条件:

1
2
3
4
GatewayClass: Accepted=True
Gateway: Programmed=True
HTTPRoute: Accepted=True
HTTPRoute: ResolvedRefs=True

使用 port-forward 验证

查找 Envoy 数据面 Service:

1
2
3
4
5
6
export ENVOY_SERVICE=$(kubectl get svc \
-n envoy-gateway-system \
--selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg \
-o jsonpath='{.items[0].metadata.name}')

echo "$ENVOY_SERVICE"

转发:

1
2
kubectl -n envoy-gateway-system port-forward \
service/${ENVOY_SERVICE} 8888:80

另开终端测试:

1
2
3
curl -v \
-H "Host: www.example.com" \
http://127.0.0.1:8888/get

如果返回示例应用内容,说明 Gateway、HTTPRoute、Envoy 数据面和后端 Service 均已连通。如果返回 404,优先检查 Host 是否与 HTTPRoute.spec.hostnames 一致。

安装 MetalLB

节点位于同一网段时,开发环境可以使用 MetalLB Layer 2 模式。

网络准备

MetalLB Layer 2 需要:

  • 节点间 TCP/UDP 7946
  • 客户端和节点处于可互通的二层网络
  • 地址池不与 DHCP 或其他主机冲突

如果启用 UFW:

1
2
ufw allow from 192.168.110.0/24 to any port 7946 proto tcp
ufw allow from 192.168.110.0/24 to any port 7946 proto udp

检查 kube-proxy

1
2
kubectl -n kube-system get configmap kube-proxy \
-o jsonpath='{.data.config\.conf}' | grep -E 'mode:|strictARP'

如果是 IPVS 模式,确保:

1
2
ipvs:
strictARP: true

修改后重启 kube-proxy:

1
kubectl -n kube-system rollout restart daemonset/kube-proxy

安装 MetalLB

本文固定使用 MetalLB v0.16.1

1
2
3
4
5
6
7
8
9
10
11
12
13
kubectl create namespace metallb-system \
--dry-run=client -o yaml | kubectl apply -f -

kubectl label namespace metallb-system \
pod-security.kubernetes.io/enforce=privileged \
pod-security.kubernetes.io/audit=privileged \
pod-security.kubernetes.io/warn=privileged \
--overwrite

curl -fsSLo /root/metallb-native-v0.16.1.yaml \
https://raw.githubusercontent.com/metallb/metallb/v0.16.1/config/manifests/metallb-native.yaml

kubectl apply -f /root/metallb-native-v0.16.1.yaml

等待:

1
2
3
4
5
6
7
8
9
kubectl wait \
--namespace metallb-system \
--for=condition=Available \
deployment/controller \
--timeout=5m

kubectl rollout status daemonset/speaker \
-n metallb-system \
--timeout=5m

检查:

1
2
kubectl get pods -n metallb-system -o wide
kubectl get crd | grep metallb

配置地址池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cat > /root/metallb-l2-config.yaml <<'EOF'
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: lan-pool
namespace: metallb-system
spec:
addresses:
- 192.168.110.220-192.168.110.230
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: lan-l2
namespace: metallb-system
spec:
ipAddressPools:
- lan-pool
EOF

kubectl apply -f /root/metallb-l2-config.yaml

确认:

1
2
kubectl get ipaddresspool -n metallb-system
kubectl get l2advertisement -n metallb-system

测试 LoadBalancer

1
2
3
4
5
6
7
8
9
10
kubectl create namespace metallb-test \
--dry-run=client -o yaml | kubectl apply -f -

kubectl -n metallb-test create deployment nginx \
--image=nginx:stable

kubectl -n metallb-test expose deployment nginx \
--port=80 \
--target-port=80 \
--type=LoadBalancer

查看:

1
kubectl get svc -n metallb-test -w

如果输出类似:

1
2
NAME    TYPE           EXTERNAL-IP       PORT(S)
nginx LoadBalancer 192.168.110.221 80:29286/TCP

访问时必须使用实际分配到的 EXTERNAL-IP,而不是地址池中的其他地址:

1
curl -v http://192.168.110.221

检查后端:

1
2
3
kubectl get pods -n metallb-test -o wide
kubectl get endpoints -n metallb-test nginx
kubectl describe svc -n metallb-test nginx

如果 EXTERNAL-IP 已分配但访问不通:

1
2
3
kubectl logs -n metallb-system deployment/controller --tail=100
kubectl logs -n metallb-system daemonset/speaker --since=10m
ip neigh show 192.168.110.221

从其他 Linux 客户端测试 ARP:

1
arping -I eth0 192.168.110.221

eth0 换成实际网卡名。

将 Kite 接入 Envoy Gateway

推荐使用独立域名:

1
https://kite.k8s.local/

不建议优先使用:

1
https://example.com/kite/

Kite 默认 Service 是 HTTP kite:8080,因此不需要额外的 HTTPS 后端适配、Envoy Gateway Backend 资源或 insecureSkipVerify

确认 Kite 后端

1
kubectl get svc kite -n kite-system

确认 Service 存在并监听 8080

1
kite   ClusterIP   ...   8080/TCP

创建 Gateway TLS 证书

开发环境生成自签名证书:

1
2
3
4
5
6
7
8
9
10
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /root/kite.k8s.local.key \
-out /root/kite.k8s.local.crt \
-subj "/CN=kite.k8s.local" \
-addext "subjectAltName=DNS:kite.k8s.local"

kubectl -n kite-system create secret tls kite-gateway-cert \
--key=/root/kite.k8s.local.key \
--cert=/root/kite.k8s.local.crt \
--dry-run=client -o yaml | kubectl apply -f -

创建 Kite Gateway 和 HTTPRoute

前提是已经存在 Envoy Gateway 的 GatewayClass

1
kubectl get gatewayclass

如果名称不是 eg,将下面 YAML 中的 gatewayClassName 改成实际名称。

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
cat <<'EOF' | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: kite-gateway
namespace: kite-system
spec:
gatewayClassName: eg
listeners:
- name: https
hostname: kite.k8s.local
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: kite-gateway-cert
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: kite-route
namespace: kite-system
spec:
parentRefs:
- name: kite-gateway
sectionName: https
hostnames:
- kite.k8s.local
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: kite
port: 8080
EOF

获取 Kite Gateway IP

1
2
3
4
5
6
7
kubectl get gateway \
-n kite-system \
kite-gateway -o wide

kubectl get svc -n envoy-gateway-system \
-l gateway.envoyproxy.io/owning-gateway-namespace=kite-system,gateway.envoyproxy.io/owning-gateway-name=kite-gateway \
-o wide

如果 MetalLB 分配:

1
192.168.110.222

则在 Windows 客户端的 hosts 文件中添加:

1
192.168.110.222 kite.k8s.local

hosts 文件路径:

1
C:\Windows\System32\drivers\etc\hosts

测试访问

在控制平面测试:

1
2
3
curl -vk \
--resolve kite.k8s.local:443:192.168.110.222 \
https://kite.k8s.local/

浏览器访问:

1
https://kite.k8s.local/

开发环境使用自签名证书,浏览器出现证书警告属于预期结果。

登录 Token:

1
kubectl -n kite-system create token kite-admin

常见故障排查

worker 上 kubectl 连接 localhost:8080

如果在 worker 直接执行 kubectl 时看到:

1
The connection to the server localhost:8080 was refused

这不代表集群故障,只代表当前用户没有 kubeconfig。如确实需要在 worker 使用 kubectl

1
2
3
mkdir -p ~/.kube
scp root@192.168.110.200:/etc/kubernetes/admin.conf ~/.kube/config
chmod 600 ~/.kube/config

生产环境不建议把管理员 kubeconfig 长期复制到 worker。

containerd 镜像拉取超时

如果 Kubernetes 镜像拉取超时,例如:

1
2
failed to pull image "registry.k8s.io/pause:3.10.1"
dial tcp 74.125.20.82:443: i/o timeout

这通常是出口、防火墙或代理问题,不是 kubeadm 参数问题。

测试:

1
2
3
4
curl -I --connect-timeout 10 --max-time 20 \
https://registry.k8s.io/v2/

ctr -n k8s.io images pull registry.k8s.io/pause:3.10.1

如果 worker 无法拉取镜像,可以从 master 导出后导入:

1
2
3
4
5
6
7
8
9
10
# master
ctr -n k8s.io images export /root/worker-images.tar \
registry.k8s.io/pause:3.10.1

scp /root/worker-images.tar root@192.168.110.201:/root/

# worker
ctr -n k8s.io images import /root/worker-images.tar
systemctl restart containerd
systemctl restart kubelet

注意:只导入 pause 镜像不能解决所有问题。worker 还可能需要 kube-proxy、Calico 和 CSI 相关镜像。

MetalLB memberlist Secret 不存在

错误:

1
2
MountVolume.SetUp failed for volume "memberlist":
secret "memberlist" not found

检查:

1
2
kubectl get secret -n metallb-system
kubectl logs -n metallb-system deployment/controller --tail=100

如果确实不存在,可以手动创建:

1
2
3
4
5
6
7
apt-get install -y openssl

kubectl create secret generic memberlist \
-n metallb-system \
--from-literal=secretkey="$(openssl rand -base64 128)"

kubectl rollout restart daemonset/speaker -n metallb-system

如果 Secret 已存在,不要反复重新生成。

MetalLB Speaker 不启动

1
2
3
kubectl get pods -n metallb-system -o wide
kubectl describe pod -n metallb-system <SPEAKER_POD>
kubectl logs -n metallb-system daemonset/speaker --tail=200

重点检查:

  • 镜像拉取是否失败
  • Pod Security 是否阻止
  • TCP/UDP 7946 是否被拦截
  • memberlist Secret 是否存在
  • 节点网卡和默认路由是否正确

Kite Gateway 访问失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
kubectl describe gateway \
-n kite-system kite-gateway

kubectl describe httproute \
-n kite-system kite-route

kubectl get svc kite \
-n kite-system

kubectl get pods -n kite-system \
-l app=kite \
-o wide

kubectl get pods -n envoy-gateway-system -o wide
kubectl logs -n envoy-gateway-system deployment/envoy-gateway --tail=100

重点检查:

1
2
3
Gateway: Accepted=True, Programmed=True
HTTPRoute: Accepted=True, ResolvedRefs=True
Backend Service: kite-system/kite:8080

常见原因:

  • 访问域名没有配置 hosts 或 DNS
  • 访问了 MetalLB 地址池中错误的 IP
  • HTTPRoute 的 hostname 与浏览器 Host 不一致
  • kite-system/kite Service 不存在
  • Kite Pod 没有 Running
  • GatewayClass 名称不是 eg
  • Gateway 监听器证书 Secret 不存在
  • MetalLB 没有给 Envoy Gateway Service 分配外部 IP

最终检查清单

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
# Kubernetes
kubectl get nodes -o wide
kubectl get pods -A

# Calico
kubectl get pods -n calico-system -o wide
kubectl get tigerastatus

# MetalLB
kubectl get pods -n metallb-system -o wide
kubectl get ipaddresspool,l2advertisement -n metallb-system
kubectl get svc -A | grep LoadBalancer

# Envoy Gateway
kubectl get pods -n envoy-gateway-system -o wide
kubectl get gatewayclass
kubectl get gateway,httproute -A

# Kite
kubectl get pods -n kite-system \
-l app=kite
kubectl get svc kite -n kite-system

# 节点 containerd
systemctl status containerd --no-pager
systemctl status kubelet --no-pager

目标状态:

1
2
3
4
5
6
7
所有节点 Ready
Calico Pod Running
MetalLB controller/speaker Running
LoadBalancer Service 获得 192.168.110.220-230 地址
Gateway Programmed=True
HTTPRoute Accepted=True
Kite 可以通过独立域名访问

生产环境注意事项

这套配置适合开发和验证环境。进入生产前至少需要补齐:

  • 使用企业 CA 或 cert-manager 管理证书。
  • 使用 OIDC/SSO 登录 Kite,不依赖临时管理员 Token。
  • 限制 Gateway IP 的访问来源。
  • 不直接把 Kite 暴露到公网。
  • 将 YAML 资源纳入 GitOps 或 CI/CD 管理。
  • 为 Envoy Gateway、MetalLB、kubelet 和核心业务 Pod 接入监控、日志和告警。
  • 控制平面、etcd、负载均衡和存储都需要重新设计高可用方案。

参考文档