Files
Deploy-Laboratory/docs/03-04-k3s-cloudflare-tunnel-配置接入.md
2026-03-29 09:08:01 +08:00

265 lines
13 KiB
Markdown
Raw Permalink 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.
# 03-04-k3s Cloudflare Tunnel 配置接入
> 本文覆盖 Tunnel 完整流程Zero Trust 云端创建、域名映射,以及将 `cloudflared` 安装到 K3s 并跑起 Pod使 **Traefik 通过 Tunnel 对外提供服务**。
>
> **状态:已验证**2026-03本仓库实验室 K3s 集群)。
## TL;DR
- **自动化验收**`./ansible/bin/verify.sh run 03-04`
- **关键前置**Traefik 可用;域名托管 CloudflareZero Trust 中 Tunnel 与 Public Hostname 已配置
- **自动化所需环境变量**(见 `ansible/env/.env.verify.example`**`CF_TUNNEL_TEST_URL`**HTTPS 完整探针 URL**或** **`CF_TUNNEL_TEST_HOST`**(仅主机名,脚本拼成 `https://HOST/`**`TUNNEL_TOKEN`** 与集群内 `kube-system/cloudflared-credentials` **二选一**(已有 Secret 可不设 env。二者探针变量皆缺时验收为 **gated**。可选 **`CF_TUNNEL_CURL_INSECURE=1`** 为探针 curl 加 `-k`(仅排障)。
- **成功判据**:达到本文「预期」且 playbook 断言通过rollout + HTTPS 探针)
- **排障**:见本文「排障」
---
## 访问链路(如何通过 Tunnel 访问 K3s 资源)
**整体流程**:公网域名 → Cloudflare Edge → Tunnel → `cloudflared` Pod → **Traefik** → 根据 Host/Path 路由到具体 Service如 Dashboard、GitLab、Homer 等)。
Traefik 是唯一入口。所有流量经 Tunnel 进入后,由 Traefik 的 IngressRoute/Ingress 按 `Host``Path` 分发到不同后端。**先保证 Traefik 内有对应路由**(如 Dashboard 的 IngressRoute再在 Zero Trust 中把域名指到 Traefik即可访问。
---
## 前置条件
- 控制节点已就绪:`01-01-k3s-控制节点含traefik.md`
- Traefik 已可用;若要通过 Tunnel 访问 Dashboard需先部署 `03-01-k3s-traefik-dashboard.md``03-03-k3s-traefik-dashboard-acme.md`
- 域名已托管在 Cloudflare且 Nameserver 已指向 Cloudflare
- 已创建 Cloudflare Zero Trust 账号
---
## 云端创建 TunnelZero Trust 操作说明)
### 1. 创建 Tunnel
1. 登录 [Cloudflare Zero Trust Dashboard](https://one.dash.cloudflare.com/)
2. 左侧导航:**Networks** → **Tunnels**(或 **Connectors****Cloudflare Tunnels**
3. 点击 **Create a tunnel**
4. 选择 **Cloudflared** 作为 Connector 类型
5. 输入 Tunnel 名称(如 `k3s-lab`),点击 **Save tunnel**
### 2. 复制 Tunnel Token
1. 在 Tunnel 创建成功后,会进入 **Install connector** 页面
2. 选择操作系统(如 Linux
3. 在安装命令中,找到形如 `cloudflared tunnel run --token <长串 Token>`**Token**
4. **复制整个 Token**(点击复制图标,或手动选中),妥善保存
5. 该 Token 将用于下方 K3s 中 `cloudflared` 部署
> 若已关闭页面:在 Tunnels 列表中点击该 Tunnel → **Configure** → **Install connector**,可重新查看/生成 Token。
### 3. 部署 cloudflared 到 K3s
[`ansible/files/03-04/cloudflared.yaml`](../ansible/files/03-04/cloudflared.yaml) **仅含 Deployment****Secret 必须单独创建**(避免 `kubectl apply` 覆盖 token。推荐与 [`ansible/playbooks/verify/03-04.yml`](../ansible/playbooks/verify/03-04.yml) 一致:先 `cloudflared-credentials`,再 apply Deployment。
1. 在集群中创建 Secret`YOUR_TOKEN` 换成 Zero Trust 里的 Tunnel token
```bash
kubectl -n kube-system create secret generic cloudflared-credentials \
--from-literal=TUNNEL_TOKEN='YOUR_TOKEN' \
--dry-run=client -o yaml | kubectl apply -f -
```
2. 应用 Deployment 并等待就绪(按实际 manifests 路径选择其一):
```bash
# 默认路径
kubectl apply -f /var/lib/rancher/k3s/server/manifests/cloudflared.yaml
kubectl -n kube-system rollout status deploy/cloudflared
```
```bash
# 自定义 data-dir如 /storage
kubectl apply -f /storage/server/manifests/cloudflared.yaml
kubectl -n kube-system rollout status deploy/cloudflared
```
3.**Deployment 清单** 放入上述 manifests 目录后K3s 重启时会自动加载Secret 仍需单独存在)。
建议要点:
- 使用官方 `cloudflared` 镜像
- Secret 不写死在明文 YAML
- `cloudflared` 放在 `kube-system` 或专用 namespace
- Tunnel 指向的 URL 在 Zero Trust 中配置为 Traefik Service无需在 `cloudflared.yaml` 内指定
#### 集群内 Traefik 地址Public Hostname 的 URL 填什么)
Tunnel 后端应指向 **集群内的 Traefik 入口**,常用写法:
| 写法 | 说明 |
|------|------|
| `traefik.kube-system.svc.cluster.local:80` | 见下「与哪份 YAML / 哪些字段对应」。**不要**手写 `http://`Zero Trust 里选 HTTP 后只填主机与端口。 |
| `192.168.2.61` | 节点 IP与 Traefik Service **EXTERNAL-IP** 之一、端口 **80** 等价。Public Hostname 的 URL **只填到 IP/主机**path 在浏览器访问时带上(见步骤 6。 |
**和仓库里哪份 YAML 的关系**
- 本仓库的 [`cloudflared.yaml`](../ansible/files/03-04/cloudflared.yaml) **只** 定义 `cloudflared` 的 Deployment**Secret `cloudflared-credentials` 单独创建****不包含** Traefik Service。Tunnel 在 Zero Trust 里指向的仍是 **集群内已存在的 Traefik Service**
- Traefik 的 **Service** 由 K3s 内置 TraefikHelmChart安装时创建资源名一般为 **`traefik`**,命名空间 **`kube-system`**。若你改过 chart 或 Service 名,以下 FQDN 与端口要以 **实际 `kubectl get svc` 输出** 为准。
**与 `kubectl get svc traefik -o yaml` 里哪些字段对应**
集群 DNS 完整名规则:`<metadata.name>.<metadata.namespace>.svc.cluster.local:<spec.ports 中 web 的 port>`
| 你填的片段 | 对应 YAML 路径(`kubectl -n kube-system get svc traefik -o yaml` |
|------------|-------------------------------------------------------------------|
| `traefik` | `metadata.name` |
| `kube-system` | `metadata.namespace` |
| `:80` | `spec.ports`**name 常为 `web`**`port: 80`HTTP 入口;若你环境 port 不是 80Tunnel URL 里端口改成一致) |
示例(节选,以你集群为准):
```yaml
metadata:
name: traefik # → FQDN 第一段
namespace: kube-system # → FQDN 第二段
spec:
ports:
- name: web
port: 80 # → Tunnel URL 里冒号后的端口
# ...
type: LoadBalancer # EXTERNAL-IP 为节点 IP 列表时,也可用 IP:80 代替集群 DNS
```
**怎么用 kubectl 查(建议逐条执行)**
```bash
# 1) 表格式:确认 NAME / PORT(S) / 集群 IP
kubectl -n kube-system get svc traefik -o wide
# 2) 打印集群 DNS 主机名(无端口)
kubectl -n kube-system get svc traefik -o jsonpath='{.metadata.name}.{.metadata.namespace}.svc.cluster.local'
echo
# 2b) 各端口名与端口(核对 HTTP 一般为 name=web、port=80
kubectl -n kube-system get svc traefik -o jsonpath='{range .spec.ports[*]}{.name}={.port}{"\n"}{end}'
# 3) 导出完整 YAML人工对照 metadata / spec.ports
kubectl -n kube-system get svc traefik -o yaml
```
HTTP 入口一般为 **name=`web` 的 `port: 80`**;若你环境端口名不是 `web`,以第 1、2、3 条里 **实际 `port` 数字** 为准Tunnel URL 中冒号后改为该数字。
`cloudflared` 与 Traefik 同集群时,**优先用** `traefik.kube-system.svc.cluster.local:80`,不依赖某台节点 IP 是否变更。
#### 临时验证(集群内 curl
官方 `cloudflared` 镜像多为 **distroless、无 `sh`**,不要用 `kubectl exec deploy/cloudflared -- sh`
**`kube-system`** 起临时 Pod 探测 Traefik与 Tunnel 后端同源):
```bash
# 根路径(常见 404无默认路由时正常
kubectl run curl-test --rm -n kube-system --restart=Never \
--image=curlimages/curl:latest -- \
curl -sS -o /dev/null -w "HTTP %{http_code}\n" \
http://traefik.kube-system.svc.cluster.local:80/
# Dashboard已按 03-01/03-03 部署时期望 200
kubectl run curl-test --rm -n kube-system --restart=Never \
--image=curlimages/curl:latest -- \
curl -sS -o /dev/null -w "HTTP %{http_code}\n" \
http://traefik.kube-system.svc.cluster.local:80/dashboard/
```
- **`/` → 404**:多数环境正常(未配置根路径路由)。
- **`/dashboard/` → 200**:说明集群 DNS 与 Traefik 可达Public Hostname 可填上述集群内地址。
### 4. 验证连接
```bash
kubectl -n kube-system get pods | grep cloudflared
kubectl -n kube-system logs deploy/cloudflared --tail=100
```
确认 Pod 为 `Running`,日志中可见 `tunnel connected`。**只有 Connector 已连接后**,才能进行下一步域名配置。
### 5. 配置域名映射Public Hostnames / Route tunnel
Zero Trust 向导顺序为:选择类型 → 命名 → **安装并运行 Connector****路由流量**。需等 Pod 跑起并显示已连接后,再配置 Public Hostnames。
1. 在 Tunnel 配置页,切换到 **Public Hostnames**(已发布应用程序)标签,点击 **Add a public hostname**
2. 配置如下:
| 字段 | 填写说明 |
|--------------------|------------------------------------------------------------------------|
| **Subdomain** | 子域名(如 `k3s``git``home`),或留空表示根域 |
| **Domain** | 下拉选择已托管在 Cloudflare 的域名(如 `jackadam.top` |
| **Path** | 留空表示全路径;或填正则如 `^/blog` 做路径匹配 |
| **Service type** | 选择 **HTTP**(集群内 Traefik 为 HTTP勿选 HTTPS |
| **URL** | 仅填 `traefik.kube-system.svc.cluster.local:80`**不要加 `http://`**(含义与核对见上文「集群内 Traefik 地址」) |
> **重要**URL 输入框会根据 Service type 自动加协议前缀。选 HTTP 时只需填 `traefik.kube-system.svc.cluster.local:80`;若手写 `http://` 会变成 `http://http://...`,导致「服务 URL 无效」。
3. 点击 **Save hostname**,按需重复添加其他子域,均指向同一内部地址。
**示例**Subdomain `k3s` + Domain `jackadam.top` → 公网 `k3s.jackadam.top` 访问 Traefik不同子域由 Traefik 的 IngressRoute 按 Host 分发。
### 6. 快速验证:以 Dashboard 为例
若已按 03-01 或 03-03 部署 Traefik Dashboard按上述步骤 5 添加一条 Public Hostname。**Traefik 无需修改**。
> **Public Hostname 的 URL 只能写到「主机 + 端口」**,不能写成 `192.168.2.61/dashboard` 这类带 path 的地址;控制台也不支持在 URL 里做 path patch/转发。路径由浏览器访问时带上,由 Traefik 按路由匹配。
**Dashboard 专用子域**(示例)
| 字段 | 填写值 |
|----------------|---------------------------------------------|
| Subdomain | `dashboard` |
| Domain | `jackadam.top`(或你的域名) |
| Path | 留空 |
| Service type | HTTP |
| URL | `traefik.kube-system.svc.cluster.local:80``192.168.2.61`(仅主机或集群 DNS**勿**在 URL 里写 `/dashboard` |
`192.168.2.61` 换成你的 Traefik 入口节点 IP`kubectl get svc traefik` 中 EXTERNAL-IP 之一一致即可)。
**访问时在域名后带上 path**:浏览器打开 **`https://dashboard.jackadam.top/dashboard/`**(路径 `/dashboard/` 由 Traefik 的 Dashboard IngressRoute 处理)。
---
**其他用法**:单域名 `k3s.jackadam.top`URL 同样只填到 `traefik...:80` 或节点 IP访问时带路径`https://k3s.jackadam.top/dashboard/`;或为每个应用单独配子域。
---
## 架构说明
- **流量路径**:公网 → Cloudflare Edge → Tunnel → `cloudflared` Pod → **Traefik Service** → 各 IngressRoute 后端
- **配置要点**Public Hostname 的 URL 为 `traefik.kube-system.svc.cluster.local:80`Service type 选 HTTP`cloudflared` 与 Traefik 同集群,可直接通过 Service 访问。
---
## 预期
- Pod 为 `Running`,日志中可见 `tunnel connected`
- 配置域名后,访问公网域名可到达 Traefik 路由
## 注意事项
- 没有 token/凭据:回到 Zero Trust 页面重新生成
- **顺序**:先跑起 Pod 并确认连接,再配置 Public Hostnames否则「路由流量」步骤无法生效
- URL 填写错误Service type 选 HTTPURL 只填 `traefik.kube-system.svc.cluster.local:80`,勿加 `http://`
## 失败排查
- 域名解析正常但访问超时:先看 Tunnel 状态与 `cloudflared` 日志
- 返回 `404`:通常是 Traefik 路由未命中
- 返回 `502`:优先排查后端链路(`06-01-k3s-networkpolicy-故障排查.md`
---
## 下一步
- 其他应用GitLab、Homer 等):在集群内创建 IngressRoute/Ingress 指定 Host 与后端,再在 Zero Trust 中添加对应子域的 Public Hostname 即可
- `05-03-k3s-安装gitlab-含runner.md`
- `05-01-k3s-部署homer首页面板.md`
## 排障
- **先看 playbook 输出**:失败时先定位是 deploy/wait/http_check 哪一步。
- **集群侧总览**`kubectl get nodes -o wide``kubectl -n kube-system get pods -o wide`
- **事件与日志**`kubectl -n <ns> describe ...``kubectl -n <ns> logs ... --tail=200`