Petrel OSS SDK 2.0 === 注意:该版本SDK需要python3.6环境 若之前安装过旧版本,请先运行 ```bash $ pip uninstall pycephs3client $ rm -rf ~/.local/lib/python3.6/site-packages/petrel_client ``` ## 建议在安装之前先升级 pip ```bash source /mnt/lustre/share/platform/env/ # 请根据实际情况确定是否需要 source python3 -m pip install --upgrade pip # 请根据实际情况确定是否需要 `sudo` 或添加 `--user` 参数 ``` ## 训练集群环境上安装 ```bash $ source /mnt/lustre/share/platform/env/ $ python setup.py sdist $ pip install --user dist/* ``` ## 通过修改 PYTHONPATH 安装 ```bash $ source /mnt/lustre/share/platform/env/ # 安装SDK依赖 $ python setup.py egg_info $ pip install -r *.egg-info/requires.txt # 将SDK编译到 ./build 目录 $ python setup.py build # 修改 PYTHONPATH 环境变量 $ export PYTHONPATH=/build/lib:$PYTHONPATH ``` ## venv环境上安装 ```bash $ python3 -m venv your_venv_name # 若已创建venv环境则无需执行 $ source your_venv_name/bin/active $ python setup.py sdist $ pip install dist/* ``` ## 系统环境上安装 ```bash $ python3 setup.py sdist $ python3 -m pip install dist/* # 请根据实际情况确定是否需要 `sudo` 或添加 `--user` 参数 ``` ## 使用 SDK 提供 `get` 和 `put` 接口,使用方式为 ```python data = client.get(url) # 默认情况由配置文件决定是否使用 MC data = client.get(url, no_cache=True) # 本次 get 直接从 ceph 读取 data = client.get(url, update_cache=True) # 本次 get 直接从 ceph 读取,并将数据缓存至 MC ``` ```python client.put(url, data) # 默认 put 不会更新 MC client.put(url, data, update_cache=True) # 本次 put 将数据存入 ceph 之后并更新 MC ``` ``注意:``若配置文件中没有启用 `MC` ,则 `no_cache` 和 `update_cache` 参数将被忽略 以下为使用 SDK 读取图片、进行图片处理后并保存图片的简单例子 ```python import cv2 import numpy as np from os.path import splitext from petrel_client.client import Client conf_path = '~/petreloss.conf' client = Client(conf_path) # 若不指定 conf_path ,则从 '~/petreloss.conf' 读取配置文件 img_url = 's3://bucket1/image.jpeg' img_gray_url = 's3://bucket1/image_gray.jpeg' img_ext = splitext(img_gray_url)[-1] # 图片读取 img_bytes = client.get(img_url) assert(img_bytes is not None) img_mem_view = memoryview(img_bytes) img_array = np.frombuffer(img_mem_view, np.uint8) img = cv2.imdecode(img_array, cv2.IMREAD_COLOR) # 图片处理 img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 图片存储 success, img_gray_array = cv2.imencode(img_ext, img_gray) assert(success) img_gray_bytes = img_gray_array.tostring() client.put(img_gray_url, img_gray_bytes) ``` 配置文件请参考 [petreloss.conf](./conf/petreloss.conf) ``请注意:配置文件中 `key = value` 的 key 前面不能有空格,否则该行视为上一行配置项 value 的一部分`` 使用样例请参考 [multi_cluster_test.py](./tests/multi_cluster_test.py) ## `Tensor` 和 `Json` 数据保存与读取 使用样例 [tensor_json_test.py](./tests/tensor_json_test.py) ## `Pillow Image` 数据保存与读取 使用样例 [pillow_image_test.py](./tests/pillow_image_test.py) ## 数据过大无法上传,则需要分片上传 使用样例 [multipart_test.py](./tests/multipart_test.py) ## 创建 Bucket ```python client.create_bucket('s3://mybucket') ``` ## 顺序的读取某个前缀的数据 ```python cluster = 'cluster1' files = client.get_file_iterator('cluster1:s3://lili1.test2/test3') for p, k in files key = '{0}:s3://{1}'.format(cluster, p) data = client.get(key) ``` ## 使用 anonymous 账户访问数据 若在配置文件中不设置 `access_key` 和 `secret_key`,将以 `anonymous` 账户访问数据。 ## McKeySizeExceed 错误 默认情况下,`MC` 所支持 `key` 的最大长度为250个字节。如果路径过长,将会出现 `McKeySizeExceed` 错误。 此时需要用户定义 `key` 的转换规则来避免该错误。 ``注意:``中文字符对应多个字节。 例如: ```python def trim_key(key): if isinstance(key, str): key = key.encode('utf-8') else: assert isinstance(key, bytes) return key[-249:] client = Client('~/petreloss.conf', mc_key_cb=trim_key) ``` 此外,可使用内置函数 `md5`、`sha256` 等,例如: ```python client = Client('~/petreloss.conf', mc_key_cb='sha256') ``` 或在配置文件中指定: ```conf [mc] mc_key_cb = sha512 ``` ``请注意`` - 用户需要保证转换规则结果的唯一性,内置转换函数也有可能发生哈希碰撞。 - 如果 `key` 为 `str` 类型且其中出现中文字符,请务必用 `encode('utf-8')` 对其进行编码。 ## 使用伪客户端 在对应客户端添加如下配置: ```conf fake = True ``` 配置文件请参考 [fake_client.conf](./conf/fake_client.conf) 使用样例请参考 [fake_client_test.py](./tests/fake_client_test.py) ## 强制更新MC 使用 `get_and_update` 接口或在 `get` 中传入 `update_cache=True` 参数将直接从存储系统读取数据并更新MC。 ## IO 统计信息 IO 统计信息可通过以下三种方式修改其`log`输出频度: - 由环境变量 `count_disp` 设置 - 由配置文件 `count_disp` 设置 (若已设置环境变量,则该方式无效) - 调用 `client.set_count_disp(count_disp)` (该方式将覆盖上述两种方式),但限于`parrots`和`pytorch`的运行机制,在某些使用场景下可能无法有效修改。 若 `count_disp` 为 `0` ,则将关闭 IO 统计信息打印。 若需要在 `console` 中打印 IO 统计信息,则需要设置 `console_log_level` 为 `INFO` 或更低级别,且 `count_disp` 需大于 `0`。 ## DataLoader `SDK` 提供的 `DataLoader` 额外支持如下参数: - `prefetch_factor`,默认2。每个 `worker` 预读 `batch` 数目。 - `persistent_workers`,默认 `False`。如果为 `True`,则每轮 `epoch` 迭代完毕后 `worker` 进程将不会关闭,下轮 `epoch` 将复用该 `worker` 进程。 用例: ```python from petrel_client.utils.data import DataLoader dataloader = DataLoader(dataset=xxx, ..., prefetch_factor=4, persistent_workers=True) ``` ## SSL 验证 使用 `https` 协议时默认不会对 `SSL` 进行验证。若需要开启验证,请在配置文件中进行如下设置 ```conf verify_ssl = True ``` ## Presigned URL,生成签名链接 ```python presigned_url = client.generate_presigned_url(url, client_method ='get_object', expires_in=3600) ``` `client_method` 取值为 `get_object` (默认值) 或 `put_object` `expires_in` 单位为秒,默认值为 3600 ## Presigned POST,生成签名 POST ```python presigned_post = client.generate_presigned_post(url, fields=None, conditions=None, expires_in=3600) ``` 参数及返回值详见 [generate_presigned_post](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.generate_presigned_post),其中参数 bucket 和 key 从 url 中提取。 ## Range 读取 range 的形式有如下几种 - '`first_byte_pos`-':读取`first_byte_pos`及后续的所有数据 - '`first_byte_pos`-`last_byte_pos`':读取`first_byte_pos`到`last_byte_pos`(包含)的数据 - '-`suffix_length`':读取最后`suffix_length`字节的数据 具体详见 HTTP 中 Range 的定义:https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35 ,但ceph目前不支持多个 Range 读取。 ```python # 假设数据长度为 10000 字节 # 读取第一段 500 字节数据 data1 = client.get(url, range='0-499') # 读取第二段 500 字节数据 data2 = client.get(url, range='500-999') # 读取最后 500 字节数据 data_last = client.get(url, range='-500') # 或 data_last = client.get(url, range='9500-') ``` ## 以流的形式读取数据 ```python stream = client.get(url, enable_stream=True) ``` 返回的 `stream` 为 `StreamingBody`,使用方法详见 https://botocore.amazonaws.com/v1/documentation/api/latest/reference/response.html ## 判断对象是否存在 ```python exists = client.contains(url) ``` ## 查询对象大小 ```python size = client.size(url) ``` 若对象不存在,产生 `NoSuchKeyError` 异常 ## 删除对象 ```python client.delete(url) ``` ## 列出当前路径包含的对象或目录 ```python contents = client.list(url) for content in contents: if content.endswith('/'): print('directory:', content) else: print('object:', content) ``` ## 判断目录是否存在 ```python client.isdir(url) ``` 注意:`Ceph`中没有目录的概念,本函数返回`True`时代表存在以该`url`作为前缀的对象,其他情况返回`False`。 ## 使用 `/mnt/cache` 目录下的 `Python` 环境 相对于 `/mnt/lustre` 目录,在 `/mnt/cache` 目录执行 `Python` 有一定的性能提升。 使用方式如下: - `source` `/mnt/cache` 目录下的 `Python` 环境 ```bash ### 例如 pt1.3v1 source /mnt/cache/share/platform/env/pt1.3v1 ### 或 s0.3.3 source /mnt/cache/share/spring/s0.3.3 ``` - 检查 `Python` 路径是否正确 ```bash which python ### 结果应为 /mnt/cache/... ``` - 设定 `PYTHONUSERBASE` 环境变量 ```bash export PYTHONUSERBASE=/mnt/cache//.local ``` - 重新安装相关依赖库(仅需首次使用时执行) ``` python -m pip install --user ```