MegaThinking

better tokens, better intelligence, contributing superior tokens to models

Download

https://rsync.samba.org/

最新版本:Rsync version 3.2.3 released

How rsync works

https://rsync.samba.org/how-rsync-works.html

Guide

https://download.samba.org/pub/rsync/rsync.html

  • --recursive: recurse into directories
  • --append: append data onto shorter files
  • --filter
1
/usr/local/Cellar/rsync/3.2.3/bin/rsync --verbose --no-whole-file --recursive --append --include='*.log' --include='*/' --exclude='*' --prune-empty-dirs dir1/ dir2/

注意 rsync 本地目录的特殊之处

https://superuser.com/questions/234273/why-doest-rsync-use-delta-transfer-for-local-files

–whole-file, This is the default when both the source and destination are specified as local paths, but only if no batch-writing option is in effect.

High Availability

https://unix.stackexchange.com/questions/48298/can-rsync-resume-after-being-interrupted

deep residual learning framework

AWS SageMaker

AWS SageMaker 训练容器镜像设计体验

不是优点的优点: 看起来只支持同构资源,训练资源分配模型为单节点单容器,理解上简单

优势

  • 容器镜像功能层次丰富,每个层次都有文档描述如何实施,Level 0 对容器镜像约束最少,自定义程度最高
    • Level 0: 完全自定义容器镜像,容器镜像指定 Entrypoint,Entrypoint 命令能处理 train 参数即可 link
    • Level 1: 改造已有容器镜像,使得其可利用 SageMaker Toolkits 来进行训练与推理(即把已有镜像改造为 SageMaker 镜像)link
    • Level 2: 使用预置的 SageMaker 容器镜像
    • Level 3: 使用扩展的预置 SageMaker 容器镜像(基于预置的容器镜像扩展功能)
  • 训练启动脚本 (Training Toolkits) 开源,并且可通过 pip install sagemaker-training 直接安装,常用深度学习引擎有独立的 toolkits,均包含在 training toolkits 中 link
  • 可在 Notebook 中直接构建容器镜像
  • 可在 local machine 测试容器镜像基本功能(可能仅限单机训练?)

主打场景

AI 开发者:文档详细且丰富(技术向),容器镜像可玩度高(约束少)

Azure Machine Learning

https://docs.microsoft.com/en-us/azure/machine-learning/how-to-train-with-custom-image

Azure 与 conda 结合,有个 Environment 的概念,对容器镜像有如下约束

  • Ubuntu 16.04 or greater.
  • Conda 4.5.# or greater.
  • Python 3.5+.

当然如果不使用 Environment,也就无上述约束

https://docs.microsoft.com/en-us/azure/machine-learning/how-to-train-tensorflow#distributed-training

资源分配模式看起来也是单节点单容器

https://github.com/Azure/MachineLearningNotebooks/tree/master/how-to-use-azureml/ml-frameworks/tensorflow/distributed-tensorflow-with-horovod

TODO …

https://developer.nvidia.com/gpudirect

环境信息

  • Kernel: 3.10.0-514.44.5.10.h254.x86_64 (uname -r)
  • Nvidia Driver: 440.33.01 (nvidia-smi)
  • MLNX OFED: 4.3-1.0.1.0 (ofed_info)
  • Mellanox/nv_peer_memory: Tag 1.1-0

容器化安装 NVIDIA Driver 看起来会出现 lsmod | grep nvidia 能找到,然而 modinfo nvidia 会提示找不到 Module 的错误

需要修改 nv_peer_memory 代码库的构建脚本,workaround 上述问题

DIY nv_peer_memory 编译

准备空目录

1
2
mkdir -p /root/nv_peer_memory
cd /root/nv_peer_memory

NVIDIA Driver

https://us.download.nvidia.com/tesla/440.33.01/NVIDIA-Linux-x86_64-440.33.01.run

