惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Security Affairs
S
Schneier on Security
N
News | PayPal Newsroom
T
Threatpost
Cloudbric
Cloudbric
H
Heimdal Security Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Google Online Security Blog
Google Online Security Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Spread Privacy
Spread Privacy
V
Vulnerabilities – Threatpost
The Last Watchdog
The Last Watchdog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
L
LINUX DO - 最新话题
P
Proofpoint News Feed
C
CXSECURITY Database RSS Feed - CXSecurity.com
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Apple Machine Learning Research
Apple Machine Learning Research
NISL@THU
NISL@THU
Application and Cybersecurity Blog
Application and Cybersecurity Blog
The Hacker News
The Hacker News
O
OpenAI News
人人都是产品经理
人人都是产品经理
C
Cyber Attacks, Cyber Crime and Cyber Security
C
Check Point Blog
C
Cisco Blogs
GbyAI
GbyAI
J
Java Code Geeks
L
LangChain Blog
I
Intezer
T
Tailwind CSS Blog
有赞技术团队
有赞技术团队
MyScale Blog
MyScale Blog
美团技术团队
The Register - Security
The Register - Security
Help Net Security
Help Net Security
WordPress大学
WordPress大学
Y
Y Combinator Blog
T
Tor Project blog
M
MIT News - Artificial intelligence
爱范儿
爱范儿
TaoSecurity Blog
TaoSecurity Blog
V
Visual Studio Blog
T
Threat Research - Cisco Blogs
P
Palo Alto Networks Blog
月光博客
月光博客
T
Tenable Blog
S
Securelist
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
DataBreaches.Net

陈少文的网站

巨变与机遇的未来十年 Kubernetes 平台管理软件压力测试方案 使用镜像部署 Hexo 静态页面 终于等到你 - GitHub 镜像仓库服务(ghcr.io) 一起来学 Go --(6)Interface 一起来学 Go --(5)Goroutine 和 Channel 什么是函数式编程 如何在 Kubernetes 集群集成 Kata 柯里化与偏函数 使用 PyGithub 自动创建 Label 软件产品是团队能力的输出 Helm 2 、Helm 3 比较 IoT 变现 Kubernetes 中的 DNS 服务 国内的 Helm 镜像源 Harbor 使用自签证书支持 Https 访问 DevOps 工具链之 Prow 如何使用 kfctl 安装 Kubeflow VS Code 无法下载 Go 插件的工具包 工程师更应具有服务精神 你不知道的 Docker 使用技巧 使用 Docker 运行 Tensorflow 论中国 什么是左移 如何清空 Git 仓库全部历史记录 一禅小和尚 有风吹过厨房 时间的玫瑰 如何在 CentOS 安装 GPU 驱动 开发 Tips(19) 使用 Velero 备份 Kubernetes 集群 Kubernetes Cheat Sheet 开发 Tips(18) 如何构建一个 Java 工程 开发 Tips(17) KubeSpray 安装 Kubernetes 报错 ip in ansible_all_ipv4_addresses 基于 Kubernetes 和 Jenkins 搭建自动化测试系统 在 Kubernetes 上动态创建 Jenkins Slave 使用 Jenkins 进行服务拨测 开发 Tips(16) Kubernetes 签发 Ingress 证书及日常故障运维 Kubernetes 中 Deployment 的基本操作 Kubernetes 中的证书 如何使用 KubeBuilder 开发一个 Operator Kubernetes 1.6.0 安装问题汇总 镜像管理工具 -- Harbor 开发 Tips(15) Docker 如何拉取镜像 开发 Tips(14) 使用 Helm 安装 harbor 开发 Tips(13) 使用 S2I 构建云原生应用 在 Kubernetes 中使用 emptyDir、hostPath、localVolume 开发 Tips(12) 开发 Tips(11) 代码质量分析工具 SonarQube 使用 Kubeadm 安装 Kubernetes 集群 一起来学 Go --(4)常用函数 Kubernetes 中的 Ceph Kubernetes 之 Volumes Kubernetes 之 Labels、Selectors 开发 Tips(10) 开源正在重构商业模式 Kubernetes 之网络 Kubernetes 之 API 使用 Helm 和 Operator 快速部署 Prometheus Kubernetes 复杂有状态应用管理框架 -- Operator Kubernetes 的包管理器 -- Helm 一起来学 Go --(3)Go Modules 如何一步一步地优化博客方案 kubectl 实用指南 Kubernetes 中的基本概念 搭建远程 Kubernetes 开发环境 大公司和小公司的 ToB 思路 开发 Tips(9) Go 入门指南 一起来学 Go --(2)数据与逻辑结构 如何预防 Web 富文本中的 XSS 攻击 django-xss-cleaner 云工作时代 一起来学 Go --(1)背景与特点 SaaS 开发团队的不同阶段 你不知道的 Git 使用技巧 输出既服务 微服务设计 继续奔跑 开发 Tips(8) 从账户安全到二次验证 Django 性能之数据库查询优化 Django 性能之分库分表 敏捷开发之研发流程 打造一致性的团队 开发 Tips(7) Pytest 进阶学习之 Mock PaaS 部署之 buildpack Go 开发配置 领域输出才是 PaaS 的核心竞争力 Pytest 入门学习 开发 Tips(6) 如何使用 Jenkins、Docker、GitLab 搭建 Django 自动化部署流程
容器化部署 DeepSeek 3FS 存储系统
微信公众号 · 2025-03-15 · via 陈少文的网站

