kubernetes 集群搭建

环境信息

操作系统:Centos7.9

Docker:20+

K8S:1.23.6

搭建Kubernetes集群

初始化操作

创建好虚拟机后,对三台虚拟机都进行进行初始化操作

在操作时能够听到喇叭提示音,如果不想听到提示音,可以通过在内核模块中移除 pcspkr 模块来完全禁用 PC 喇叭: rmmod pcspkr

修改主机名称

[root@localhost ~]# hostnamectl set-hostname Kubernetes-Master [root@localhost ~]# hostnamectl Static hostname: kubernetes-master Pretty hostname: Kubernetes-Master Icon name: computer-vm Chassis: vm Machine ID: 4e60493f0355468f9c57a968a0973490 Boot ID: 0b3047df130f40b1b7c29e7e8738f812 Virtualization: vmware Operating System: CentOS Linux 7 (Core) CPE OS Name: cpe:/o:centos:centos:7 Kernel: Linux 3.10.0-1160.el7.x86_64 Architecture: x86-64

修改IP地址

[root@kubernetes-master ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens33 [root@kubernetes-master ~]# systemctl restart network

关闭firewalld

[root@localhost ~]# systemctl stop firewalld.service [root@localhost ~]# systemctl disable firewalld.service Removed symlink /etc/systemd/system/multi-user.target.wants/firewalld.service. Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.

关闭selinux

# 临时关闭 [root@localhost ~]# setenforce 0 # 永久关闭 [root@localhost ~]# sed -i 's/enforcing/disabled/' /etc/selinux/config

关闭swap

# 临时关闭 [root@localhost ~]# swapoff -a # 永久关闭 [root@localhost ~]# sed -ri 's/.*swap.*/#&/' /etc/fstab # 重启虚拟机 [root@localhost ~]# reboot

添加hosts

[root@kubernetes-master ~]# cat >> /etc/hosts << EOF 192.168.0.200 kubernetes-master 192.168.0.201 kubernetes-node1 192.168.0.202 kubernetes-node2 EOF

加载br_netfilter

配置的 br_netfilter 实现桥接流量透传,将桥接的IPV4流量传递到iptables的链,加载br_netfilter内核模块

[root@kubernetes-master ~]# cat > /etc/sysctl.d/k8s.conf << EOF net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF [root@kubernetes-master ~]# modprobe br_netfilter [root@kubernetes-master ~]# echo "br_netfilter" > /etc/modules-load.d/k8s.conf [root@kubernetes-master ~]# sysctl --system

开启ip_forward

配置net.ipv4.ip_forward 实现 IP 层转发,docker部署时会自动开启, containerd 需要手动开启

# 修改sysctl配置文件 cat >> /etc/sysctl.conf << EOF net.ipv4.ip_forward = 1 EOF # 重载sysctl配置使修改生效 sysctl -p /etc/sysctl.conf # 验证是否开启(输出net.ipv4.ip_forward = 1) sysctl net.ipv4.ip_forward

配置yum源

[root@kubernetes-master ~]# mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak [root@kubernetes-master ~]# cd /etc/yum.repos.d/ [root@kubernetes-master yum.repos.d]# wget -nc https://mirrors.aliyun.com/repo/Centos-7.repo [root@kubernetes-master yum.repos.d]# mv Centos-7.repo CentOS-Base.repo [root@kubernetes-master yum.repos.d]# # 设置docker仓库(阿里云镜像) [root@kubernetes-master ~]# yum install -y yum-utils [root@kubernetes-master ~]# yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo [root@kubernetes-master yum.repos.d]# yum clean all [root@kubernetes-master yum.repos.d]# yum update -y [root@kubernetes-master yum.repos.d]# yum list [root@kubernetes-master yum.repos.d]# yum makecache

设置时间

[root@kubernetes-master ~]# timedatectl set-ntp true [root@kubernetes-master ~]# timedatectl set-timezone Asia/Shanghai [root@kubernetes-master ~]# timedatectl status

禁用NM管理DNS

NetworkManager 默认会接管 /etc/resolv.conf,导致修改dns配置会被回刷,需要禁用NetworkManager管理DNS

vim /etc/NetworkManager/NetworkManager.conf # 在[main]下添加 [main] dns=none # 禁用 NM 管理 DNS # 重启NetworkManager sudo systemctl restart NetworkManager

再修改/etc/resolv.conf,不会被回刷

container-runtime

container-runtime(容器运行时)是 Kubernetes 集群中负责管理容器生命周期(创建、启动、停止、销毁)的底层软件,需遵循 K8S 定义的容器运行时接口(CRI)标准,为 Pod 内的容器提供隔离的运行环境。常见的 container-runtime 包括:

  • containerd:目前 K8S 默认推荐的容器运行时(从 1.24 版本起移除对 Docker 的直接支持),轻量且稳定,专注于容器生命周期管理;
  • CRI-O:专为 K8S 设计的轻量级运行时,完全兼容 CRI 标准,无 Docker 依赖;
  • Docker:早期主流运行时,因未原生实现 CRI(需通过 dockershim 适配),现已被 K8S 逐步弃用,实际底层依赖 containerd 提供容器运行能力。

以下介绍dockercontainerd安装方法,任选一种即可

docker

安装docker

yum install -y \ docker-ce-20.10.24 \ docker-ce-cli-20.10.24 \ containerd.io-1.6.20 \ --nogpgcheck

设置docker开机启动

[root@kubernetes-master ~]# systemctl start docker [root@kubernetes-master ~]# systemctl enable docker Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service. [root@kubernetes-master ~]# [root@kubernetes-master ~]# docker version Client: Docker Engine - Community Version: 20.10.24 API version: 1.41 Go version: go1.19.7 Git commit: 297e128 Built: Tue Apr 4 18:22:57 2023 OS/Arch: linux/amd64 Context: default Experimental: true Server: Docker Engine - Community Engine: Version: 20.10.24 API version: 1.41 (minimum version 1.12) Go version: go1.19.7 Git commit: 5d6db84 Built: Tue Apr 4 18:21:02 2023 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.6.20 GitCommit: 2806fc1057397dbaeefbea0e4e17bddfbd388f38 runc: Version: 1.1.5 GitCommit: v1.1.5-0-gf19387a docker-init: Version: 0.19.0 GitCommit: de40ad0 [root@kubernetes-master ~]#

配置镜像源 & 修改cgroup

配置docker的镜像源,同时Docker 的 cgroup 驱动改为 systemd,和 kubelet 一致

[root@kubernetes-master ~]# mkdir -p /etc/docker [root@kubernetes-master ~]# vim /etc/docker/daemon.json # /etc/docker/daemon.json { "registry-mirrors": ["https://docker.1ms.run"], "exec-opts": ["native.cgroupdriver=systemd"] } [root@kubernetes-master ~]# systemctl daemon-reload [root@kubernetes-master ~]# systemctl restart docker [root@kubernetes-master ~]# # 验证Docker驱动已改为systemd(输出必须是systemd) [root@kubernetes-master ~]# docker info | grep -i cgroup Cgroup Driver: systemd Cgroup Version: 1

containerd

安装 containerd

# 安装依赖 yum install -y yum-utils device-mapper-persistent-data lvm2 # 配置containerd yum源 yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 安装containerd yum install -y containerd.io-1.6.20 --nogpgcheck

配置 containerd

# 生成默认配置文件 containerd config default > /etc/containerd/config.toml # 修改配置:替换sandbox镜像(国内源)、设置cgroup驱动为systemd、配置镜像加速 vim /etc/containerd/config.toml # 1. 替换sandbox sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.6" # 2. 配置镜像加速 [plugins."io.containerd.grpc.v1.cri".registry.mirrors] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] endpoint = ["https://docker.1ms.run"] # 3. 设置cgroup驱动为systemd sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml # 启动并设置开机自启containerd systemctl start containerd systemctl enable containerd # 验证containerd版本(输出1.6.20) containerd --version # 验证cgroup驱动(输出SystemdCgroup = true) grep -n "SystemdCgroup" /etc/containerd/config.toml

修改/etc/containerd/config.toml的配置内容:

  • 替换 sandbox 镜像(pause 镜像)地址:[plugins."io.containerd.grpc.v1.cri".sandbox_image]sandbox_image字段的"k8s.gcr.io/pause:3.6"替换为"registry.aliyuncs.com/google_containers/pause:3.6"

pause镜像 是 K8S 每个 Pod 的基础根容器,原地址 k8s.gcr.io 国内无法访问,替换为阿里云镜像源,保证 Pod 能正常创建。

  • 设置 containerdcgroup 驱动为 systemd[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] 段落的 SystemdCgroup 字段设置为true,让 containerd 的 cgroup 驱动和 kubelet 保持一致

  • 添加 Docker 镜像加速:[plugins."io.containerd.grpc.v1.cri".registry.mirrors]段落下方新增镜像地址

[plugins."io.containerd.grpc.v1.cri".registry.mirrors] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] endpoint = ["https://docker.1ms.run"] # 新增的镜像加速地址