1
2
3
4
5
# 下载 `NVIDIA-Linux-x86_64-440.33.01.run`
curl -o NVIDIA-Linux-x86_64-440.33.01.run 'https://us.download.nvidia.com/tesla/440.33.01/NVIDIA-Linux-x86_64-440.33.01.run'

# 解压至当前目录
./NVIDIA-Linux-x86_64-440.33.01.run -x

nv_peer_memory

https://github.com/Mellanox/nv_peer_memory/tree/1.1-0

https://www.mellanox.com/products/GPUDirect-RDMA

1
2
curl -o nv_peer_memory-1.1-0.tar.gz 'https://github.com/Mellanox/nv_peer_memory/archive/1.1-0.tar.gz'
tar xzf nv_peer_memory-1.1-0.tar.gz

DIY 编译

1
cd nv_peer_memory-1.1-0

修改 Makefile 中的 nv_sources 为 NVIDIA Driver 源码位置

1
nv_sources=/root/nv_peer_memory/NVIDIA-Linux-x86_64-440.33.01/kernel

修改 create_nv.symvers.sh 中的 nvidia_mod 为主机上安装的 NVIDIA Driver .ko 位置,例如

1
nvidia_mod=/var/k8s/nvidia/drivers/nvidia.ko

编译

参考 nv_peer_memory README.md

1
2
3
./build_module.sh

rpmbuild --rebuild /tmp/nvidia_peer_memory-1.1-0.src.rpm

安装 rpm

1
rpm -ivh /root/rpmbuild/RPMS/x86_64/nvidia_peer_memory-1.1-0.x86_64.rpm

测试

1
lsmod | grep nv_peer_mem

NCCL_DEBUG=INFO,例如

NCCL version 2.4.8+cuda10.1

1
NCCL INFO Ring 00 : 3 -> 10 [send] via NET/IB/0/GDRDMA

Trick

  • nvidia_peer_memory 代码中的 create_nv.symvers.sh 可独立执行,由于容器化安装 NVIDIA Driver 场景,modinfo nvidia 会报找不到 mod 的错,可找一台直接在主机侧安装了 NVIDIA driver 的机器,bash -x create_nv.symvers.sh 确认执行过程,以及相关变量取值

  • 如下命令可显示 mod 对应的 ko 文件位置

1
2
$/sbin/modinfo -F filename -k 3.10.0-514.44.5.10.h142.x86_64 nvidia
/lib/modules/3.10.0-514.44.5.10.h142.x86_64/kernel/drivers/video/nvidia.ko

init_process_group

store TCPStore

rank == 0 作为 TCPStore rendezvous handler 的 server

hostname

port

tcp://

rank

world_size

TCPStore

isServer 为 True 时,内部启动 TCPStoreDaemon

waitWorkerReady 为 True 时,10ms 轮询一次是否获取到足够到 workerNumber

https://github.com/linux-rdma/perftest/blob/master/src/write_bw.c

main

1
2
user_param.verb = WRITE;
user_param.tst = BW;

parser

1
2
-c, --connection=<RC/XRC/UC/DC> Connection type RC/XRC/UC/DC (default RC)
-s, --size=<size> Size of message to exchange (default 65536)

init_perftest_params

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
...

#define DEF_SIZE_BW (65536)
#define DEF_SIZE_LAT (2)
#define DEF_CACHE_LINE_SIZE (64)
#define DEF_PAGE_SIZE (4096)
#define DEF_FLOWS (1)

...

user_param->size = (user_param->tst == BW ) ? DEF_SIZE_BW : DEF_SIZE_LAT;

user_param->connection_type = (user_param->connection_type == RawEth) ? RawEth : RC;

...

user_param->cache_line_size = get_cache_line_size();
user_param->cycle_buffer = sysconf(_SC_PAGESIZE);

if (user_param->cycle_buffer <= 0) {
user_param->cycle_buffer = DEF_PAGE_SIZE;
}

...

user_param->flows = DEF_FLOWS;

get_cache_line_size()

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
	int size = 0;
