DCP:Snowflake 通往本地私有数据的桥接实践
DataHot 速览
Snowflake 的数据连接代理 DCP(Data Connectivity Proxy)可让 Snowflake 访问位于企业防火墙之后的数据库,无需开放入站端口、VPN 或公网 IP。文章以自建环境为例完整走通链路:在家用网络中搭建 Linux 主机、运行 Docker 版 Postgres、在同一主机部署 DCP agent,并在 Snowflake 侧配置 DCP 对象、网络规则与外部访问集成,最后验证隧道并用 Openflow connector 指向该私有库。文中提示 DCP 目前仅支持配合 Snowflake Openflow 使用,而非虚拟仓库发出的任意 SQL。
为什么值得关注:对需要把本地或私有云数据库接入 Snowflake 的数据团队来说,这是一份端到端可复现的连接方案,清楚说明了无需入站端口、VPN 的实现路径及当前功能边界。
本文目录 14 节
译文
AI 逐段翻译
Snowflake 的 数据连接代理 (DCP)允许 Snowflake 访问位于你防火墙后面的数据库。无需入站端口、无需 VPN、无需公网 IP。Snowflake 在摘要中描述得很好,但真正理解它的最简单方式是自己搭建整条路径:在你的家庭网络中放一台真实的 Linux 机器,在上面运行一个真实的 Postgres 数据库,并让一个真实的 DCP 代理将流量回传到 Snowflake。
这就是本文从头到尾要讲的内容:
- 在你的家庭网络中搭建一台 Linux 主机
- 安装 Docker 并在其上运行一个示例 Postgres 数据库
- 在同一台主机上部署 DCP 代理
- 配置 Snowflake 侧(DCP 对象、网络规则、外部访问集成)
- 验证隧道,并将 Openflow 连接器指向该私有数据库
关于范围的提醒: DCP 目前文档中说明可用于 Snowflake Openflow,而非来自虚拟仓库的任意 SQL。

架构一览
代理只会发起 出站连接:一个连接到 Snowflake 的控制平面,每个活动数据连接再各一个连接到 Postgres。你的路由器永远不需要端口转发。
第 1 部分:搭建 Linux 主机
以下任意一种都可以,因为 DCP 的要求并不高(Linux 内核 5.x+、Docker、1 vCPU、512 MB RAM、出站 443):
- 一台闲置的 Raspberry Pi 4/5
- 一台旧的迷你 PC / NUC
- 在你已有机器上用 VirtualBox、UTM 或 Proxmox 运行的虚拟机
本文假设使用 Ubuntu Server 22.04 LTS,因为无论你选择上述哪一种,从这里开始的设置都是相同的。
选项 A:Raspberry Pi
- 使用 Raspberry Pi Imager 将 Ubuntu Server 22.04 LTS(64 位)刷写到 SD 卡,并在烧录器的高级选项中设置主机名、SSH 密钥和 Wi-Fi/静态 IP。
- 启动 Pi,然后 ssh <user>@<pi-ip>。

