第一章 Ubuntu24.04环境下的K8S部署【入门保姆级】

一、Docker的由来

说到K8S的由来,不得不提到Docker的由来,开发者在自己的环境中开发了一套程序,当他要上线的时候,发现线上的服务器环境,他开发的程序却出现了报错,然后程序员就会说一句,这程序在我电脑上跑的好好的哇,怎么到你那就不行了呢,后来发现,这是由于系统环境引起的,比如这个程序需要安装的依赖版本出现了偏差,环境的组件版本也出现了偏差,才导致了这样的问题。

于是Docker就诞生了,说到Docker的诞生,他的启发来源于货轮,起初货轮在运输的时候,因为物件的不同,搬运工往往要一件一件的搬上床,有的很大要几个人搬,有的很小一个人可以搬好几件,这样就导致货轮的运输和空间效率都很低,于是货轮的集装箱就由此诞生了,不管你有什么物件,全部使用标准集装箱运输,既提高了空间利用率,又提高了帮运效率。这就是为什么Docker的图标像个货轮。

二、K8S的由来

有了Docker后,虽然解决了单个船货运的问题,但是如果是多个船,多个港口,要如何解决调度和部署的问题呢,比如一个货轮坏了,另一个货轮如何让客户快速无感接收坏掉货轮的货品,保障货运的稳定性,于是K8S就诞生了,他解决了多机容器调度、扩容、故障转移的痛点,并可以统一管理海量容器集群。

理解下面几个K8S基础组件,方便后续知道我们为什么要装:

1.containerd

早期为了过度,K8S支持Docker,后续版本就不再支持Docker了,使用了containerd进行了替代,可以理解为容器。

2.kubelet

节点管理员:节点上的"容器管家",所有节点必须安装

3.kubeadm

搭建工具:集群部署的"自动化工具"

4.kubectl

你的指挥工具:集群管理的"命令行客户端"

5.calico

容器网络接口(CNI)插件,解决容器集群的网络连通性和网络安全性问题

三、实验环境

1.准备3台服务器:

下方所有配置,未特别说明在哪台机子上配置的,所有节点都要配置一遍。

(1)test1(Master主节点):

bash 复制代码
IP:172.18.22.20
NAME:TEST1
作用:K8S的Master主节点
系统:Ubuntu24.04

(2)test2:

bash 复制代码
IP:172.18.22.21
NAME:TEST2
作用:K8S的分节点
系统:Ubuntu24.04

(3)test3:

bash 复制代码
IP:172.18.22.22
NAME:TEST3
作用:K8S的分节点
系统:Ubuntu24.04

2.配置HOST,使得直接能通过域名互访:

bash 复制代码
# 三台服务器都添加HOSTS信息
vim /etc/hosts
172.18.22.20 test1
172.18.22.21 test2
172.18.22.22 test3

3.软件和版本说明

calico :v3.28.0

K8S :v1.28.2

Ubuntu远程工具(可以快速上传文件到服务器):MobaXterm

代码编辑软件:Visual Studio Code

下载calico:Watt Toolkit

4.教程说明

教程很多操作过程,都尽量依赖windows来协助完成,目的是觉得对刚入门的同学比较友好,容易学习和理解,有基础的也可以使用linux的命令来实现,在我觉得只要能达到目的,快的 、容易理解的、顺手的一定是最优解,借助一些软件、系统也不是不可以,不必拘于专业性,那都是花里胡哨装X用的不是。

教程是笔者自学分享,有什么写不好的地方,有大神也可以提出修改意见。

四、安装Docker

1.Docker虽然已经被containerd取代,但我们安装他,是为了我们后续学习时了解原理使用

bash 复制代码
# 安装说明网址:https://vuepress.mirror.docker-practice.com/install/ubuntu/#%E5%AE%89%E8%A3%85-docker
# 下面两个命令容易受到网络影响,不行就多试几次

# 新手小白,直接用官方提供的脚本,一条命令安装最快,原理是官方打包了Docker必要的安全密钥等信息这样能让新手快速安装
curl -fsSL https://get.docker.com | sh

# 国内可能解析不了上面地址,所以也可以用阿里云镜像来装
curl -fsSL https://get.daocloud.io/docker | sh

五、安装K8S

1.环境配置

(1)关闭交换分区

​ 简单理解就是虚拟内存,把内存信息保存到磁盘上的功能,因为K8S对延时等要求很高,若放置到机械硬盘上,后续可能会出现很多奇怪问题,所以必须关闭。

bash 复制代码
vim /etc/fstab

# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
# / was on /dev/ubuntu-vg/lv-0 during curtin installation
/dev/disk/by-id/dm-uuid-LVM-XPJYtf1S6Ah1o3qM0OVtNOGwz8aHpopZeZQTrSRSVtLpk5u8o2nVowtEnQj2zq2b / ext4 defaults 0 1
# /boot was on /dev/sda2 during curtin installation
/dev/disk/by-uuid/68c92334-a340-456c-ade5-f4ea332a7572 /boot ext4 defaults 0 1

