部署#

SecretFlow提供了仿真和生产两种部署模式,两者的对比如下表所示。建议您认真阅读说明,根据需求选择适合您的部署方式。

部署模式

场景

如何部署Ray

如何运行代码

代码差异

仿真模式

单机构内部局域网环境下的仿真实验。

由多个仿真节点组成的单个Ray集群。

选择任一节点执行一遍代码。

sf.init 中填写 parties 参数。

生产模式

正式生产环境,比如多个机构协作。

每个机构都需要独立部署各自的Ray集群。

每个机构都需要同时执行代码

sf.init 中填写 cluster_config 参数,剩余代码与仿真模式完全一样。

前置知识:关于Ray#

隐语使用Ray作为分布式计算调度框架。Ray集群由一个主节点和零或若干个从节点组成,更多Ray的知识可以访问Ray官网

基于Kuscia部署#

Kuscia是一款基于K3s的轻量级隐私计算任务编排框架,旨在屏蔽异构的基础设施和协议,提供统一的隐私计算底座。

使用Kuscia,你可以不需要关注SecretFlow组网的细节,直接通过kubectl命令或者API接口轻松地管理和执行SecretFlow作业。此外,Kuscia还支持通信安全和并发运行SecretFlow作业。

如果你需要在业务中使用SecretFlow,推荐基于Kuscia部署运行SecretFlow。详情请参考Kuscia的快速入门文档。

仿真模式#

SecretFlow被设计为可以快速仿真,既可以单机运行,也可以在多个节点上运行。

注意

基于单个ray集群的SecretFlow只适用于仿真。 生产环境建议阅读后面的生产相关章节。


单机仿真#

使用 secretflow.init 在单机模式下运行SecretFlow。该模式会直接启动只有一个节点的ray集群,并且当程序退出时会自动关闭。

>>> import secretflow as sf
>>> sf.init(parties=['alice', 'bob'], address='local')

集群模式仿真#

在隐语的集群仿真模式下,每个Ray节点模拟一个机构,具体做法是通过给每个Ray节点添加机构名称标记,从而保证机构的计算被调度到相应的Ray节点上。 整体通信网络如下。

仿真

下面的例子展示了如何在多个节点上部署一个包含alice和bob的集群。

启动主节点#

在第一台机器上启动带有 “alice” 标识的主节点。


注意

  1. 请使用真实的ip地址和端口。

  2. {"alice": 16} 意味着alice最多可以同时运行16个worker。 您可以按需要自行调整。


ray start --head --node-ip-address="ip" --port="port" --resources='{"alice": 16}' --include-dashboard=False --disable-usage-stats

当屏幕中输出 “Ray runtime started.” 意味着主节点成功启动。

现在我们的集群只有一个主节点,接下来我们启动更多节点。

启动其他节点#

在另一台机器上启动带有 “bob” 标志的节点。 这个节点将会连接主节点并加入集群。


注意

请将 ip:port 更换为主节点的 node-ip-addressport


ray start --address="ip:port" --resources='{"bob": 16}' --disable-usage-stats

当屏幕输出中出现 “Ray runtime started.” 意味着节点启动成功。现在由两个Ray节点构成的Ray集群已经搭建完毕。其中,头节点模拟机构alice,从节点模拟机构bob。

你也可以继续重复上述步骤以启动带有其他参与方标识的节点。

启动SecretFlow#

现在你可以启动SecretFlow。下面这段代码表示alice和bob分别执行了一个返回输入值的函数。


提示

  1. 请使用主节点的 node-ip-addressport 填充 sf.initaddress 参数。

  2. 如果你启动了更多的节点(比如carol、davy等),记得在parties=['alice', 'bob']参数中添加新的参与方名称。


>>> import secretflow as sf
# Replace with the `node-ip-address` and `port` of head node.
>>> sf.init(parties=['alice', 'bob'], address='ip:port')
>>> alice = sf.PYU('alice')
>>> bob = sf.PYU('bob')
>>> alice(lambda x : x)(2)
<secretflow.device.device.pyu.PYUObject object at 0x7fe932a1a640>
>>> bob(lambda x : x)(2)
<secretflow.device.device.pyu.PYUObject object at 0x7fe6fef03250>

