Ubuntu 22.04下K8S集群搭建避坑指南:从Docker到Harbor镜像仓库的全流程

最近在帮团队搭建一套用于内部开发测试的Kubernetes环境,选用了Ubuntu 22.04作为基础系统。整个过程走下来,发现网上很多教程要么过于简略,要么假设读者已经具备丰富的运维经验,对于初次接触容器编排的开发者来说,很容易在某个环节卡住,然后花费大量时间排查。这篇文章就是把我踩过的坑、验证过的优化点,以及如何构建一个包含私有镜像仓库的完整可运维环境,系统地梳理出来。无论你是想搭建个人学习环境,还是为中小团队部署一套稳定的容器平台,这里面的经验都能帮你少走弯路。

我们最终的目标是构建一个高可用的多节点K8S集群,并集成一个企业级的私有镜像仓库Harbor。整个流程会覆盖从系统初始化、容器运行时配置、集群部署,到镜像仓库的安装与对接。我会特别强调那些容易出错的地方,比如网络配置、服务依赖、版本兼容性等,并提供经过验证的解决方案。

1. 基础环境准备与系统调优

在开始安装任何软件之前,打好系统基础至关重要。Ubuntu 22.04本身是一个优秀的起点,但默认配置并不完全适合运行生产级别的Kubernetes集群。我们需要进行一系列优化,以确保集群的稳定性和性能。

1.1 系统初始化与网络配置

首先,确保所有节点(包括控制平面节点和工作节点)都能稳定联网,并且彼此之间可以通过主机名和IP地址互相访问。我建议在本地/etc/hosts文件中为所有节点添加静态解析,这能避免初期因DNS问题导致的集群组建失败。

# 在所有节点上执行,将以下内容添加到 /etc/hosts 文件末尾
# 请根据你的实际IP和主机名修改
192.168.1.101 master-01
192.168.1.102 master-02
192.168.1.103 master-03
192.168.1.111 worker-01
192.168.1.112 worker-02
192.168.1.113 worker-03
192.168.1.200 harbor-registry

接下来,禁用Swap分区。Kubernetes的设计要求节点不能使用交换内存,否则kubelet会无法启动。很多安装失败的根本原因就是Swap没有彻底关闭。

# 临时关闭当前所有Swap空间
sudo swapoff -a

# 永久禁用,编辑 /etc/fstab 文件,注释掉所有包含‘swap’的行
sudo sed -i '/swap/s/^/#/' /etc/fstab

# 验证Swap已关闭
free -h

注意:仅仅执行swapoff -a是临时生效,重启后会恢复。必须修改/etc/fstab文件才能永久生效。这是新手最容易忽略的一步。

1.2 内核参数与系统服务优化

Kubernetes对Linux内核参数有一定要求,特别是网络相关的参数。我们需要加载必要的内核模块并调整sysctl配置。

# 加载内核模块
sudo modprobe overlay
sudo modprobe br_netfilter

# 确保模块开机自动加载
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

# 配置网络相关的sysctl参数
cat <<EOF | sudo 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
EOF

# 应用配置
sudo sysctl --system

此外,确保系统基础工具齐全。虽然Ubuntu 22.04最小化安装可能缺少一些常用工具,但以下工具对于后续的安装和调试非常有用。

  • curl/wget: 用于从网络下载文件。
  • vim/nano: 文本编辑器,用于修改配置文件。
  • socat/conntrack: Kubernetes网络依赖的工具。
  • apt-transport-https/ca-certificates: 确保HTTPS源可用。

可以使用以下命令一次性安装:

sudo apt update && sudo apt install -y curl wget vim nano socat conntrack ipset apt-transport-https ca-certificates software-properties-common

2. 容器运行时安装:不止于Docker

Kubernetes需要一个容器运行时来拉取镜像和运行容器。虽然Docker是最广为人知的选择,但自Kubernetes 1.24版本起,它已不再内置dockershim,这意味着我们需要额外安装cri-dockerd适配器,或者直接选择其他兼容的运行时,如containerd。这里我提供两种主流方案的详细步骤和对比。

2.1 方案A:使用Containerd(推荐)

Containerd是CNCF毕业项目,现在是Kubernetes默认推荐的运行时。它比Docker更轻量,组件更清晰,故障点更少。