# 注释掉这行
#/swap.img      none    swap    sw      0       0

(2)修改内核模块

bash 复制代码
# 临时加载模块(Ubuntu 24.04无需提前安装工具,默认有modprobe)
sudo modprobe br_netfilter
# 注释:给小区修「跨楼栋通道」,小区里有很多楼栋(比如「宿主机楼栋」「容器专属楼栋」),住户要串门,得有通道连不同楼栋吧?这个命令就是 加载一个内核模块(相当于给 Linux 系统装个 "小工具"),作用是:让系统能识别「桥接设备」(可以理解成小区里的 "跨楼栋走廊")。K8S 后续会装「网络插件」(比如 Calico、Flannel),这些插件会自动建这个 "跨楼栋走廊",但得先有这个 "小工具" 才能用这个走廊。没有它:不同楼栋的住户(容器)根本没法通信,比如 A 容器在 "1 号楼",B 容器在 "2 号楼",俩容器见不着面。


# 验证加载成功(输出 br_netfilter 即正常)
lsmod | grep br_netfilter


# 创建配置文件(这个文件不存在,一般要自己创建)
vim /etc/sysctl.d/k8s.conf
# 粘贴以下内容
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1

net.ipv4.ip_forward = 1
# net.bridge.bridge-nf-call-iptables=1 + net.bridge.bridge-nf-call-ip6tables=1:启用用户访问集群的流量分发,及内部容器交换数据流量的综合管理。
# net.ipv4.ip_forward = 1:启用集群网关,

# 永久加载模块让 br_netfilter 内核模块永久生效(开机自动加载)
vim /etc/modules-load.d/k8s.conf
br_netfilter

(3)防火墙(根据情况可跳过)

Ubuntu默认一般时关闭的,如果没有关闭按照下面操作即可,如果关闭了跳过这步骤即可。

bash 复制代码
# 查看防火墙状态,如果显示Status: inactive,说明防火墙是关闭的;显示Status: active则是开启的。
sudo ufw status

# 关闭防火墙
sudo ufw disable



# 如果要开启防火墙,则需要开启以下端口
# 放开 kube-apiserver 端口 6443
sudo ufw allow 6443/tcp

# 放开 etcd 端口 2379 和 2380
sudo ufw allow 2379/tcp
sudo ufw allow 2380/tcp

# 放开 kube-controller-manager 端口 10257
sudo ufw allow 10257/tcp

# 放开 kube-scheduler 端口 10259
sudo ufw allow 10259/tcp

# 放开 kubelet 端口 10250
sudo ufw allow 10250/tcp

# 放开 NodePort Service 的动态分配端口范围30000-32767
sudo ufw allow 30000:32767/tcp

# 放开 Calico 的端口
sudo ufw allow 179/tcp
sudo ufw allow 4789/udp
sudo ufw allow 5743/tcp
sudo ufw allow 443/udp


# 重新加载防火墙规则
sudo firewall-cmd --reload

(4)时间同步(根据情况可跳过)

K8S对服务器之间的时间要求非常高,如果时间不同步可能引发多重问题,所以建议配置时间同步,如果你的服务器是超融合集群(或者VMware虚拟机),那么没有配置的必要,因为虚拟服务器本身就和超融合校准,双重同步反而容易出现故障。

bash 复制代码
sudo apt update && sudo apt install -y chrony
sudo vim /etc/chrony/chrony.conf
# 找到所有以 pool 开头的行(比如 pool 2.ubuntu.pool.ntp.org iburst),要么用 # 注释掉(在行首加 #),要么直接删除(按 Delete 键);
# 阿里云时间服务器(优先用,快且稳定)
server ntp.aliyun.com iburst
server time1.aliyun.com iburst
# 华为云时间服务器(备用,防止阿里云故障)
server time1.huaweicloud.com iburst
server time2.huaweicloud.com iburst
# 解释:server 表示 "添加一个时间老师",iburst 表示 "启动时快速同步"(不用等很久)。

# 重启 chrony 服务
sudo systemctl restart chronyd
# 设置开机自启
sudo systemctl enable chronyd

# 快速判断是否同步
timedatectl status
# 显示 System clock synchronized: yes(系统时钟已同步)。


# 查看时间同步状态
chronyc tracking
# 成功的标准(重点看 3 个参数):
# System time:偏差值(比如 +0.000123 seconds),≤0.001 秒(1ms)就是优秀;
# NTP synchronized:显示 yes(表示已经和时间老师同步成功);
# Reference ID:显示某个国内服务器的 IP(比如阿里云的 IP),表示正在用这个老师同步。

# 查看连接的时间服务器
chronyc sources -v
# 成功的标准:
# 有一个服务器前面带 *(表示当前正在同步的主老师);
# 其他服务器前面带 +(表示备用老师,正常连接);
# 没有出现 ?(? 表示连接失败,可能是网络问题)。

2.安装containerd服务