1. 部署方案

在开始容器化部署之前,先提几点要求:

  • 为了简化交付,只需要一个镜像
  • 为了可靠性,尽可能多副本部署
  • 通过不同的参数启动不同的服务
  • 通过环境变量注入配置,渲染到配置文件中

下面是 DeepSeek 3FS 的部署方案:

需要部署:

  • 一个 Monitor 用来收集监控数据,数据存储在 ClickHouse 中
  • 一个 Admin CLI 提供集群管理的终端,每个组件都需要通过 Admin CLI 提交配置
  • 三个 MgmtD 用来管理集群,将组件、心跳等信息存储在 FoundationDB 中
  • 三个 Meta 用来提供 Chain 和文件信息,数据存储在 FoundationDB 中
  • 尽可能多的 Storage 用来存储数据, 文件以 Chunk 的形式存储在磁盘目录中
  • 若干 Fuse 用来提供 POSIX 文件系统接口给应用层,如果能基于 USRBIO 用户态的接口调用存储服务,相较于 Fuse 会有 3-5 倍的性能提升。

这些服务部署的主机全都需要 RDMA 网络(不仅是支持 RDMA 的网卡,还要交换机支持 RDMA)连接起来。消费 3FS 存储服务的应用程序也需要运行在支持 RDMA 的主机上。

默认情况下 MgmtD 和 Storage 服务有端口冲突,需要部署在不同的主机上。可能大部分团队并没有规划专用的 RDMA 存储集群,使用 3FS 时要和训练、推理任务部署在一起,以充分利用资源。本着尽可能节约资源的原则,将 Monitor 与 Mgmd 部署在一起;为了方便管理集群部署的 Admin CLI 也与 Mgmd 部署在一起。

2. 编译 3FS 镜像

  • 启动编译容器
1
nerdctl run -it --rm -v $(pwd):/app shaowenchen/demo:3fsbuilder bash

基础镜像使用的是 Ubuntu 22.04,我也制作了基于 Ubuntu 20.04 的镜像 shaowenchen/demo:3fsbuilder-2004

  • 创建 Dockerfile
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
ARG BASE_IMAGE=shaowenchen/demo:3fsbuilder
FROM ${BASE_IMAGE} as builder
RUN git clone https://github.com/deepseek-ai/3FS && \
    cd 3FS && \
    git submodule update --init --recursive && \
    ./patches/apply.sh && \
    cmake -S . -B build -DCMAKE_CXX_COMPILER=clang++-14 -DCMAKE_C_COMPILER=clang-14 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DSHUFFLE_METHOD=g++11 && \
    cmake --build build -j 100
