CentOS 8部署Kubernetes 1.18集群:从系统配置到网络部署全指南

CentOS 8部署Kubernetes 1.18集群:从系统配置到网络部署全指南

1. 项目概述与核心价值

最近在整理内部技术资产,翻出来一份几年前在CentOS 8上手动部署Kubernetes 1.18集群的详细记录。虽然现在Kubernetes版本已经迭代到了v1.30+,CentOS 8也早已停止维护,但这个组合在特定场景下依然有很强的参考价值。很多企业的遗留系统、受控的离线环境,或者一些对稳定性有极致要求的生产场景,仍然可能被“锁定”在某个经过充分验证的特定版本上。部署一个Kubernetes集群,远不是敲几行kubeadm init就能完事的,尤其是在操作系统版本也相对“老”的情况下,你会遇到各种依赖冲突、服务配置和网络问题。这份记录,就是我当年踩了无数坑之后,梳理出来的一套从零开始、手把手式的部署指南,目标是把一个高可用的Kubernetes 1.18集群稳稳当当地跑在CentOS 8上。

这个项目适合谁呢?如果你是运维工程师、DevOps或正在学习K8s的开发者,需要在一个相对“干净”但版本特定的Linux环境上搭建用于开发、测试甚至特定生产用途的Kubernetes集群,那么这篇内容会非常有用。它不仅仅是一份命令清单,我会详细解释每一条命令背后的意图、每一个配置项的作用,以及当命令执行失败时,你应该去哪里找线索、怎么排查。毕竟,部署的成功只是一瞬间,而理解整个系统如何协同工作,才是应对日后各种运维挑战的关键。

2. 环境准备与系统基础配置

在真正开始安装Kubernetes组件之前,我们必须为集群搭建一个稳固的“地基”。这个阶段的工作看似琐碎,但至关重要,它直接决定了后续安装过程是顺风顺水还是一路坎坷。很多初学者部署失败,问题往往就出在基础环境没有配置好。

2.1 操作系统与硬件要求

首先,我们明确一下基础环境。我使用的是CentOS 8.2,内核版本为4.18.0。虽然CentOS 8的生命周期已结束,但在这个时间点上,它的软件包版本与Kubernetes 1.18有较好的兼容性。你需要准备至少两台机器(一台Master控制节点,一台Node工作节点),当然,三台可以构建一个更简单的高可用方案。每台机器建议配置:2核CPU、4GB内存、20GB磁盘。这只是最低要求,生产环境请根据实际负载大幅提高配置。

第一件事,是确保所有节点的主机名和网络配置正确且能相互通信。为每台机器设置一个易于识别的主机名,例如k8s-masterk8s-node1

# 在Master节点执行 hostnamectl set-hostname k8s-master # 在Node节点执行 hostnamectl set-hostname k8s-node1

然后,编辑每台机器的/etc/hosts文件,添加所有集群节点的IP和主机名映射。这一步极其重要,因为Kubernetes的很多组件(如kubelet、API Server)会通过主机名进行通信,如果DNS解析失败,会导致集群状态异常。

# 假设你的机器IP如下,请替换为实际IP 192.168.1.100 k8s-master 192.168.1.101 k8s-node1

配置完成后,务必使用ping命令测试所有节点间的网络连通性,确保能通过主机名和IP互相ping通。

2.2 系统服务与防火墙配置

CentOS 8默认使用firewalld作为防火墙,而Kubernetes集群内部组件需要大量的端口互相通信。一个常见的做法是直接关闭防火墙和SELinux以简化初期部署,但这在生产环境中是危险的。更推荐的做法是配置防火墙规则,只开放必要的端口。

对于Kubernetes 1.18,Master节点需要开放6443(API Server)、2379-2380(etcd)、10250(kubelet API)、10251(scheduler)、10252(controller manager)等端口。Node节点需要开放10250、30000-32767(NodePort服务范围)等端口。为了教程的清晰和可复现性,我们这里先采用关闭防火墙的策略,但你必须清楚,在生产环境中这是不可接受的。

# 关闭防火墙 systemctl stop firewalld systemctl disable firewalld # 关闭SELinux(需要重启生效,或临时设置为permissive) setenforce 0 sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

注意:这只是为了部署流程顺畅。在实际生产环境中,你应该制定严格的防火墙策略,仅允许可信来源访问特定端口,并保持SELinux处于enforcing模式,通过定制策略来保障安全。