安装Containerd:

# 下载containerd的发布包(以1.7.13为例,请检查最新版本)
export CONTAINERD_VERSION="1.7.13"
wget https://github.com/containerd/containerd/releases/download/v${CONTAINERD_VERSION}/containerd-${CONTAINERD_VERSION}-linux-amd64.tar.gz

# 解压到系统目录
sudo tar Cxzvf /usr/local containerd-${CONTAINERD_VERSION}-linux-amd64.tar.gz

# 下载并安装runc
wget https://github.com/opencontainers/runc/releases/download/v1.1.10/runc.amd64
sudo install -m 755 runc.amd64 /usr/local/sbin/runc

# 生成并安装systemd服务文件
sudo mkdir -p /usr/local/lib/systemd/system/
sudo curl -o /usr/local/lib/systemd/system/containerd.service https://raw.githubusercontent.com/containerd/containerd/main/containerd.service
sudo systemctl daemon-reload
sudo systemctl enable --now containerd

配置Containerd: 生成默认配置文件,并修改其中的镜像加速器和cgroup驱动。Kubernetes推荐使用systemd作为cgroup驱动。

sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml

# 使用sed修改配置:启用systemd cgroup驱动,并配置国内镜像加速器
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo sed -i 's|registry.k8s.io/pause:3.9|registry.aliyuncs.com/google_containers/pause:3.9|' /etc/containerd/config.toml

# 重启containerd使配置生效
sudo systemctl restart containerd
sudo systemctl status containerd

2.2 方案B:使用Docker + cri-dockerd

如果你或你的团队对Docker生态工具(如docker build, docker-compose)有强依赖,可以选择此方案。

安装Docker Engine: 官方提供了便捷的安装脚本,但为了可控性,我建议使用APT仓库安装。

# 添加Docker官方GPG密钥和仓库
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 配置镜像加速和cgroup驱动
sudo mkdir -p /etc/docker
cat <<EOF | sudo tee /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2",
  "registry-mirrors": ["https://your-mirror.mirror.aliyuncs.com"]
}
EOF

sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl enable docker

安装cri-dockerd适配器: 这是让Kubelet通过CRI接口与Docker通信的关键桥梁。

# 下载并安装cri-dockerd
export CRIDOCKERD_VERSION="0.3.7"
wget https://github.com/Mirantis/cri-dockerd/releases/download/v${CRIDOCKERD_VERSION}/cri-dockerd-${CRIDOCKERD_VERSION}.amd64.tgz
tar xzvf cri-dockerd-${CRIDOCKERD_VERSION}.amd64.tgz
sudo mv cri-dockerd/cri-dockerd /usr/local/bin/

# 安装systemd服务文件
wget https://raw.githubusercontent.com/Mirantis/cri-dockerd/master/packaging/systemd/cri-docker.service
wget https://raw.githubusercontent.com/Mirantis/cri-dockerd/master/packaging/systemd/cri-docker.socket
sudo mv cri-docker.service cri-docker.socket /etc/systemd/system/
sudo sed -i -e 's,/usr/bin/cri-dockerd,/usr/local/bin/cri-dockerd,' /etc/systemd/system/cri-docker.service

sudo systemctl daemon-reload
sudo systemctl enable cri-docker.service
sudo systemctl enable --now cri-docker.socket

两种方案的对比如下:

特性维度ContainerdDocker + cri-dockerd
架构复杂度轻量,组件少较重,包含Docker Daemon、containerd、cri-dockerd多层
资源占用内存和CPU占用更低相对较高
稳定性作为K8s原生选择,兼容性更好依赖适配层,可能引入额外问题
运维工具链主要使用crictl可使用完整的docker命令,对开发者更友好
学习成本需要学习新的CLI工具沿用熟悉的Docker命令

提示:对于生产环境,我强烈推荐使用Containerd。它更简洁,与Kubernetes的集成更紧密,出问题的概率更低。如果你只是搭建一个开发测试环境,并且重度依赖Docker CLI,那么可以选择方案B。

3. 使用kubeadm部署高可用Kubernetes集群

Kubeadm是Kubernetes官方推荐的集群部署工具,它简化了初始化流程,但依然保留了足够的灵活性。我们将部署一个包含三个控制平面节点的高可用集群,以及三个工作节点。