指定sock文件

K8s 1.24 及以上版本已移除 dockershim(Docker 与 K8s 的适配层),默认容器运行时改为 containerd,但 crictl 的默认配置仍指向 dockershim.sock,导致连接失败;需配置 crictl 指向 containerd 的正确套接字

# 创建 crictl 配置文件 cat > /etc/crictl.yaml << EOF runtime-endpoint: unix:///run/containerd/containerd.sock image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false EOF # 配置完成后,直接执行 crictl ps 即可正常输出(需要安装kubernetes才能使用crictl) [root@kubernetes-master ~]# crictl pull nginx:1.20.2 Image is up to date for sha256:0584b370e957bf9d09e10f424859a02ab0fda255103f75b3f8c7d410a4e96ed5 [root@kubernetes-master ~]# crictl pull busybox:1.31.1 Image is up to date for sha256:1c35c441208254cb7c3844ba95a96485388cef9ccc0646d562c7fc026e04c807 [root@kubernetes-master ~]# crictl pull mysql:8.0.32 Image is up to date for sha256:412b8cc72e4a28e086097c3fcb1ca391beaefe86bc421a57bc53f7596461ce3b [root@kubernetes-master ~]# crictl images ps IMAGE TAG IMAGE ID SIZE IMAGE TAG IMAGE ID SIZE docker.io/library/busybox 1.31.1 1c35c44120825 765kB docker.io/library/mysql 8.0.32 412b8cc72e4a2 157MB docker.io/library/nginx 1.20.2 0584b370e957b 56.7MB

安装 kubernetes

[root@kubernetes-master ~]# cat > /etc/yum.repos.d/kubernetes.repo << EOF [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=0 repo_gpgcheck=0 gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-key.gpg EOF [root@kubernetes-master ~]# yum install -y kubelet-1.23.6 kubeadm-1.23.6 kubectl-1.23.6 [root@kubernetes-master ~]# systemctl enable kubelet && systemctl start kubelet

修改网络配置

可选配置,因为上述虚拟机由vmware创建,虚拟机网络模式为桥接模式,当主机网络发生变动时,K8S节点网络也会出现问题,于是有以下方案:

master节点设置两张网卡,一张用于网桥模式,一张仅主机模式,剩余node节点均为仅主机模式,master配置网络转发和dns解析让node上网

在如下示例中,设置K8S的master IP=10.0.0.10,node in 10.0.0.0/24

前置条件:关机所有虚拟机,将node的网络模式改为仅主机,master设置两张网卡:网桥模式 + 仅主机模式

以下操作在master节点上进行

配置网络转发

# 临时开启(重启后失效) echo 1 > /proc/sys/net/ipv4/ip_forward # 永久开启(写入sysctl配置) echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf sysctl -p

配置nat规则

# 查看网卡,ens33是仅主机模式,ens36是共享主机网络 [root@kubernetes-master ~]# nmcli device DEVICE TYPE STATE CONNECTION ens36 ethernet 已连接 ens36 ens33 ethernet 已连接 ens33 lo loopback 未托管 -- # 配置iptables NAT规则,让node节点的流量通过外网接口转发出去,如果配置错误,将-A改为-D即可删除 iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o ens36 -j MASQUERADE # 保存iptables规则(不同系统命令略有差异) # CentOS/RHEL iptables-save > /etc/sysconfig/iptables # Ubuntu/Debian iptables-save > /etc/iptables/rules.v4 # 查看nat转发表 iptables -t nat -L -n --line-numbers # 查看nat表规则及流量统计 iptables -t nat -L -n -v