containerd 简单来说,containerd 是一款开源的、轻量级的容器运行时,核心职责是在服务器上管理容器的全生命周期(从镜像拉取、容器创建启动,到停止删除、资源隔离),是连接容器编排平台(如 K8s)和底层操作系统内核的 "桥梁",和 K8s 不是同一家公司的产品,它们是独立开发但紧密协作的开源项目,共同构建了现代容器云生态。

(1)安装containerd

bash 复制代码
apt install containerd -y

(2)替换配置文件config.toml

​ 安装完成后修改/etc/containerd/中的config.toml文件内容,直接复制内容更换,后续再来理解配置,这边直接照抄就行,主要配置内容就是:

​ 这文件就干了 3 件事:

​ (a)给管家定好 "工位" 和 "仓库",让它有地方干活;

​ (b)对接 K8S 的 "需求",让两者配合不吵架;

​ (c)优化 "拿货渠道" 和 "操作规范",让容器启动快、运行稳。

bash 复制代码
disabled_plugins = []
imports = []
oom_score = 0
plugin_dir = ""
required_plugins = []
root = "/var/lib/containerd"
state = "/run/containerd"
temp = ""
version = 2

[cgroup]
  path = ""

[debug]
  address = ""
  format = ""
  gid = 0
  level = ""
  uid = 0

[grpc]
  address = "/run/containerd/containerd.sock"
  gid = 0
  max_recv_message_size = 16777216
  max_send_message_size = 16777216
  tcp_address = ""
  tcp_tls_ca = ""
  tcp_tls_cert = ""
  tcp_tls_key = ""
  uid = 0

[metrics]
  address = ""
  grpc_histogram = false

[plugins]

  [plugins."io.containerd.gc.v1.scheduler"]
    deletion_threshold = 0
    mutation_threshold = 100
    pause_threshold = 0.02
    schedule_delay = "0s"
    startup_delay = "100ms"

  [plugins."io.containerd.grpc.v1.cri"]
    device_ownership_from_security_context = false
    disable_apparmor = false
    disable_cgroup = false
    disable_hugetlb_controller = true
    disable_proc_mount = false
    disable_tcp_service = true
    enable_selinux = false
    enable_tls_streaming = false
    enable_unprivileged_icmp = false
    enable_unprivileged_ports = false
    ignore_image_defined_volumes = false
    max_concurrent_downloads = 3
    max_container_log_line_size = 16384
    netns_mounts_under_state_dir = false
    restrict_oom_score_adj = false
    sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.7"
    selinux_category_range = 1024
    stats_collect_period = 10
    stream_idle_timeout = "4h0m0s"
    stream_server_address = "127.0.0.1"
    stream_server_port = "0"
    systemd_cgroup = false
    tolerate_missing_hugetlb_controller = true
    unset_seccomp_profile = ""

    [plugins."io.containerd.grpc.v1.cri".cni]
      bin_dir = "/opt/cni/bin"
      conf_dir = "/etc/cni/net.d"
      conf_template = ""
      ip_pref = ""
      max_conf_num = 1

    [plugins."io.containerd.grpc.v1.cri".containerd]
      default_runtime_name = "runc"
      disable_snapshot_annotations = true
      discard_unpacked_layers = false
      ignore_rdt_not_enabled_errors = false
      no_pivot = false
      snapshotter = "overlayfs"

      [plugins."io.containerd.grpc.v1.cri".containerd.default_runtime]
        base_runtime_spec = ""
        cni_conf_dir = ""
        cni_max_conf_num = 0
        container_annotations = []
        pod_annotations = []
        privileged_without_host_devices = false
        runtime_engine = ""
        runtime_path = ""
        runtime_root = ""
        runtime_type = ""

        [plugins."io.containerd.grpc.v1.cri".containerd.default_runtime.options]

      [plugins."io.containerd.grpc.v1.cri".containerd.runtimes]

        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
          base_runtime_spec = ""
          cni_conf_dir = ""
          cni_max_conf_num = 0
          container_annotations = []
          pod_annotations = []
          privileged_without_host_devices = false
          runtime_engine = ""
          runtime_path = ""
          runtime_root = ""
          runtime_type = "io.containerd.runc.v2"

          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
            BinaryName = ""
            CriuImagePath = ""
            CriuPath = ""
            CriuWorkPath = ""
            IoGid = 0
            IoUid = 0
            NoNewKeyring = false
            NoPivotRoot = false
            Root = ""
            ShimCgroup = ""
            SystemdCgroup = true

      [plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime]
        base_runtime_spec = ""
        cni_conf_dir = ""
        cni_max_conf_num = 0
        container_annotations = []
        pod_annotations = []
        privileged_without_host_devices = false
        runtime_engine = ""
        runtime_path = ""
        runtime_root = ""
        runtime_type = ""

        [plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime.options]

    [plugins."io.containerd.grpc.v1.cri".image_decryption]
      key_model = "node"

    [plugins."io.containerd.grpc.v1.cri".registry]
      config_path = ""

      [plugins."io.containerd.grpc.v1.cri".registry.auths]

      [plugins."io.containerd.grpc.v1.cri".registry.configs]
        [plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.40.62".tls]
            insecure_skip_verify = true
        [plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.40.62".auth]
            username = "admin"
            password = "Harbor12345"

      [plugins."io.containerd.grpc.v1.cri".registry.headers]

      [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
         [plugins."io.containerd.grpc.v1.cri".registry.mirrors."192.168.40.62"]
            endpoint = ["https://192.168.40.62:443"]
          [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
             endpoint = ["https://vh3bm52y.mirror.aliyuncs.com","https://registry.docker-cn.com"]

    [plugins."io.containerd.grpc.v1.cri".x509_key_pair_streaming]
      tls_cert_file = ""
      tls_key_file = ""

  [plugins."io.containerd.internal.v1.opt"]
    path = "/opt/containerd"

  [plugins."io.containerd.internal.v1.restart"]
    interval = "10s"

  [plugins."io.containerd.internal.v1.tracing"]
    sampling_ratio = 1.0
    service_name = "containerd"

  [plugins."io.containerd.metadata.v1.bolt"]
    content_sharing_policy = "shared"

  [plugins."io.containerd.monitor.v1.cgroups"]
    no_prometheus = false

  [plugins."io.containerd.runtime.v1.linux"]
    no_shim = false
    runtime = "runc"
    runtime_root = ""
    shim = "containerd-shim"
    shim_debug = false

  [plugins."io.containerd.runtime.v2.task"]
    platforms = ["linux/amd64"]
    sched_core = false

  [plugins."io.containerd.service.v1.diff-service"]
    default = ["walking"]

  [plugins."io.containerd.service.v1.tasks-service"]
    rdt_config_file = ""

  [plugins."io.containerd.snapshotter.v1.aufs"]
    root_path = ""

  [plugins."io.containerd.snapshotter.v1.btrfs"]
    root_path = ""

  [plugins."io.containerd.snapshotter.v1.devmapper"]
    async_remove = false
    base_image_size = ""
    discard_blocks = false
    fs_options = ""
    fs_type = ""
    pool_name = ""
    root_path = ""

  [plugins."io.containerd.snapshotter.v1.native"]
    root_path = ""

  [plugins."io.containerd.snapshotter.v1.overlayfs"]
    root_path = ""
    upperdir_label = false

  [plugins."io.containerd.snapshotter.v1.zfs"]
    root_path = ""

  [plugins."io.containerd.tracing.processor.v1.otlp"]
    endpoint = ""
    insecure = false
    protocol = ""

[proxy_plugins]

[stream_processors]

  [stream_processors."io.containerd.ocicrypt.decoder.v1.tar"]
    accepts = ["application/vnd.oci.image.layer.v1.tar+encrypted"]
    args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]
    env = ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]
    path = "ctd-decoder"
    returns = "application/vnd.oci.image.layer.v1.tar"

  [stream_processors."io.containerd.ocicrypt.decoder.v1.tar.gzip"]
    accepts = ["application/vnd.oci.image.layer.v1.tar+gzip+encrypted"]
    args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]
    env = ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]
    path = "ctd-decoder"
    returns = "application/vnd.oci.image.layer.v1.tar+gzip"