2.3 安装基础依赖与配置系统参数

Kubernetes运行需要一些基础工具和优化的系统参数。首先安装必要的软件包:

dnf install -y yum-utils device-mapper-persistent-data lvm2

接下来是几个关键的系统内核参数调整,它们影响着容器的网络、桥接流量和内存管理。编辑/etc/sysctl.d/k8s.conf文件:

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 vm.swappiness = 0 EOF

执行sysctl --system使配置生效。其中,net.ipv4.ip_forward=1是核心,它允许Linux主机转发IP数据包,这是Pod跨节点通信的基础。bridge-nf-call-iptables参数则确保iptables规则能正确应用于桥接设备上的流量,这是Service网络正常工作的前提。

最后,关闭系统的Swap交换分区。Kubernetes设计认为,内存不足时应通过调度Pod到其他节点或驱逐Pod来解决,使用Swap会导致性能不可预测和调度复杂性。

# 临时关闭 swapoff -a # 永久关闭,注释掉/etc/fstab中swap相关的行 sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab

3. 容器运行时与Kubernetes组件安装

地基打牢后,我们开始安装核心的“发动机”——容器运行时和Kubernetes组件。这里我们选择Docker作为容器运行时,虽然现在containerd是更主流和轻量的选择,但Kubernetes 1.18时代,Docker的集成度和社区资料更丰富。使用kubeadm这个官方工具可以极大地简化安装过程。

3.1 安装Docker容器运行时

CentOS 8的默认仓库可能不包含我们需要的Docker版本。我们需要添加Docker的官方仓库。这里安装的是与K8s 1.18兼容性较好的docker-ce-19.03版本。

# 添加Docker仓库 dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo # 安装指定版本的Docker dnf install -y docker-ce-19.03.15 docker-ce-cli-19.03.15 containerd.io

安装完成后,需要配置Docker的cgroup驱动。Kubernetes推荐使用systemd作为cgroup驱动,而非Docker默认的cgroupfs,这有助于提高系统稳定性。创建或编辑/etc/docker/daemon.json

{ "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "storage-driver": "overlay2", "storage-opts": [ "overlay2.override_kernel_check=true" ] }

然后启动并设置Docker开机自启:

systemctl enable docker && systemctl start docker

你可以通过docker info | grep Cgroup来验证驱动是否已改为systemd

3.2 配置Kubernetes仓库并安装组件

接下来安装kubelet、kubeadm和kubectl。由于国内网络访问Google仓库可能不稳定,我们使用阿里云的镜像仓库。

# 添加阿里云Kubernetes仓库 cat <<EOF > /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg EOF # 安装指定版本(1.18.20是一个较稳定的补丁版本) dnf install -y kubelet-1.18.20 kubeadm-1.18.20 kubectl-1.18.20 --disableexcludes=kubernetes

--disableexcludes=kubernetes参数很重要,它确保在安装时不会因为仓库排除规则而跳过Kubernetes包。安装完成后,设置kubelet开机自启(但先不要启动,等kubeadm init之后再启动):

systemctl enable kubelet

3.3 配置kubeadm初始化参数

在Master节点执行kubeadm init之前,最好先通过配置文件来定义集群的初始化参数,这样更清晰且易于版本管理。创建一个配置文件,例如kubeadm-config.yaml

apiVersion: kubeadm.k8s.io/v1beta2 kind: ClusterConfiguration kubernetesVersion: v1.18.20 controlPlaneEndpoint: "k8s-master:6443" # 如果是单Master,就写Master的IP或域名 networking: podSubnet: "10.244.0.0/16" # 为Flannel网络插件预留的Pod网段,如果使用Calico等需修改 imageRepository: registry.aliyuncs.com/google_containers # 使用国内镜像加速 --- apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration cgroupDriver: systemd # 必须与Docker的cgroup驱动一致

这个配置文件做了几件关键事:指定了Kubernetes版本;定义了控制平面的访问端点;设置了Pod的网络CIDR(这里以Flannel为例);最关键的是指定了国内镜像仓库,避免因拉取k8s.gcr.io镜像失败导致初始化卡住。同时,它确保了kubelet使用systemdcgroup驱动。

4. 初始化Master节点与部署网络插件

万事俱备,现在可以初始化Master节点了。这是将分散的组件组装成一个大脑的关键一步。

4.1 执行kubeadm init初始化

使用我们刚才创建的配置文件进行初始化:

kubeadm init --config=kubeadm-config.yaml --upload-certs | tee kubeadm-init.log

这个命令会做一系列工作:检查系统状态、拉取控制平面镜像、生成各种证书和静态Pod清单文件(如etcd、api-server等)、启动控制平面组件。--upload-certs参数将证书密钥上传到集群,为后续添加其他控制平面节点(实现高可用)做准备。tee命令将输出同时显示在屏幕并保存到文件,便于后续排查。

初始化成功的最关键标志,是最后几行输出,它会给出两条你必须执行的命令,以及一条用于将Node节点加入集群的命令:

mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config # 以及类似下面的join命令,务必记下来 kubeadm join k8s-master:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:xxxxxxxx...

请立即执行前两条mkdircp命令,它们将管理员的Kubeconfig文件复制到你的家目录下,这样kubectl命令才能正常工作。你可以运行kubectl get nodes查看,此时Master节点应处于NotReady状态,因为网络插件还没装。

4.2 部署Pod网络插件(CNI)

Pod之间要能通信,必须安装一个CNI(容器网络接口)插件。这里我们选择最经典的Flannel,它简单可靠。注意,Flannel的配置必须与我们在kubeadm-config.yaml中设置的podSubnet10.244.0.0/16)匹配。