FROM ${BASE_IMAGE}
COPY --from=builder /app/3FS/build/bin /opt/3fs/bin
COPY --from=builder /app/3FS/configs /opt/3fs/etc
COPY --from=builder /app/3FS/deploy /opt/3fs/deploy
COPY --from=builder /app/3FS/build/third_party/jemalloc/lib/libjemalloc.so.2 /lib/x86_64-linux-gnu/
RUN mkdir -p /var/log/3fs/ && \
    wget -O /opt/3fs/bin/3fs-entrypoint.sh https://raw.githubusercontent.com/shaowenchen/demo/master/3fs-deploy/3fs-entrypoint.sh && \
    chmod +x /opt/3fs/bin/3fs-entrypoint.sh
WORKDIR /opt/3fs/bin
  • 编译镜像

默认是 Ubuntu 22.04

1
nerdctl build --build-arg BASE_IMAGE=shaowenchen/demo:3fsbuilder -t shaowenchen/demo:3fs .

也可以使用 Ubuntu 20.04

1
nerdctl build --build-arg BASE_IMAGE=shaowenchen/demo:3fsbuilder-2004 -t shaowenchen/demo:3fs-2004 .

不方便编译的同学,可以直接去镜像中拿二进制和依赖的动态库文件,如果访问 Dockerhub 网络不通,可以使用 shaowenchen/demo:3fs 镜像地址。

3. 部署依赖的中间件

3.1 ClickHouse

  • 部署 Operator
1
kubectl apply -f https://raw.githubusercontent.com/shaowenchen/ops-hub/refs/heads/master/storage/clickhouse-operator-0.24.5/crd.install-bundle.yaml
1
kubectl apply -f https://raw.githubusercontent.com/shaowenchen/ops-hub/refs/heads/master/storage/clickhouse-operator-0.24.5/deploy.install-bundle.yaml
  • 创建 ClickHouse 集群
1
kubectl apply -f https://raw.githubusercontent.com/shaowenchen/ops-hub/refs/heads/master/storage/clickhouse-operator-0.24.5/cluster.yaml

在 yaml 中设置有 clickhouse 的访问凭证信息。

  • 等待就绪
1
2
3
4
5
6
7
kubectl get pod -l clickhouse.altinity.com/cluster=test-cluster

NAME                                  READY   STATUS    RESTARTS   AGE
chi-test-cluster-test-cluster-0-0-0   1/1     Running   0          7m53s
chi-test-cluster-test-cluster-0-1-0   1/1     Running   0          3m9s
chi-test-cluster-test-cluster-1-0-0   1/1     Running   0          7m55s
chi-test-cluster-test-cluster-1-1-0   1/1     Running   0          3m14s
  • 暴露服务
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: chi-test-cluster-exposed
  namespace: default
  labels:
    clickhouse.altinity.com/cluster: test-cluster
spec:
  type: NodePort
  selector:
    clickhouse.altinity.com/cluster: test-cluster
  ports:
    - name: tcp
      port: 9000
      protocol: TCP
      targetPort: 9000
      nodePort: 30000
    - name: http
      port: 8123
      protocol: TCP
      targetPort: 8123
    - name: interserver
      port: 9009
      protocol: TCP
      targetPort: 9009
EOF

这样在 http://node-ip:30000 就可以访问 ClickHouse 了。

  • 创建数据库

进入任意一个 ClickHouse 的 Pod

1
kubectl exec -it chi-test-cluster-test-cluster-0-0-0 bash

下载数据库脚本

1
wget https://raw.githubusercontent.com/deepseek-ai/3FS/refs/heads/main/deploy/sql/3fs-monitor.sql

创建数据库

1
clickhouse-client -n < 3fs-monitor.sql

3.2 FoundationDB

  • 部署 Operator
1
kubectl apply -f https://raw.githubusercontent.com/shaowenchen/ops-hub/refs/heads/master/storage/fdb-kubernetes-operator-2.0.0/apps.foundationdb.org_foundationdbbackups.yaml
1
kubectl apply -f https://raw.githubusercontent.com/shaowenchen/ops-hub/refs/heads/master/storage/fdb-kubernetes-operator-2.0.0/apps.foundationdb.org_foundationdbclusters.yaml
1
kubectl apply -f https://raw.githubusercontent.com/shaowenchen/ops-hub/refs/heads/master/storage/fdb-kubernetes-operator-2.0.0/apps.foundationdb.org_foundationdbrestores.yaml
1
kubectl apply -f https://raw.githubusercontent.com/shaowenchen/ops-hub/refs/heads/master/storage/fdb-kubernetes-operator-2.0.0/deployment.yaml
  • 创建 FoundationDB 集群
