cloudflare/mesh ↗ Docker 镜像为 Linux 容器打包了 Cloudflare Mesh 节点。它在基于 Wolfi ↗ 的精简运行时中,以无界面模式运行 Cloudflare One Client 的 warp-svc 守护进程。
使用容器镜像,将 Mesh 节点添加到 Docker Compose 应用栈、Kubernetes 集群和 CI/CD 流水线,无需在主机上安装软件包。
latest 标签是多平台清单。Docker 会自动选择适合主机架构的镜像。
| 架构 | 标签 |
|---|---|
| 多架构 | latest |
| x86-64 | latest-amd64 |
| ARM64 | latest-arm64 |
启动容器前,创建 Mesh 节点并获取其 token。
-
在 Cloudflare 仪表板中,前往 Networking(网络) > Mesh。
前往 Mesh ↗ -
选择 Add participant(添加参与者) > Add node(添加节点)。
-
输入节点名称(例如
k8s-gateway或docker-agent)。 -
选择 Create node(创建节点)。
-
选择 Kubernetes、Docker Compose 或 Docker CLI。
-
按照显示的说明操作。仪表板会隐藏节点 token,但复制的命令需要 token 时会将其包含在内。Docker Compose 配置和 Kubernetes 清单会引用秘密,而不会直接包含 token。
使用具有 Cloudflare One Connectors Write 或 Cloudflare One Connector: WARP Write 权限的 API token,创建节点并获取其连接器 token:
set -euo pipefail
NODE_RESPONSE=$(
curl --fail-with-body --silent --show-error \
"https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/warp_connector" \
--request POST \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{"name":"k8s-gateway"}'
)
jq -e '.success == true and (.result.id | type == "string")' \
<<< "$NODE_RESPONSE" > /dev/null
NODE_ID=$(jq -r '.result.id' <<< "$NODE_RESPONSE")
TOKEN_RESPONSE=$(
curl --fail-with-body --silent --show-error \
"https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/warp_connector/$NODE_ID/token" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
)
MESH_NODE_TOKEN=$(jq -er \
'select(.success == true) | .result | select(type == "string" and length > 0)' \
<<< "$TOKEN_RESPONSE")安装 jq,然后在运行命令前设置 ACCOUNT_ID 和 CLOUDFLARE_API_TOKEN。命令会在发生 HTTP 或 API 错误时停止。请在命令返回零且设置 NODE_ID 和 MESH_NODE_TOKEN 后再继续。如果创建节点后 token 获取失败,仅使用现有 NODE_ID 重试 token 请求。不要重新执行节点创建请求。将 MESH_NODE_TOKEN 传入容器。
如果这是首个 Mesh 节点,请配置必需的账户设置。你可以使用仪表板向导、API 或 Terraform。
Docker Compose 是将 Mesh 节点与应用服务一起运行的推荐方式。在 compose.yaml 中添加 cloudflare-mesh 服务:
services:
cloudflare-mesh:
image: cloudflare/mesh:latest
container_name: cloudflare-mesh
cap_add:
- NET_ADMIN
- NET_RAW
devices:
- /dev/net/tun:/dev/net/tun
environment:
MESH_NODE_TOKEN: ${MESH_NODE_TOKEN}
SRCNAT_ENABLED: "true"
sysctls:
net.ipv4.ip_forward: "1"
net.ipv6.conf.all.forwarding: "1"
net.ipv6.conf.default.forwarding: "1"
volumes:
- mesh_data:/var/lib/cloudflare-warp
restart: unless-stopped
volumes:
mesh_data:启动应用栈:
MESH_NODE_TOKEN="<YOUR-TOKEN>" docker compose up -d确认节点已连接:
docker exec cloudflare-mesh warp-cli status对于不使用 Compose 的独立容器:
docker run -d \
--name cloudflare-mesh \
--cap-add NET_ADMIN \
--cap-add NET_RAW \
--device /dev/net/tun \
--sysctl net.ipv4.ip_forward=1 \
--sysctl net.ipv6.conf.all.forwarding=1 \
--sysctl net.ipv6.conf.default.forwarding=1 \
-e MESH_NODE_TOKEN="$MESH_NODE_TOKEN" \
-e SRCNAT_ENABLED=true \
-v mesh_data:/var/lib/cloudflare-warp \
--restart unless-stopped \
cloudflare/mesh:latest此示例创建具有持久化注册状态的单副本 StatefulSet。它需要允许 NET_ADMIN、NET_RAW 和 /dev/net/tun 主机访问的 Kubernetes 集群(例如 GKE Standard)。
kubectl create secret generic cloudflare-mesh \
--from-literal=MESH_NODE_TOKEN="$MESH_NODE_TOKEN"将以下内容保存为 cloudflare-mesh.yaml:
apiVersion: v1
kind: Service
metadata:
name: cloudflare-mesh
spec:
clusterIP: None
selector:
app: cloudflare-mesh
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: cloudflare-mesh
spec:
serviceName: cloudflare-mesh
replicas: 1
selector:
matchLabels:
app: cloudflare-mesh
template:
metadata:
labels:
app: cloudflare-mesh
spec:
containers:
- name: mesh
image: cloudflare/mesh:latest
env:
- name: MESH_NODE_TOKEN
valueFrom:
secretKeyRef:
name: cloudflare-mesh
key: MESH_NODE_TOKEN
- name: SRCNAT_ENABLED
value: "true"
securityContext:
capabilities:
add:
- NET_ADMIN
- NET_RAW
volumeMounts:
- name: warp-data
mountPath: /var/lib/cloudflare-warp
- name: dev-net-tun
mountPath: /dev/net/tun
volumes:
- name: dev-net-tun
hostPath:
path: /dev/net/tun
type: CharDevice
volumeClaimTemplates:
- metadata:
name: warp-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gikubectl apply -f cloudflare-mesh.yaml
kubectl rollout status statefulset/cloudflare-mesh
kubectl exec cloudflare-mesh-0 -- warp-cli statusPersistentVolumeClaim 会在 Pod 重启后保留 Mesh 注册。
要将应用容器连接到 Mesh,请将 Mesh 镜像作为 sidecar 添加到同一 Pod 中。Pod 中的容器共享网络命名空间,因此 Mesh sidecar 可以将应用连接到 Cloudflare,无需更改应用。
为 sidecar 创建单独的 Mesh 节点和 Kubernetes Secret:
kubectl create secret generic cloudflare-mesh-sidecar \
--from-literal=MESH_NODE_TOKEN="$MESH_NODE_TOKEN"将以下内容保存为 cloudflare-mesh-sidecar.yaml:
apiVersion: v1
kind: Service
metadata:
name: cloudflare-mesh-sidecar-headless
spec:
clusterIP: None
selector:
app: cloudflare-mesh-sidecar
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: cloudflare-mesh-sidecar
spec:
serviceName: cloudflare-mesh-sidecar-headless
replicas: 1
selector:
matchLabels:
app: cloudflare-mesh-sidecar
template:
metadata:
labels:
app: cloudflare-mesh-sidecar
spec:
containers:
- name: application
image: busybox:1.37.0
command:
- sh
- -c
- |
echo "Hello from the Kubernetes sidecar example" > /tmp/index.html
httpd -f -p 8080 -h /tmp
ports:
- name: http
containerPort: 8080
- name: mesh
image: cloudflare/mesh:latest
env:
- name: MESH_NODE_TOKEN
valueFrom:
secretKeyRef:
name: cloudflare-mesh-sidecar
key: MESH_NODE_TOKEN
- name: SRCNAT_ENABLED
value: "true"
securityContext:
capabilities:
add:
- NET_ADMIN
- NET_RAW
volumeMounts:
- name: warp-data
mountPath: /var/lib/cloudflare-warp
- name: dev-net-tun
mountPath: /dev/net/tun
volumes:
- name: dev-net-tun
hostPath:
path: /dev/net/tun
type: CharDevice
volumeClaimTemplates:
- metadata:
name: warp-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
name: cloudflare-mesh-sidecar
spec:
selector:
app: cloudflare-mesh-sidecar
ports:
- name: http
port: 8080
targetPort: httpkubectl apply -f cloudflare-mesh-sidecar.yaml
kubectl rollout status statefulset/cloudflare-mesh-sidecar
kubectl exec cloudflare-mesh-sidecar-0 -c mesh -- warp-cli status| 参数 | 描述 |
|---|---|
MESH_NODE_TOKEN |
首次注册时必填。在 Cloudflare 仪表板 ↗中的 Networking(网络) > Mesh 下,或通过 API 创建 token。 |
SRCNAT_ENABLED |
控制源 NAT。默认值为 true。接受 true、false、1 或 0。 |
/var/lib/cloudflare-warp |
存储注册状态。使用卷持久化此路径,以便在重新创建容器后保持稳定的 Mesh 身份。 |
所需能力和设备
| 能力 / 设备 | 需要的原因 |
|---|---|
NET_ADMIN |
创建并配置隧道接口、路由和 nftables 规则。 |
NET_RAW |
启用 ICMP 等原始套接字操作。Docker 通常默认授予此能力,但此处仍显式声明。 |
/dev/net/tun |
创建 WARP TUN 接口。 |
| IP 转发 sysctl | 节点为路由子网转发流量时需要。 |
源 NAT(地址伪装)默认开启(SRCNAT_ENABLED=true)。Mesh 节点接收来自 Cloudflare 边缘的流量并将其转发到本地网络上的目标时,会将源 IP 从 Mesh CGNAT 地址(100.96.x.x)转换为节点自身的本地接口 IP。这确保返回流量正确路由,无需在 VPC 或本地网络中配置静态路由。
仅在连接的网络已具有通往 Mesh IP 范围(100.96.0.0/12)的返回路由时,才设置 SRCNAT_ENABLED=false。返回流量路由的更多详情,请参阅路由。
对于带有 CIDR 路由的高可用性部署:
- 在多个副本中使用同一个 Mesh 节点 token。
- 为每个 Pod 分配自己的
PersistentVolumeClaim。
Cloudflare 以主备模式运行副本。如果活动副本离线,流量会自动故障转移到备用副本。单个副本不提供冗余。
容器支持主机名路由。要通过主机名路由解析 Kubernetes Service,请确保主机名匹配集群实际的 DNS 后缀。默认后缀为 cluster.local,产生类似 service.namespace.svc.cluster.local 的 Service 名称。
在每个站点部署单独的 Mesh 节点容器,为每个节点身份使用单独的节点 token。每个节点应将其本地可访问的子网通告为 CIDR 路由。配置各站点的路由器或工作负载,将发往远程子网的流量通过本地 Mesh 节点发送。
当 SRCNAT_ENABLED=true 时,目标会看到 Mesh 节点的本地地址。禁用源 NAT 时,连接的网络需要通过其 Mesh 节点返回的路由。
确认 MESH_NODE_TOKEN 中设置了正确的 Mesh 节点 token。持久卷中现有的注册状态优先;只有明确希望丢弃该注册并创建新身份时,才移除该卷。
检查 token、设备配置文件、Gateway 代理、拆分隧道配置、出站防火墙连接和容器日志:
docker logs cloudflare-mesh确认主机名路由匹配集群实际的 DNS 后缀。通常默认后缀为 cluster.local,产生类似 service.namespace.svc.cluster.local 的 Service 名称。
首先检查源 NAT 和返回路由。确认 SRCNAT_ENABLED 设置为 true,或网络具有通往 Mesh IP 范围的返回路由。
docker exec -it cloudflare-mesh warp-cli statuskubectl exec cloudflare-mesh-0 -- warp-cli status- 添加路由 — 使 Mesh 中的任何设备都能访问容器化节点后方的子网。
- 启用高可用性 — 运行多个副本,提升生产环境的韧性。
- 从 Workers 连接 — 使用 VPC Network 绑定,从 Cloudflare Workers 访问私有服务。
- 技巧与最佳实践 — 云 VPC 配置、MTU 调整以及与 Cloudflare Tunnel 并行运行。