返回
RSS Snowflake Engineering (Medium) AI 逐段翻译 发布 2026-09-18 22:01 收录于 09-19

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 节
  1. 架构一览
  2. 第 1 部分:搭建 Linux 主机
  3. 选项 A:Raspberry Pi
  4. 选项 B:虚拟机(VirtualBox 示例)
  5. 第 2 部分:安装 Docker
  6. 第 3 部分:运行一个示例 Postgres 数据库
  7. 第 4 部分:在 Snowflake 中创建 DCP 对象
  8. 第 5 部分:在你的家庭 Linux 机器上部署 DCP 代理
  9. 第 6 部分:从 Snowflake 验证隧道
  10. 第 7 部分:将 DCP 指向 Postgres 容器
  11. 第 8 部分:配置 Openflow PostgreSQL 连接器
  12. 清理
  13. 有几件事值得向所有跟着操作的人特别说明
  14. 下一步

译文

AI 逐段翻译

Snowflake 的 数据连接代理 (DCP)允许 Snowflake 访问位于你防火墙后面的数据库。无需入站端口、无需 VPN、无需公网 IP。Snowflake 在摘要中描述得很好,但真正理解它的最简单方式是自己搭建整条路径:在你的家庭网络中放一台真实的 Linux 机器,在上面运行一个真实的 Postgres 数据库,并让一个真实的 DCP 代理将流量回传到 Snowflake。

这就是本文从头到尾要讲的内容:

  1. 在你的家庭网络中搭建一台 Linux 主机
  2. 安装 Docker 并在其上运行一个示例 Postgres 数据库
  3. 在同一台主机上部署 DCP 代理
  4. 配置 Snowflake 侧(DCP 对象、网络规则、外部访问集成)
  5. 验证隧道,并将 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

  1. 使用 Raspberry Pi Imager 将 Ubuntu Server 22.04 LTS(64 位)刷写到 SD 卡,并在烧录器的高级选项中设置主机名、SSH 密钥和 Wi-Fi/静态 IP。
  2. 启动 Pi,然后 ssh <user>@<pi-ip>。

选项 B:虚拟机(VirtualBox 示例)

  1. 下载 Ubuntu Server 22.04 LTS ISO。
  2. 创建虚拟机:2 vCPU、2 GB RAM、20 GB 磁盘、桥接适配器 网络(这样它会在你的家庭局域网上获得自己的 IP,像物理机一样可访问)。
  3. 安装 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/hosts

Postgres 已经通过 -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 上,人们正在那里通过高亮和回应这个故事来继续对话。

这篇内容对你有用吗?

反馈只用于改善内容筛选,不等同于收藏

分享这条资讯
分享海报
保存图片
iOS 也可以长按图片保存