# 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 可用;域名托管 Cloudflare;Zero 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 账号 --- ## 云端创建 Tunnel(Zero 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 内置 Traefik(HelmChart)安装时创建,资源名一般为 **`traefik`**,命名空间 **`kube-system`**。若你改过 chart 或 Service 名,以下 FQDN 与端口要以 **实际 `kubectl get svc` 输出** 为准。 **与 `kubectl get svc traefik -o yaml` 里哪些字段对应** 集群 DNS 完整名规则:`..svc.cluster.local:`。 | 你填的片段 | 对应 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 不是 80,Tunnel 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 选 HTTP,URL 只填 `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 describe ...`、`kubectl -n logs ... --tail=200`。