1
kubectl apply -f https://raw.githubusercontent.com/shaowenchen/ops-hub/refs/heads/master/storage/fdb-kubernetes-operator-2.0.0/cluster.yaml

这里的实例名字已经改为 fdb-a800。

  • 等待就绪
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
kubectl get pod -l foundationdb.org/fdb-cluster-name=fdb-a800

NAME                                READY   STATUS    RESTARTS   AGE
fdb-a800-cluster-controller-49248   2/2     Running   0          4h21m
fdb-a800-log-19564                  2/2     Running   0          4h21m
fdb-a800-log-30929                  2/2     Running   0          4h21m
fdb-a800-log-98017                  2/2     Running   0          4h21m
fdb-a800-log-9876                   2/2     Running   0          4h21m
fdb-a800-storage-16687              2/2     Running   0          4h21m
fdb-a800-storage-31181              2/2     Running   0          4h21m
fdb-a800-storage-90277              2/2     Running   0          4h21m
  • 获取 FDB 集群的连接信息
1
2
3
kubectl exec -it fdb-a800-cluster-controller-49248  cat /var/dynamic-conf/fdb.cluster

fdb_a800:xxx@fdb-a800-log-19564.fdb-a800.default.svc.cluster.local:4501,fdb-a800-log-30929.fdb-a800.default.svc.cluster.local:4501,fdb-a800-log-98017.fdb-a800.default.svc.cluster.local:4501

但在 Containerd 启动的容器中无法解析上面的域名,需要使用 Service 的 IP。

  • 暴露服务
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: fdb-a800-exposed
spec:
  ports:
  - port: 4501
    protocol: TCP
    targetPort: 4501
  selector:
    foundationdb.org/fdb-cluster-name: fdb-a800
    foundationdb.org/fdb-process-class: log
  type: ClusterIP
EOF

下面才是容器需要的连接信息:

1
fdb_a800:xxx@x.x.x.x:4501

这里有一个细节是,在 K8s 集群的节点上使用 nerdctl 启动的容器可以访问集群的 Service\Pod IP。

没有使用 NodePort 的原因在于,会出现下面的错误,端口不一致断言。

1
Assertion pkt.canonicalRemotePort == peerAddress.port failed @ /home/foundationdb_ci/src/oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOo/foundationdb/fdbrpc/FlowTransport.actor.cpp 1545:

在集群外访问 FoundationDB,建议使用二进制部署 FoundationDB,使用物理机 IP 作为连接地址。

1
2
3
4
wget https://github.com/apple/foundationdb/releases/download/7.1.26/foundationdb-clients_7.1.26-1_amd64.deb
wget https://github.com/apple/foundationdb/releases/download/7.1.26/foundationdb-server_7.1.26-1_amd64.deb
dpkg -i foundationdb-clients_7.1.26-1_amd64.deb
dpkg -i foundationdb-server_7.1.26-1_amd64.deb

/etc/foundationdb/fdb.cluster 中的 IP 替换为主机的 IP,重启之后,即可从任意连通的来源访问 FoundationDB 服务。

1
systemctl restart foundationdb.service

4. 容器启动 3FS 服务

4.1 公共环境变量

这里主要设置一些公共的变量,每次启动服务之前都需要设置。

1
2
3
export CLUSTER_ID=stage
export FDB_CLUSTER="xxx:xxx@x.x.x.x:4500"
export IMAGE=shaowenchen/demo:3fs

4.2 一个 Monitor

  • 设置环境变量

CLUSTER_ID 是 3FS 的集群 ID,需要保持集群组件中一致。

在设置 DEVICE_FILTER 之前可以先运行 ibdev2netdev 查看有哪些可用的设备,这里需要注意不要将 IB 和 RoCE 设备混用。