# 下载Flannel的配置文件 wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml # 如果网络问题,可以找国内镜像或手动下载 # 应用这个配置文件 kubectl apply -f kube-flannel.yml

应用后,使用kubectl get pods -n kube-system来观察kube-flannel-ds-*这些Pod的状态。等待几分钟,直到它们全部变为Running状态。此时,再运行kubectl get nodes,应该能看到Master节点的状态变成了Ready

实操心得:如果Flannel Pod一直卡在InitContainerCreating,最常见的原因是镜像拉取失败。可以手动使用docker pull拉取quay.io/coreos/flannel:v0.13.0镜像,或者编辑kube-flannel.yml文件,将其中的镜像地址替换为国内镜像(如registry.cn-hangzhou.aliyuncs.com/google_containers/flannel)。另一个常见问题是/run/flannel目录权限,确保节点上该目录存在且可写。

5. 添加Worker节点与基础功能验证

现在,大脑(Master)已经就绪,我们需要给它添加手脚(Worker Node)。

5.1 将Node节点加入集群

在另一台已经完成了第2章和第3章所有步骤(安装Docker、kubelet、kubeadm,关闭Swap等)的Node节点上,执行之前在Master节点初始化成功后输出的那条kubeadm join命令。命令格式如下:

kubeadm join <control-plane-host>:<control-plane-port> --token <token> --discovery-token-ca-cert-hash sha256:<hash>

加入过程可能需要一两分钟。你可以在Master节点上通过kubectl get nodes -w-w参数用于持续观察)来查看Node节点的加入状态。当Node节点状态变为Ready,就表示加入成功。

如果忘记了join命令或token过期(默认24小时),可以在Master节点上重新生成:

# 生成新的token kubeadm token create --print-join-command

5.2 验证集群基础功能

节点就绪后,我们需要验证集群的核心功能是否正常。运行以下命令进行一系列检查:

  1. 查看集群节点状态kubectl get nodes,所有节点应为Ready
  2. 查看核心系统Pod状态kubectl get pods -n kube-system,所有Pod(特别是coredns)应为Running。如果coredns卡在Pending,通常是网络插件没装好。
  3. 部署一个测试应用:这是最直接的验证方式。
# 部署一个Nginx Deployment kubectl create deployment nginx-test --image=nginx:1.14-alpine # 将其暴露为一个NodePort服务 kubectl expose deployment nginx-test --port=80 --type=NodePort # 查看服务,获取分配的NodePort(例如 32645) kubectl get svc nginx-test

然后,你可以通过<Node节点的IP>:<NodePort>在浏览器中访问,如果看到Nginx欢迎页,说明Pod部署、Service网络和节点间通信全部正常。

  1. 检查集群组件健康状态kubectl get componentstatus(或kubectl get cs),应显示schedulercontroller-manager等组件为Healthy