(可选)如何关闭集群#

当你需要关闭集群时,请使用以下命令。请在所有机器上执行命令。

请注意在机器上的所有ray进程都会被停止,这意味着所有ray集群将会停止。

ray stop

(可选)如何在集群模式下启动SPU#

SPU 在不同节点上包含了多个工作进程。大部分SPU的代码由C++编写以确保性能。SPU基于Brpc,这意味着SPU拥有一个独立于Ray网络之外的服务网格。换言之,你必须单独处理SPU的端口。目前,我们正在尝试合并两者。

SPU的配置可以参考。


提示

  1. 请使用主节点的 node-ip-addressport 填充 sf.initaddress 参数。

  2. aliceaddress 请填写可以被bob访通的地址,并且选择一个 未被占用的端口 ,注意不要和Ray端口冲突。

  3. alicelisten_addr 可以和alice address里的端口一样。

  4. bobaddress 请填写可以被alice访通的地址,并且选择一个 未被占用的端口 ,注意不要和Ray端口冲突。

  5. boblisten_addr 可以和bob address里的端口一样。


import spu
import secretflow as sf

# Use ray head adress please.
sf.init(parties=['alice', 'bob'], address='Ray head node address')

cluster_def={
    'nodes': [
        {
            'party': 'alice',
            # Please choose an unused port.
            'address': 'ip:port of alice',
            'listen_addr': '0.0.0.0:port'
        },
        {
            'party': 'bob',
            # Please choose an unused port.
            'address': 'ip:port of bob',
            'listen_addr': '0.0.0.0:port'
        },
    ],
    'runtime_config': {
        'protocol': spu.spu_pb2.SEMI2K,
        'field': spu.spu_pb2.FM128,
        'sigmoid_mode': spu.spu_pb2.RuntimeConfig.SIGMOID_REAL,
    }
}

spu = sf.SPU(cluster_def=cluster_def)

详细的SPU配置可以参考 SPU config


注意

你可以看到在很多教程中使用 sf.utils.testing.cluster_def 建立SPU。请注意它只能在单机模式下使用,因为它使用了 127.0.0.1 作为默认ip。

>>> spu = sf.SPU(sf.utils.testing.cluster_def(['alice', 'bob', 'carol']))

使用docker部署仿真SecretFlow#

在此之前,您可能需要先了解docker网络的概念。Docker网络目前比较主流的两种网络是host网络bridge网络,您可以点击链接阅读官方文档了解更多。

隐语推荐使用host网络模式启动容器,下面会向您解释原因。

使用WSL(Windows Subsystem for Linux)部署仿真SecretFlow#

如果您在WSL2使用隐语的话,您可以在WSL2上使用不同的发行版部署隐语

下列步骤将演示如何在WSL上部署仿真模式的SecretFlow。

  1. 在您的WSL上安装不同的发行版

您应该安装至少两个不同版本的发行版,例如Ubuntu 20.04.6 LTS 和 Ubuntu 22.04.2 LTS 或者至少两个不同的发行版,例如Ubuntu 和 Debian,在这个例子里面alice位于Ubuntu 20.04.6 LTS 而且bob 位于 Ubuntu 22.04.2 LTS:

  • alice:

    (secretflow) alice@DESKTOP-SAOB7DQ:~$ lsb_release -a
    No LSB modules are available.
    Distributor ID: Ubuntu
    Description:    Ubuntu 20.04.6 LTS
    Release:        20.04
    Codename:       focal
    (secretflow) alice@DESKTOP-SAOB7DQ:~$
    
  • bob:

    (secretflow) bob@DESKTOP-SAOB7DQ:~$ lsb_release -a
    No LSB modules are available.
    Distributor ID: Ubuntu
    Description:    Ubuntu 22.04.2 LTS
    Release:        22.04
    Codename:       jammy
    (secretflow) bob@DESKTOP-SAOB7DQ:~$
    
  1. 在不同的发行版上安装隐语