选项 B:虚拟机(VirtualBox 示例)
- 下载 Ubuntu Server 22.04 LTS ISO。
- 创建虚拟机:2 vCPU、2 GB RAM、20 GB 磁盘、桥接适配器 网络(这样它会在你的家庭局域网上获得自己的 IP,像物理机一样可访问)。
- 安装 Ubuntu Server,并在设置期间启用 OpenSSH。
无论哪种方式,确认你已经登录:
lsb_release -a
uname -r # confirm kernel 5.x or later第 2 部分:安装 Docker
sudo apt update && sudo apt upgrade -y
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker
docker run hello-world # sanity check第 3 部分:运行一个示例 Postgres 数据库
在容器中启动 Postgres,并用一张很小的 orders 表填充它,这样之后 Snowflake 就有有意义的数据可以拉取。
mkdir -p ~/dcp-demo/pgdata
docker run -d \
--name demo-postgres \
--restart unless-stopped \
-e POSTGRES_USER=demo_user \
-e POSTGRES_PASSWORD=change_me \
-e POSTGRES_DB=demo_db \
-p 5432:5432 \
-v ~/dcp-demo/pgdata:/var/lib/postgresql/data \
postgres:16用示例数据填充它:
docker exec -it demo-postgres psql -U demo_user -d demo_db -c "
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer TEXT NOT NULL,
item TEXT NOT NULL,
amount NUMERIC(10,2) NOT NULL,
ordered_at TIMESTAMP DEFAULT now()
);
INSERT INTO orders (customer, item, amount) VALUES
('Ada Lovelace', 'Mechanical Keyboard', 129.99),
('Grace Hopper', 'USB Debugger', 45.50),
('Alan Turing', 'Enigma Replica', 899.00);
"我们将在第 8 部分接线的 Openflow Postgres 连接器通过逻辑复制而不是普通轮询来复制,所以这个容器需要的不只是一张用于初始化的表。官方镜像中 wal_level 默认为 replica,而逻辑复制需要将其设置为 logical:
docker exec -it demo-postgres psql -U demo_user -d demo_db -c "ALTER SYSTEM SET wal_level = logical;"
docker restart demo-postgres然后创建一个具备复制能力的用户,以及一个覆盖你想复制表的 publication。orders 已经有主键(order_id),这是逻辑复制所需要的:
docker exec -it demo-postgres psql -U demo_user -d demo_db -c "
CREATE ROLE dcp_replicator WITH LOGIN PASSWORD 'change_me_too' REPLICATION;
GRANT SELECT ON orders TO dcp_replicator;
CREATE PUBLICATION dcp_demo_pub FOR TABLE orders;
"记下主机的局域网 IP。你将把它(或你为其分配的主机名)用作 DCP 网络规则中的目标:
hostname -I | awk '{print $1}'为了获得稳定的引用,在同一台机器的 /etc/hosts 中添加一行,将你在 Snowflake 中要使用的 FQDN 映射到该局域网 IP,而不是 127.0.0.1。DCP 代理在第 5 部分中运行在自己的 Docker 容器里,拥有自己隔离的网络命名空间,因此该容器内的 127.0.0.1 只指向它自己,而不是运行 Postgres 的主机。
echo "<host-lan-ip> home-postgres.internal" | sudo tee -a /etc/hostsPostgres 已经通过 -p 5432:5432 发布,因此可以在该局域网 IP 上访问。记住这个 IP;你将在第 5 部分再次传入它,以便代理容器自行解析相同的主机名。
在继续之前设置一个 DHCP 保留。 上面的 /etc/hosts 条目和第 5 部分中的 --add-host 标志都硬编码了这个局域网 IP。如果你的路由器在下一次租约续期或重启时分配了一个新 IP——在 Wi-Fi 上比以太网更可能——这两个映射都会悄悄失效,隧道或复制就会停止工作,而且没有明显错误。在路由器的管理界面中,找到 Pi 的 MAC 地址(在 Pi 上执行 ip link show),并将其当前 IP 保留给该 MAC。这样做一次之后,你就不需要再改动这两个配置文件了。
第 4 部分:在 Snowflake 中创建 DCP 对象
如果这是账户中的第一个 DCP 对象,请签发 DCP 用于安全代理通信的按账户证书。这只需每个账户运行一次,而不是每个 DCP 对象运行一次;请注意,你应该等待至少 30 分钟再启动代理。
SELECT SYSTEM$ISSUE_PER_ACCOUNT_CERTIFICATES();在 Snowflake 工作表中,使用 ACCOUNTADMIN(或被授予 CREATE DATA CONNECTIVITY PROXY 的角色):
CREATE DATA CONNECTIVITY PROXY home_lab_dcp;可选地,通过网络策略限制哪些 IP 可以与 DCP 控制平面通信:
CREATE DATA CONNECTIVITY PROXY home_lab_dcp
NETWORK_POLICY = my_network_policy;生成一个一次性引导令牌(第二个参数是有效期天数;对于演示请保持较短):
SELECT SYSTEM$GENERATE_DATA_CONNECTIVITY_PROXY_BOOTSTRAP_TOKEN('home_lab_dcp', 7);复制生成的 JWT。接下来你将把它粘贴到 Linux 主机上的文件中。它必须是 Snowflake Access JWT;以 PAT_ 为前缀的令牌在这里不起作用。
第 5 部分:在你的家庭 Linux 机器上部署 DCP 代理
回到 Ubuntu 主机上:
sudo mkdir -p /etc/dcp-agent/secrets
echo '<paste the bootstrap token from Part 4>' | sudo tee /etc/dcp-agent/secrets/dcp-bootstrap-token
sudo chmod 600 /etc/dcp-agent/secrets/dcp-bootstrap-token对于真实部署,你会从密钥管理器拉取它,而不是直接粘贴它。即使在家庭实验室的文章中也值得提一下,因为这种模式(将它写入同一文件路径,然后正常启动或轮换)是完全相同的。
启动代理容器。在第 3 部分中编辑主机的 /etc/hosts 只对直接在主机上解析名称的进程有帮助。代理运行在自己的容器中,拥有自己的网络命名空间,因此需要通过 --add-host 将相同的 home-postgres.internal 映射直接添加到其容器中:
docker run -d \
--name dcp-agent \
--restart unless-stopped \
--add-host home-postgres.internal:<host-lan-ip> \
-v /etc/dcp-agent/secrets:/etc/dcp-agent/secrets:ro \
snowflakedb/dcp-client:latest \
--sf-bootstrap-credentials /etc/dcp-agent/secrets/dcp-bootstrap-token使用你在第 3 部分中记下的相同局域网 IP。跳过这个标志,代理将完全无法解析该主机名;或者更糟,如果你在那里默认使用 127.0.0.1,它会将其解析为自身。
代理读取 JWT,向 DCP 控制平面进行身份验证,接收其 mTLS 证书对,并打开到中继的隧道。观察它启动:
docker logs -f dcp-agent第 6 部分:从 Snowflake 验证隧道
回到 Snowflake 中:
DESCRIBE DATA CONNECTIVITY PROXY home_lab_dcp;查找 AGENT_HEALTH = HEALTHY 和 AGENT_STATUS = DCP_AGENT_LIFECYCLE_CONNECTED。如果它卡在待处理状态,Linux 主机上的 docker logs dcp-agent 是第一个要检查的地方。不过大多数情况下,代理卡住根本不是 Docker 问题。而是主机与 Snowflake 之间某处的出站 443 被阻止了。
这会让人们困惑,因为家庭网络在设计上就假定出站流量总是正常的,而通常也确实如此。但仍有一些因素会挡住去路:
- ISP 层面的过滤。 一些住宅 ISP,尤其是在商务精简版或共享 IP 套餐上,会限制非标准模式的出站流量。443 端口上的普通 HTTPS 几乎总能通过,但如果其他原因都解释不了停滞,这一点值得排除。
- 路由器或防火墙设备规则。 如果你运行的是 pfSense、OPNsense 或 UniFi 网关之类的东西,并且用的是出站规则,而不是通常默认的“允许所有出站”,那就再检查一下 443 是否被限定为比 DCP 所需更小的目的地集合。
- 从主机本身进行 DNS 解析,而不只是从网络。 代理需要先解析 Snowflake 的端点,然后才能尝试连接。从 Ubuntu 主机上快速执行 curl -v https://<your-account>.snowflakecomputing.com,就能一次性告诉你这是 DNS 失败、连接超时,还是 TLS 握手更靠后的某个环节出了问题。
- 主机上习惯性运行的企业 VPN 客户端。 如果这台机器也安装了工作 VPN 客户端,并且它决定接管默认路由,出站流量就可能被路由到 DCP 从未打算到达的地方。
这一切都不需要特殊防火墙规则来修复。它需要的是大多数家用路由器出厂时就自带的默认“允许所有出站”姿态,并且保持不变。一旦从主机上 curl 能干净地到达 Snowflake,代理连上就只是时间问题。
第二条独立的出站路径:遥测。 除了上面的控制平面连接之外,Snowflake 还会在引导时向代理提供一个遥测主机名,形式如下:
<org>-<account>.telemetry.<locator>.snowflakecomputing.com这需要自己独立的 443 端口出站 TLS,与控制平面端点不同。能连上你的 Postgres 数据库并不能确认这条路径——那是目的地侧,不是遥测侧——而且在代理能够到达它之前,Snowflake 中的连接历史和一些路由检查阶段会一直为空。主机和容器都要检查,因为代理运行在它自己的网络命名空间中:
export TELEMETRY_HOST="<org>-<account>.telemetry.<locator>.snowflakecomputing.com"# from the Pi's host network
getent hosts $TELEMETRY_HOST
curl -v --max-time 10 https://$TELEMETRY_HOST
# from inside the agent's own container/network namespace
docker run --rm --network container:dcp-agent curlimages/curl -v --max-time 10 https://$TELEMETRY_HOST如果主机层面的检查成功,而容器层面的检查不成功,那就是与第 3 部分中 home-postgres.internal 解析问题同一类的容器出站问题:容器并没有自动继承主机的网络视图。
第 7 部分:将 DCP 指向 Postgres 容器
创建一个网络规则,指定 Postgres 主机和端口。使用你在第 3 部分中写入 /etc/hosts 的 DNS 风格主机名,而不是原始 IP。注意特殊的 MODE,它正是让这条规则通过 DCP 路由、而不是走普通 Snowflake 出站的原因:
CREATE NETWORK RULE home_postgres_rule
MODE = DATA_CONNECTIVITY_PROXY_EGRESS
TYPE = HOST_PORT
VALUE_LIST = ('home-postgres.internal:5432');把它包进一个外部访问集成中:
CREATE EXTERNAL ACCESS INTEGRATION home_postgres_eai
ALLOWED_NETWORK_RULES = (home_postgres_rule)
ENABLED = TRUE;创建 EAI 本身并不会把它链接到 DCP 对象——必须告诉 DCP 它被允许为哪些 EAI 承载流量:
ALTER DATA CONNECTIVITY PROXY home_lab_dcp SET
EXTERNAL_ACCESS_INTEGRATIONS = (home_postgres_eai);跳过这一步,EAI 在第 8 部分中照样能顺利附加到你的 Openflow 运行时,但流量实际上永远不会通过 home_lab_dcp 路由——它会失败,就好像网络规则不存在一样。
第 8 部分:配置 Openflow PostgreSQL 连接器
先把 home_postgres_eai 附加到你的 Openflow 运行时。这是一个常见的遗漏。