[timeouts]
  "io.containerd.timeout.bolt.open" = "0s"
  "io.containerd.timeout.shim.cleanup" = "5s"
  "io.containerd.timeout.shim.load" = "5s"
  "io.containerd.timeout.shim.shutdown" = "3s"
  "io.containerd.timeout.task.state" = "2s"

[ttrpc]
  address = ""
  gid = 0
  uid = 0

(3)启动服务,并开机运行

bash 复制代码
systemctl start containerd
systemctl enable containerd

3.安装 kubelet、kubeadm、kubectl

​ kubelet(节点管理员):节点上的"容器管家",所有节点必须安装

​ kubeadm(搭建工具):集群部署的"自动化工具"

​ kubectl(你的指挥工具):集群管理的"命令行客户端"

bash 复制代码
# 用 Ubuntu 官方自带的密钥工具,从 Ubuntu 官方密钥服务器,精准下载并导入「K8s 官方验真密钥」,自动存入系统 "密钥保险柜"
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys B53DC80D13EDEF05
# sudo:用管理员权限执行(后面要写系统文件,普通用户没权限);
# apt-key:Ubuntu 官方内置的「apt 仓库密钥管理工具」
# adv:允许 apt-key 执行「高级操作」这里的 "高级操作" 就是「从远程密钥服务器拉取密钥」(普通操作只能管理本地密钥,高级操作能远程获取)
# --keyserver hkp://keyserver.ubuntu.com:80:指定「拉取密钥的服务器地址 + 端口」
# --recv-keys B53DC80D13EDEF05:精准拉取「指定 "密钥指纹" 对应的密钥」