3.1 安装kubeadm、kubelet和kubectl

在所有节点(包括控制平面和工作节点)上执行以下步骤。

# 添加Kubernetes APT仓库的GPG密钥
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

# 添加Kubernetes APT仓库
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list

# 安装组件(这里以1.28.5为例,请选择稳定的版本)
sudo apt update
sudo apt install -y kubelet=1.28.5-1.1 kubeadm=1.28.5-1.1 kubectl=1.28.5-1.1
sudo apt-mark hold kubelet kubeadm kubectl # 防止被意外升级

关键避坑点:版本兼容性 务必确保kubeadmkubeletkubectl以及容器运行时的版本是兼容的。你可以查阅Kubernetes官方发布说明。安装后,kubelet服务会处于启动失败状态,这是正常的,因为还没有生成配置文件,等集群初始化完成后就会正常。

3.2 初始化第一个控制平面节点

选择master-01作为初始化的主节点。

# 在master-01上执行
# 使用你选择的容器运行时套接字路径
# 如果是containerd,使用 --cri-socket unix:///var/run/containerd/containerd.sock
# 如果是cri-dockerd,使用 --cri-socket unix:///var/run/cri-dockerd.sock

sudo kubeadm init \
  --control-plane-endpoint="k8s-api.yourdomain.com:6443" \
  --upload-certs \
  --pod-network-cidr=10.244.0.0/16 \
  --cri-socket unix:///var/run/containerd/containerd.sock \
  --image-repository registry.aliyuncs.com/google_containers

命令参数解释:

  • --control-plane-endpoint: 这是高可用的关键。应指向一个负载均衡器(如HAProxy)的VIP,或者一个DNS名称(后期可以通过修改/etc/hosts或DNS服务器指向负载均衡器)。初期测试可以先填写第一个控制平面节点的IP和端口,例如192.168.1.101:6443
  • --upload-certs: 自动将证书上传到集群,方便其他控制平面节点加入。
  • --pod-network-cidr: 设置Pod网络的CIDR,需要与你后续安装的网络插件匹配(这里以Flannel为例)。
  • --image-repository: 使用国内镜像源,加速镜像拉取。

初始化成功后,会输出类似下面的信息,其中包含加入集群所需的命令,务必妥善保存

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

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

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of control-plane nodes by running the following on each as root:

  kubeadm join k8s-api.yourdomain.com:6443 --token ... --discovery-token-ca-cert-hash sha256:... \
    --control-plane --certificate-key ...

You can now join any number of worker nodes by running the following on each as root:

  kubeadm join k8s-api.yourdomain.com:6443 --token ... --discovery-token-ca-cert-hash sha256:...

按照提示,配置kubectl

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

3.3 安装Pod网络插件(CNI)

没有网络插件,Pod之间无法通信。我们选择经典的Flannel。

kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

等待片刻,使用kubectl get pods -n kube-system查看,直到所有kube-flannel Pod都处于Running状态。

3.4 加入其他控制平面节点和工作节点

将初始化成功输出的kubeadm join命令,分别在master-02master-03以及所有worker节点上以root权限执行。

验证集群状态: 在所有控制平面节点都加入后,执行以下命令检查集群健康状况。

kubectl get nodes -o wide
kubectl get pods -n kube-system -o wide

你应该看到所有节点状态都是Ready,并且核心系统Pod(如corednskube-proxykube-flannel)都运行正常。

4. 部署与配置Harbor私有镜像仓库

拥有一个私有镜像仓库对于企业开发流程至关重要。Harbor提供了镜像存储、安全扫描、权限管理、复制等高级功能,远超简单的Docker Registry。

4.1 Harbor服务器准备

我们在一台独立的服务器(harbor-registry)上部署Harbor。这台服务器需要安装好Docker和Docker Compose(Harbor v2.x 以Docker Compose方式部署)。

# 在harbor-registry服务器上安装Docker Compose
# 从GitHub Release下载特定版本(以v2.23.0为例)
sudo curl -L "https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version # 验证安装

4.2 下载与配置Harbor

访问Harbor GitHub Release页面,下载离线安装包(包含所有所需镜像)。

