#!/bin/bash # 在本机安装 Kubernetes 1.37.1 单节点,容器运行时走现有 Docker(数据盘 /usr/cluster)。 # 不改主机 nginx:80,不重启已经在跑的大模型容器。 set -eu export DEBIAN_FRONTEND=noninteractive log() { echo "=== $* ==="; } KVER=1.37.1-1.1 IMG_REPO=registry.aliyuncs.com/google_containers WORKDIR=/usr/cluster/k8s BACKUP=/usr/cluster/k8s-reset-backup-20261012 mkdir -p "$WORKDIR" "$BACKUP" check_health() { local label=$1 ok=1 u code for u in \ http://127.0.0.1:3000/api/status \ http://127.0.0.1:8000/health \ http://127.0.0.1:8088/ \ http://127.0.0.1:3100/api/public/health \ http://127.0.0.1:3001/api/health \ http://127.0.0.1:9090/-/ready \ http://127.0.0.1:8090/ \ http://127.0.0.1:80/ do code=$(curl -s -o /dev/null -m 8 -w '%{http_code}' "$u" || echo err) echo "HEALTH $label $code $u" case "$code" in 200|301|302|307|308) ;; *) ok=0 ;; esac done docker ps --format '{{.Names}}' | grep -qx 'vllm-qwen3.6-27b' || ok=0 if [ "$ok" -ne 1 ]; then echo "HEALTH_FAIL $label" return 1 fi echo "HEALTH_OK $label" } log "prep cni and kubelet dropins" if [ -d /etc/cni/net.d ]; then mkdir -p "$BACKUP/cni" cp -a /etc/cni/net.d "$BACKUP/cni/" || true rm -rf /etc/cni/net.d/* fi mkdir -p /etc/cni/net.d /opt/cni/bin if [ -f /root/.kube/config ]; then cp -a /root/.kube/config "$BACKUP/kubeconfig.old" rm -f /root/.kube/config fi mkdir -p "$BACKUP/kubelet-dropin" if [ -d /etc/systemd/system/kubelet.service.d ]; then cp -a /etc/systemd/system/kubelet.service.d/. "$BACKUP/kubelet-dropin/" || true rm -f /etc/systemd/system/kubelet.service.d/* fi if [ -f /etc/default/kubelet ] && grep -q network-plugin /etc/default/kubelet; then mv /etc/default/kubelet "$BACKUP/kubelet.default" fi ip link del tunl0 2>/dev/null || ip tunnel del tunl0 2>/dev/null || true modprobe br_netfilter || true cat > /etc/modules-load.d/k8s.conf << 'EOF' br_netfilter EOF cat > /etc/sysctl.d/k8s.conf << 'EOF' net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF sysctl --system >/dev/null log "cri-dockerd 0.4.8" if ! /usr/local/bin/cri-dockerd --version 2>/dev/null | grep -q '0.4.8'; then curl -fL --retry 3 -o "$WORKDIR/cri-dockerd-0.4.8.amd64.tgz" \ https://github.com/Mirantis/cri-dockerd/releases/download/v0.4.8/cri-dockerd-0.4.8.amd64.tgz tar -xzf "$WORKDIR/cri-dockerd-0.4.8.amd64.tgz" -C "$WORKDIR" install -m 0755 "$WORKDIR/cri-dockerd/cri-dockerd" /usr/local/bin/cri-dockerd fi /usr/local/bin/cri-dockerd --version systemctl mask cri-dockerd.service >/dev/null 2>&1 || true systemctl daemon-reload systemctl enable cri-docker.socket cri-docker.service systemctl restart cri-docker.socket systemctl restart cri-docker.service sleep 2 crictl --runtime-endpoint unix:///run/cri-dockerd.sock version log "apt kubernetes 1.37" install -d -m 0755 /etc/apt/keyrings curl -fsSL https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.37/deb/Release.key \ | gpg --dearmor --yes -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg cat > /etc/apt/sources.list.d/kubernetes.list << 'EOF' deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.37/deb/ / EOF apt-get update apt-cache madison kubeadm | head -5 || true systemctl stop kubelet || true systemctl disable kubelet || true apt-get install -y -o Dpkg::Options::=--force-confnew \ kubelet="$KVER" kubeadm="$KVER" kubectl="$KVER" conntrack socat apt-mark hold kubelet kubeadm kubectl systemctl unmask kubelet || true kubeadm version -o short kubectl version --client -o yaml | awk '/gitVersion/' log "kubeadm config" cat > "$WORKDIR/kubeadm-config.yaml" << EOF apiVersion: kubeadm.k8s.io/v1beta4 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 10.0.1.47 bindPort: 6443 nodeRegistration: criSocket: unix:///run/cri-dockerd.sock imagePullPolicy: IfNotPresent name: prod102 kubeletExtraArgs: - name: node-ip value: 10.0.1.47 --- apiVersion: kubeadm.k8s.io/v1beta4 kind: ClusterConfiguration kubernetesVersion: v1.37.1 imageRepository: ${IMG_REPO} controlPlaneEndpoint: 10.0.1.47:6443 networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 apiServer: certSANs: - 10.0.1.47 - prod102 extraArgs: - name: bind-address value: 10.0.1.47 EOF kubeadm config images pull --config "$WORKDIR/kubeadm-config.yaml" kubeadm init --config "$WORKDIR/kubeadm-config.yaml" --ignore-preflight-errors=FileExisting-tc mkdir -p /root/.kube cp /etc/kubernetes/admin.conf /root/.kube/config chmod 600 /root/.kube/config export KUBECONFIG=/root/.kube/config log "flannel" curl -fL --retry 3 -o "$WORKDIR/kube-flannel.yml" \ https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml python3 - << 'PY' from pathlib import Path p = Path("/usr/cluster/k8s/kube-flannel.yml") text = p.read_text() text = text.replace('"Network": "10.244.0.0/16"', '"Network": "10.244.0.0/16",\n "EnableNFTables": false') if '"EnableNFTables": false' not in text: raise SystemExit("flannel net-conf was not updated") p.write_text(text) print("flannel yaml updated") PY kubectl apply -f "$WORKDIR/kube-flannel.yml" kubectl taint nodes --all node-role.kubernetes.io/control-plane:NoSchedule- || true log "wait node" kubectl wait --for=condition=Ready node/prod102 --timeout=240s kubectl get nodes -o wide kubectl get pods -A -o wide log "smoke pod" cat > "$WORKDIR/learn-nginx.yaml" << 'EOF' apiVersion: v1 kind: Pod metadata: name: learn-nginx namespace: default spec: restartPolicy: Never containers: - name: nginx image: nginx:latest imagePullPolicy: IfNotPresent EOF kubectl delete pod learn-nginx --ignore-not-found kubectl apply -f "$WORKDIR/learn-nginx.yaml" kubectl wait --for=condition=Ready pod/learn-nginx --timeout=180s kubectl get pod learn-nginx -o wide kubectl delete pod learn-nginx --wait=true log "listen" ss -lnt | grep 6443 || true check_health after-install echo "INSTALL_OK"