vim /etc/apt/sources.list.d/kubernetes.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
# 因为第一步已经把apt-key 导入的密钥已存入「系统默认密钥环」,所以这边不需要再指定这个仓库需要引用的密钥路径了。
# deb:仓库类型标识,告诉 apt:这是一个「二进制软件包仓库」(软件是编译好的,下载就能直接安装,不用自己编译)
# https://mirrors.aliyun.com/kubernetes/apt/ → 仓库的 "实际下载地址
# kubernetes-xenial 这个仓库的 "专属名字" 
# main 仓库里的 "软件主目录"

# 更新库
apt update
# 查看版本
apt-cache madison kubeadm
# 安装kubelet kubeadm kubectl 
apt install -y kubelet kubeadm kubectl 

# 安装完成所有服务器都重启下
reboot

# 【拓展】
# 因为Ubuntu24.04官方源没有kubelet kubeadm kubectl二阿里仓库有,所以在安装他们的时候会自动匹配到阿里软件仓库安装
# 如果官方源有,阿里源也有,没有指定的情况下,apt默认会哪个源的版本新,就选哪个,如果版本一样,哪个仓库优先级高选哪个(会按源配置文件的加载顺序)
# 配置规则,指定kubelet kubeadm kubectl从mirrors.aliyun.com下载,配置后,apt会先寻找标记为mirrors.aliyun.com的仓库,也就是kubernetes.list文件中写的下载地址匹配。

sudo vim /etc/apt/preferences.d/kubernetes-pref
Package: kubelet kubeadm kubectl  # 只对这三个软件生效
Pin: origin mirrors.aliyun.com    # 指定阿里源(origin 是源的域名)
Pin-Priority: 1001                # 优先级(1001 以上是强制优先)

4.K8S的安装配置文件

(1)在Master服务器,生成安装初始化配置文件

bash 复制代码
# 在K8S的Master服务器上生成K8S的安装配置文件,路径/etc/kubernetes/kubeadm.yaml
kubeadm config print init-defaults > /etc/kubernetes/kubeadm.yaml

(2)配置安装初始化文件

bash 复制代码
# 修改kubeadm.yaml文件内容,为以下标注地方

vim /etc/kubernetes/kubeadm.yaml

apiVersion: kubeadm.k8s.io/v1beta3
bootstrapTokens:
- groups:
  - system:bootstrappers:kubeadm:default-node-token
  token: abcdef.0123456789abcdef
  ttl: 24h0m0s
  usages:
  - signing
  - authentication
kind: InitConfiguration
localAPIEndpoint:
# 修改成Master控制节点的服务器IP
  advertiseAddress: 172.18.22.20
  bindPort: 6443
nodeRegistration:
# find / -name "containerd.sock"查找下文件路径,然后修改下面参数,假设文件路径为/run/containerd/containerd.sock
# 其实就是告诉K8S,containerd得软件交互入口路径Unix 域套接字,为方便理解,你可以把他理解为API接口,这就是为什么containerd已经安装好了,为什么还要告诉Master这个软件路径,因为这个路径不是软件安装位置信息,而是软件得交互入口路径。
  criSocket: unix:///run/containerd/containerd.sock
  imagePullPolicy: IfNotPresent
# 改成Master的主机名,之前配置得HOSTS信息就起到作用。
  name: test1
  taints: null
---
apiServer:
  timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta3
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controllerManager: {}
dns: {}
etcd:
  local:
    dataDir: /var/lib/etcd
# 改成阿里云镜像仓库地址,注意冒号后面要空格。
imageRepository: registry.aliyuncs.com/google_containers
kind: ClusterConfiguration
# 这里指定了安装版本号,如果想安装最新版,注释掉这行即可
kubernetesVersion: 1.28.0
networking:
  dnsDomain: cluster.local
# serviceSubnet可以理解为总机号码,用于给外部统一访问集群得一个IP地址入口,也有点类似NAT的公网地址,serviceSubnet、podSubnet、服务器IP必须不同段
  serviceSubnet: 10.10.10.0/24
# 容器pod的IP地址,可以理解为分机号,用于给容器之间通讯的地址,类似NAT协议中的私有地址,serviceSubnet、podSubnet、服务器IP必须不同段
  podSubnet: 10.10.11.0/24
# 注释或者删除这个POD
# pod
scheduler: {}


# 新增下面两组配置
---
# 定义下载地址
apiVersion: kubeproxy.config.k8s.io/v1alpha1
# 定义这个配置应用于kube-proxy组件
kind: KubeProxyConfiguration
# kube-proxy组件有两种主流转发模式:
# iptables(默认):逐条遍历路由规则,规则越多越耗资源,只能 "随机" 或 "轮询" 转发
# ipvs,把规则存在哈希表里,不管多少规则,都能快速找到,支持多种策略:轮询、最少连接(优先发给空闲 Pod)、哈希等,能根据 Pod 实时负载调整
mode: ipvs


---
# 定义下载地址
apiVersion: kubelet.config.k8s.io/v1beta1
# 定义这个配置应用于Kubelet组件
kind: KubeletConfiguration
# 旧版可能是cgroupfs,资源松散管理弱,配置systemd,起到效果就是保障资源的严格稳定,比如默认配置了1G内存,systemd就是起到不让容器超过1G的效果
cgroupDriver: systemd