1
2
3
4
5
6
export CLICKHOUSE_DB=3fs
export CLICKHOUSE_HOST=x.x.x.x
export CLICKHOUSE_PASSWD=xxxx
export CLICKHOUSE_PORT=30000
export CLICKHOUSE_USER=app
export DEVICE_FILTER="mlx5_0,mlx5_1,mlx5_4,mlx5_5"
  • 启动 Monitor

Monitor 会占用端口 10000

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
nerdctl run --name 3fs_monitor \
               --privileged \
               --network host \
               -d --restart always \
               --env CLUSTER_ID=${CLUSTER_ID} \
               --env CLICKHOUSE_DB=${CLICKHOUSE_DB} \
               --env CLICKHOUSE_HOST=${CLICKHOUSE_HOST} \
               --env CLICKHOUSE_PASSWD=${CLICKHOUSE_PASSWD} \
               --env CLICKHOUSE_PORT=${CLICKHOUSE_PORT} \
               --env CLICKHOUSE_USER=${CLICKHOUSE_USER} \
               --env FDB_CLUSTER=${FDB_CLUSTER} \
               --env DEVICE_FILTER=${DEVICE_FILTER} \
               ${IMAGE} \
               ./3fs-entrypoint.sh monitor

4.3 一个 Admin CLI

Admin CLI 是一个管理终端,不占用端口,可以和其他服务公用一个节点部署。

  • 设置环境变量

这里的 MGMTD_SERVER_ADDRESSES 需要提前规划出来,不用先启动服务,可以直接设置;REMOTE_IP 是 Monitor 服务暴露的地址。

1
2
export MGMTD_SERVER_ADDRESSES="RDMA://x.x.x.x:8000,RDMA://x.x.x.x:8000,RDMA://x.x.x.x:8000"
export REMOTE_IP=x.x.x.x:10000
  • 启动 Admin CLI
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
nerdctl run --name 3fs_admin_cli \
            --privileged \
            --network host \
            -d --restart always \
            --env FDB_CLUSTER=${FDB_CLUSTER} \
            --env MGMTD_SERVER_ADDRESSES=${MGMTD_SERVER_ADDRESSES} \
            --env DEVICE_FILTER=${DEVICE_FILTER} \
            --env CLUSTER_ID=${CLUSTER_ID} \
            --env REMOTE_IP=${REMOTE_IP} \
            ${IMAGE} \
            ./3fs-entrypoint.sh admin_cli
  • 进入 Admin CLI 容器
1
nerdctl exec -it 3fs_admin_cli bash
  • 初始化集群
1
/opt/3fs/bin/admin_cli -cfg /opt/3fs/etc/admin_cli.toml "init-cluster --mgmtd /opt/3fs/etc/mgmtd_main.toml 1 1048576 6"

1 表示链表 ID;1048576 表示块大小(以字节为单位),也就是 1M;6 表示文件条带化大小。

4.4 多个 MgmtD

  • 设置环境变量

每个 Mgmtd 设置的 MGMTD_NODE_ID 应该不一样。

1
2
3
4
export MGMTD_NODE_ID="1"
export DEVICE_FILTER="mlx5_0,mlx5_1,mlx5_4,mlx5_5"
export REMOTE_IP=x.x.x.x:10000
export MGMTD_SERVER_ADDRESSES="RDMA://x.x.x.x:8000,RDMA://x.x.x.x:8000,RDMA://x.x.x.x:8000"

如果是 IPoIB,需要在启动时添加 --cfg /opt/3fs/etc/mgmtd_main.toml 参数,并将其中默认的 RDMA 改为 IPoIB

  • 启动 Mgmtd

Mgmtd 服务监听端口 8000、9000

Mgmtd 端口的可能会修改不成功,最好保持不变,不要尝试修改 9000 端口。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
nerdctl run --name 3fs_mgmtd \
            --privileged \
            --network host \
            -d --restart always \
            --env CLUSTER_ID=${CLUSTER_ID} \
            --env FDB_CLUSTER=${FDB_CLUSTER} \
            --env MGMTD_NODE_ID=${MGMTD_NODE_ID} \
            --env DEVICE_FILTER=${DEVICE_FILTER} \
            --env REMOTE_IP=${REMOTE_IP} \
            --env MGMTD_SERVER_ADDRESSES=${MGMTD_SERVER_ADDRESSES} \
            ${IMAGE} \
            ./3fs-entrypoint.sh mgmtd

