部署#
前置知识:关于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” 标识的主节点。
注意
请使用真实的ip地址和端口。
{"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-address
和 port
。
ray start --address="ip:port" --resources='{"bob": 16}' --disable-usage-stats
当屏幕输出中出现 “Ray runtime started.” 意味着节点启动成功。现在由两个Ray节点构成的Ray集群已经搭建完毕。其中,头节点模拟机构alice,从节点模拟机构bob。
你也可以继续重复上述步骤以启动带有其他参与方标识的节点。
启动SecretFlow#
现在你可以启动SecretFlow。下面这段代码表示alice和bob分别执行了一个返回输入值的函数。
提示
请使用主节点的
node-ip-address
和port
填充sf.init
的address
参数。如果你启动了更多的节点(比如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的配置可以参考。
提示
请使用主节点的
node-ip-address
和port
填充sf.init
的address
参数。alice
的address
请填写可以被bob访通的地址,并且选择一个 未被占用的端口 ,注意不要和Ray端口冲突。alice
的listen_addr
可以和aliceaddress
里的端口一样。bob
的address
请填写可以被alice访通的地址,并且选择一个 未被占用的端口 ,注意不要和Ray端口冲突。bob
的listen_addr
可以和bobaddress
里的端口一样。
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网络模式启动容器,下面会向您解释原因。
(推荐)host网络#
如果您使用host方式启动docker容器(比如在启动时添加参数--network host
),则容器会直接共享宿主机的网络。因此这种情况下搭建SecretFlow集群无需特别的配置,您可以直接使用宿主机的地址作为节点间通信的地址。部署步骤可以参考前文。
(不推荐)bridge网络#
如果您使用bridge网络启动docker容器,则意味着容器会拥有独立的隔离网络,ip地址与宿主机不同,并且不在同一个bridge网络上的容器默认是不通的。虽然您可以通过端口映射的方式把端口映射到宿主机上,但是由于ray多个节点之间的通信比较复杂,其中涉及到很多端口,比较容易出错不易配置正确,所以我们并不建议使用bridge模式搭建仿真集群。
如果因为某些原因您确实无法使用host网络,希望下面的说明对您使用bridge网络部署SecretFlow有帮助。
情形一: 在一台机器上使用多个docker容器模部署仿真集群#
下列步骤展示了如何在一台机器上启动多个容器。由于在同一台机器上,因此多个容器可以使用同一个bridge网络通信。
创建一个bridge网络
下列命令创建了一个网段为192.168.0.1/24的bridge网络,名称叫做sfnet。 注意该网段仅为示例,您可以根据自己的需求自行修改网段地址。
docker network create sfnet --subnet 192.168.0.1/24
启动第一个容器模拟机构alice
下列命令启动了名为secretflow0
的容器,使用第一步创建的bridege网络,并且指定了ip为192.168.0.10。
docker run -it --network sfnet --ip 192.168.0.10 --name secretflow1 secretflow/secretflow-anolis8:latest bash
容器启动成功后,接着我们启动ray的主节点。下列命令启动了Ray的主节点,监听端口为9001(您可以自由选择一个未使用的端口)。
ray start --head --port=9001 --resources='{"alice": 16}' --include-dashboard=False --disable-usage-stats
启动第二个容器模拟机构bob
下列命令启动了名为secretflow1
的容器,使用第一步创建的bridege网络,并且指定了ip为192.168.0.20。
docker run -it --network sfnet --ip 192.168.0.20 --name secretflow2 secretflow/secretflow-anolis8:latest bash
启动容器后,我们启动Ray的从节点。下面命令启动了Ray的从节点,连接上一步启动的主节点。
ray start --address=192.168.0.10:9001 --resources='{"bob": 16}' --disable-usage-stats
至此我们已经搭建了两个Ray节点,分别代表机构alice和bob。
启动SecretFlow
启动SecretFlow 我们在第一个容器内启动SecretFlow,下列Python代码说明SecretFlow成功连接到了Ray集群
>>> import secretflow as sf
>>> sf.init(parties=['alice', 'bob'], address='192.168.0.10:9001')
2023-02-18 07:08:29,697 INFO worker.py:1352 -- Connecting to existing Ray cluster at address: 192.168.0.10:9001...
2023-02-18 07:08:29,718 INFO worker.py:1538 -- Connected to Ray cluster.
>>> # your code to run
(可选)启动SPU设备
这一步描述了如何启动SPU设备。假设SPU在alice上使用端口9100,在bob上使用端口9200。(端口号仅为示例,您可以任意选择未被占用的端口)
import spu
import secretflow as sf
# Use ray head adress please.
sf.init(parties=['alice', 'bob'], address='192.168.0.10:9001')
cluster_def={
'nodes': [
{
'party': 'alice',
'address': '192.168.0.10:9100',
'listen_addr': '0.0.0.0:9100'
},
{
'party': 'bob',
'address': '192.168.0.20: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.
(强烈不推荐)情形二:在多台机器上使用多个docker容器部署仿真集群#
如果您需要在多台机器上使用bridge网络部署多个容器来搭建Ray集群,由于Ray的通信协议复杂(使用到很多端口且会发生跨节点互相访问),导致需要每台机器上都要透出很多端口,操作很复杂且很容易出错。Ray官方对此的支持也并不好,所以我们非常不推荐这种部署模式。
生产模式#
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,并且运行代码。
提示
请使用主节点的
node-ip-address
和port
填充sf.init
的address
参数。alice
的address
请填写可以被bob访通的地址,并且选择一个 未被占用的端口 ,注意不要和Ray和SPU的端口冲突。bob
的address
请填写可以被alice访通的地址,并且选择一个 未被占用的端口 ,注意不要和Ray和SPU的端口冲突。注意
self_party
为alice
。请注意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
字段稍有不同。然后运行代码。
提示
使用主节点的
node-ip-address
和port
来填充sf.init
的address
参数。注意,这里填写的是bob的头节点地址,请不要填写成alice的。alice
的address
请填写可以被bob访通的地址,并且选择一个 未被占用的端口 ,注意不要和Ray和SPU的端口冲突。bob
的address
请填写可以被alice访通的地址,并且选择一个 未被占用的端口 ,注意不要和Ray和SPU的端口冲突。注意
self_party
为bob
。请注意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
生产环境建议#
启动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 )
序列化/反序列化增强。
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 )