在接触连接器之前,先创建目标数据库和一个用于落地的仓库,并把二者都授予你的 Openflow 运行时执行所用的角色——运行时无法写入它未被授予权限的对象:
CREATE DATABASE IF NOT EXISTS dcp_demo_db;
CREATE WAREHOUSE IF NOT EXISTS dcp_demo_wh WITH WAREHOUSE_SIZE = 'XSMALL' AUTO_SUSPEND = 60;
GRANT USAGE ON DATABASE dcp_demo_db TO ROLE <your_openflow_runtime_execute_as_role>;
GRANT CREATE SCHEMA ON DATABASE dcp_demo_db TO ROLE <your_openflow_runtime_execute_as_role>;
GRANT USAGE ON WAREHOUSE dcp_demo_wh TO ROLE <your_openflow_runtime_execute_as_role>;
GRANT OPERATE ON WAREHOUSE dcp_demo_wh TO ROLE <your_openflow_runtime_execute_as_role>;从 Openflow 连接器目录中,添加 PostgreSQL 连接器,而不是手工构建控制器服务。它会要求:

该连接器还需要上传 PostgreSQL JDBC 驱动 .jar,因为 Openflow 默认不捆绑它。目的地与本文其他地方一样,都是 home-postgres.internal:5432。Snowflake 的中继会通过 EAI 自动将其映射到 home_lab_dcp,所以仍然不需要手工配置任何手动隧道引用。