#if !defined(__FreeBSD__)
size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
if (size == 0) {
#if defined(__sparc__) && defined(__arch64__)
char* file_name =
"/sys/devices/system/cpu/cpu0/l2_cache_line_size";
#else
char* file_name =
"/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size";
#endif

FILE *fp;
char line[10];
fp = fopen(file_name, "r");
if (fp == NULL) {
return DEF_CACHE_LINE_SIZE;
}
if(fgets(line,10,fp) != NULL) {
size = atoi(line);
fclose(fp);
}
}
#endif
if (size <= 0)
size = DEF_CACHE_LINE_SIZE;

getconf LEVEL1_DCACHE_LINESIZE

main -> alloc_ctx

1
2
3
4
5
6
7
8
9
10
ctx->size = user_param->size;

num_of_qps_factor = (user_param->mr_per_qp) ? 1 : user_param->num_of_qps;

/* holds the size of maximum between msg size and cycle buffer,
* aligned to cache line,
* it is multiply by 2 for send and receive
* with reference to number of flows and number of QPs */
ctx->buff_size = INC(BUFF_SIZE(ctx->size, ctx->cycle_buffer),
ctx->cache_line_size) * 2 * num_of_qps_factor * user_param->flows;

65536 = 64Kb

generally, 16 pages

root cause: ulimit -l is 16 (default) in container

本地复现一个 TcpStore 的测试用例问题,修改了部分代码,因此需要源码编译 PyTorch

环境信息

  • macOS 10.15.7
  • XCode 12.2 (12B45b)

源码信息

master latest commit (2020-11-14): f8248543a13b0144a6f5d0a549f72b1e470d88aa

1
2
3
commit f8248543a13b0144a6f5d0a549f72b1e470d88aa (github/master, github/gh/ljk53/194/base, github/HEAD, master)
Author: Rohan Varma <rvarm1@fb.com>
Date: Sat Nov 14 13:36:31 2020 -0800

构建

(1) https://github.com/pytorch/pytorch#from-source

(2) https://github.com/pytorch/pytorch/blob/master/CONTRIBUTING.md#c-development-tips

glog

1
brew install glog

conda

1
2
3
4
5
6
7
8
conda create -n pytorch-dev python=3.6

conda activate pytorch-dev

conda install numpy ninja pyyaml mkl mkl-include setuptools cmake cffi typing_extensions future six requests dataclasses

# Add these packages if torch.distributed is needed
conda install pkg-config libuv

build and install

uninstall

1
2
3
4
conda uninstall torch
pip uninstall torch

rm -rf build/

then reinstall

1
2
export CMAKE_PREFIX_PATH=${CONDA_PREFIX:-"$(dirname $(which conda))/../"}
MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ MAX_JOBS=8 BUILD_CAFFE2=0 BUILD_CAFFE2_OPS=0 USE_GLOG=1 USE_DISTRIBUTED=1 USE_MKLDNN=0 USE_CUDA=0 USE_FBGEMM=0 USE_NNPACK=0 USE_QNNPACK=0 USE_XNNPACK=0 python setup.py develop

Quad-Core Intel Core i7 ~ 45min

测试