# 下载并解压(以v2.10.0为例)
wget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-offline-installer-v2.10.0.tgz
tar xzvf harbor-offline-installer-v2.10.0.tgz
cd harbor

修改配置文件harbor.yml。以下是最关键的几项配置:

# 设置访问Harbor的主机名或IP地址
hostname: harbor-registry.yourdomain.com # 或直接使用IP,如 192.168.1.200

# 设置HTTP端口(如果不用HTTPS)
http:
  port: 8080

# 注释掉HTTPS部分以使用HTTP(仅限内网测试环境)
# https:
#   port: 443
#   certificate: /your/certificate/path
#   private_key: /your/private/key/path

# Harbor管理员初始密码
harbor_admin_password: YourStrongAdminPassword123

# 数据库密码
database:
  password: root123

# 数据持久化目录
data_volume: /data/harbor

注意:生产环境必须配置HTTPS。你可以使用自签名证书或从Let‘s Encrypt获取免费证书。配置HTTPS时,需要将证书和私钥的路径正确填写到配置文件中。

4.3 安装与启动Harbor

执行安装脚本,它会根据配置文件拉取镜像并启动所有服务。

sudo ./install.sh

安装成功后,会提示访问地址和管理员账号信息。通过浏览器访问 http://harbor-registry.yourdomain.com:8080 即可登录管理界面。

4.4 将Kubernetes集群对接Harbor

要让K8S集群能从私有仓库拉取镜像,需要在集群中创建对应的Secret。

首先,在Harbor界面创建一个项目(例如dev),并创建一个具有推送拉取权限的用户。

然后,在Kubernetes集群的每个节点上,登录Harbor:

sudo docker login harbor-registry.yourdomain.com:8080 -u your-username -p your-password

对于Containerd运行时,需要将Docker凭证转换为Containerd格式,或者直接在/etc/containerd/config.toml中配置镜像仓库认证。

更推荐的方式是在K8S中创建imagePullSecret:

# 在需要拉取私有镜像的命名空间中创建Secret
kubectl create secret docker-registry harbor-registry-secret \
  --docker-server=harbor-registry.yourdomain.com:8080 \
  --docker-username=your-username \
  --docker-password=your-password \
  --namespace=default

在Pod的YAML文件中引用这个Secret:

apiVersion: v1
kind: Pod
metadata:
  name: myapp
spec:
  containers:
  - name: myapp
    image: harbor-registry.yourdomain.com:8080/dev/myapp:v1.0
  imagePullSecrets:
  - name: harbor-registry-secret

4.5 Harbor日常维护与故障排查

Harbor由多个容器组成,日常维护主要使用docker-compose命令。

# 进入Harbor安装目录
cd /path/to/harbor

# 查看所有服务状态
sudo docker-compose ps

# 停止Harbor
sudo docker-compose down

# 启动Harbor
sudo docker-compose up -d

# 查看特定服务的日志(例如core)
sudo docker-compose logs core -f

常见问题:

  1. 上传镜像失败,提示“413 Request Entity Too Large”:需要调整Nginx(Harbor的代理组件)的客户端最大body大小。修改harbor.yml中的storage_proxy部分,或直接修改生成的docker-compose.yml中nginx服务的配置,添加client_max_body_size 1024m;
  2. 登录失败,提示“x509: certificate signed by unknown authority”:当使用自签名证书时,需要在K8S节点和Docker客户端信任该证书。将Harbor的CA证书复制到/etc/docker/certs.d/harbor-registry.yourdomain.com:8080/ca.crt(Docker)和/usr/local/share/ca-certificates/(系统)并执行update-ca-certificates
  3. 磁盘空间不足:Harbor的镜像和Chart数据默认存储在data_volume配置的路径下。定期清理未使用的镜像或设置存储配额策略。

整个流程走下来,最深的体会是“细节决定成败”。比如,系统时区不一致可能导致证书验证出错,/etc/hosts配置错误会让节点间通信彻底中断,而容器运行时的一个小版本不兼容就可能让集群初始化卡住半天。建议在每一步操作后都进行简单的验证,例如安装完容器运行时后拉取一个hello-world镜像,初始化完集群后立刻检查核心Pod状态,不要等到所有步骤做完再回头排查。

更多推荐