# Kubernetes ## 安装 Kubernetes ### 更新系统并安装依赖 ```bash # 更新系统并安装依赖 sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl gnupg # 临时关闭 sudo swapoff -a # 永久禁用:编辑 /etc/fstab 文件,注释掉 swap 相关的行 # 找到类似下面这行,在行首加上 # # /swap.img none swap sw 0 0 sudo sed -i 's/\/swap.img/#\/swap.img/g' /etc/fstab # 关闭防火墙(生产环境建议配置规则) sudo ufw disable # 禁用 SELinux(如已安装) sudo setenforce 0 sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config # 配置主机名和 hosts 文件 (可选,但强烈推荐) sudo hostnamectl set-hostname k8s-master # 配置系统参数 cat < /dev/null # 安装kubelet/kubeadm/kubectl sudo apt update # 取消保持状态 sudo apt-mark unhold kubelet kubeadm kubectl # 安装最新版本 sudo apt install -y kubelet kubeadm kubectl # 锁定版本(避免自动升级) sudo apt-mark hold kubelet kubeadm kubectl ``` ### 将镜像文件保存到指定的路径(可选) ```bash # 停止服务 sudo systemctl stop containerd sudo systemctl stop kubelet # Generate default containerd config sudo mkdir -p /etc/containerd sudo containerd config default | sudo tee /etc/containerd/config.toml # Edit the config to enable SystemdCgroup sudo mkdir -p /data/containerd/root # 修改 containerd 服务的默认存储路径 sudo sed -i 's/root = "\/var\/lib\/containerd"/root = "\/data\/containerd\/root"/' /etc/containerd/config.toml # 迁移现有数据 sudo rsync -avz /var/lib/containerd/ /data/containerd/root/ sudo mv /var/lib/containerd /var/lib/containerd-bak sudo systemctl daemon-reload sudo systemctl start containerd sudo systemctl enable containerd sudo systemctl start kubelet sudo systemctl status containerd ``` ### 配置镜像加速 ```bash # 注意:配置镜像加速器 # k8s、crictl 可以使用 config.toml 中的镜像配置 # ctr 需要指定 --hosts-dir # config_path 和 plugins."io.containerd.grpc.v1.cri".registry.mirrors 是互斥的,只能配置一个,优先使用 config_path # 配置镜像加速器配置文件路径 # [plugins."io.containerd.grpc.v1.cri".registry] # config_path = " # ... # [plugins."io.containerd.grpc.v1.cri".registry.mirrors] # [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] # endpoint = ["https://docker.m.daocloud.io"] # 启用 SystemdCgroup sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml # 配置镜像加速器配置文件路径 sudo sed -i 's/registry.k8s.io\/pause:3.8/registry.aliyuncs.com\/google_containers\/pause:3.9/' /etc/containerd/config.toml # 创建配置文件 sudo mkdir -p /etc/containerd/certs.d/docker.io sudo tee /etc/containerd/certs.d/docker.io/hosts.toml << EOF server = "https://docker.io" [host."https://docker.m.daocloud.io"] capabilities = ["pull", "resolve"] [host."https://docker.m.daocloud.io/library"] capabilities = ["pull", "resolve"] EOF sudo mkdir -p /etc/containerd/certs.d/registry.k8s.io sudo tee /etc/containerd/certs.d/registry.k8s.io/hosts.toml << EOF server = "https://registry.k8s.io" [host."https://k8s.m.daocloud.io"] capabilities = ["pull", "resolve"] EOF # 在 config.toml 中启用镜像加速器 # 源站 --> 替换为 # https://github.com/DaoCloud/public-image-mirror # docker.elastic.co --> elastic.m.daocloud.io # docker.io --> docker.m.daocloud.io # gcr.io --> gcr.m.daocloud.io # ghcr.io --> ghcr.m.daocloud.io # k8s.gcr.io --> k8s-gcr.m.daocloud.io # k8s.gcr.io 已被迁移到 registry.k8s.io # registry.k8s.io --> k8s.m.daocloud.io # mcr.microsoft.com --> mcr.m.daocloud.io # nvcr.io --> nvcr.m.daocloud.io # quay.io --> quay.m.daocloud.io # registry.ollama.ai --> ollama.m.daocloud.io # 实验内测中 [plugins."io.containerd.grpc.v1.cri".registry.mirrors] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.elastic.co"] endpoint = ["https://elastic.m.daocloud.io"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] endpoint = ["https://docker.m.daocloud.io"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io/library"] endpoint = ["https://docker.m.daocloud.io/library"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"] endpoint = ["https://gcr.m.daocloud.io"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."ghcr.io"] endpoint = ["https://ghcr.m.daocloud.io"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"] endpoint = ["https://k8s-gcr.m.daocloud.io"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.k8s.io"] endpoint = ["https://k8s.m.daocloud.io"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."mcr.microsoft.com"] endpoint = ["https://mcr.m.daocloud.io"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."nvcr.io"] endpoint = ["https://nvcr.m.daocloud.io"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."quay.io"] endpoint = ["https://quay.m.daocloud.io"] # 检查配置是否生效 sudo systemctl restart containerd sudo systemctl status containerd sudo containerd config dump | grep -A 5 'plugins."io.containerd.grpc.v1.cri".registry' sudo ctr plugins ls | grep -i cri # io.containerd.grpc.v1 cri linux/amd64 ok ``` ## 初始化 Kubernetes 集群 ```bash # 拉取镜像 sudo kubeadm config images pull \ --image-repository=registry.aliyuncs.com/google_containers \ --kubernetes-version=v1.28.15 sudo ctr -n k8s.io image ls # # 拉取一个测试镜像 # sudo ctr images pull --hosts-dir "/etc/containerd/certs.d" docker.io/calico/cni:v3.28.0 # sudo ctr -n k8s.io image pull --hosts-dir "/etc/containerd/certs.d" registry.k8s.io/pause:3.9 # # 检查新目录的磁盘使用情况 # sudo du -sh /data/containerd # Restart containerd sudo systemctl restart containerd sudo ctr version # 通过阿里云获取镜像文件 sudo ctr images pull registry.aliyuncs.com/google_containers/coredns:v1.10.1 sudo ctr images tag registry.aliyuncs.com/google_containers/coredns:v1.10.1 registry.k8s.io/coredns/coredns:v1.10.1 sudo ctr images pull registry.aliyuncs.com/google_containers/etcd:3.5.9-0 sudo ctr images tag registry.aliyuncs.com/google_containers/etcd:3.5.9-0 registry.k8s.io/etcd:3.5.9-0 sudo ctr images pull registry.aliyuncs.com/google_containers/kube-apiserver:v1.28.15 sudo ctr images tag registry.aliyuncs.com/google_containers/kube-apiserver:v1.28.15 registry.k8s.io/kube-apiserver:v1.28.15 sudo ctr images pull registry.aliyuncs.com/google_containers/kube-controller-manager:v1.28.15 sudo ctr images tag registry.aliyuncs.com/google_containers/kube-controller-manager:v1.28.15 registry.k8s.io/kube-controller-manager:v1.28.15 sudo ctr images pull registry.aliyuncs.com/google_containers/kube-proxy:v1.28.15 sudo ctr images tag registry.aliyuncs.com/google_containers/kube-proxy:v1.28.15 registry.k8s.io/kube-proxy:v1.28.15 sudo ctr images pull registry.aliyuncs.com/google_containers/kube-scheduler:v1.28.15 sudo ctr images tag registry.aliyuncs.com/google_containers/kube-scheduler:v1.28.15 registry.k8s.io/kube-scheduler:v1.28.15 sudo ctr images pull registry.aliyuncs.com/google_containers/registry.k8s.io/pause:3.9 sudo ctr images tag registry.aliyuncs.com/google_containers/registry.k8s.io/pause:3.9 registry.k8s.io/pause:3.9 #获取本机内网IP ip addr show eth0 # 192.168.0.196 # 初始化集群 sudo kubeadm init \ --apiserver-advertise-address=192.168.0.196 \ --image-repository=registry.aliyuncs.com/google_containers \ --pod-network-cidr=192.168.0.0/16 \ --kubernetes-version=v1.28.15 # 配置kubectl(普通用户) mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config # 设置环境变量(永久设置) echo "export KUBECONFIG=$HOME/.kube/config" >> ~/.bashrc source ~/.bashrc # 检查节点状态 kubectl get nodes kubectl get pods -A kubectl get pods --all-namespaces # 如果你還沒有安裝 CNI,你很可能會看到 coredns 的 Pods 處於 Pending (等待中) 狀態。 kubectl cluster-info # Then install a CNI plugin (e.g., Calico): sudo ctr images pull --hosts-dir "/etc/containerd/certs.d" docker.io/calico/cni:v3.27.0 sudo ctr images pull --hosts-dir "/etc/containerd/certs.d" docker.io/calico/node:v3.27.0 sudo ctr images pull --hosts-dir "/etc/containerd/certs.d" docker.io/calico/kube-controllers:v3.27.0 # 安装 Calico 网络插件 # 方式一: wget https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/tigera-operator.yaml wget https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/custom-resources.yaml kubectl create -f ./tigera-operator.yaml kubectl create -f ./custom-resources.yaml # 方式二: wget https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml kubectl apply -f ./calico.yaml # 查看 Calico 插件的 Pods 是否正常运行 watch -n 1 kubectl get pods -A kubectl describe pod calico-node-4q9kh -n kube-system # 配置 crictl 让其使用 containerd 作为运行时 sudo tee /etc/crictl.yaml << EOF runtime-endpoint: unix:///run/containerd/containerd.sock image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false EOF # 检查配置是否生效 sudo crictl info sudo crictl pull docker.io/library/mysql:8 sudo crictl images # 通过 crictl 获取镜像文件 sudo crictl pull registry.k8s.io/coredns/coredns:v1.10.1 sudo crictl pull registry.k8s.io/etcd:3.5.9-0 sudo crictl pull registry.k8s.io/kube-apiserver:v1.28.15 sudo crictl pull registry.k8s.io/kube-controller-manager:v1.28.15 sudo crictl pull registry.k8s.io/kube-proxy:v1.28.15 sudo crictl pull registry.k8s.io/kube-scheduler:v1.28.15 sudo crictl pull registry.k8s.io/pause:3.9 ``` ### 设置集群单节点可以使用 ```bash # 重要:设置集群单节点可以使用,否则服务由于不能分配到资源,不能正常启动 kubectl describe node arno | grep Taints # 你可以直接使用節點的角色來移除,或者指定節點名稱 # --all 會對所有擁有該角色的節點生效,在單節點環境中很方便 kubectl taint nodes --all node-role.kubernetes.io/control-plane- # 再次检查 kubectl describe node arno | grep Taints ``` ### 测试功能 ```bash # 测试 kubectl create deployment nginx-test --image=nginx kubectl get pods kubectl expose deployment nginx-test --type=NodePort --port=80 kubectl get services curl http://localhost:30209 ``` ### 修改 containerd 以指定组的权限来运行 ```bash # 让 containerd 以指定组的权限来运行 sudo groupadd containerd sudo usermod -aG containerd $USER getent group containerd # containerd:x:1002:arno # 修改 /etc/containerd/config.toml 以下内容 [grpc] address = "/run/containerd/containerd.sock" uid = 0 gid = 1002 # 重启服务 sudo systemctl restart containerd # 查看是否生效 newgrp containerd ls -l /run/containerd/containerd.sock crictl images ``` ## Deployment、StatefulSet 和 DaemonSet 的区别 `Deployment`、`StatefulSet` 和 `DaemonSet` 是三种最常用的应用部署控制器,但它们的设计目标和适用场景完全不同。 简单来说: - **Deployment**:用于部署**无状态应用**,核心是“随意扩缩、随意替换”。 - **StatefulSet**:用于部署**有状态应用**,核心是“身份唯一、顺序稳定”。 - **DaemonSet**:用于确保**每个节点上都运行一个副本**,核心是“节点覆盖、常驻运行”。 下面我们用一个更生动的比喻,然后进行详细的技术对比。 ### 核心比喻:公司员工安排 想象一下你要为一个新公司安排三种不同类型的员工: - **Deployment (客服团队)** - **特点**:团队里有10个客服人员。他们每个人都做完全一样的工作,没有名字,只有工号(比如 `客服-A`, `客服-B`)。客户打进电话,随便接给谁都行。 - **扩缩容**:业务忙了,老板说:“客服加到20人!” 于是就新招10个一模一样的人。业务闲了,就随便裁掉几个,剩下的继续工作,不受影响。 - **替换**:某个客服(Pod)生病请假了(挂了),公司会立刻招一个新人来顶替他的位置,保证总人数不变。这个新人是全新的,和之前那个没任何关系。 - **核心**:员工之间**可随意替换**,不关心具体是哪个人,只关心总人数。 - **StatefulSet (管理层团队)** - **特点**:公司有CEO、CTO、CFO三位高管。他们每个人都有**唯一的身份和职责**。CEO的位子只能是CEO,不能随便换成CTO。 - **身份和顺序**:他们的职位是固定的 (`ceo-0`, `cto-1`, `cfo-2`)。招聘时必须按顺序来:先招CEO,CEO到位了再招CTO,最后招CFO。离职时也得按相反顺序来,保证权力平稳交接。 - **专属资源**:每个人都有自己**专属的办公室和电脑**(稳定的存储和网络标识)。即使CEO离职了,新来的CEO也会接管原来的办公室和电脑,里面的文件资料都在。 - **核心**:每个成员**身份唯一、不可替代**,并且有严格的顺序和专属资源。 - **DaemonSet (安保/保洁团队)** - **特点**:公司有3层楼,老板要求**每一层楼都必须有一个保安和一个保洁员**。 - **节点绑定**:公司新租了一层楼(增加一个Node),就必须自动为这层楼配一个保安和保洁(自动部署一个Pod)。如果退租一层楼(移除一个Node),这层楼的保安和保洁也就自动撤离了。 - **数量**:团队总人数不固定,取决于楼层(Node)的数量。你不能说“我要5个保安”,你只能说“每层楼都要有保安”。 - **核心**:确保**每个单元(Node)上都有一个副本**,用于执行该单元的特定任务(如监控、日志收集)。 --- ### 详细技术对比表 | 特性 | Deployment | StatefulSet | DaemonSet | | :--- | :--- | :--- | :--- | | **核心用途** | 管理**无状态**应用。 | 管理**有状态**应用。 | 在集群中**每个(或部分)节点**上运行一个 Pod 副本。 | | **Pod 身份** | Pod 是**可互换的(Fungible)**,没有唯一身份。Pod 名称是随机的,如 `myapp-deploy-random-string`。 | Pod 拥有**稳定且唯一的身份**。Pod 名称是**有序且可预测的**,如 `web-0`, `web-1`。 | Pod 名称包含节点名,如 `fluentd-abc12`,但身份不重要,重要的是它所在的节点。 | | **网络** | 所有 Pod 共享一个 Service IP。Pod 自身的 IP 和主机名在重启后会改变。 | 每个 Pod 拥有**稳定的、唯一的网络标识**(DNS 条目),如 `web-0.svc.cluster.local`。重启后不变。 | Pod 使用所在节点的网络,通常配置 `hostPort` 或 `hostNetwork`。 | | **存储** | 通常使用共享存储(如 ReadWriteMany)或不使用持久化存储。所有副本共享同一个 PVC。 | 每个 Pod 副本拥有**自己独立的、持久化的存储卷(PVC)**。Pod `web-0` 始终绑定 `pvc-web-0`。 | 通常直接访问节点的文件系统,或使用 `hostPath` 卷来读写节点上的数据。 | | **伸缩与部署** | **并行、无序**。可以一次性创建或删除多个副本。支持滚动更新(Rolling Update)。 | **有序、串行**。部署、扩容、缩容、更新都按 Pod 序号(0, 1, 2...)**顺序执行**。 | **自动**。当一个新节点加入集群时,DaemonSet 会自动在该节点上部署一个 Pod。节点移除时,Pod 也被回收。 | | **`replicas` 字段** | **需要**。用户明确指定需要运行的 Pod 数量。 | **需要**。用户明确指定需要运行的 Pod 数量。 | **不需要**。Pod 的数量由匹配 `nodeSelector` 的节点数量决定。 | | **适用场景** | - Web 服务器 (Nginx, Apache)
- API 网关
- 无状态的微服务 | - 数据库 (MySQL, PostgreSQL, MongoDB)
- 消息队列 (Kafka, RabbitMQ)
- 分布式文件系统 (Zookeeper) | - 日志收集器 (Fluentd, Logstash)
- 监控代理 (Prometheus Node Exporter, Datadog Agent)
- 网络插件 (Calico, Flannel)
- 存储插件 (Ceph, GlusterFS) | --- ### 如何选择? 你可以通过问自己以下几个问题来决定使用哪一个: - **我的应用需要在集群的每个节点上都运行一个实例吗?** - **是** -> 使用 **DaemonSet**。 (例如,为了从每个节点收集日志) - **我的应用需要稳定的网络标识(固定的DNS名)或每个实例需要独立的持久化数据吗?** - **是** -> 使用 **StatefulSet**。 (例如,部署一个主从复制的数据库,每个节点的数据都不能丢失) - **以上都不是?我的应用是无状态的,可以随意创建和销毁,而不用担心数据丢失或实例身份吗?** - **是** -> 使用 **Deployment**。 (这是最常见的情况,例如部署一个网站后端服务) 掌握这三者的区别,你就掌握了在 Kubernetes 中部署不同类型应用的核心武器。