离线单节点部署
Kubernetes 离线单节点部署
本章介绍如何在无互联网连接的环境中部署单节点 Kubernetes 集群,适用于内网开发和测试环境。
部署概述
离线单节点部署需要提前在联网环境准备好所有必需的安装包,然后在目标服务器上进行离线安装。
流程图:
联网环境(准备) 目标服务器(部署)
┌─────────────┐ ┌─────────────┐
│ 下载二进制 │ │ 传输离线包 │
│ 导出镜像 │ ──────> │ 安装组件 │
│ 打包资源 │ │ 初始化集群 │
└─────────────┘ └─────────────┘
特点:
- ✅ 不依赖外网
- ✅ 部署速度快
- ✅ 版本可控
- ✅ 适合内网环境
- ❌ 准备工作较复杂
适用场景:
- 🔒 内网隔离的开发环境
- 🧪 安全测试环境
- 📚 培训和演示(无网络)
一、环境要求
1.1 硬件要求
| 配置级别 | CPU | 内存 | 磁盘 | 适用场景 |
|---|---|---|---|---|
| 最低配置 | 2 核 | 4GB | 30GB | 学习 |
| 推荐配置 | 4 核 | 8GB | 60GB | 开发 |
| 高配 | 8 核 | 16GB | 120GB | 测试 |
1.2 软件要求
- 操作系统:Ubuntu 22.04 / CentOS 8+ / RHEL 8+
- 内核版本:>= 4.19
- Python:>= 3.6(用于运行部署脚本)
二、准备离线资源包(联网环境)
2.1 创建工作目录
mkdir -p ~/k8s-offline/{images,packages,scripts,configs}
cd ~/k8s-offline
2.2 设置版本变量
# 定义版本号
export K8S_VERSION=1.30.0
export CONTAINERD_VERSION=1.7.11
export RUNC_VERSION=1.1.10
export CNI_VERSION=1.4.0
export CRICTL_VERSION=1.30.0
export CALICO_VERSION=3.27.0
# 保存版本信息
cat <<EOF > versions.txt
K8S_VERSION=$K8S_VERSION
CONTAINERD_VERSION=$CONTAINERD_VERSION
RUNC_VERSION=$RUNC_VERSION
CNI_VERSION=$CNI_VERSION
CRICTL_VERSION=$CRICTL_VERSION
CALICO_VERSION=$CALICO_VERSION
EOF
2.3 下载二进制文件
cd packages
echo "下载 Kubernetes 组件..."
wget https://dl.k8s.io/release/v${K8S_VERSION}/bin/linux/amd64/kubeadm
wget https://dl.k8s.io/release/v${K8S_VERSION}/bin/linux/amd64/kubelet
wget https://dl.k8s.io/release/v${K8S_VERSION}/bin/linux/amd64/kubectl
echo "下载 containerd..."
wget https://github.com/containerd/containerd/releases/download/v${CONTAINERD_VERSION}/containerd-${CONTAINERD_VERSION}-linux-amd64.tar.gz
echo "下载 runc..."
wget https://github.com/opencontainers/runc/releases/download/v${RUNC_VERSION}/runc.amd64
echo "下载 CNI plugins..."
wget https://github.com/containernetworking/plugins/releases/download/v${CNI_VERSION}/cni-plugins-linux-amd64-v${CNI_VERSION}.tgz
echo "下载 crictl..."
wget https://github.com/kubernetes-sigs/cri-tools/releases/download/v${CRICTL_VERSION}/crictl-v${CRICTL_VERSION}-linux-amd64.tar.gz
echo "下载 systemd 服务文件..."
wget -O containerd.service https://raw.githubusercontent.com/containerd/containerd/main/containerd.service
wget -O kubelet.service https://raw.githubusercontent.com/kubernetes/release/master/cmd/krel/templates/latest/kubelet/kubelet.service
wget -O 10-kubeadm.conf https://raw.githubusercontent.com/kubernetes/release/master/cmd/krel/templates/latest/kubeadm/10-kubeadm.conf
cd ..
echo "✅ 二进制文件下载完成"
2.4 导出容器镜像
cd images
# 生成镜像列表
echo "生成镜像列表..."
kubeadm config images list --kubernetes-version v${K8S_VERSION} > image-list.txt
# 添加网络插件镜像
cat <<EOF >> image-list.txt
docker.io/calico/cni:v${CALICO_VERSION}
docker.io/calico/node:v${CALICO_VERSION}
docker.io/calico/kube-controllers:v${CALICO_VERSION}
quay.io/tigera/operator:v1.32.0
EOF
echo "镜像列表:"
cat image-list.txt
# 拉取并导出镜像
echo "开始拉取并导出镜像..."
while IFS= read -r image; do
echo "处理镜像: $image"
sudo crictl pull "$image" || {
echo "警告: $image 拉取失败,跳过"
continue
}
image_file=$(echo "$image" | tr '/:' '_').tar
sudo ctr -n k8s.io image export "$image_file" "$image" || {
echo "警告: $image 导出失败"
continue
}
echo "✅ $image 已导出"
done < image-list.txt
cd ..
echo "✅ 镜像导出完成"
ls -lh images/*.tar
2.5 下载网络插件配置
cd configs
echo "下载 Calico 配置文件..."
wget https://raw.githubusercontent.com/projectcalico/calico/v${CALICO_VERSION}/manifests/calico.yaml
cd ..
2.6 创建安装脚本
系统准备脚本:
cat <<'EOF' > scripts/prepare-system.sh
#!/bin/bash
# 系统准备脚本
set -e
GREEN='\033[0;32m'
NC='\033[0m'
echo -e "${GREEN}[1/6] 禁用 swap...${NC}"
swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
echo -e "${GREEN}[2/6] 加载内核模块...${NC}"
cat <<EOFMOD | tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOFMOD
modprobe overlay
modprobe br_netfilter
echo -e "${GREEN}[3/6] 配置内核参数...${NC}"
cat <<EOFSYS | tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOFSYS
sysctl --system > /dev/null
echo -e "${GREEN}[4/6] 关闭防火墙...${NC}"
if command -v ufw &> /dev/null; then
ufw disable || true
elif command -v systemctl &> /dev/null; then
systemctl stop firewalld || true
systemctl disable firewalld || true
fi
echo -e "${GREEN}[5/6] 禁用 SELinux...${NC}"
if [ -f /etc/selinux/config ]; then
setenforce 0 || true
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
fi
echo -e "${GREEN}[6/6] 验证配置...${NC}"
lsmod | grep br_netfilter
lsmod | grep overlay
echo -e "${GREEN}✅ 系统准备完成${NC}"
EOF
chmod +x scripts/prepare-system.sh
安装 containerd 脚本:
cat <<'EOF' > scripts/install-containerd.sh
#!/bin/bash
# 安装 containerd
set -e
WORK_DIR=/root/k8s-offline
GREEN='\033[0;32m'
NC='\033[0m'
echo -e "${GREEN}[1/7] 解压 containerd...${NC}"
tar Cxzvf /usr/local "${WORK_DIR}/packages/containerd-"*.tar.gz
echo -e "${GREEN}[2/7] 安装 runc...${NC}"
install -m 755 "${WORK_DIR}/packages/runc.amd64" /usr/local/sbin/runc
echo -e "${GREEN}[3/7] 安装 CNI plugins...${NC}"
mkdir -p /opt/cni/bin
tar Cxzvf /opt/cni/bin "${WORK_DIR}/packages/cni-plugins-linux-amd64-"*.tgz
echo -e "${GREEN}[4/7] 安装 crictl...${NC}"
tar -xzvf "${WORK_DIR}/packages/crictl-"*.tar.gz -C /usr/local/bin/
echo -e "${GREEN}[5/7] 创建 containerd 配置...${NC}"
mkdir -p /etc/containerd
containerd config default | tee /etc/containerd/config.toml > /dev/null
echo -e "${GREEN}[6/7] 配置 systemd cgroup 驱动...${NC}"
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
echo -e "${GREEN}[7/7] 启动 containerd...${NC}"
cp "${WORK_DIR}/packages/containerd.service" /etc/systemd/system/
systemctl daemon-reload
systemctl enable containerd
systemctl start containerd
echo -e "${GREEN}✅ containerd 安装完成${NC}"
systemctl status containerd --no-pager
EOF
chmod +x scripts/install-containerd.sh
安装 Kubernetes 组件脚本:
cat <<'EOF' > scripts/install-k8s.sh
#!/bin/bash
# 安装 Kubernetes 组件
set -e
WORK_DIR=/root/k8s-offline
GREEN='\033[0;32m'
NC='\033[0m'
echo -e "${GREEN}[1/4] 安装 kubeadm、kubelet、kubectl...${NC}"
install -m 755 "${WORK_DIR}/packages/kubeadm" /usr/local/bin/
install -m 755 "${WORK_DIR}/packages/kubelet" /usr/local/bin/
install -m 755 "${WORK_DIR}/packages/kubectl" /usr/local/bin/
echo -e "${GREEN}[2/4] 创建 kubelet 目录...${NC}"
mkdir -p /etc/systemd/system/kubelet.service.d
mkdir -p /var/lib/kubelet
echo -e "${GREEN}[3/4] 安装 systemd 服务...${NC}"
cp "${WORK_DIR}/packages/kubelet.service" /etc/systemd/system/
cp "${WORK_DIR}/packages/10-kubeadm.conf" /etc/systemd/system/kubelet.service.d/
# 修复路径
sed -i 's|/usr/bin|/usr/local/bin|g' /etc/systemd/system/kubelet.service
sed -i 's|/usr/bin|/usr/local/bin|g' /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
echo -e "${GREEN}[4/4] 启动 kubelet...${NC}"
systemctl daemon-reload
systemctl enable kubelet
echo -e "${GREEN}✅ Kubernetes 组件安装完成${NC}"
kubeadm version
kubelet --version
kubectl version --client
EOF
chmod +x scripts/install-k8s.sh
导入镜像脚本:
cat <<'EOF' > scripts/load-images.sh
#!/bin/bash
# 导入容器镜像
set -e
WORK_DIR=/root/k8s-offline
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
cd "${WORK_DIR}/images"
echo -e "${GREEN}开始导入镜像...${NC}"
total=$(ls -1 *.tar 2>/dev/null | wc -l)
count=0
for tar_file in *.tar; do
count=$((count + 1))
echo -e "${YELLOW}[$count/$total] 导入 $tar_file...${NC}"
ctr -n k8s.io image import "$tar_file" || {
echo "警告: $tar_file 导入失败"
}
done
echo -e "${GREEN}✅ 所有镜像已导入${NC}"
echo "已导入的镜像列表:"
crictl images
EOF
chmod +x scripts/load-images.sh
一键部署脚本:
cat <<'EOF' > scripts/deploy-single.sh
#!/bin/bash
# 单节点离线部署一键脚本
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
echo_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
echo_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# 检查 root 权限
if [ "$EUID" -ne 0 ]; then
echo_error "请使用 root 用户运行此脚本"
exit 1
fi
echo ""
echo "==================================="
echo " Kubernetes 单节点离线部署"
echo "==================================="
echo ""
read -p "请输入节点 IP 地址: " NODE_IP
if [ -z "$NODE_IP" ]; then
echo_error "IP 地址不能为空"
exit 1
fi
read -p "请输入节点主机名 [k8s-single]: " HOSTNAME
HOSTNAME=${HOSTNAME:-k8s-single}
echo ""
echo_info "配置信息:"
echo_info " 节点 IP: $NODE_IP"
echo_info " 主机名: $HOSTNAME"
echo ""
read -p "确认无误?(y/n): " confirm
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
exit 0
fi
WORK_DIR=/root/k8s-offline
cd "$WORK_DIR"
# 步骤 1: 系统准备
echo ""
echo_info "====== 步骤 1/6: 系统准备 ======"
./scripts/prepare-system.sh
# 设置主机名
hostnamectl set-hostname $HOSTNAME
echo "127.0.0.1 $HOSTNAME" >> /etc/hosts
# 步骤 2: 安装 containerd
echo ""
echo_info "====== 步骤 2/6: 安装 containerd ======"
./scripts/install-containerd.sh
# 步骤 3: 导入镜像
echo ""
echo_info "====== 步骤 3/6: 导入容器镜像 ======"
./scripts/load-images.sh
# 步骤 4: 安装 Kubernetes 组件
echo ""
echo_info "====== 步骤 4/6: 安装 Kubernetes 组件 ======"
./scripts/install-k8s.sh
# 步骤 5: 初始化集群
echo ""
echo_info "====== 步骤 5/6: 初始化集群 ======"
kubeadm init \
--kubernetes-version=v1.30.0 \
--pod-network-cidr=10.244.0.0/16 \
--apiserver-advertise-address=$NODE_IP | tee /tmp/kubeadm-init.log
# 配置 kubectl
mkdir -p /root/.kube
cp -i /etc/kubernetes/admin.conf /root/.kube/config
# 移除 Master 污点
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
# 步骤 6: 安装网络插件
echo ""
echo_info "====== 步骤 6/6: 安装网络插件 ======"
kubectl apply -f configs/calico.yaml
echo ""
echo_info "========================================="
echo_info " 单节点部署完成!"
echo_info "========================================="
echo ""
echo_info "集群信息:"
echo_info " kubectl get nodes"
echo ""
kubectl get nodes
echo ""
echo_info "系统 Pod 状态:"
kubectl get pods -n kube-system
echo ""
echo_info "部署完成!请等待所有 Pod 启动(约 2-3 分钟)"
EOF
chmod +x scripts/deploy-single.sh
2.7 打包离线资源
cd ~
# 创建 README
cat <<EOF > k8s-offline/README.md
# Kubernetes ${K8S_VERSION} 离线单节点部署包
## 版本信息
$(cat k8s-offline/versions.txt)
## 目录结构
- images/ - 容器镜像文件
- packages/ - 二进制文件
- scripts/ - 安装脚本
- configs/ - 配置文件
## 快速部署
\`\`\`bash
tar -xzf k8s-offline-single-v${K8S_VERSION}.tar.gz
cd k8s-offline
sudo ./scripts/deploy-single.sh
\`\`\`
## 手动部署
\`\`\`bash
sudo ./scripts/prepare-system.sh
sudo ./scripts/install-containerd.sh
sudo ./scripts/load-images.sh
sudo ./scripts/install-k8s.sh
# 然后手动初始化集群
\`\`\`
EOF
# 打包
echo "正在打包离线资源..."
tar -czf k8s-offline-single-v${K8S_VERSION}.tar.gz k8s-offline/
# 生成校验和
md5sum k8s-offline-single-v${K8S_VERSION}.tar.gz > k8s-offline-single-v${K8S_VERSION}.tar.gz.md5
sha256sum k8s-offline-single-v${K8S_VERSION}.tar.gz > k8s-offline-single-v${K8S_VERSION}.tar.gz.sha256
echo ""
echo "✅ 离线包制作完成!"
echo ""
ls -lh k8s-offline-single-v${K8S_VERSION}.tar.gz*
echo ""
echo "MD5: $(cat k8s-offline-single-v${K8S_VERSION}.tar.gz.md5)"
三、离线部署(目标服务器)
3.1 传输离线包
# 方式1: USB 存储
# 直接复制到 U 盘
# 方式2: 内网 SCP
scp k8s-offline-single-v1.30.0.tar.gz root@192.168.1.10:/root/
# 方式3: NFS 共享
# mount -t nfs 192.168.1.100:/share /mnt
# cp /mnt/k8s-offline-single-v1.30.0.tar.gz /root/
3.2 验证完整性
cd /root
# MD5 校验
md5sum -c k8s-offline-single-v1.30.0.tar.gz.md5
# SHA256 校验
sha256sum -c k8s-offline-single-v1.30.0.tar.gz.sha256
# 解压
tar -xzf k8s-offline-single-v1.30.0.tar.gz
cd k8s-offline
3.3 一键部署
# 执行一键部署脚本
sudo ./scripts/deploy-single.sh
# 脚本会提示输入:
# 1. 节点 IP 地址
# 2. 节点主机名(可选)
# 3. 自动执行所有安装步骤
部署过程输出:
===================================
Kubernetes 单节点离线部署
===================================
[INFO] ====== 步骤 1/6: 系统准备 ======
[INFO] [1/6] 禁用 swap...
[INFO] [2/6] 加载内核模块...
...
[INFO] ====== 步骤 2/6: 安装 containerd ======
[INFO] [1/7] 解压 containerd...
...
[INFO] ====== 步骤 3/6: 导入容器镜像 ======
[INFO] 开始导入镜像...
[1/10] 导入 registry.k8s.io_kube-apiserver_v1.30.0.tar...
...
[INFO] ====== 步骤 4/6: 安装 Kubernetes 组件 ======
[INFO] [1/4] 安装 kubeadm、kubelet、kubectl...
...
[INFO] ====== 步骤 5/6: 初始化集群 ======
[init] Using Kubernetes version: v1.30.0
...
[INFO] ====== 步骤 6/6: 安装网络插件 ======
daemonset.apps/calico-node created
deployment.apps/calico-kube-controllers created
[INFO] =========================================
[INFO] 单节点部署完成!
[INFO] =========================================
3.4 手动部署(可选)
如果需要更细粒度的控制:
# 步骤 1: 系统准备
sudo ./scripts/prepare-system.sh
# 步骤 2: 安装 containerd
sudo ./scripts/install-containerd.sh
# 步骤 3: 导入镜像
sudo ./scripts/load-images.sh
# 步骤 4: 安装 Kubernetes 组件
sudo ./scripts/install-k8s.sh
# 步骤 5: 初始化集群
sudo kubeadm init \
--kubernetes-version=v1.30.0 \
--pod-network-cidr=10.244.0.0/16
# 配置 kubectl
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 移除 Master 污点
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
# 步骤 6: 安装网络插件
kubectl apply -f configs/calico.yaml
四、验证部署
4.1 检查节点状态
# 查看节点
kubectl get nodes
# 输出示例
NAME STATUS ROLES AGE VERSION
k8s-single Ready control-plane 5m v1.30.0
4.2 检查系统 Pod
# 查看所有 Pod
kubectl get pods --all-namespaces
# 等待所有 Pod Running
kubectl wait --for=condition=Ready pods --all -n kube-system --timeout=300s
4.3 部署测试应用
# 创建 Deployment
kubectl create deployment nginx --image=nginx:1.25 --replicas=2
# 查看 Pod
kubectl get pods -o wide
# 暴露服务
kubectl expose deployment nginx --port=80 --type=NodePort
# 测试访问
kubectl get svc nginx
curl http://192.168.1.10:<node-port>
# 清理
kubectl delete deployment nginx
kubectl delete service nginx
五、常见问题排查
5.1 镜像导入失败
# 检查镜像文件
ls -lh images/*.tar
# 手动导入单个镜像
sudo ctr -n k8s.io image import images/<image-file>.tar
# 查看已导入的镜像
crictl images
5.2 containerd 启动失败
# 查看日志
journalctl -u containerd -f
# 检查配置
cat /etc/containerd/config.toml
# 重新生成配置
sudo rm /etc/containerd/config.toml
containerd config default | sudo tee /etc/containerd/config.toml
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl restart containerd
5.3 Pod 一直 Pending
# 检查节点状态
kubectl describe node
# 检查污点
kubectl describe node | grep Taints
# 确认已移除污点
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
六、离线升级
6.1 准备新版本离线包
在联网环境重复第二章步骤,使用新版本号。
6.2 升级流程
# 1. 传输新版本离线包
scp k8s-offline-single-v1.30.1.tar.gz root@server:/root/
# 2. 解压
tar -xzf k8s-offline-single-v1.30.1.tar.gz
cd k8s-offline
# 3. 导入新镜像
sudo ./scripts/load-images.sh
# 4. 升级 kubeadm
sudo install -m 755 packages/kubeadm /usr/local/bin/
# 5. 查看升级计划
sudo kubeadm upgrade plan
# 6. 执行升级
sudo kubeadm upgrade apply v1.30.1
# 7. 升级 kubelet 和 kubectl
sudo install -m 755 packages/kubelet /usr/local/bin/
sudo install -m 755 packages/kubectl /usr/local/bin/
sudo systemctl daemon-reload
sudo systemctl restart kubelet
# 8. 验证
kubectl get nodes
七、维护管理
7.1 备份离线包
# 保存多个版本
/backup/
├── k8s-offline-single-v1.30.0.tar.gz
├── k8s-offline-single-v1.30.1.tar.gz
└── k8s-offline-single-v1.31.0.tar.gz
7.2 备份集群配置
# 备份 kubectl 配置
cp ~/.kube/config ~/.kube/config.backup
# 备份所有资源
kubectl get all --all-namespaces -o yaml > cluster-backup.yaml
# 备份 etcd(重要)
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-snapshot.db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
7.3 重置集群
# 重置 kubeadm
sudo kubeadm reset
# 清理配置
sudo rm -rf /etc/cni/net.d
sudo rm -rf ~/.kube/config
# 清理 iptables
sudo iptables -F && sudo iptables -t nat -F && sudo iptables -X
八、最佳实践
8.1 离线包管理
- ✅ 使用版本号命名离线包
- ✅ 保留多个历史版本
- ✅ 定期更新离线包
- ✅ 验证校验和确保完整性
- ✅ 制作详细的部署文档
8.2 部署规范
- ✅ 测试离线包后再用于生产
- ✅ 记录部署日志
- ✅ 制定回滚计划
- ✅ 保持版本一致性
8.3 安全建议
- ✅ 离线包加密存储
- ✅ 限制访问权限
- ✅ 审计部署操作
- ✅ 定期安全扫描
九、故障恢复
9.1 快速恢复脚本
cat <<'EOF' > /usr/local/bin/k8s-recover.sh
#!/bin/bash
# Kubernetes 快速恢复脚本
set -e
echo "开始恢复 Kubernetes 集群..."
# 1. 检查服务状态
systemctl restart containerd
systemctl restart kubelet
# 2. 等待服务启动
sleep 10
# 3. 检查集群状态
kubectl get nodes
kubectl get pods -A
echo "✅ 恢复完成"
EOF
chmod +x /usr/local/bin/k8s-recover.sh
小结
本章介绍了离线单节点部署:
✅ 完整离线包:包含所有必需组件
✅ 一键部署:30 分钟完成部署
✅ 不依赖网络:适合内网环境
✅ 版本可控:稳定可靠
✅ 易于维护:脚本化管理
✅ 支持升级:离线升级流程
准备时间:1-2 小时(联网环境)
部署时间:30 分钟(目标服务器)
适用场景:内网开发、测试环境
下一步:
- 学习离线高可用部署
- 了解在线部署方案
- 开始Kubernetes 基础教程