下面是非注释版本,直接复制使用,上面的就当理解阅读用,因为注释多了容易报错

bash 复制代码
apiVersion: kubeadm.k8s.io/v1beta3
bootstrapTokens:
- groups:
  - system:bootstrappers:kubeadm:default-node-token
  token: abcdef.0123456789abcdef
  ttl: 24h0m0s
  usages:
  - signing
  - authentication
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: 172.18.22.20
  bindPort: 6443
nodeRegistration:
  criSocket: unix:///run/containerd/containerd.sock
  imagePullPolicy: IfNotPresent
  name: test1
  taints: null
---
apiServer:
  timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta3
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controllerManager: {}
dns: {}
etcd:
  local:
    dataDir: /var/lib/etcd

imageRepository: registry.aliyuncs.com/google_containers
kind: ClusterConfiguration
networking:
  dnsDomain: cluster.local
  serviceSubnet: 10.10.10.0/24
  podSubnet: 10.10.11.0/24

scheduler: {}

---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: ipvs

---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd

(4)Master安装初始化K8S

bash 复制代码
# 在master上执行一键部署集群,使用kubeadm.yaml配置文件,不管 "系统验证类" 的预检通不通,都忽略错误、强行继续初始化集群。
kubeadm init --config=/etc/kubernetes/kubeadm.yaml --ignore-preflight-errors=SystemVerification
# Preflight(预检):kubeadm init 执行前,会自动做一系列 "系统健康检查"(比如服务器内存是否≥2G、swap 是否关闭、内核版本是否达标、端口是否被占用、系统参数是否符合要求等),目的是保证集群能稳定运行;
# SystemVerification(系统验证):是 "预检错误" 的一个类别,包含「服务器硬件 / 系统参数 / 内核版本」等检查(比如内存不足 2G、swap 没关、内核版本略低、文件描述符限制不够等)。

(5)非ROOT用户管理K8S

bash 复制代码
# 给普通用户建一个 "存放登录密钥的文件夹
mkdir -p $HOME/.kube
# 把root权限的 "集群登录密钥"(admin.conf)复制到普通用户的.kube目录下,并重命名为config
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
# 把复制后的config文件的 "所有者" 改成当前普通用户(原本复制过来还是 root 权限,普通用户读不了)
sudo chown $(id -u):$(id -g) $HOME/.kube/config

(6)其他节点安装K8S

(a)生成加入集群的合法校验值
bash 复制代码
# 在 Master 节点执行,核心作用是:自动生成 Worker 节点加入集群所需的完整 kubeadm join 命令(包含合法的 token + 证书校验值)
kubeadm token create --print-join-command
# 假设生成这样数值,注意token有效时间只有24小时,过期需要重新生成
kubeadm join 172.18.22.20:6443 --token tejs9x.o9sguaimf5m16npi --discovery-token-ca-cert-hash sha256:c003be492cc76e0ee8074c1f046c14a944fc95b7c61fefd7e5175138d0e44d6b
(b)加入节点
bash 复制代码
# 在test2、test3客户端粘贴生成的校验值并执行,并加入参数 --ignore-preflight-errors=SystemVerification 不管 "系统验证类" 的预检通不通,都忽略错误、强行继续初始化集群。
kubeadm join 172.18.22.20:6443 --token tejs9x.o9sguaimf5m16npi --discovery-token-ca-cert-hash sha256:c003be492cc76e0ee8074c1f046c14a944fc95b7c61fefd7e5175138d0e44d6b --ignore-preflight-errors=SystemVerification
(c)查看集群状态

kubectl get nodes如果直接输入这个命令,系统可能默认去连 localhost:8080(老版本 K8s 的无效地址),而实际集群的 APIServer 地址是 172.18.22.20:6443,我们在部署好kubeadm后就会在kubernetes下生成一个admin.conf文件,这是是一个 YAML 格式的配置文件,本质是 `kubectl` 访问集群的 "精准访问说明书"

bash 复制代码
# 把配置写入root的bashrc文件最后面加入一条配置export KUBECONFIG=/etc/kubernetes/admin.conf,告诉 kubectl 去哪找 admin.conf的规则,永久固化到 root 用户的终端启动配置里
echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> /root/.bashrc
# 立即生效配置(不用重启终端)
source /root/.bashrc
# 验证
kubectl get nodes

5.安装calico

(1)什么是calico

Calico是 Kubernetes(K8s)生态中最主流的容器网络接口(CNI)插件 ,核心作用是解决容器集群的网络连通性网络安全性问题,是生产环境中企业级 K8s 集群的首选网络方案之一。K8s 由 Google 开发,Calico 由 Tigera (VMware) 开发,两者都是独立开源项目,通过 CNI 标准接口集成,K8s 负责容器调度,Calico 负责网络连接和安全策略