6. 常见问题深度排查与优化配置

即便按照步骤操作,你也可能会遇到一些问题。这里我汇总了几个最常见的问题及其排查思路,这比成功的步骤更有价值。

6.1 初始化失败:镜像拉取问题

问题现象kubeadm init卡在[pull-image]阶段,或提示ImagePullBackOff

排查与解决

  1. 检查网络:确保节点能访问外网或你配置的国内镜像仓库。可以手动docker pull registry.aliyuncs.com/google_containers/kube-apiserver:v1.18.20测试。
  2. 使用预拉取镜像kubeadm支持在初始化前先拉取所有所需镜像。
    kubeadm config images pull --config=kubeadm-config.yaml
  3. 镜像列表与替换:如果某个镜像实在拉取不到,可以使用docker tag命令将已拉取到的相似镜像重命名为目标镜像名,或者寻找其他可靠的国内镜像源进行替换。

6.2 Node节点NotReady

问题现象:Node加入后,状态一直为NotReady

排查步骤

  1. 在Node节点检查kubelet服务systemctl status kubelet -l。查看日志是否有明显错误。
  2. 检查CNI插件Pod:在Master上kubectl describe pod <flannel-pod-name> -n kube-system,查看Events和容器状态。常见问题是镜像拉取失败或网络权限问题。
  3. 检查Node节点上的网络接口:在Node节点上运行ip addr show,查看是否多了一个cni0的网桥,以及每个Pod是否有一个veth开头的虚拟网卡。如果没有,说明CNI插件没有正常工作。
  4. 检查kubelet日志:在Node节点上journalctl -xeu kubelet,关注其中与CNI、cgroup相关的错误。一个经典错误是“cgroup驱动不匹配”,确保Docker(daemon.json)和kubelet(kubeadm-config.yaml/var/lib/kubelet/config.yaml)都配置为systemd

6.3 CoreDNS Pod处于Pending或CrashLoopBackOff状态

问题现象kubectl get pods -n kube-system显示coredns pods没有运行。

排查与解决

  1. 首先检查网络插件:CoreDNS依赖集群网络。确保Flannel等CNI插件所有Pod都已Running
  2. 检查Node的污点(Taint):Master节点默认有污点node-role.kubernetes.io/master:NoSchedule,这会导致普通Pod(包括CoreDNS)无法调度上去。如果你只有单节点集群(既是Master又是Worker),需要移除这个污点:
    kubectl taint nodes --all node-role.kubernetes.io/master-
  3. 查看CoreDNS Pod详情kubectl describe pod -n kube-system -l k8s-app=kube-dns,从Events中寻找线索。

6.4 后续优化与配置建议

集群跑起来后,可以考虑做一些优化,让它更易用、更稳定:

  1. 配置kubectl命令自动补全

    # Bash echo 'source <(kubectl completion bash)' >> ~/.bashrc # Zsh echo 'source <(kubectl completion zsh)' >> ~/.zshrc
  2. 安装Dashboard(Web UI):虽然命令行是王道,但Dashboard对于可视化查看资源状态很有帮助。注意,安装后需要配置访问权限(ServiceAccount、Token等),切勿直接暴露到公网。

    kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml
  3. 配置日志与监控:考虑部署EFK(Elasticsearch, Fluentd, Kibana)栈收集日志,以及Prometheus + Grafana监控集群和应用的运行状态。这是生产级集群的标配。

  4. 资源清理:如果初始化失败需要重来,可以执行kubeadm reset来清理节点。注意,这会删除所有通过kubeadm创建的资源,但不会清除iptables规则或CNI网络配置,可能需要手动清理。

部署一个Kubernetes集群就像搭积木,每一步都有其明确的目的和依赖。从系统配置、运行时安装、组件初始化到网络部署,环环相扣。我个人的体会是,第一次部署时,耐心地阅读每一行命令的输出和错误日志,比盲目搜索答案更有效。理解每个组件的作用(kubelet是节点代理,kube-apiserver是总网关,etcd是数据库,kube-proxy负责网络规则),能让你在遇到问题时快速定位方向。这个基于CentOS 8和Kubernetes 1.18的部署流程,其方法论是通用的,当你需要部署更新版本的K8s或其他Linux发行版时,只需调整软件仓库地址、镜像版本和部分配置参数即可。