4.5 多个 Meta

Meta 和 Mgmtd 监听的端口不一样,可以部署在同一台机器上。

  • 设置环境变量

每个 Meta 设置的 META_NODE_ID 应该不一样。

1
2
3
4
export MGMTD_SERVER_ADDRESSES="RDMA://x.x.x.x:8000,RDMA://x.x.x.x:8000,RDMA://x.x.x.x:8000"
export META_NODE_ID="101"
export DEVICE_FILTER="mlx5_0,mlx5_1,mlx5_4,mlx5_5"
export REMOTE_IP=x.x.x.x:10000
  • 启动 Meta

Meta 服务监听端口 8001、9001

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
nerdctl run  --name 3fs_meta \
               --privileged \
               -d --restart always \
               --network host \
               --env CLUSTER_ID=${CLUSTER_ID} \
               --env FDB_CLUSTER=${FDB_CLUSTER} \
               --env MGMTD_SERVER_ADDRESSES=${MGMTD_SERVER_ADDRESSES} \
               --env META_NODE_ID=${META_NODE_ID} \
               --env DEVICE_FILTER=${DEVICE_FILTER} \
               --env REMOTE_IP=${REMOTE_IP} \
               ${IMAGE} \
               ./3fs-entrypoint.sh meta

4.6 多个 Storage

Storage 服务需要与 Mgmtd 服务分开,它们使用了相同的端口。如果采用混合部署,那么需要修改 Storage 服务的端口。

  • 设置环境变量

每个 Storage 设置的 STORAGE_NODE_ID 应该不一样;TARGET_PATHS 是存储的目录,由于这里是容器化部署可以保持固定值,通过修改挂载参数设置存储目录。

1
2
3
4
5
export MGMTD_SERVER_ADDRESSES="RDMA://x.x.x.x:8000,RDMA://x.x.x.x:8000,RDMA://x.x.x.x:8000"
export STORAGE_NODE_ID="1001"
export TARGET_PATHS="/3fs/data"
export DEVICE_FILTER="mlx5_0,mlx5_1,mlx5_4,mlx5_5"
export REMOTE_IP=x.x.x.x:10000
  • 修改系统参数
1
sysctl -w fs.aio-max-nr=67108864
  • 启动 Storage

Storage 服务监听端口 8000、9000

mkdir -p /data/3fs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
nerdctl run --name 3fs_storage \
            --privileged \
            -d --restart always \
            --network host \
            -v /data/3fs:/3fs/data \
            --env CLUSTER_ID=${CLUSTER_ID} \
            --env FDB_CLUSTER=${FDB_CLUSTER} \
            --env MGMTD_SERVER_ADDRESSES=${MGMTD_SERVER_ADDRESSES} \
            --env STORAGE_NODE_ID=${STORAGE_NODE_ID} \
            --env TARGET_PATHS=${TARGET_PATHS} \
            --env DEVICE_FILTER=${DEVICE_FILTER} \
            --env REMOTE_IP=${REMOTE_IP} \
            ${IMAGE} \
            ./3fs-entrypoint.sh storage

4.7 Admin Cli 查看节点

  • 进入 Admin CLI 容器
1
nerdctl exec -it 3fs_admin_cli bash
  • 查看节点
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/opt/3fs/bin/admin_cli  -cfg /opt/3fs/etc/admin_cli.toml list-nodes

