# Kubernetes集群搭建

# Prerequisites

## 硬件资源

目前登录使用的是机房内的一台机器（192.168.40.50） 连接已经配好的虚拟机地址 虚拟机硬件资源如下： os版本：Ubuntu 24.04 LTS (GNU/Linux 6.8.0-31-generic x86\_64) k8s版本：Kubernetes v1.30.6

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1730453790805/f2db13d5-11a2-471a-8322-6022375cf017.png align="center")

将会使用kubeadm进行安装

## 安装前要求检测

* 一台兼容的 Linux 主机。Kubernetes 项目为基于 Debian 和 Red Hat 的 Linux 发行版以及一些不提供包管理器的发行版提供通用的指令。
    
* 每台机器 2 GB 或更多的 RAM（如果少于这个数字将会影响你应用的运行内存）。
    
* 控制平面机器需要 CPU 2 核心或更多。
    
* 集群中的所有机器的网络彼此均能相互连接（公网和内网都可以）。
    
* 节点之中不可以有重复的主机名、MAC 地址或 product\_uuid。请参见[这里](https://kubernetes.io/zh-cn/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#verify-mac-address)了解更多详细信息。
    
* 开启机器上的某些端口。请参见[这里](https://kubernetes.io/zh-cn/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#check-required-ports)了解更多详细信息。
    
* 交换分区的配置。kubelet 的默认行为是在节点上检测到交换内存时无法启动。更多细节参阅[交换内存管理](https://kubernetes.io/zh-cn/docs/concepts/architecture/nodes/#swap-memory)。
    
* 如果 kubelet 未被正确配置使用交换分区，则你**必须**禁用交换分区。例如，`sudo swapoff -a` 将暂时禁用交换分区。要使此更改在重启后保持不变，请确保在如 `/etc/fstab`、`systemd.swap` 等配置文件中禁用交换分区，具体取决于你的系统如何配置。
    

# Install Steps

## 配置必要插件以及网络参数

k8s 需要通过ipv4来进行不同pod之间的通信

```sh
# 安装两个必要内核插件，overlay 和 br_netfilter
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
​
# 动态加载这两个模块
sudo modprobe overlay
sudo modprobe br_netfilter
​
# 设置所需的 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
​
# 应用 sysctl 参数而不重新启动
sudo sysctl --system
​
# 使用以下命令验证 net.ipv4.ip_forward 是否设置为 1
sudo sysctl net.ipv4.ip_forward
```

## 下载containerd service 选择 container runtime

这里选择containerd而不是docker（kubernetes新版本已将默认容器编排切换为containerd）

```json
## 1、containerd
# 下载包
wget https://github.com/containerd/containerd/releases/download/v1.7.22/containerd-1.7.22-linux-amd64.tar.gz
​
# 将下载的包解压到/usr/local下
tar Cxzvf /usr/local containerd-1.7.22-linux-amd64.tar.gz

# 下载服务启动文件
wget -O /etc/systemd/system/containerd.service https://raw.githubusercontent.com/containerd/containerd/main/containerd.service

# 文件内容如下，下载不下来直接复制
cat /etc/systemd/system/containerd.service
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target local-fs.target
[Service]
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/containerd
Type=notify
Delegate=yes
KillMode=process
Restart=always
RestartSec=5
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity
# Comment TasksMax if your systemd version does not supports it.
# Only systemd 226 and above support this version.
TasksMax=infinity
OOMScoreAdjust=-999
[Install]
WantedBy=multi-user.target
# 启动containerd
systemctl daemon-reload
systemctl enable --now containerd
​
## 2、Installing runc
wget https://github.com/opencontainers/runc/releases/download/v1.2.0-rc.3/runc.amd64
TODO:worker1
sudo install -m 755 runc.amd64 /usr/local/sbin/runc
```

containerd也可以通过apt来下载：

```json
`sudo apt-get install containerd.io`
```

## 修改containerd配置文件 (!important)

### containerd 相关配置

```json
# 创建containerd目录
sudo mkdir /etc/containerd
​
# 恢复默认配置文件
sudo containerd config default | sudo tee /etc/containerd/config.toml > /dev/null

​
# 切换为国内源
sed -i 's/registry.k8s.io/registry.aliyuncs.com\/google_containers/' /etc/containerd/config.toml
​
# 修改SystemCgroup为true
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
    SystemdCgroup = true
​
# 重启服务
sudo systemctl daemon-reload
sudo systemctl restart containerd
```

### 设置cgroup

```shell
sudo sed -i 's/SystemdCgroup = true/SystemdCgroup = false/' /etc/containerd/config.toml
```

#### 为什么还需要在config中设置镜像源

使用kubeadm init 并指定`image-repository` 后仍需要在 `containerd` 的 `config.toml` 文件中指定镜像源的原因，主要是由于这两个配置的生命周期不同：

* kubeadm init 配置镜像源仅用于初始化过程加速必要的相关组件：如 `kube-apiserver`、`kube-controller-manager`、`kube-scheduler` 等）
    
* `containerd` 自身的配置不受 `kubeadm` 影响 。Kubernetes 组件启动后，由 `containerd` 负责持续管理容器和镜像的生命周期。当 `containerd` 需要重新拉取镜像（例如镜像更新或节点重启后），它将遵循自己的 `config.toml` 配置文件，而不是 `kubeadm` 的参数。未在 `containerd` 中配置镜像源时，它会默认访问官方的 [`k8s.gcr.io`](http://k8s.gcr.io) 源，而这一源在中国大陆访问通常较慢或受限。
    

设置完成后保存、重新加载containerd配置、重启containerd服务、验证配置是否生效

```shell
sudo systemctl daemon-reload
sudo systemctl restart containerd
sudo systemctl status containerd
```

## 下载kubeadm以及相关组件

```bash
sudo apt-get update
# apt-transport-https may be a dummy package; if so, you can skip that package
sudo apt-get install -y apt-transport-https ca-certificates curl gpg

# If the directory `/etc/apt/keyrings` does not exist, it should be created before the curl command, read the note below.
# sudo mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

# This overwrites any existing configuration in /etc/apt/sources.list.d/kubernetes.list
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list

sudo apt-get update

sudo apt-get install -y kubelet kubeadm kubectl

sudo apt-mark hold kubelet kubeadm kubectl
```

## 初始化master节点

### 命令行部署

国内如果不指定image源，在拉取sandbox的时候会出现异常 sudo kubeadm init --image-repository [registry.aliyuncs.com/google\_containers](http://registry.aliyuncs.com/google_containers) --pod-network-cidr=10.128.0.0/16

### yaml部署（推荐）

首先执行kubeadm config print init-defaults 输出kubeadm默认配置

之后需要在kubeadm-config.yaml这个文件中修改以下几个地方：

1. hostname 修改name为自己的主机名称，上下文： nodeRegistration: criSocket: unix:///run/containerd/containerd.sock imagePullPolicy: IfNotPresent imagePullSerial: true name: k8s-master taints: null
    
2. podSubnet 追加podSubnet podSubnet: [10.244.0.0/16](http://10.244.0.0/16) 上下文： networking: dnsDomain: [cluster.local](http://cluster.local) serviceSubnet: [10.96.0.0/12](http://10.96.0.0/12) podSubnet: [10.244.0.0/16](http://10.244.0.0/16)
    
3. KubeletConfiguration 文件最后追加 KubeletConfiguration apiVersion: [kubelet.config.k8s.io/v1beta1](http://kubelet.config.k8s.io/v1beta1) kind: KubeletConfiguration cgroupDriver: systemd
    

之后，可以根据yaml文件初始化master节点：kubeadm init --config=kubeadm-config.yaml

## master初始化完成

初始化完成后显示示例：

```json
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

Alternatively, if you are the root user, you can run:

export KUBECONFIG=/etc/kubernetes/admin.conf

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/

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

kubeadm join 192.168.40.140:6443 --token wssf4y.fikow8fj6w1f2oar \
        --discovery-token-ca-cert-hash sha256:f942ac3be4f63eff7ff780145bf2937bd10656e7a361daf2da002fe2ece6a76e
```

注意：`export KUBECONFIG=$HOME/.kube/config` 这里需要设置环境变量，当使用普通用户非root的时候，如果没有上述操作，可能会默认读取/etc下的config文件

## 初始化flannel网络插件

下载配置文件

```yaml
wget https://raw.githubusercontent.com/coreos/flannel/a70459be0084506e4ec919aa1c114638878db11b/Documentation/kube-flannel.yml
```

如果连接失败可参考文件：

```yaml
apiVersion: v1
kind: Namespace
metadata:
  labels:
    k8s-app: flannel
    pod-security.kubernetes.io/enforce: privileged
  name: kube-flannel
---
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    k8s-app: flannel
  name: flannel
  namespace: kube-flannel
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    k8s-app: flannel
  name: flannel
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes/status
  verbs:
  - patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    k8s-app: flannel
  name: flannel
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: flannel
subjects:
- kind: ServiceAccount
  name: flannel
  namespace: kube-flannel
---
apiVersion: v1
data:
  cni-conf.json: |
    {
      "name": "cbr0",
      "cniVersion": "0.3.1",
      "plugins": [
        {
          "type": "flannel",
          "delegate": {
            "hairpinMode": true,
            "isDefaultGateway": true
          }
        },
        {
          "type": "portmap",
          "capabilities": {
            "portMappings": true
          }
        }
      ]
    }
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "EnableNFTables": false,
      "Backend": {
        "Type": "vxlan"
      }
    }
kind: ConfigMap
metadata:
  labels:
    app: flannel
    k8s-app: flannel
    tier: node
  name: kube-flannel-cfg
  namespace: kube-flannel
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app: flannel
    k8s-app: flannel
    tier: node
  name: kube-flannel-ds
  namespace: kube-flannel
spec:
  selector:
    matchLabels:
      app: flannel
      k8s-app: flannel
  template:
    metadata:
      labels:
        app: flannel
        k8s-app: flannel
        tier: node
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/os
                operator: In
                values:
                - linux
      containers:
      - args:
        - --ip-masq
        - --kube-subnet-mgr
        command:
        - /opt/bin/flanneld
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: EVENT_QUEUE_DEPTH
          value: "5000"
        image: docker.io/flannel/flannel:v0.26.0
        name: kube-flannel
        resources:
          requests:
            cpu: 100m
            memory: 50Mi
        securityContext:
          capabilities:
            add:
            - NET_ADMIN
            - NET_RAW
          privileged: false
        volumeMounts:
        - mountPath: /run/flannel
          name: run
        - mountPath: /etc/kube-flannel/
          name: flannel-cfg
        - mountPath: /run/xtables.lock
          name: xtables-lock
      hostNetwork: true
      initContainers:
      - args:
        - -f
        - /flannel
        - /opt/cni/bin/flannel
        command:
        - cp
        image: docker.io/flannel/flannel-cni-plugin:v1.5.1-flannel2
        name: install-cni-plugin
        volumeMounts:
        - mountPath: /opt/cni/bin
          name: cni-plugin
      - args:
        - -f
        - /etc/kube-flannel/cni-conf.json
        - /etc/cni/net.d/10-flannel.conflist
        command:
        - cp
        image: docker.io/flannel/flannel:v0.26.0
        name: install-cni
        volumeMounts:
        - mountPath: /etc/cni/net.d
          name: cni
        - mountPath: /etc/kube-flannel/
          name: flannel-cfg
      priorityClassName: system-node-critical
      serviceAccountName: flannel
      tolerations:
      - effect: NoSchedule
        operator: Exists
      volumes:
      - hostPath:
          path: /run/flannel
        name: run
      - hostPath:
          path: /opt/cni/bin
        name: cni-plugin
      - hostPath:
          path: /etc/cni/net.d
        name: cni
      - configMap:
          name: kube-flannel-cfg
        name: flannel-cfg
      - hostPath:
          path: /run/xtables.lock
          type: FileOrCreate
        name: xtables-lock
```

### 设置镜像源

```toml
[plugins."io.containerd.grpc.v1.cri".registry.mirrors] 
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."[docker.io](http://docker.io)"] 
endpoint = ["[https://docker.1panel.live(https://docker.1panel.live)","[https://docker.agsv.top](https://docker.agsv.top)","[https://docker.agsvpt.work](https://docker.agsvpt.work)"] 
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."[k8s.gcr.io](http://k8s.gcr.io)"]
endpoint = ["[registry.aliyuncs.com/google_containers](http://registry.aliyuncs.com/google_containers)"]
```

目前境内一些好用的镜像源：

```json
https://docker.1panel.live ✅网友自建
地址 https://docker.agsv.top  ✅网友自建
备用 https://docker.agsvpt.work  ✅网友自建
地址①:https://dockerpull.com ✅网友自建
地址②:https://dockerproxy.cn ✅网友自建
```

配置完成后进行应用 kubectl apply -f kube-flannel.yml

kubectl get pods --all-namespaces 可以检查看dns和flannel相关pod有没有正常启动，

## 初始化工作节点

工作节点前置任务完成后（安装containerd&安装kubeadm）

```bash
kubeadm join <master-ip>:<port> --token abcdef.0123456789abcdef \
    --discovery-token-ca-cert-hash sha256:<discovery-token-ca-cert-hash>
```

**主节点的 IP 和端口**：

主节点的 IP 地址可以通过在主节点上运行 `ip addr show` 或 `ifconfig` 命令来获取。

端口通常是 `6443`，这是 Kubernetes API 服务器默认使用的端口。

**加入令牌（Token）**：

当你使用 `kubeadm init` 初始化主节点时，命令会输出一个加入令牌。这个令牌用于工作节点加入集群时的身份验证。

如果你丢失了这个令牌，可以通过以下命令在主节点上重新获取：

```bash
kubeadm token list
```

这将列出所有可用的令牌及其过期时间。你可以选择一个合适的令牌用于 `kubeadm join` 命令。

**发现令牌的 CA 证书哈希**：

发现令牌的 CA 证书哈希是用于验证集群信息的证书的哈希值。这个哈希值也可以在主节点上通过以下命令获取：

```bash
kubeadm token create --print-join-command
```

这个命令会输出一个完整的 `kubeadm join` 命令，包括所需的 CA 证书哈希。你可以直接复制这个命令中的哈希值。

```bash
# 可以在master节点生成新的加入令牌
kubeadm token create --print-join-command
```

初始化完成后可以跳到主节点，kubectl get nodes 查看是否加入成功

工作节点不需要进行flannel配置，工作节点加入集群后会自动进行同步

### 初始化节点失败后回退方法

重置 Kubernetes 节点，使其恢复到初始化前的状态 sudo kubeadm reset -f

手动清除配置文件： sudo rm -rf /etc/kubernetes /var/lib/etcd /var/lib/kubelet /var/lib/dockershim /var/run/kubernetes

查看是否有端口占用，如果有kill一下 sudo lsof -i :10250