1
2
3
4
5
6
Python 3.6.12 |Anaconda, Inc.| (default, Sep  8 2020, 17:50:39)
[GCC Clang 10.0.0 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> torch.__version__
'1.8.0a0+f1a8a82'

TcpStore

1
2
3
4
5
6
7
python test/distributed/test_c10d.py

Python 3.6.12 |Anaconda, Inc.| (default, Sep 8 2020, 17:50:39)
[GCC Clang 10.0.0 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch.distributed as dist
>>> server_store = dist.TCPStore("127.0.0.1", 18668, 1, True)

or

1
./build/bin/TCPStoreTest

https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination

简而言之,pod 被删除时

  1. kubelet 触发 container runtime 对 pod 中的每个 container 的 1 号进程,发送 TERM 信号
  2. 等待 the grace period expires (terminationGracePeriodSeconds 默认为 30s)
  3. 如果 the grace period expires 后 containers 仍未退出,则 kubelet 触发 container runtime,向 pod 中的每个 container 中仍然处于 running 状态的进程发送 KILL 信号

正确处理 TERM 信号,可以让业务优雅退出(or 更快退出);例如

假设 pod command 为

https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#run-a-command-in-a-shell

1
2
command: ["/bin/bash"]
args: ["-c", "/home/rt/run.sh"]

or

1
2
3
4
command:
- "/bin/bash"
- "-c"
- "/home/rt/run.sh"

/bin/bash /home/rt/run.sh 是 1 号进程

在 /home/rt/run.sh 中可以如此处理,以达到优雅退出的目的

1
2
3
4
5
6
7
8
9
10
11
function prog_exit {
echo "receive SIGTERM signal"
pkill python
}

trap prog_exit SIGTERM

# main function
python /home/rt/train.py &

wait $!

ref: docker stop

https://docs.docker.com/engine/reference/commandline/stop/

The main process inside the container will receive SIGTERM, and after a grace period, SIGKILL.

k8s subPath and s3 fuse mount (s3fs)

意识流分析,致敬一个生产环境中的问题,真是看了很多代码

https://github.com/s3fs-fuse/s3fs-fuse

实现了 fuse 接口,底层对接 s3 协议

打开 s3fs info 日志,观察可知

fuse 周期性触发

  1. s3fs_getattr
    1. check_object_access
      1. get_object_attribute
        1. s3 head request
    2. check_parent_object_access
  2. s3fs_opendir
    1. check_object_access
      1. check_parent_object_access
  3. s3fs_readdir 方法,该方法底层会调用 list_bucket func,到s3 server查询对象列表
    1. 查询到有新的对象后,会 add 到本地 stat cache,并且下载到本地

所以使用 s3fs mount s3 bucket 到本地时,s3fs 会周期性同步远端的数据到本地,写入时,也会同步到远端

1
kill -SIGUSR2 ${s3fs_pid}

可以动态调整 s3fs 日志级别,s3fs 默认日志输出到 /var/log/messages

messages.2.gz 文件解压,可以使用 gzip -d messages.2.gz


生产环境出了一个诡异的问题

server / worker 两个实例,在不同节点,挂载了同一个桶

k8s subPath 方式挂载子对象,例如 test-bucket/output1

在开始阶段 server s3fs 会上传 test-bucket/output1 文件夹,两次

而 worker s3fs 不会

debug 了一会儿,直接原因与 s3 对象的权限相关,用户创建的对象,s3fs mount 时,没权限 head,但是又能 list 到

s3fs 如果 list 该对象为非空时,可正常读写该对象下的内容,也与验证结果符合 -.0


分割线,前边是一些分析

k8s subPath

subPath 是相对于 volume root path 的一个 sub path,用于挂载 volume 内的子目录

实现上首先会将 volume 挂载于节点的 pod volume 路径下

  1. 再去 open subPath,open 之后,成为 kubelet 进程下的一个 fd
  2. 接下来创建 volume-subpaths 下的一个路径(文件名与 volumeMount index 一致),并且判断 subPath 是文件还是文件夹
    1. 文件夹,mkdir 一把
    2. 文件,写一个空文件
  3. 最后将该 fd mount 到上述创建的路径中

不明点,在 1 步骤上,实际上 subPath 为 noObject;从文件系统的角度不确定它为文件夹,还是对象,而 kubelet 却执行成功了,并顺利挂载到容器中

从 s3fs 的日志,对 subPath 首先执行了

  1. s3fs_mkdir,mode 0750
  2. s3fs_chmod,mode 40777

即触发了两次 s3 PUT.OBJECT 的操作

这块就不深入分析了为何如此了

感叹一下,系统层层调用之后,问题定位需要极其清晰的思路,大胆假设,小心求证;解决问题的思路是第一重要的


另外 s3fs 1.80 版本 err ret 不打印错误信息,只打印了个 response code,这个也比较伤,建议升级到当前最新版本
s3fs 1.86,会把错误信息也打印出来

0%