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

376 lines
14 KiB
Markdown
Raw Normal View History

2026-07-04 20:23:48 +08:00
---
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 -sfL https://get.rke2.io | 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 时非常有用。但正式脚本中不建议滥用,容易造成误操作。
---
## 常见陷阱与最佳实践
### 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