Files
Qiniu/technical/xinfra-preview/k8s-rke2-fundamentals.md
T

378 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [k8s, rke2, preview, infra]
create time: 2026-07-04 12:00
---
# K8s + RKE2 基础
## 概述
Kubernetes(K8s)是容器编排的事实标准,Rancher RKE2 是 CNCF 认证的轻量级、安全优先的 K8s 发行版,专为边缘和混合云场景设计。xinfra 平台的容器调度底层基于 RKE2,后续所有服务部署都建立在对这个知识栈的理解之上。
## 核心概念
### Kubernetes 核心对象关系
```mermaid
graph TD
Node[Node / 节点] --> Pod[Pod / 最小部署单元]
Pod --> C[Containers / 容器]
Deployment[Deployment / 副本控制] --> Pod
Service[Service / 网络暴露] --> Pod
Namespace[Namespace / 资源隔离] --> Deployment
Namespace --> Service
PV[PersistentVolume / 持久存储] --> PVC[PVC / 申请存储]
PvcRef[PVC ref by Pod] --> Pod
```
| 对象 | 职责 | 类比 |
| ---------------------- | ------------------------------- | -------- |
| **Node** | 运行 Pod 的物理机或虚拟机 | 服务器 |
| **Pod** | 一个或多个容器的打包体 | 应用实例 |
| **Deployment** | 管理 Pod 的副本数、滚动更新策略 | 发布控制器 |
| **Service** | 为一组 Pod 提供稳定的访问入口(负载均衡) | 反向代理 |
| **Namespace** | 逻辑隔离的资源分组 | 多租户文件夹 |
| **ConfigMap / Secret** | 将配置注入 Pod(非敏感/敏感数据分离) | .env 文件 |
| **Ingress** | 七层路由,HTTP/HTTPS 域名到 Service 的映射 | Nginx 规则 |
### RKE2 vs 原生 K8s
RKE2(Rancher Kubernetes Engine v2)的核心特点:
1. **单二进制部署**:不需要单独安装 etcd、containerd 等依赖,一条命令拉起整集群
2. **SST(Simple System Tray)**内置 SQLite,etcd 作为可选而非必须
3. **FIPS 140-2 合规**:内置加密要求,适合企业对安全审计的需要
4. **自动注册与拉取**:可配合 Rancher Server 实现节点无感加入
> [!info] 为什么选 RKE2?
> xinfra 需要管理**七机房多套集群**,RKE2 的"一键部署 + 低运维成本"特性正好契合——减少环境差异导致的兼容问题,让平台专注于上层抽象而非底层排障。
### RKE2 安装方式对比
| 方式 | 命令特点 | 适用场景 | 是否支持 Rancher 集成 |
|------|---------|---------|---------------------|
| **单二进制直接运行** | curl → sh(安装脚本直出,无需额外依赖) | 开发测试、小规模集群(≤3 节点) | ✅ — 自动发现并注册 |
| **Systemd 服务管理** | `systemctl enable --now rke2-server` | 生产环境标准化部署 | ✅ |
| **Air-gap(离线)** | 预下载 `.tar.gz` 包 + `INSTALL_DIR=/opt/rke2` 指向本地文件 | 内网无外网环境的机房 | ❌ — 需手动同步镜像 |
> [!warning] Air-gap 是常见坑点
> RKE2 Server/Agent 首次启动时会从官方 registry 拉取 K8s 组件 OCI 镜像。如果机房**无法访问外网**,必须提前准备 image bundle tarball,并通过 `--image-volume-mount /var/lib/rancher/rke2/agent/image-store` 指定本地缓存路径。
### 节点注册机制详解
Worker 节点加入集群有两种认证方式:
```bash
# 方式 1:静态 Token(最简单,适合小集群)
# 服务端会在 /etc/rancher/rke2/rke2.yaml 旁生成 token
export TOKEN="K10xxx...from server node"
rke2 agent --server https://master-ip:6443 --token $TOKEN
# 方式 2:Bootstrap 证书签名(推荐,支持 TLS 双向认证)
# Agent 首次连接时发送 CSR(Certificate Signing Request),
# kube-controller-manager 审批后颁发客户端证书
```
关键文件分布:
| 路径 | 内容 | 说明 |
|------|------|------|
| `/etc/rancher/rke2/rke2.yaml` | kubeconfig(含 admin 权限证书) | ⚠️ 等同于集群 master key,务必备份保护 |
| `/var/lib/rancher/rke2/agent/` | 容器镜像、kubelet 证书 | Agent 工作目录 |
| `/var/lib/rancher/rke2/server/` | etcd 数据、控制平面组件 | Server 工作目录 |
| `/var/lib/rancher/rke2/bin/` | rke2、kubectl、crictl 等 | 可执行文件软链到原位置 |
### RKE2 配置管理
RKE2 支持多种配置来源,优先级从低到高:
```yaml
# 1. 系统级配置(最低优先级)
# /etc/rancher/rke2/config.yaml
tls-san:
- "k8s.company.com"
cluster-cidr: 10.42.0.0/16
service-cidr: 10.43.0.0/16
# 2. Systemd Override(覆盖默认 service 参数)
# /etc/systemd/system/rke2-server.service.d/override.conf
[Service]
Environment="RKE2_TOKEN=${TOKEN}"
Environment="NODE_LABEL=node-role=true,datacenter=bj"
# 3. CLI 参数(最高优先级,覆盖 config.yaml)
# systemctl start rke2-server --token=TOKEN --cluster-cidr=10.42.0.0/16
```
> [!tip] 自定义 CNI 插件
> RKE2 默认使用 Canal(Calico + Flannel 组合)。如果需要更换为 Cilium,只需在 `config.yaml` 中添加:
> ```yaml
> cni: cilium
> ```
> 重新拉起 Server 即可自动替换网络方案——这是 RKE2 比原生 K8s 更省心的地方:**网络方案可以配置驱动,而不是代码依赖**。
### RKE2 集群架构
```mermaid
graph TB
subgraph "Master Node(控制平面)"
RKE2[RKE2 Server<br/>rke2 server --token=TOKEN]
APIServer[Kube-apiserver]
ControllerMgr[Kube-controller-manager]
Scheduler[Kube-scheduler]
ETCD[(etcd DB)]
CCM[Cloud Controller Manager]
APIServer --> ControllerMgr
APIServer --> Scheduler
APIServer <--> ETCD
RKE2 --> APIServer
RKE2 --> ControllerMgr
RKE2 --> Scheduler
RKE2 --> ETCD
RKE2 --> CCM
end
subgraph "Worker Node(工作节点)"
RKE2W[RKE2 Agent<br/>rke2 agent --server=https://MASTER_IP:6443 --token=TOKEN]
Kubelet1[Kubelet]
KubeProxy1[Kube-proxy]
Containerd1[containerd]
RKE2W --> Kubelet1
RKE2W --> KubeProxy1
RKE2W --> Containerd1
end
Dev[kubectl / Wayne API] --> APIServer
APIServer -. kubeconfig .-> RKE2W
Kubelet1 -. etcd sync .-> ETCD
```
关键流程说明:
1. **RKE2 Server 启动后**会自动拉取对应 K8s 版本的组件包(kube-apiserver、controller-manager 等),解压到 `/var/lib/rancher/rke2/`,通过 systemd 管理运行
2. **Agent 节点**只需传入 `--token`(静态认证令牌)和 `--server` URL,即可自动完成 TLS 证书握手并加入集群
3. **默认使用 containerd** 作为容器运行时——无需单独安装 Docker,这也是 RKE2 比 k3s 更"正规"的原因之一
### Pod 生命周期与调度流程
Pod 从创建到运行的完整链路:
```mermaid
sequenceDiagram
participant user as 开发者 (kubectl/Wayne)
participant apiserver as kube-apiserver
participant scheduler as kube-scheduler
participant node as Node Kubelet
participant containerd as containerd
user->>apiserver: POST /api/v1/namespaces/default/pods
apiserver->>apiserver: 验证 RBAC + 持久化到 etcd
Note over apiserver: Pod 状态 = Pending
apiserver-->>user: 201 Created
scheduler->>apiserver: LIST pods (unassigned)
scheduler->>scheduler: 预选 (Fit) → 优先级排序 (Score)
scheduler->>apiserver: PATCH pod -> set nodeName
Note over apiserver: Pod 状态仍为 Pending(等待 Kubelet 响应)
apiserver-->>node: WATCH pod assigned to this node
node->>containerd: Pull image (if not cached)
node->>containerd: Create container
node->>node: Set readinessProbe
Note over node: Pod 状态 = Running
node->>apiserver: UPDATE pod status
```
> **启发问题**:为什么调度器选中节点后,Pod 不会立即变成 Running,而还需要 Kubelet 配合?——因为调度器只负责"选地址",真正的镜像拉取、容器创建、网络挂载都由 Kubelet 在本机完成。这体现了**控制面与数据面的职责分离**。
### 典型部署流程
```bash
# 1. 编写 Deployment YAML
kubectl apply -f deployment.yaml
# 2. 编写 Service YAML
kubectl apply -f service.yaml
# 3. 查看状态
kubectl get pods -n <namespace>
kubectl describe pod <pod-name> -n <namespace>
# 4. 滚动更新(改镜像版本)
kubectl set image deployment/<name> app=<new-image> -n <namespace>
kubectl rollout status deployment/<name> -n <namespace>
```
**Deployment YAML 骨架:**
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-service
namespace: default
spec:
replicas: 3 # 期望副本数
strategy:
type: RollingUpdate # 滚动更新策略
rollingUpdate:
maxSurge: 1 # 更新时最多比期望多 1 个 Pod
maxUnavailable: 0 # 更新时不允许不可用
selector:
matchLabels:
app: my-service
template: # Pod 模板
metadata:
labels:
app: my-service
spec:
containers:
- name: app
image: registry/my-service:v1.2.0
ports:
- containerPort: 8080
```
### Service 类型详解
Service 是 Pod 的"稳定门面",不同 type 决定了流量如何到达 Pod:
| type | 工作原理 | 适用场景 |
|------|---------|---------|
| **ClusterIP**(默认) | K8s 内部虚拟 IP,仅集群内可访问 | 服务间调用,不对外暴露 |
| **NodePort** | 在每个节点上开放一个端口,外部可通过 `<NodeIP>:<port>` 访问 | 快速测试、开发环境验证 |
| **LoadBalancer** | 对接云厂商 LB 自动创建公网 IP | 云上生产环境 |
| **Ingress** | 七层路由,基于域名/路径分发到不同 Service | 多服务共享入口,HTTPS 终结 |
> [!warning] NodePort vs Ingress
> NodePort 只做到四层 TCP/UDP 负载均衡——无法按域名路由。如果你的应用需要 `api.example.com` → Service-A、`web.example.com` → Service-B 这种能力,必须用 **Ingress**。xinfra 中通常配合 Nginx Ingress Controller 使用。
**Ingress 示例:**
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-service-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true" # 强制 HTTPS
spec:
rules:
- host: api.myapp.company.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service # 对应 ClusterIP Service
port:
number: 80
```
---
### kubectl 速查手册
日常运维最高频的命令组合:
```bash
# ── 查看 ──
kubectl get pods -A # 所有 namespace 的 Pod
kubectl get svc,ingress -n prod # 查看 Service + Ingress
kubectl top pod -n staging --containers # 容器级 CPU/Memory 用量
# ── 调试 ──
kubectl exec -it <pod> -- /bin/sh # 进入容器 Shell
kubectl logs -f <pod> -c <sidecar-name> -n ns # 查看指定 Sidecar 日志
kubectl describe pod <pod> -n ns # 查看详细事件(调度失败原因等)
# ── 资源操作 ──
kubectl rollout undo deployment/<name> -n ns # 回滚到上一版本
kubectl scale deployment/<name> --replicas=5 -n ns # 手动扩缩容
kubectl patch deployment/<name> -p '{"spec":{"template":{"spec":{"containers":[{"name":"app","image":"new:v2"}]}}}}' -n ns # 热更新镜像
```
> [!tip] `-A` 标志 = `--all-namespaces`,当你不确定资源在哪个 namespace 时非常有用。但正式脚本中不建议滥用,容易造成误操作。
详见 [[k8s-rke2-fundamentals/core-objects-quiz]] — 覆盖 Pod 生命周期、Service 类型、探针机制等核心考点的 10 道选择题自测。
---
## 常见陷阱与最佳实践
### 1. Resource Limit 缺失
不设置 `resources.limits` 的 Pod 在节点资源紧张时会抢占其他 Pod 资源,导致雪崩:
```yaml
# 推荐:每个容器都要设请求值和上限
containers:
- name: app
resources:
requests: { cpu: "100m", memory: "128Mi" } # 调度依据
limits: { cpu: "500m", memory: "512Mi" } # 硬上限
```
### 2. Liveness vs Readiness 混淆
- **livenessProbe**:判断容器是否"活着",失败则重启 Pod
- **readinessProbe**:判断容器能否接收流量,失败则从 Service 后端摘除
```yaml
# 正确姿势:两个都设,readiness 更激进(快判快入),liveness 更保守
readinessProbe:
httpGet: { path: /healthz, port: 8080 }
initialDelaySeconds: 3
periodSeconds: 5
livenessProbe:
httpGet: { path: /healthz, port: 8080 }
initialDelaySeconds: 15
periodSeconds: 10
```
### 3. 频繁查看日志的习惯
```bash
# 实时跟踪单个 Pod 日志
kubectl logs -f <pod-name> -n <namespace>
# 如果是重启过的 Pod,看上一次容器的日志
kubectl logs -f <pod-name> -n <namespace> --previous
```
### 4. CrashLoopBackOff — 最常见的 Pod 异常状态
Pod 反复崩溃重启时,排查顺序如下:
```mermaid
flowchart TD
S[CrashLoopBackOff] --> C1{kubectl describe pod<br/>看 REASON}
C1 -->|ImagePullBackOff| I1[检查镜像仓库可达性 + ImagePullSecrets]
C1 -->|OOMKilled| M1[调大 memory limit / 排查内存泄漏]
C1 -->|CreateContainerConfigError| CM1[检查 ConfigMap/Secret 是否存在]
C1 -->|ErrImagePull| E1[镜像 tag 错误或 registry 认证失败]
C1 -->|Initialized/Ready/-/Running| LOGS[kubectl logs -p]
LOGS --> APP[应用自身启动失败?]
APP -->|Yes| FIX[检查启动参数 + 依赖服务连接性]
APP -->|No| DEEP[深入 containerd/containerd-shim 日志]
```
### 5. Ingress 404 或无法访问
开发环境常见"Service 存在但外部打不通"的问题,排查清单:
1. **Nginx Ingress Controller Pod 是否 Running** → `kubectl get pods -n ingress-nginx`
2. **Ingress 资源是否有 `loadBalancer.ip`** → `kubectl get ingress -n <ns>`(若无 IP,可能云厂商 LB 未就绪)
3. **Endpoint 是否有地址** → `kubectl get endpoints <service-name> -n <ns>`(为空说明没有 Ready 的 Pod 匹配)
4. **DNS 解析是否正确** → 内网环境可能需要手动配置 hosts 或使用 CoreDNS
## 关联笔记
以下文档构成了 xinfra 容器化部署的知识链:
- [[technical/xinfra-preview/wayne-overview]] — Wayne 上层的 YAML 模板就是基于这些 K8s 对象
- [[technical/xinfra-preview/cachecloud-overview]] — CacheCloud 管理 Redis,其宿主机未来可能迁移至 K8s 部署
- [[technical/xinfra-preview/cloud-dm-overview]] — K8s 内的 MySQL 实例由 CloudDM 做 SQL 审核
- [[technical/xinfra-preview/ansible-playbook-basics]] — Ansible Playbook 负责 RKE2 集群的底层裸机/VMAutomation