注意,在该测试环境下,K8S-node需要借助master的nat转发上网,nat每次开机都会回刷nat转发表,所以每次开机都需要配置nat转发,如果想规避此类操作。可以用一个软路由镜像实现上网

修改网卡配置

[root@kubernetes-node1 ~]# cat /etc/sysconfig/network-scripts/ifcfg-ens33 TYPE="Ethernet" PROXY_METHOD="none" BROWSER_ONLY="no" BOOTPROTO="none" DEFROUTE="yes" IPV4_FAILURE_FATAL="no" IPV6INIT="yes" IPV6_AUTOCONF="yes" IPV6_DEFROUTE="yes" IPV6_FAILURE_FATAL="no" IPV6_ADDR_GEN_MODE="stable-privacy" NAME="ens33" UUID="e89c2ca2-4307-4ae7-a4e8-8953b047d621" DEVICE="ens33" ONBOOT="yes" IPADDR="10.0.0.11" PREFIX="24" GATEWAY="10.0.0.10" DNS1="223.5.5.5" IPV6_PRIVACY="no"

DHCP网卡

cat > /etc/sysconfig/network-scripts/ifcfg-ens36 <<EOF TYPE=Ethernet BOOTPROTO=dhcp # 改成DHCP自动获取 DEVICE=ens36 NAME=ens36 ONBOOT=yes HWADDR=00:0c:29:66:c7:28 IPV6INIT=no USERCTL=no PEERDNS=yes # 允许DHCP自动分配DNS(关键) DEFROUTE=yes # 让该网卡作为默认路由(优先走DHCP分配的网关) EOF

Master节点初始化

  • docker:在Master节点上进行初始化操作,docker按照以下执行
[root@kubernetes-master ~]# kubeadm init --apiserver-advertise-address=192.168.0.200 --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.23.6 --service-cidr=10.96.0.0/12 --pod-network-cidr=10.244.0.0/16 # 安装完成后检查 # 查看kubelet状态(应为active (running)) [root@kubernetes-master ~]# systemctl status kubelet # 验证健康检查端口(10248通) [root@kubernetes-master ~]# curl -sSL http://localhost:10248/healthz # 输出ok # 查看控制平面容器(应有kube-apiserver/etcd等) [root@kubernetes-master ~]# docker ps | grep kube
  • containerd:在Master节点上进行初始化操作,containerd按照以下执行
# 初始化集群,指定containerd的套接字路径 [root@kubernetes-master ~]# kubeadm init \ --apiserver-advertise-address=10.0.0.10 \ --image-repository registry.aliyuncs.com/google_containers \ --kubernetes-version v1.23.6 \ --service-cidr=10.96.0.0/12 \ --pod-network-cidr=10.1.0.0/16 \ --cri-socket=unix:///run/containerd/containerd.sock # 查看kubelet状态(应为active (running)) [root@kubernetes-master ~]# systemctl status kubelet # 验证健康检查端口(10248通) [root@kubernetes-master ~]# curl -sSL http://localhost:10248/healthz # 查看控制平面容器(应有kube-apiserver/etcd等) [root@kubernetes-master ~]# docker ps | grep kube

参数示例如下

  • —apiserver-advertise-address=10.0.0.10 \ # 指定APIServer对外通告的IP
  • —image-repository registry.aliyuncs.com/google_containers \ # 指定镜像仓库
  • —kubernetes-version v1.23.6 \ # 指定K8s版本
  • —service-cidr=10.96.0.0/12 \ # 指定Service网段
  • —pod-network-cidr=10.1.0.0/16 \ # 指定Pod网段 -
  • -cri-socket=unix:///run/containerd/containerd.sock # 指定CRI套接字路径

如果上述操作报错,按照如下流程处理

# 查看 kubelet 状态,大概率输出 active (failed) 或 inactive (dead) systemctl status kubelet # 查看详细日志 journalctl -xeu kubelet | grep -i error # 常见报错关键词: - Failed to load kubelet config file /var/lib/kubelet/config.yaml: no such file or directory 第一次 init 失败后该文件未生成 / 被清理。 - cgroup driver mismatch:Docker kubelet cgroup 驱动不一致; - SELinux is enabled:SELinux 未关闭; - read-only file system:文件系统权限问题; - br_netfilter not loaded:内核模块未加载; - port 10248 is in use:端口被占用(极少)。 # 重置 kubeadm 残留状态(清理失败的 init 残留) kubeadm reset -f # 强制重置,清空所有k8s残留配置 rm -rf /var/lib/kubelet/* # 清空kubelet目录(包括无效配置) rm -rf /etc/kubernetes/* # 清空k8s证书/配置目录 # 修复 Docker 的 cgroup 驱动(改为 systemd,和 kubelet 一致) cat > /etc/docker/daemon.json << EOF { "registry-mirrors": ["https://docker.1ms.run"], "exec-opts": ["native.cgroupdriver=systemd"] } EOF # 重启Docker生效,验证Docker驱动已改为systemd(输出必须是systemd) systemctl daemon-reload systemctl restart docker docker info | grep -i cgroup # 关闭swap(kubelet强制要求) swapoff -a sed -i '/swap/s/^/#/' /etc/fstab # 永久关闭 # 加载br_netfilter内核模块 modprobe br_netfilter echo "br_netfilter" > /etc/modules-load.d/k8s.conf # 确保内核参数生效 sysctl --system # 关闭SELinux和防火墙 setenforce 0 sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config systemctl stop firewalld && systemctl disable firewalld

Master节点初始化配置

[root@kubernetes-master ~]# mkdir -p $HOME/.kube [root@kubernetes-master ~]# cp -i /etc/kubernetes/admin.conf $HOME/.kube/config [root@kubernetes-master ~]# chown $(id -u):$(id -g) $HOME/.kube/config

Node节点加入Master节点