连接器启动后,它会读取支撑 dcp_demo_pub 的复制槽,三条预置的 orders 行会落入你的 Snowflake 表中,它们经过了这样的路径: Postgres → DCP 代理(仅出站)→ Snowflake 中继 → Openflow 连接器,而且你的家用路由器上没有开放任何一个入站端口。
关于配置 PostgreSQL 连接器的详细指南可在此处获取 这里。
清理
当你实验完成后:
docker stop dcp-agent demo-postgres
docker rm dcp-agent demo-postgres以及在 Snowflake 中:
DROP EXTERNAL ACCESS INTEGRATION home_postgres_eai;
DROP NETWORK RULE home_postgres_rule;
DROP DATA CONNECTIVITY PROXY home_lab_dcp;有几件事值得向所有跟着操作的人特别说明
- 代理只是一根笨管道。 它在 TCP 层面运作,永远看不到凭据或查询内容。这些内容留在 Snowflake 和 Openflow 连接器内部。
- 高可用就是更多代理。 对于真实部署(不是家庭实验室),至少要在不同的故障域中运行两个代理实例;如果其中一个掉线,控制平面会自动重新路由。
- 无需停机即可轮换令牌。 代理会在每个证书轮换周期重新读取凭据文件,因此替换令牌只需一次原子文件替换。无需重启。
这就是完整闭环:一个从未接受过来自互联网的入站连接的数据库,却能以某种方式从 Snowflake 访问到。
下一步
如果你觉得这有用,请在 LinkedIn 上关注我,获取更多数据工程和 Snowflake AI Data Cloud 用例。
DCP:Snowflake 通往本地数据的桥梁最初发布于 Snowflake Builders Blog: Data Engineers, App Developers, AI, & Data Science 在 Medium 上,人们正在那里通过高亮和回应这个故事来继续对话。
这篇内容对你有用吗?
反馈只用于改善内容筛选,不等同于收藏