bash 复制代码
我们在改vim /etc/sysctl.d/k8s.conf文件时候 
net.bridge.bridge-nf-call-ip6tables = 1 
net.bridge.bridge-nf-call-iptables = 1 
net.ipv4.ip_forward = 1
这些也是网络管理参数,可能有人就会混淆,他和calico的区别:

sysctl 内核参数:节点级、内核层的基础网络开关,负责启用内核的转发 / 桥接规则处理能力,是所有容器网络插件(Calico/Flannel/Cilium)的运行前提;
Calico:集群级、应用层的容器网络解决方案,利用内核的基础能力,实现 Pod 网络连通、IP 管理、精细化网络策略等高级功能。
简单说:sysctl是 "允许做",Calico是 "具体怎么做"------ 二者不是 "谁替代谁",而是 "底层支撑 + 上层实现" 的关系。

(2)calico和K8S的兼容性查询

bash 复制代码
# 打开网址:
https://docs.tigera.io/calico/latest/getting-started/kubernetes/requirements#kubernetes-compatibility

如下图我们就可以看到calico3.28兼容K8S的1.28版本

(3)下载calico的release包

(4)解压并加载

再自己电脑把release-v3.28.0.tgz用winrar软件解压后,calico-cni.tar、calico-node.tar、calico-kube-controllers.tar通过远程工具上传到3台服务器上即可。

(5)安装组件

(a)解压组件到空间中
bash 复制代码
# 把calico-cni、calico-node、calico-kube-controllers压缩包,解压后放入到k8s.io空间仓库中,可以理解为windows系统中,有一种绿色程序解压后双击注册,然后再打开软件就能使用,这里的动作就类似把压缩文件拷贝到D盘并且解压。
ctr -n k8s.io images import /root/calico-cni.tar
ctr -n k8s.io images import /root/calico-node.tar
ctr -n k8s.io images import /root/calico-kube-controllers.tar
# kubelet(节点管家),调用containerd(仓库货架)进行创建容器时会优先从k8s.io空间中查找组件,没有才会到网络中查找,这个是硬性约定(底层逻辑)。

# 【拓展】
# 1. 创建test空间命令
ctr namespaces create test

# 导入进去后,之前/root下的calico-cni、calico-node、calico-kube-controllers这三个包基本就没用了可以删除
sudo rm /root/calico-cni.tar
# 批量删除(三个tar包一起删)
sudo rm /root/calico-cni.tar /root/calico-node.tar /root/calico-kube-controllers.tar

成功导入示意图:

(b)3台服务器查网卡
bash 复制代码
ip add
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000
    link/ether 00:0c:29:b3:27:f1 brd ff:ff:ff:ff:ff:ff
    altname enp2s1
    inet 172.18.22.20/24 brd 172.18.22.255 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:feb3:27f1/64 scope link
       valid_lft forever preferred_lft forever
3: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
    link/ether e6:d6:79:42:77:e4 brd ff:ff:ff:ff:ff:ff
    inet 10.10.10.10/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever
    inet 10.10.10.1/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever 
# 这里可以看出我们的IP地址172.18.22.20对应的网卡是ens33,我这边服务器都是ens33,所以就不在一一描述
(c)修改安装说明书calico.yaml

calico.yaml,其实类似windows的".reg"注册表文件,告诉系统怎么使用或调用calico的相关组件。

bash 复制代码
# 增加下面两行参数
	- name: IP_AUTODETECTION_METHOD
      value: "interface=ens33"

# 变量名(IP自动检测规则):IP_AUTODETECTION_METHOD
# 指定网卡:ens33

# 核心作用是强制 Calico 节点(calico-node)使用服务器上名为 ens33 的网卡 IP 作为集群内的节点通信 IP ------ 解决多网卡场景下 Calico 自动选错网卡导致的跨节点网络不通问题。
(d)3台服务器都执行安装calico命令(类似windows的注册组件)
bash 复制代码
# 进入到calico.yaml所在目录/root后,输入安装指令,类似windows的注册calico组件动作。
kubectl apply -f calico.yaml
# kubectl:K8s 集群的命令行操作工具
# apply:应用配置
# -f:指定配置文件位置,没写则默认当前目录
# calico.yaml:配置文件名
(e)状态查询
bash 复制代码
# 当3个服务器都安装完成后,在Master test1运行下面命令,可以查看部署情况,如果都是running则表示部署成功
kubectl get pods -n kube-system -o wide | grep calico
# kubectl:K8s 集群的命令行操作工具
# get pods:获取集群中所有 Pod 资源的列表
# -n kube-system:K8s 把核心组件(Calico、kube-proxy、coredns 等)都放在 kube-system 命名空间,不加这个参数看不到 Calico Pod
# -o wide:默认输出只有 Pod 名、状态、运行时长,-o wide 是排查节点级问题的关键,会多输出NODE(运行节点)、IP(Pod 的 IP 地址)、RESTARTS(Pod 重启次数)
# | grep calico:grep 只保留NAME列中含 "calico" 的行避免信息杂乱
# 输出信息:
calico-kube-controllers-8d76c5f9b-h2tlp   1/1     Running   0            53m     10.10.11.131   test1   <none>           <none>
calico-node-8nxdz                         1/1     Running   0            53m     172.18.22.21   test2   <none>           <none>
calico-node-cvtng                         1/1     Running   0            53m     172.18.22.20   test1   <none>           <none>
calico-node-xdbhk                         1/1     Running   0            53m     172.18.22.22   test3   <none>           <none>