在不同的发行版上执行下列步骤建立隐语的开发环境

  • 安装Miniconda 改变您的工作目录到您的home目录安装Miniconda

    cd ~
    

    下载Miniconda

    wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
    

    安装Miniconda

    sh Miniconda3-latest-Linux-x86_64.sh
    

    在Miniconda的安装完成以后,您可以删除Miniconda的安装包

  • 创建隐语的虚拟环境, 在不同发行版上的Python版本应该完全一致

    conda create --name secretflow python==3.8.15
    
  • 激活隐语的虚拟环境

    conda activate secretflow
    
  • 启动SecretFlow

    pip install -U secretflow
    
  1. 获取您的WSL的IP地址

因为WSL的实现原理,WSL里的不同发行版共用相同的IP地址,所以我们只能够在单机模式下部署隐语。使用命令 ifconfig获得您的WSL的IP地址,如果出现 command not found,您可以安装 net-tools,您可以在Ubuntu上运行下列命令安装它

```bash
apt install net-tools
```

Ubuntu下的一个例子

(secretflow) alice@DESKTOP-SAOB7DQ:~$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.23.139.91  netmask 255.255.240.0  broadcast 172.23.143.255
        inet6 fe80::215:5dff:fe23:1cd6  prefixlen 64  scopeid 0x20<link>
        ether 00:15:5d:23:1c:d6  txqueuelen 1000  (Ethernet)
        RX packets 1782122  bytes 7504258331 (7.5 GB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1077846  bytes 63011688 (63.0 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 701234  bytes 573429022 (573.4 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 701234  bytes 573429022 (573.4 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

IP 地址 172.23.139.91 是我们需要的, 我们把它记作ray_ip

  1. 启动Ray集群主节点

使用 ray_ip 在标记为”alice”的发行版上启动主节点这里,ray_ip172.23.139.91 而且我们选择端口20010 (我们把它记作 ray_port)作为Ray的端口那么在主节点启动Ray集群的命令就可以如下所示

ray start --head --node-ip-address="172.23.139.91" --port="20010" --resources='{"alice": 16}' --include-dashboard=False --disable-usage-stats

运行日志

(secretflow) alice@DESKTOP-SAOB7DQ:~$ ray start --head --node-ip-address="172.23.139.91" --port="20010" --resources='{"alice": 16}' --include-dashboard=False --disable-usage-stats
Usage stats collection is disabled.

Local node IP: 172.23.139.91

--------------------
Ray runtime started.
--------------------

Next steps
  To connect to this Ray runtime from another node, run
    ray start --address='172.23.139.91:20010'

  Alternatively, use the following Python code:
    import ray
    ray.init(address='auto', _node_ip_address='172.23.139.91')

  To connect to this Ray runtime from outside of the cluster, for example to
  connect to a remote cluster from your laptop directly, use the following
  Python code:
    import ray
    ray.init(address='ray://<head_node_ip_address>:10001')

  To see the status of the cluster, use
    ray status

  If connection fails, check your firewall settings and network configuration.

  To terminate the Ray runtime, run
    ray stop
(secretflow) alice@DESKTOP-SAOB7DQ:~$
  1. 启动Ray集群其他节点

使用 ray_ipray_port 启动Ray集群的其他节点在标记为”bob”的发行版上:

  • ray_ip:172.23.139.91

  • ray_port:20010 然后运行命令

ray start --address="172.23.139.91:20010" --resources='{"bob": 16}' --disable-usage-stats

运行日志

(secretflow) bob@DESKTOP-SAOB7DQ:~$ ray start --address="172.23.139.91:20010" --resources='{"bob": 16}' --disable-usage-stats
Local node IP: 172.23.139.91

--------------------
Ray runtime started.
--------------------

To terminate the Ray runtime, run
  ray stop
(secretflow) bob@DESKTOP-SAOB7DQ:~$
  1. 检查Ray集群的状态

在Ray集群的任一节点检查Ray集群的状态,比如标记为”alice”的发行版上

ray status

运行日志

(secretflow) alice@DESKTOP-SAOB7DQ:~$ ray status
======== Autoscaler status: 2023-05-18 10:33:28.673249 ========
Node status
---------------------------------------------------------------
Healthy:
 1 node_4cb5fcb410e8b11a62bcebe5653918f0c6ae2b996e028e8e5ec22199
 1 node_d74a3ee633f939d04af3f67f7d4910c064c95a040e6998bfb706aed6
Pending:
 (no pending nodes)
Recent failures:
 (no failures)

Resources
---------------------------------------------------------------
Usage:
 0.0/24.0 CPU
 0.0/16.0 alice
 0.0/16.0 bob
 0.00/8.894 GiB memory
 0.00/4.110 GiB object_store_memory

Demands:
 (no resource demands)
(secretflow) alice@DESKTOP-SAOB7DQ:~$

如上所示,可以发现两个Ray集群的节点

  1. 启动SecretFlow

我们使用ray_ipray_port 在标记为”alice”的第一个发行版上启动隐语, 下面在bash上运行的Python代码表示 隐语已经成功地连接到了Ray集群. 你也可以直接运行Python代码脚本

(secretflow) alice@DESKTOP-SAOB7DQ:~$ python
Python 3.8.15 (default, Nov 24 2022, 15:19:38)
[GCC 11.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import secretflow as sf
>>> sf.init(parties=['alice', 'bob'], address='172.23.139.91:20010')
2023-05-18 10:36:07,871 INFO worker.py:1352 -- Connecting to existing Ray cluster at address: 172.23.139.91:20010...
2023-05-18 10:36:07,879 INFO worker.py:1538 -- Connected to Ray cluster.
>>># your code to run
>>>
  1. (可选)启动SPU设备

这一步描述了如何启动SPU设备。假设SPU在alice上使用端口9100,在bob上使用端口9200。(端口号仅为示例,您可以任意选择未被占用的端口)

  • ray_ip:172.23.139.91

  • ray_port:20010

import spu
import secretflow as sf

# Use ray head adress please.
# sf.init(parties=['alice', 'bob'], address='ray_ip:ray_port')
sf.init(parties=['alice', 'bob'], address='172.23.139.91:20010')

cluster_def={
    'nodes': [
        {
            'party': 'alice',
            'address': '172.23.139.91:9100',
            'listen_addr': '0.0.0.0:9100'
        },
        {
            'party': 'bob',
            'address': '172.23.139.91:9200',
            'listen_addr': '0.0.0.0:9200'
        },
    ],
    'runtime_config': {
        'protocol': spu.spu_pb2.SEMI2K,
        'field': spu.spu_pb2.FM128,
        'sigmoid_mode': spu.spu_pb2.RuntimeConfig.SIGMOID_REAL,
    }
}

spu = sf.SPU(cluster_def=cluster_def)

# your code to run.

生产模式#

SecretFlow提供了多控制器模式用于生产以提升安全性。(如果您想了解更多,欢迎阅读隐语编程思想

用于生产的SecretFlow由多个ray集群组成,每个参与方拥有各自的ray集群。 与此同时,每一个参与方都要同时执行代码,才能完成任务的协作 。生产模式的架构如下图所示。

仿真

下列步骤将演示如何部署生产模式的SecretFlow。

创建跨机构的SecretFlow集群。#

下面的例子展示了如何构建一个由alice和bob组成的生产集群。


注意

请牢记alice和bob需要同时运行代码。


在alice节点上启动SecretFlow#

alice首先启动ray集群。注意这里的命令是启动Ray的主节点。

ray start --head --node-ip-address="ip" --port="port" --include-dashboard=False --disable-usage-stats

屏幕输出中显示”Ray runtime started.”,则说明Ray的主节点启动成功。至此alice的Ray集群已经创建完成。

alice使用集群配置初始化SecretFlow,并且运行代码。


提示

  1. 请使用主节点的 node-ip-addressport 填充 sf.initaddress 参数。

  2. aliceaddress 请填写可以被bob访通的地址,并且选择一个 未被占用的端口 ,注意不要和Ray和SPU的端口冲突。

  3. bobaddress 请填写可以被alice访通的地址,并且选择一个 未被占用的端口 ,注意不要和Ray和SPU的端口冲突。

  4. 注意 self_partyalice

  5. 请注意sf.init不需要提供 parties 参数,而是需要提供 cluster_config 来描述两个机构之间的通信地址和端口。

  6. 为了确保alice和bob的端口能够被对方访问同时系统的防火墙不被关闭,您应该把alice和bob的IP地址加入到对方的IP白名单

  7. telnet 命令通常用于测试端口的可访问性。


cluster_config ={
    'parties': {
        'alice': {
            # replace with alice's real address.
            'address': 'ip:port of alice',
            'listen_addr': '0.0.0.0:port'
        },
        'bob': {
            # replace with bob's real address.
            'address': 'ip:port of bob',
            'listen_addr': '0.0.0.0:port'
        },
    },
    'self_party': 'alice'
}

sf.init(address='alice ray head node address', cluster_config=cluster_config)

# your code to run.

在bob节点上启动SecretFlow。#

bob首先启动ray集群

ray start --head --node-ip-address="ip" --port="port" --include-dashboard=False --disable-usage-stats

屏幕输出中显示”Ray runtime started.”,则说明Ray的主节点启动成功。至此bob的Ray集群已经创建完成。

bob使用和alice类似的集群配置初始化SecretFlow,除了 self_party 字段稍有不同。然后运行代码。


提示

  1. 使用主节点的 node-ip-addressport来填充 sf.initaddress 参数。注意,这里填写的是bob的头节点地址,请不要填写成alice的。

  2. aliceaddress 请填写可以被bob访通的地址,并且选择一个 未被占用的端口 ,注意不要和Ray和SPU的端口冲突。

  3. bobaddress 请填写可以被alice访通的地址,并且选择一个 未被占用的端口 ,注意不要和Ray和SPU的端口冲突。

  4. 注意self_partybob

  5. 请注意sf.init不需要提供 parties 参数,而是需要提供 cluster_config 来描述两个机构之间的通信地址和端口。

  6. 为了确保alice和bob的端口能够被对方访问同时系统的防火墙不被关闭,您应该把alice和bob的IP地址加入到对方的IP白名单

  7. telnet 命令通常用于测试端口的可访问性。



cluster_config ={
    'parties': {
        'alice': {
            # replace with alice's real address.
            'address': 'ip:port of alice',
            'listen_addr': '0.0.0.0:port'
        },
        'bob': {
            # replace with bob's real address.
            'address': 'ip:port of bob',
            'listen_addr': '0.0.0.0:port'
        },
    },
    'self_party': 'bob'
}

sf.init(address='bob ray head node address', cluster_config=cluster_config)

# your code to run.

如何构建SPU#

除了SPU的创建需要多个参会方同时执行外,创建SPU的方式与模拟模式一样,请阅读前文了解更多细节。

为了避免启动时差带来的比如连接超时等问题,您可能需要设置SPU的link_desc参数来调整连接相关参数,具体参见 SPU

生产环境建议#

  1. 启动tls验证。

    SecretFlow的跨机构grpc通信可以配置TLS以提升安全性。

    alice的配置示例。

    tls_config = {
        "ca_cert": "ca root cert of other parties (e.g. bob)",
        "cert": "server cert of alice in pem",
        "key": "server key of alice in pem",
    }
    
    sf.init(address='ip:port', 
            cluster_config=cluster_config, 
            tls_config=tls_config
    )
    

    bob的配置示例。

    tls_config = {
        "ca_cert": "ca root cert of other parties (e.g. alice)",
        "cert": "server cert of bob in pem",
        "key": "server key of bob in pem",
    }
    
    sf.init(address='ip:port', 
            cluster_config=cluster_config, 
            tls_config=tls_config
    )
    
  2. 序列化/反序列化增强。

    SecretFlow使用 pickle 进行序列化/反序列化,可能存在被攻击的风险。你可以在初始化SecretFlow时通过 cross_silo_serializing_allowed_list 参数设置允许序列化的对象。示例如下(请不要直接使用该示例,请根据实际需要自行修改)。

    allowed_list =  {
        "numpy.core.numeric": ["*"],
        "numpy": ["dtype"],
    }
    
    sf.init(address='ip:port', 
            cluster_config=cluster_config, 
            cross_silo_serializing_allowed_list=allowed_list
    )