Id    Type     Status               Hostname      Pid  Tags  LastHeartbeatTime    ConfigVersion  ReleaseVersion
1     MGMTD    PRIMARY_MGMTD        58ca52320c3b  16   []    N/A                  1(UPTODATE)    250228-dev-1-999999-8c9883c2
2     MGMTD    HEARTBEAT_CONNECTED  391173a5a978  16   []    2025-03-15 13:45:48  1(UPTODATE)    250228-dev-1-999999-8c9883c2
3     MGMTD    HEARTBEAT_CONNECTED  8c05a1fbdf97  16   []    2025-03-15 13:45:50  1(UPTODATE)    250228-dev-1-999999-8c9883c2
101   META     HEARTBEAT_CONNECTED  b3fea43900e6  202  []    2025-03-15 13:45:48  4(UPTODATE)    250228-dev-1-999999-8c9883c2
102   META     HEARTBEAT_CONNECTED  fcebcb6ca5c3  192  []    2025-03-15 13:45:52  4(UPTODATE)    250228-dev-1-999999-8c9883c2
103   META     HEARTBEAT_CONNECTED  fea159ba0bd6  194  []    2025-03-15 13:45:49  4(UPTODATE)    250228-dev-1-999999-8c9883c2
1001  STORAGE  HEARTBEAT_CONNECTED  919404e2891f  188  []    2025-03-15 13:45:51  7(UPTODATE)    250228-dev-1-999999-8c9883c2
1002  STORAGE  HEARTBEAT_CONNECTED  e19b012d073c  180  []    2025-03-15 13:45:51  7(UPTODATE)    250228-dev-1-999999-8c9883c2
1003  STORAGE  HEARTBEAT_CONNECTED  fd2423d5e695  173  []    2025-03-15 13:45:52  7(UPTODATE)    250228-dev-1-999999-8c9883c2
1004  STORAGE  HEARTBEAT_CONNECTED  e8b6fbb9a774  184  []    2025-03-15 13:45:52  7(UPTODATE)    250228-dev-1-999999-8c9883c2
1005  STORAGE  HEARTBEAT_CONNECTED  283ae64fb9c4  168  []    2025-03-15 13:45:53  7(UPTODATE)    250228-dev-1-999999-8c9883c2

5. 创建 3FS 存储实例

  • 进入 Admin CLI
1
nerdctl exec -it 3fs_admin_cli bash
  • 创建用户
1
/opt/3fs/bin/admin_cli -cfg /opt/3fs/etc/admin_cli.toml "user-add --root --admin 0 root"
1
2
3
4
5
6
7
Uid                0
Name               root
Token              AAA1+B5D8QCAeUKZ2wCBx/b5(Expired at N/A)
IsRootUser         true
IsAdmin            true
Gid                0
SupplementaryGids

这里的 AAA1+B5D8QCAeUKZ2wCBx/b5 就是 root 用户的 token。

  • 安装依赖
1
2
3
apt update
apt install -y python3-pip
pip install -r /opt/3fs/deploy/data_placement/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
  • 执行 data_placement 计算命令
1
2
python3 /opt/3fs/deploy/data_placement/src/model/data_placement.py \
   -ql -relax -type CR --num_nodes 5 --replication_factor 3 --min_targets_per_disk 6

这里的 replication_factor 不能大于 num_nodes,需要记录一下输出的结果,下一步会用到。

1
2025-03-15 16:21:45.429 | SUCCESS  | __main__:run:148 - saved solution to: output/DataPlacementModel-v_5-b_10-r_6-k_3-λ_2-lb_1-ub_1
  • 生成 chainTable

这里的 node_id_beginnode_id_end 需要和上面的 storage 节点的 STORAGE_NODE_ID 一致。

1
2
3
4
5
python3 /opt/3fs/deploy/data_placement/src/setup/gen_chain_table.py \
   --chain_table_type CR --node_id_begin 1001 --node_id_end 1005 \
   --num_disks_per_node 1 --num_targets_per_disk 6 \
   --target_id_prefix 1 --chain_id_prefix 9 \
   --incidence_matrix_path output/DataPlacementModel-v_5-b_10-r_6-k_3-λ_2-lb_1-ub_1/incidence_matrix.pickle
  • 创建 Storage Target
1
/opt/3fs/bin/admin_cli --cfg /opt/3fs/etc/admin_cli.toml --config.user_info.token $(<"/opt/3fs/etc/token.txt") < output/create_target_cmd.txt

这里会输出一个 target 在每个节点上的分布情况。

  • 上传 chainTable
1
/opt/3fs/bin/admin_cli --cfg /opt/3fs/etc/admin_cli.toml --config.user_info.token $(<"/opt/3fs/etc/token.txt") "upload-chains output/generated_chains.csv"
1
/opt/3fs/bin/admin_cli --cfg /opt/3fs/etc/admin_cli.toml --config.user_info.token $(<"/opt/3fs/etc/token.txt") "upload-chain-table --desc stage 1 output/generated_chain_table.csv"