# kubectl get nodes也会从NotReady变成 Ready 
kubectl get nodes

NAME    STATUS   ROLES           AGE     VERSION
test1   Ready    control-plane   7d23h   v1.28.2
test2   Ready    <none>          7d23h   v1.28.2
test3   Ready    <none>          7d23h   v1.28.2

正常状态:

状态 出现场景 核心含义 处理
Init:0/3Init:1/3Init:2/3 刚执行 kubectl apply -f calico.yaml calico-node 正在执行初始化容器(安装 CNI、配置网络),数字是 "已完成的初始化容器数 / 总数" 等待即可,通常 1~2 分钟内会推进
Init:3/3 初始化容器全部执行完 所有初始化步骤完成,即将启动 calico-node 主容器 短期过渡,会自动切到 ContainerCreating
ContainerCreating Init:3/3 之后 K8s 正在为 calico-node 主容器分配网络、挂载卷 等待即可,正常持续 10~30 秒
1/1 Running 部署成功后 Calico 容器(calico-node/calico-kube-controllers)完全就绪并运行,集群网络正常 无需处理,这是目标状态
Pending(短期,<1 分钟) calico-kube-controllers 刚创建时 控制器 Pod 正在等待 K8s 调度到节点 等待调度完成即可
Terminating(短期,<1 分钟) 升级 / 删除 Calico Pod 时 Pod 正在正常终止(旧 Pod 退出,新 Pod 启动) 等待自动完成(DaemonSet 升级时常见)

异常状态:

状态 出现场景 核心含义 处理
Init:ImagePullBackOff 部署后 Pod 一直卡这个状态 镜像拉取失败(节点未导入 Calico 镜像、版本不匹配、镜像在非 k8s.io 空间) 1.去 NODE 列对应的节点导入镜像;2. 核对 yaml 镜像版本与导入的一致;3. 确认镜像导入到 k8s.io 空间
Init:ErrImagePull Init:ImagePullBackOff 之前 镜像拉取 "即时失败"(版本不存在、名称写错、私有仓库认证失败) 先核对镜像名称 / 版本,再按ImagePullBackOff处理
Init:CrashLoopBackOff 镜像拉取成功后 初始化容器执行失败(权限不足、CNI 目录无法写入、etcd 连通失败) 1.kubectl describe pod Pod名 -n kube-system 看 Events;2. 检查节点 /etc/cni/net.d 目录权限;3. 验证 etcd 连通性
CrashLoopBackOff(无 Init 前缀) calico-node 主容器启动后 主进程崩溃(网段冲突、内核参数未开 ip_forward、etcd 地址写错) 1.查看 Pod 日志:kubectl logs Pod名 -n kube-system;2. 检查节点内核参数:sysctl net.ipv4.ip_forward;3. 核对 Calico 配置(网段、etcd)
Error 异常状态重试多次后 初始化 / 主容器失败,K8s 停止重试(比如镜像拉取失败次数耗尽、进程崩溃次数超限) 先解决底层问题(镜像 / 配置 / 权限),再删除 Pod 触发重建:kubectl delete pod Pod名 -n kube-system
Pending(长期,>5 分钟) calico-kube-controllers 一直卡这个状态 所有节点的 calico-node 未就绪,控制器 Pod 无可用节点调度 先解决 calico-node 的异常(比如 ImagePullBackOff),控制器会自动调度

如果到这里,你的显示都是Running,那么恭喜你,你的K8S入门部署就算完成啦!

相关推荐
探索云原生2 小时前
Buildah 简明教程:让镜像构建更轻量,告别 Docker 依赖
linux·docker·云原生·go·cicd
走路带_风3 小时前
Ubuntu server 22.04 安装kubernetes
云原生·容器·kubernetes
学习3人组4 小时前
CentOS9安装Docker
docker·容器·eureka
Xyz996_4 小时前
K8S-Configmap资源
云原生·容器·kubernetes
Warren985 小时前
datagrip新建oracle连接教程
数据库·windows·云原生·oracle·容器·kubernetes·django
ascarl20106 小时前
准确--Kubernetes 修改 NodePort 端口范围操作文档
云原生·容器·kubernetes
何包蛋H6 小时前
Docker Maven 插件深度配置指南:Spotify vs Fabric8
docker·容器·maven
从零开始学习人工智能6 小时前
解决Docker容器无法访问宿主机IP和端口的全维度实践指南
tcp/ip·docker·容器
运维技术小记6 小时前
以Jellyfin为例,给群晖NAS容器里的应用升级
容器