部署#

前置知识:关于Ray#

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

仿真#

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网络模式启动容器,下面会向您解释原因。

生产模式#

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 来描述两个机构之间的通信地址和端口。


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的主节点启动成功。至此alice的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 来描述两个机构之间的通信地址和端口。



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
    )