将从节点加入主节点:

  • 在 Master 节点获取 kubeadm join 命令(包含 token 和哈希值)
  • 在 Node 节点执行 kubeadm join 命令,自动生成 kubelet 配置文件;kubeadm join 会自动启动 kubelet 并加入集群。

token 仅用于 Node 节点首次加入集群时的身份验证,一旦 Node 节点成功加入集群并生成了本地证书(/var/lib/kubelet/pki/),后续通信不再依赖 token—— 即使 token 过期,已加入的 Node 节点也能正常工作,无需重新 join。

# 在master上获取 `kubeadm join` 命令 # 查看token时间 [root@kubernetes-master ~]# kubeadm token list # 方式1:如果 Master 刚执行完 init,直接查看 [root@kubernetes-master ~]# kubeadm token create --print-join-command # 方式2:如果 token 过期,重新生成永久 token(有效期 365 天) [root@kubernetes-master ~]# kubeadm token create --ttl 0 # 生成永不过期 token [root@kubernetes-master ~]# openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //' # 获取 ca 哈希值 # 在node节点上执行,join 命令(替换为master上的信息): [root@kubernetes-node ~]# kubeadm join 192.168.0.200:6443 --token <生成的token> --discovery-token-ca-cert-hash sha256:<ca哈希值>

在 Node 节点执行 kubeadm join 命令

[root@kubernetes-node2 ~]# kubeadm join 192.168.0.200:6443 --token ah7f8n.4buw5tt3edn876ro --discovery-token-ca-cert-hash sha256:583c34ccb826e29c604bb4a1e204251ed0854e0fb37607c1f0153f4cc8f6dfdb [preflight] Running pre-flight checks [preflight] Reading configuration from the cluster... [preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml' [kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml" [kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env" [kubelet-start] Starting the kubelet [kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap... This node has joined the cluster: * Certificate signing request was sent to apiserver and a response was received. * The Kubelet was informed of the new secure connection details. Run 'kubectl get nodes' on the control-plane to see this node join the cluster. [root@kubernetes-node2 ~]#

在master节点上查看