6. Fuse 挂载

  • 设置环境变量

这里的 TOKEN 就是上面创建的 root 用户的 token。

1
2
3
4
5
6
7
export CLUSTER_ID=stage
export FDB_CLUSTER="x:x@x.x.x.x:4500"
export MGMTD_SERVER_ADDRESSES="RDMA://x.x.x.x:8000,RDMA://x.x.x.x:8000,RDMA://x.x.x.x:8000"
export REMOTE_IP=x.x.x.x:x
export DEVICE_FILTER="mlx5_0,mlx5_1,mlx5_4,mlx5_5"
export TOKEN=xxx
export IMAGE=shaowenchen/demo:3fs
  • 创建主机挂载点
  • 启动 Fuse
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
nerdctl run --name 3fs_fuse \
            --privileged \
            -d --restart always \
            --network host \
            --mount type=bind,source=/mnt/3fs,target=/mnt/3fs,bind-propagation=shared \
            --env CLUSTER_ID=${CLUSTER_ID} \
            --env FDB_CLUSTER=${FDB_CLUSTER} \
            --env MGMTD_SERVER_ADDRESSES=${MGMTD_SERVER_ADDRESSES} \
            --env REMOTE_IP=${REMOTE_IP} \
            --env DEVICE_FILTER=${DEVICE_FILTER} \
            --env TOKEN=${TOKEN} \
            ${IMAGE} \
            ./3fs-entrypoint.sh fuse

-v 只能将主机的目录挂载到容器中,而这里需要的是将容器中的目录挂载到主机上,所以使用 --mount 参数配合 bind-propagation=shared 进行挂载。

  • 在主机上查看挂载点
1
2
3
4
df -h /mnt/3fs/

Filesystem      Size  Used Avail Use% Mounted on
hf3fs.stage      57T  2.5T   55T   5% /mnt/3fs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
tree  /mnt/3fs

/mnt/3fs
└── 3fs-virt
    ├── get-conf
    │   ├── sys.io_job_deq_timeout -> '1ms'
    │   ├── sys.io_worker_coros.hi -> 8
    │   ├── sys.io_worker_coros.lo -> 8
    │   ├── sys.max_jobs_per_ioring -> 32
    │   ├── sys.periodic_sync.enable -> true
    │   ├── sys.periodic_sync.flush_write_buf -> true
    │   ├── sys.periodic_sync.interval -> '30s'
    │   ├── sys.storage.net_client.rdma_control.max_concurrent_transmission -> 64
    │   ├── usr.attr_timeout -> 30.0
    │   ├── usr.dryrun_bench_mode -> false
    │   ├── usr.enable_read_cache -> true
    │   ├── usr.entry_timeout -> 30.0
    │   ├── usr.flush_on_stat -> true
    │   ├── usr.negative_timeout -> 5.0
    │   ├── usr.readonly -> false
    │   ├── usr.symlink_timeout -> 5.0
    │   └── usr.sync_on_stat -> true
    ├── iovs
    │   ├── submit-ios -> /dev/shm/sem.hf3fs-submit-ios.c52d4079-b1c0-4684-9d08-6f2ee18d8a6e
    │   ├── submit-ios.ph -> /dev/shm/sem.hf3fs-submit-ios.dd3eac7c-3802-422d-93a7-efaf56c6e2af
    │   └── submit-ios.pl -> /dev/shm/sem.hf3fs-submit-ios.e8cf7b88-e65a-4cb5-a3ba-de71351bccee
    ├── rm-rf
    └── set-conf

5 directories, 20 files

7. 总结

本篇主要介绍了如何使用容器化部署 DeepSeek 3FS 存储系统,主要包括以下几个步骤:

  • 编译 3FS 镜像
  • 部署 ClickHouse 和 FoundationDB 中间件
  • 启动 Monitor、Admin CLI、MgmtD、Meta、Storage 服务

根据启动参数的不同,可以启动不同的服务,3fs-entrypoint.sh 脚本内容已经提交到 https://github.com/shaowenchen/demo/blob/master/3fs-deploy/ 以供参考。