[root@kubernetes-master ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION kubernetes-master NotReady control-plane,master 80m v1.23.6 kubernetes-node1 NotReady <none> 22m v1.23.6 kubernetes-node2 NotReady <none> 109s v1.23.6 [root@kubernetes-master ~]#

部署CNI网络插件

在主节点上使用 kubectl get no查看node状态,可以发现状态均为NotReady,现在先使用kubectl做一些基础检查

排查发现,当前集群所有节点 NotReadycoredns 处于 Pending 状态,核心原因是未部署网络插件(flannel/calico),K8s 集群必须部署网络插件才能完成节点网络初始化、Pod 网络分配,进而让节点进入 Ready 状态、coredns 调度运行。

# 查看node信息 [root@kubernetes-master ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION kubernetes-master NotReady control-plane,master 96m v1.23.6 kubernetes-node1 NotReady <none> 38m v1.23.6 kubernetes-node2 NotReady <none> 17m v1.23.6 # 查看组件状态,此为 kubectl get componentstatus 的缩写 [root@kubernetes-master ~]# kubectl get cs Warning: v1 ComponentStatus is deprecated in v1.19+ NAME STATUS MESSAGE ERROR scheduler Healthy ok controller-manager Healthy ok etcd-0 Healthy {"health":"true","reason":""} [root@kubernetes-master ~]# # 查看命名空间 [root@kubernetes-master ~]# kubectl get namespaces NAME STATUS AGE default Active 94m kube-node-lease Active 94m kube-public Active 94m kube-system Active 94m # 查看kube-system下的pod信息 [root@kubernetes-master ~]# kubectl get pod -n kube-system NAME READY STATUS RESTARTS AGE coredns-6d8c4cb4d-2jffp 0/1 Pending 0 94m coredns-6d8c4cb4d-lwcpw 0/1 Pending 0 94m etcd-kubernetes-master 1/1 Running 1 (86m ago) 95m kube-apiserver-kubernetes-master 1/1 Running 1 (86m ago) 95m kube-controller-manager-kubernetes-master 1/1 Running 1 (86m ago) 95m kube-proxy-2dd5k 1/1 Running 0 37m kube-proxy-8tcp2 1/1 Running 1 (86m ago) 94m kube-proxy-dljq6 1/1 Running 0 16m kube-scheduler-kubernetes-master 1/1 Running 1 (86m ago) 95m [root@kubernetes-master ~]#

flannel

下载flannel配置文件

[root@kubernetes-master ~]# mkdir /opt/k8s [root@kubernetes-master ~]# mkdir /opt/k8s/flannel [root@kubernetes-master ~]# cd /opt/k8s/flannel/ [root@kubernetes-master flannel]# curl -o kube-flannel.yml https://raw.githubusercontent.com/flannel-io/flannel/v0.19.0/Documentation/kube-flannel.yml

查看配置文件中使用的镜像源

# 配置文件里实际生效的镜像是国内rancher源,无需替换 [root@kubernetes-master flannel]# grep -n "image:" kube-flannel.yml | grep -v "#" 126: image: rancher/mirrored-flannelcni-flannel-cni-plugin:v1.1.0 138: image: rancher/mirrored-flannelcni-flannel:v0.18.1 153: image: rancher/mirrored-flannelcni-flannel:v0.18.1

部署网络插件

[root@kubernetes-master flannel]# kubectl apply -f kube-flannel.yml

验证 flannel 部署状态

# 查看 flannel 命名空间下的 DaemonSet(期望数=节点数,当前数逐步匹配) [root@kubernetes-master flannel]# kubectl get ds -n kube-flannel NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE kube-flannel-ds 3 3 0 3 0 <none> 9s # 查看 flannel Pod(等待 30 秒,状态变为 Running) [root@kubernetes-master flannel]# kubectl get pods -n kube-flannel NAME READY STATUS RESTARTS AGE kube-flannel-ds-qqg7p 1/1 Running 0 2m34s kube-flannel-ds-vt655 1/1 Running 0 2m34s kube-flannel-ds-zj5xl 1/1 Running 0 2m34s # 等待 1-2 分钟(flannel 配置节点网络需要时间),查看集群节点状态 [root@kubernetes-master flannel]# kubectl get nodes NAME STATUS ROLES AGE VERSION kubernetes-master Ready control-plane,master 129m v1.23.6 kubernetes-node1 Ready <none> 71m v1.23.6 kubernetes-node2 Ready <none> 50m v1.23.6 # 查看pods状态(coredns 从 Pending → Running) [root@kubernetes-master flannel]# kubectl get pods -n kube-system NAME READY STATUS RESTARTS AGE coredns-6d8c4cb4d-2jffp 1/1 Running 0 134m coredns-6d8c4cb4d-lwcpw 1/1 Running 0 134m etcd-kubernetes-master 1/1 Running 1 (126m ago) 134m kube-apiserver-kubernetes-master 1/1 Running 1 (126m ago) 134m kube-controller-manager-kubernetes-master 1/1 Running 1 (126m ago) 134m kube-proxy-2dd5k 1/1 Running 0 76m kube-proxy-8tcp2 1/1 Running 1 (126m ago) 134m kube-proxy-dljq6 1/1 Running 0 55m kube-scheduler-kubernetes-master 1/1 Running 1 (126m ago) 134m

calico

下载calico配置文件

# 下载Calico 3.25.0的部署文件(适配K8S 1.23) wget https://docs.projectcalico.org/v3.25/manifests/calico.yaml

编辑yaml文件,设置pod网段

# 搜索环境变量部分,修改或确认如下 - name: CALICO_IPV4POOL_CIDR value: "10.1.0.0/16" # 必须和K8S的podSubnet一致

如果有多张网卡,设置强制让calico选择某网卡进行通信

- name: CLUSTER_TYPE value: "k8s,bgp" # 新增如下内容 - name: IP_AUTODETECTION_METHOD value: "interface=ens33"

部署网络插件

# 执行部署 kubectl apply -f calico.yaml # 查看Calico Pod状态(需全部Running) kubectl get pods -n kube-system | grep calico # 如果长时间没有running,铲除掉yaml创建出的资源,修改yaml的镜像配置 kubectl delete -f calico.yaml

运行测试

创建一个简单的nginx服务,测试kubernetes集群

# 创建nginx服务 [root@kubernetes-master ~]# kubectl create deployment nginx --image=nginx deployment.apps/nginx created # 暴露端口 [root@kubernetes-master ~]# kubectl expose deployment nginx --port=80 --type=NodePort service/nginx exposed # 查看pod和service信息 [root@kubernetes-master ~]# kubectl get pod NAME READY STATUS RESTARTS AGE nginx-85b98978db-lhmzl 1/1 Running 0 20s [root@kubernetes-master ~]# kubectl get pod,svc NAME READY STATUS RESTARTS AGE pod/nginx-85b98978db-lhmzl 1/1 Running 0 24s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3h20m service/nginx NodePort 10.101.21.137 <none> 80:32639/TCP 13s

错误排查

在创建nginx容器时,我们发现pod状态为ImagePullBackOff,使用kubectl describe可以查看详情信息,根据如下信息,可以看出是因为docker一直在拉取”nginx”镜像,可能是网络原因导致。

[root@kubernetes-master ~]# kubectl get pod NAME READY STATUS RESTARTS AGE nginx-85b98978db-xmx57 0/1 ImagePullBackOff 0 9m2s [root@kubernetes-master ~]# kubectl describe pod nginx-85b98978db-xmx57 Name: nginx-85b98978db-xmx57 Namespace: default Priority: 0 Service Account: default Node: kubernetes-node1/192.168.0.201 Start Time: Sun, 21 Dec 2025 22:56:37 +0800 Labels: app=nginx pod-template-hash=85b98978db Annotations: <none> Status: Pending IP: 10.244.1.2 IPs: IP: 10.244.1.2 Controlled By: ReplicaSet/nginx-85b98978db Containers: nginx: Container ID: Image: nginx Image ID: Port: <none> Host Port: <none> State: Waiting Reason: ImagePullBackOff Ready: False Restart Count: 0 Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-zcrdc (ro) Conditions: Type Status Initialized True Ready False ContainersReady False PodScheduled True Volumes: kube-api-access-zcrdc: Type: Projected (a volume that contains injected data from multiple sources) TokenExpirationSeconds: 3607 ConfigMapName: kube-root-ca.crt ConfigMapOptional: <nil> DownwardAPI: true QoS Class: BestEffort Node-Selectors: <none> Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s node.kubernetes.io/unreachable:NoExecute op=Exists for 300s Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 9m29s default-scheduler Successfully assigned default/nginx-85b98978db-xmx57 to kubernetes-node1 Warning Failed 58s kubelet Failed to pull image "nginx": rpc error: code = Unknown desc = context canceled Warning Failed 58s kubelet Error: ErrImagePull Normal BackOff 57s kubelet Back-off pulling image "nginx" Warning Failed 57s kubelet Error: ImagePullBackOff Normal Pulling 42s (x2 over 9m28s) kubelet Pulling image "nginx" [root@kubernetes-master ~]#

删除Deployment

如果需要删除 nginx Deployment,可以使用

kubectl delete deployment nginx

删除 Deployment 后,Pod 显示 Terminating 状态但未消失,是因为 K8s 的 优雅终止机制(默认等待 30 秒),或 Pod 存在「终止阻塞」(如镜像拉取进程未退出、网络资源未释放)。如果需要强制删除

# --grace-period=0:跳过 30 秒优雅等待,立即终止;--force:强制删除(针对卡住的 Pod)。 kubectl delete pod nginx-85b98978db-xmx57 --force --grace-period=0 # 清理残留的 Service kubectl delete svc nginx

手动加载镜像

针对上述nginx拉取慢的问题,我们可以尝试在master节点先拉取镜像,然后将master的镜像通过scp传入各node节点

在 Master 节点拉取镜像并保存

# 拉取 nginx 镜像 docker pull nginx:latest # 保存镜像为压缩包 docker save nginx:alpine > nginx.tar

拷贝镜像到所有 Node 节点

scp nginx.tar root@kubernetes-node1:/opt/ scp nginx.tar root@kubernetes-node2:/opt/

在每个 Node 节点加载镜像

# 登录 node1 执行 docker load < /opt/nginx.tar # 登录 node2 执行 docker load < /opt/nginx.tar

转发流量详解

通过docker ps可以看到,仅在node2上才有nginx的pod,那为什么访问3台设备的对应端口都能看到nginx页面呢?原因是 K8s 的 kube-proxy 组件 + 网络插件(flannel) 共同实现的「集群级端口转发 + 跨节点网络互通」,这也是 K8s 区别于单机 Docker 的核心特性。

我们可以将流量走向拆分为三步:

NodePort 服务提供集群级端口暴露

示例中创建的 NodePort 类型 Service 不是 “绑定某一个节点的端口”,而是:

  1. K8s 会在集群所有节点(Master/Node1/Node2)上监听同一个 NodePort 端口;
  2. 这个端口由每个节点上的 kube-proxy 进程负责监听(在所有节点用 ss -tnlp | grep <端口号> 能看到 kube-proxy 监听);
  3. 无论访问哪个节点的这个端口,kube-proxy 都会把请求转发到 nginx Pod 所在的节点(Node2)。
# kube-proxy 监听所有节点的 NodePort [root@kubernetes-master ~]# ss -tnlp | grep 32639 LISTEN 0 128 *:32639 *:* users:(("kube-proxy",pid=2526,fd=13))

kube-proxy 的转发逻辑(iptables/IPVS 模式)

K8s 默认用 iptables 模式实现 Service 转发,核心逻辑:

  1. 当用户访问 192.168.0.200:30773(Master):
    • Master 节点的 kube-proxy 已通过 K8s API Server 知道 nginx Pod 运行在 Node2(IP:10.244.2.2);
    • kube-proxy 预先在 Master 节点的 iptables 中配置了转发规则:将访问 30773 端口的流量,转发到 nginx Pod 的 IP:80(10.244.2.2:80)。
  2. 同理,访问 192.168.0.201:30773(Node1)时:
    • Node1 的 kube-proxy 也会通过 iptables 规则,把流量转发到 Node2 上的 nginx Pod。
# 基于iptables 规则的分层的逻辑, KUBE-SERVICES 会把 NodePort 流量转发到 KUBE-NODEPORTS 链 [root@kubernetes-master ~]# iptables -t nat -L KUBE-SERVICES Chain KUBE-SERVICES (2 references) target prot opt source destination KUBE-SVC-2CMXP7HKUVJN7L6M tcp -- anywhere 10.101.21.137 /* default/nginx cluster IP */ tcp dpt:http KUBE-SVC-NPX46M4PTMTKRN6Y tcp -- anywhere 10.96.0.1 /* default/kubernetes:https cluster IP */ tcp dpt:https KUBE-SVC-TCOU7JCQXEZGVUNU udp -- anywhere 10.96.0.10 /* kube-system/kube-dns:dns cluster IP */ udp dpt:domain KUBE-SVC-ERIFXISQEP7F7OF4 tcp -- anywhere 10.96.0.10 /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:domain KUBE-SVC-JD5MR3NA4I4DYORP tcp -- anywhere 10.96.0.10 /* kube-system/kube-dns:metrics cluster IP */ tcp dpt:9153 KUBE-NODEPORTS all -- anywhere anywhere /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL # 进一步查看KUBE-NODEPORTS 链,确认会将访问 32639 端口的流量转发到KUBE-SVC-2CMXP7HKUVJN7L6M链,最终指向 nginx Pod 的 IP:80)。 [root@kubernetes-master ~]# iptables -t nat -L KUBE-NODEPORTS Chain KUBE-NODEPORTS (1 references) target prot opt source destination KUBE-SVC-2CMXP7HKUVJN7L6M tcp -- anywhere anywhere /* default/nginx */ tcp dpt:32639

flannel 保证跨节点网络互通

转发的流量要能从 Master/Node1 到达 Node2 上的 nginx Pod,依赖 flannel 实现的「Pod 网络互通」:

  1. flannel 会为每个节点分配一个 Pod 网段(比如 Node2 是 10.244.2.0/24),并在所有节点配置路由规则;
  2. 当 Master/Node1 要访问 Node2 上的 Pod IP(10.244.2.2)时,flannel 会通过 VXLAN 隧道(基于节点间的物理网络),把流量封装后发送到 Node2;
  3. Node2 收到流量后解封装,转发到本地的 nginx Pod。
# Node2 的 Pod 网段(10.244.2.0/24),Master 访问该网段的流量会通过 flannel.1 转发到 Node2 [root@kubernetes-master ~]# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.0.1 0.0.0.0 UG 100 0 0 ens33 10.244.0.0 0.0.0.0 255.255.255.0 U 0 0 0 cni0 10.244.1.0 10.244.1.0 255.255.255.0 UG 0 0 0 flannel.1 10.244.2.0 10.244.2.0 255.255.255.0 UG 0 0 0 flannel.1 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 192.168.0.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33

在任意节点使用kubectl

根据k8s架构,kubectl实际上是在通过api-server接口调用k8s服务,即kubectl命令实际上是在访问api-server,上述操作完成后,在node节点上使用kubectl我们能看到如下输出

[root@localhost containerd]# kubectl get node The connection to the server localhost:8080 was refused - did you specify the right host or port?

因为在master节点初始化时,我们已经在~/.kube/config文件中写入了关于kubernetes的配置信息,为了让从节点也能使用kubectl,我们还需要做以下操作

  1. 将master节点中/etc/kuber/admin.config拷贝到从节点对应目录下

  2. 在从节点上配置环境变量

# 使用scp将配置文件由主节点(192.168.0.200)拷贝到从节点 [root@kubernetes-node1 ~]# scp root@10.0.0.11:/etc/kubernetes/admin.conf /etc/kubernetes/admin.conf admin.conf 100% 5641 5.4MB/s 00:00 [root@kubernetes-node1 ~]# echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile [root@kubernetes-node1 ~]# source ~/.bash_profile [root@kubernetes-node1 ~]# kubectl get node NAME STATUS ROLES AGE VERSION kubernetes-master Ready control-plane,master 8d v1.23.6 kubernetes-node1 Ready <none> 8d v1.23.6 kubernetes-node2 Ready <none> 8d v1.23.6

命令自动补全

# 安装bash-completion yum install bash-completion -y # 设置临时环境变量 source /usr/share/bash-completion/bash_completion source <(kubectl completion bash) # 设置永久环境变量 echo "source <(kubectl completion bash)" >> ~/.bashrc source ~/.bashrc

etcdctl

K8s 集群中 etcd 通常以容器化(静态 Pod)方式运行,有两种运行方式

  • 在pod中直接运行

  • 在宿主机上运行,需要安装 etcdctl 二进制文件

pod运行

# 找到etcd的pod,进入容器执行命令 [root@kubernetes-master ~]# kubectl get pod -n kube-system | grep etcd etcd-kubernetes-master 1/1 Running 30 (9h ago) 9h

查看 etcd 成员列表

kubectl exec -it etcd-kubernetes-master -n kube-system -- \ ETCDCTL_API=3 etcdctl member list \ --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/peer.crt \ --key=/etc/kubernetes/pki/etcd/peer.key \ --write-out=table

检查 etcd 端点健康状态

kubectl exec -it etcd-kubernetes-master -n kube-system -- \ ETCDCTL_API=3 etcdctl endpoint health \ --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/peer.crt \ --key=/etc/kubernetes/pki/etcd/peer.key \ --write-out=table --cluster

查看 etcd 端点状态

kubectl exec -it etcd-kubernetes-master -n kube-system -- \ ETCDCTL_API=3 etcdctl endpoint status \ --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/peer.crt \ --key=/etc/kubernetes/pki/etcd/peer.key \ --write-out=table --cluster

宿主机安装etcdctl

# 查看etcd版本(需要和容器内保持一致),例如:etcd Version: 3.5.7 kubectl exec -it etcd-kubernetes-master -n kube-system -- etcd --version # 下载etcdctl,替换版本号(比如 3.5.7) ETCD_VERSION=3.5.7 wget https://github.com/etcd-io/etcd/releases/download/v${ETCD_VERSION}/etcd-v${ETCD_VERSION}-linux-amd64.tar.gz # 解压并复制 etcdctl 到系统 PATH tar -zxvf etcd-v${ETCD_VERSION}-linux-amd64.tar.gz cp etcd-v${ETCD_VERSION}-linux-amd64/etcdctl /usr/local/bin/

常用命令

配置证书

export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt export ETCDCTL_KEY=/etc/kubernetes/pki/etcd/peer.key export ETCDCTL_CERT=/etc/kubernetes/pki/etcd/peer.crt

状态检查

# member list 成员列表 # endpoint health 端点健康 # endpoint status 端点状态 ETCDCTL_API=3 ETCDCTL_ENDPOINTS=https://127.0.0.1:2379 etcdctl member list --write-out=table ETCDCTL_API=3 ETCDCTL_ENDPOINTS=https://127.0.0.1:2379 etcdctl endpoint health --write-out=table --cluster ETCDCTL_API=3 ETCDCTL_ENDPOINTS=https://127.0.0.1:2379 etcdctl --write-out=table --cluster

环境配置

设置环境变量

如果觉得每次etcdctl前面要带参数太麻烦,还可以设置别名

# 编辑当前用户的 bash 配置(推荐) vi ~/.bashrc # 配置 etcdctl v3 环境变量(永久生效) export ETCDCTL_API=3 export ETCDCTL_ENDPOINTS=https://127.0.0.1:2379 export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt export ETCDCTL_CERT=/etc/kubernetes/pki/etcd/peer.crt export ETCDCTL_KEY=/etc/kubernetes/pki/etcd/peer.key # 让配置立即生效 source ~/.bashrc

配置完成后即可简化命令

etcdctl member list --write-out=table etcdctl endpoint health --write-out=table --cluster etcdctl endpoint status --write-out=table --cluster

设置别名

如果不想改环境变量,也可以创建别名,把完整命令封装成简短关键词:

# 临时别名(仅当前会话生效) alias etcdctl3='ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/peer.crt --key=/etc/kubernetes/pki/etcd/peer.key' # 永久别名(写入 ~/.bashrc) echo "alias etcdctl3='ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/peer.crt --key=/etc/kubernetes/pki/etcd/peer.key'" >> ~/.bashrc source ~/.bashrc

配置完成后即可简化命令,直接用别名+子命令即可

etcdctl3 member list --write-out=table etcdctl3 endpoint health --write-out=table --cluster etcdctl3 endpoint status --write-out=table --cluster

NFS

NFS (Network File System,网络文件系统)是基于TCP/IP协议的应用,可以通过网络,让不同的机器、不同的操作系统可以共享彼此的文件。

NFS在文件传送或信息传送过程中依赖于RPC服务。

RPC (Remote Procedure Call,远程过程调用) 是能使客户端执行其他系统中程序的一种机制。

NFS服务器可以看作是一个FILE SERVER。它可以让客户端通过网络将远端的NFS SERVER共享目录MOUNT到自己的系统中。

安装NFS

本次示例是测试环境,直接以master节点作为NFS的服务端(作为文件共享服务器),node节点为客户端

服务端配置

  1. 安装nfs相关软件包
yum install -y nfs-utils rpcbind
  1. 创建存放数据的目录(共享目录)
mkdir -p /data/nfs-share chmod 755 /data/nfs-share # 根据需求修改 chown -R nobody:nobody /data/nfs-share # 匹配NFS默认匿名用户
  1. 修改配置文件

格式:共享目录 允许访问的网段/IP(权限参数)

10.0.0.0/8 表示允许该网段所有节点访问(根据实际集群网段调整)

cat > /etc/exports << EOF /data/nfs-share 10.0.0.0/8(rw,sync,no_root_squash,no_subtree_check,insecure) EOF

参数说明:

  • rw:客户端拥有读写权限;
  • ro :客户端只读权限,根据需求可选rw或ro;
  • sync:数据同步写入磁盘(保证数据不丢失,性能略低);
  • no_root_squash:客户端 root 用户访问时,保留 root 权限(测试环境可用,生产慎用)。
  • no_subtree_check:关闭子目录检查,提升 NFS 稳定性(K8s 推荐加)
  • insecure:允许客户端从非特权端口(大于 1024)访问(K8s 环境必备);
  1. 启动nfs服务端
systemctl start rpcbind systemctl start nfs-server systemctl enable rpcbind systemctl enable nfs-server
  1. 验证是否已经共享了文件
[root@kubernetes-master data]# exportfs /data/nfs-share 10.0.0.0/8
  1. 上述检查无误后,应用配置
exportfs -arv

客户端配置(每个节点都需要配置)

  1. 安装nfs客户端,设置开机自启
yum install -y nfs-utils rpcbind systemctl start rpcbind systemctl start nfs-server systemctl enable rpcbind systemctl enable nfs-server
  1. 客户端节点访问NFS服务端
[root@kubernetes-node1 ~]# showmount -e 10.0.0.10 Export list for 10.0.0.10: /data/nfs-share 10.0.0.0/8

挂载NFS

示例:将NFS挂载到客户端

查询 NFS 服务端的共享列表

[root@kubernetes-node1 ~]# showmount -e 10.0.0.10 Export list for 10.0.0.10: /data/nfs-share 10.0.0.0/8

临时挂载

# 1. 创建本地挂载点目录 mkdir -p /mnt/nfs-share # 2. 临时挂载(重启后失效) mount -t nfs 10.0.0.10:/data/nfs-share /mnt/nfs-share # 3. 验证挂载是否成功 [root@kubernetes-node1 ~]# df -h | grep nfs-share 10.0.0.10:/data/nfs-share 17G 6.1G 11G 36% /mnt/nfs-share [root@kubernetes-node1 ~]# mount | grep nfs-share 10.0.0.10:/data/nfs-share on /mnt/nfs-share type nfs4 (rw,relatime,vers=4.1,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.0.0.11,local_lock=none,addr=10.0.0.10)

永久挂载,需要将挂载信息写入/etc/fstab

# 创建本地挂载点目录 mkdir -p /mnt/nfs-share # 永久挂载(写入/etc/fstab,重启后自动挂载) echo "10.0.0.10:/data/nfs-share /mnt/nfs-share nfs defaults 0 0" >> /etc/fstab # 验证fstab配置是否正确(无报错即正常) mount -a

卸载

如果之前将 NFS 写入了/etc/fstab实现永久挂载,卸载后必须删除这行配置,否则系统重启时会报错

# 方式1:通过挂载点卸载(最常用、最稳妥) umount /mnt/nfs-share # 方式2:通过NFS服务端路径卸载(备选) umount 10.0.0.10:/data/nfs-share

导入离线镜像

有时候,我们可以在其他设备下载镜像,再导入到K8S中,以ghost为例

获取镜像

注意:拉取镜像时候指定主机(最后运行镜像的主机)的架构是x86还是arm,镜像架构不对会导致运行报错exec format error

使用docker拉取镜像

# 拉取 x86_64 架构的镜像 docker pull --platform linux/amd64 docker.io/library/ghost:5.82.0 # 导出镜像 docker save docker.io/library/ghost:5.82.0 -o ghost-5.82.0-amd64.tar

使用ctr拉取镜像

# 使用 ctr 拉取 x86_64 架构镜像 ctr images pull --platform linux/amd64 docker.io/library/ghost:5.82.0 # 导出镜像 ctr images export ghost-5.82.0-amd64.tar docker.io/library/ghost:5.82.0

导入镜像

容器运行时为containerd

# 用 ctr 导入镜像,指定 k8s.io 命名空间(crictl默认命名空间),--all-platforms:保留镜像的所有架构版本 ctr -n k8s.io images import --all-platforms ghost.tar # 查看镜像 ctr -n k8s.io images list crictl images

容器运行时为docker

# docker导入镜像 docker load -i ghost.tar

Helm安装

Helm 是 Kubernetes(K8s)的官方包管理器,类比于 Linux 系统的 apt/yum(Debian/Ubuntu/CentOS 包管理)、Python 的 pip、Node.js 的 npm—— 它将 Kubernetes 中分散的多个资源(Deployment、Service、ConfigMap、Ingress 等)打包成一个可复用的 “软件包”(称为 Chart),解决了手动管理 K8s YAML 配置的痛点,实现应用的一键部署、版本升级、回滚、卸载。

安装Helm

安装Helm可以参考Helm官方文档,此处选择兼容 K8s 1.23 的 Helm3 稳定版本(如 v3.14.0),适配 linux/amd64 架构

  • 下载heml:wget https://get.helm.sh/helm-v3.2.3-linux-amd64.tar.gz
  • 解压 tar -zxvf helm-v3.2.3-linux-amd64.tar.gz
  • 将解压目录下的程序移动到/usr/local/bin
  • 添加阿里云heml仓库
[root@kubernetes-master ~]# mkdir helm [root@kubernetes-master ~]# cd helm/ # 下载heml [root@kubernetes-master helm]# wget https://get.helm.sh/helm-v3.14.0-linux-amd64.tar.gz --2026-01-11 18:17:26-- https://get.helm.sh/helm-v3.2.3-linux-amd64.tar.gz 正在解析主机 get.helm.sh (get.helm.sh)... 13.107.213.74, 13.107.246.74, 2620:1ec:46::73, ... 正在连接 get.helm.sh (get.helm.sh)|13.107.213.74|:443... 已连接。 已发出 HTTP 请求,正在等待回应... 200 OK 长度:12924654 (12M) [application/x-tar] 正在保存至: “helm-v3.2.3-linux-amd64.tar.gz” # 解压 [root@kubernetes-master helm]# tar -zxvf helm-v3.2.3-linux-amd64.tar.gz linux-amd64/ linux-amd64/README.md linux-amd64/LICENSE linux-amd64/helm [root@kubernetes-master helm]# ls helm-v3.2.3-linux-amd64.tar.gz linux-amd64 # 将heml程序移动到/usr/local/bin [root@kubernetes-master helm]# cd linux-amd64/ [root@kubernetes-master linux-amd64]# ls helm LICENSE README.md [root@kubernetes-master linux-amd64]# cp helm /usr/local/bin/

设置helm自动补全

$ source < (helm completion bash) $ echo 'source <(helm completion bash)' >> ~/.bashrc $ KUBECONFIG=/root/.kube/config