<!-- Debian 中,user's home dir 的 template 是 /etc/skel -->
## MPI Cluster 架構
- 在一個 Cluster 中會有多台機器(電腦),稱為 node
- 一個 node 當作 **Manager** (Master)
- 分配運算工作給 nodes
- 多個 nodes 當作 **Woker** (Slave)
- 執行 manager 分配的運算工作
- Nodes 間使用網路溝通,為了方便操作,通常會將每個 nodes **設定靜態 IP**
- OpenMPI 依賴兩種服務進行溝通
- **SSH**:
- 負責指令傳輸
- Master 需要能免密碼登入到 worler
- **NFS**:
- 透過共用資料夾,進行資料傳輸
- 所有 nodes 只要有一台當作 NFS-Server,其他 nodes 直接存取 server 的資料夾就好
- 可以用 **manager** 當 **NFS-Server**,worker 使用 NFS-Client 存取 manager 上的資料夾
### 用虛擬機建立 MPI Cluster
所以如果要用多台虛擬機上搭建一個 MPI Cluster,需要
1. 建立多台虛擬機,安裝好 Linux
2. 設定網卡
- 為了方便設定和操作,網卡設定要符合以下條件
- 為了可以下載套件,要能連上 Internet
- 讓所有機器連上同個 LAN,並使用靜態 IP
- 本機也可以連上所有虛擬機 (即本機也在同個 LAN)
- 推薦的設定是每台機器都用兩張網卡,分別是 . . .
- **NAT**:
- 保證機器可以連上 Internet
- 因為只是拿來上網,所以可以直接選用 DHCP
- **VirtualBox Host-Only** (僅限主機介面卡)
- 雖然 NAT 已經可以讓虛擬機互通,但是外部機器 (包括本機) 沒辦法連上
- 如果使用 Host-Only 就可以讓本機可以連上虛擬機,所以我會在這張網卡設定靜態 IP
- 為了方便,可以編輯 */etc/hosts*,設定其他幾台機器的名稱和對應的 IP address
3. 安裝必要套件
- **編譯環境**
- **GCC**: build-essential (gcc, g++, gfortran, make)
- **OpenMPI**: openmpi-bin, libopenmpi-dev, openmpi-doc
- **SSH**
- **Open-SSH**: openssh-client, openssh-server
- **NFS**
- **Server** (裝在 Manager): nfs-kernel-server
- **Client** (裝在 Worker): nfs-common
- 推薦套件
- **ifconfig**: net-tools
4. 設定 SSH 和 NFS
- **SSH**
- Manager 必須要能 [無密碼登入](https://dywang.csie.cyut.edu.tw/dywang/security/node84.html) 其他 nodes
- 要在 manager 上產生一組 RSA Key,並複製給其他 nodes
- **NFS**
- 在 manager 上設定要共享出去的資料夾路徑
- 在 workers 上掛載 manager 共享的資料夾
## 建立 MPI Cluster - 使用 Virtualbox 及 Ubuntu Server
- 建立共有三個 nodes 的 cluster
- 一個 manager node
- 兩個 worker nodes
- 建立虛擬機時,可以同時啟動三台虛擬機進行 Linux 安裝
- 也可以先裝好一台,再用複製的方式,複製兩台虛擬機,減少下載套件的時間
:::info
**關於 Linux 發行版**
- 不管使用哪種 Linux 發行版都可以執行 OpenMPI,可以用自己慣用的就好
- 我用 Ubuntu 是因為我自己對 Ubuntu 比較熟
- 用 Server 版是因為它佔用的額外資源比較少 (沒有 GUI)
:::
### 建立多部虛擬機 並安裝 Linux
- 下載 Linux ISO Image,我使用的是 Ubuntu Server
https://ubuntu.com/download/server
- 使用 Virtualbox 建立虛擬機
- 因為筆電資源有限,可以選擇兩顆核心和 2G 記憶體就行

- 啟用兩張網卡
- 選擇要使用的機器,按 "設定"

- 選擇網路

- 點 "介面卡1",勾選 "啟用網路卡",並選擇附加到 **NAT**

- 點 "介面卡2",勾選 "啟用網路卡",並選擇附加到 **Host-only** (僅限主機介面卡)

- 按確定儲存設定
- 安裝 Ubuntu Server
- 安裝時不需要特別的設定,使用預設選項就行
- 設定 Server's name (hostname) 時盡量讓每台機器有不同的名稱,以利辨識
- 名稱沒有限制,方便自己辨識就行,我使用的名稱是 *mpi-mgr*, *mpi-w0*, *mpi-w1*
- 不過 Hostname 也可以在安裝完成後修改,如果採用先裝好一台再複製的方法,可以在複製機器後修改

- 安裝過程中可以勾選安裝 Open-ssh server
- 如果沒裝到也沒關係,之後可以透過指令安裝

> 裝好 OS 之後就可以先關機,然後複製出其他幾台機器
> 也可以先安裝好所有 nodes 都必要的套件,再開始複製
### 安裝必要套件
**共同套件**
- 列出的套件是所有 nodes 上都需要的套件
- 下面指令可以一次安裝所有必要套件
- 如果怕出錯可以一個一個分開安裝
```bash
sudo apt install \
build-essential \
libopenmpi-dev openmpi-doc \
openssh-client openssh-server \
net-tools
```
> 如果採用先裝一台,再複製出另外兩台的作法
> 可以在安裝完以上套件後就開始複製
**Manager**
- 安裝 NFS server
- 只需要安裝在 Manager
```bash
sudo apt install nfs-kernel-server
```
**Workers**
- 安裝 NFS Client
- 需要在所有 Worker 上安裝
```bash
sudo apt install nfs-common
```
> Ubuntu 使用的套件管理工具是 `apt`
> 如果使用其他 Linux 發行版,使用的工具可能不同 (例: CentOS 使用 `yum`)
> 把 `apt` 換成對應管理工具的名稱就可以正常執行
### 設定 Hostname
- 如果採用複製的方式,所有機器上的 hostname 會相同,不利於辨識
- 編輯 */etc/hostname* 設定檔,可以修改 hostname
```
sudo vim /etc/hostname
或
sudo nano /etc/hostname
```
- 重新登入後可以看到 hostname 已經改變
- Hostname 沒有特定規則,只要能讓自己方便辨識就好,我使用的是
- Manager: *mpi-mgr*
- Worker: *mpi-w0*, *mpi-w1*
### 設定網卡
- 下 `ifconfig`,查詢目前的網卡資訊

- 通常 192.168 開頭的就是 Host-Only
- 這邊我拿到的 IP address 是 192.168.34.125,遮罩是 255.255.255.0
- 代表使用的子網路是 192.168.34.0/24
- 每台電腦上的設定不一定相同 (不一定也是 192.168.34.0),操作時要依照自己的實際情況調整
- 記下自己網卡的名稱和子網路,以我的為例
- 網卡名稱: enp0s8
- 子網路: 192.168.34.0/24
:::info
- 根據我的子網路,我能使用的 IP address 必須是 192.168.34 開頭,也就是 192.168.34.XX
- XX 可以自行決定,只要每一台機器不重複就可以
- 但子網路中,通常有幾個位置是保留做特殊用途的,所以 XX 盡量避開
- 0, 1, 100, 255
- 且盡量使用 100 以下的數字 (2~99)
- 我使用的設定如下
- `mpi-mgr`: 192.168.34.40
- `mpi-w0`: 192.168.34.41
- `mpi-w1`: 192.168.34.42
:::
<!-- :::info
**IP address 設定**
- 設定 IP address 時需要注意子網路的位置,如果設置錯誤則會無法上網或連線到其他 nodes
- 以我的子網路為例,192.168.34.0/24 代表該網路上的 IP address 只能是 192.168.34.XX
- 且通常來說,有以下幾個位置不能使用
- 192.168.34.0: 用來代表子網路本身
- 192.168.34.1: 應該會被本機使用
- 192.168.34.255: 用來廣播
- 192.168.34.100: 被 DHCP 伺服器使用
- 另外 192.168.34.101~254 通常會被分配給使用 DHCP 的機器
- 如果你現在有多台機器在啟動中,可能有某台機器正在使用該範圍的號碼
-
::: -->
- 編輯 */etc/netplan/00-installer-config.yaml*
- 可以使用任何編輯器,例如 `nano` 或 `vim`
- 因為是系統設定檔,最前面記得加 `sudo`
```
sudo nano /etc/netplan/00-installer-config.yaml
或
sudo vim /etc/netplan/00-installer-config.yaml
```
- 原先內容會類似這樣
```py
# This is the network config written by 'subiquity'
network:
ethernets:
enp0s3:
dhcp4: true
enp0s8:
dhcp4: true
version: 2
```
- 刪除 Host-Only 的 dhcp 設定,並加入靜態 IP 設定
- 靜態 IP 的格式是
```py
addresses: [ address1, address2, ... ]
```
- 如果要把 IP 設定為 192.168.34.40
```py
addresses: [ 192.168.34.40/24 ]
```
- 完整檔案如下
```py
# This is the network config written by 'subiquity'
network:
ethernets:
enp0s3:
dhcp4: true
enp0s8:
addresses: [192.168.34.40/24]
version: 2
```
- 存檔後,執行下面指令,啟用新的設定
```
sudo netplan apply
```
- 下 `ifconfig` 確認是否拿到正確 IP
- 按照上述步驟,分別設定好每一個 nodes 上的靜態 IP
- 使用 `ping` 可以檢查設定是否成功
- 用 *mpi-mgr*,ping *mpi-w0*, *mpi-w1*
```
ping 192.168.34.41
ping 192.168.34.42
```
- 最好在本機上也嘗試看看能不能 ping 到虛擬機
### 編輯 hosts 檔案
- */etc/hosts* 是 Linux 系統中的設定檔,可以紀錄機器的 IP 和對應的名稱
- 設定好後,就可以用名稱代替該機器的 IP,方便設定和維護
- 設定檔格式如下
```
<ip_address> <name>
```
- 在 */etc/hosts* 加入以下內容
```
192.168.34.40 mpi-mgr
192.168.34.41 mpi-w0
192.168.34.42 mpi-w1
```
- 就可以在指令中用名稱取代 IP
```
ping mpi-w0
等同
ping 192.168.34.41
```
- 在所有 nodes 都進行以上設定
### 設定 SSH
- 在 manager 中,嘗試用 ssh 登入其他 nodes
```
ssh user@mpi-w0
```
- user 要換成你的使用者名稱,user 必須是存在於 *mpi-w0* 的使用者
- 如果連線成功,先下 `logout` 登出,切換回 manager
- 在 manager,切換到 *~*,建立目錄 *.ssh*,並切換進去
```
cd ~
sudo mkdir .ssh
cd .ssh
```
- *.ssh* 是隱藏資料夾,直接下 `ls` 是看不到的
- 用 `ls -a` 可以顯示隱藏資料夾
- *.ssh* 可能已經存在,可以直接使用,不需要刪除
- 產生 RSA Key,並把產出的檔案 *id_rsa.pub*,改名為 *authorized_keys*
```
ssh-keygen -t rsa
cp id_rsa.pub authorized_keys
```
- 產生 RSA Key 時,會詢問一些資訊
- 可以全部使用預設,一直按 enter 就好
- 把 RSA key 複製給所有 worker
```
ssh-copy-id user@mpi-w0
ssh-copy-id user@mpi-w1
.
.
.
```
- 再次使用 ssh 連線到其他機器
```
ssh user@mpi-w0
```
- 如果不須要密碼就表示設定成功
### 設定 NFS
- 在 manager 中,建立一個目錄,並修改權限,用來和所有 nodes 共享
```
cd ~
mkdir mpi
chmod 777 mpi
```
- 目錄名稱及位置可以自行決定,不一定要和我一樣
- 進入剛建立的目錄,下 `pwd` 取得完整路徑並複製
```
cd mpi
pwd
```
- 以我的為例,目錄的路徑是
```
/home/pj/mpi
```
- 編輯 NFS 設定檔,把剛剛的目錄共享給其他 nodes
```
sudo nano /etc/exports
```
- 設定檔格式如下
```
<目錄位置> <host> (<權限>)
```
- host 可以是 IP 或是 hostname
- 加入以下內容
```
/home/pj/mpi mpi-w0(rw,sync,no_subtree_check)
/home/pj/mpi mpi-w1(rw,sync,no_subtree_check)
```
- 路徑要記得換成你自己的
- 重新載入設定檔,並重啟服務
```
sudo exportfs -a
sudo systemctl restart nfs-kernel-server
```
- 在所有 **worker** 中也建立一個目錄,用來連接 manager 上的共享目錄
- 目錄不一定要用相同路徑
```
cd ~
mkdir mpi
```
- 把目錄掛載到 manager 的共享資料夾
```
sudo mount mpi-mgr:/home/pj/mpi ~/mpi
```
- 路徑記得換成你自己的
- 在目錄中加入一個檔案,用其他機器查看該目錄中是否有出現新增的檔案
- 在 manager 新增一個檔案到共用目錄中
```
touch ~/mpi/test_file
```
- 在 worker 檢查共用目錄,是否有剛剛新增的檔案
```
cd ~/mpi
ls
```
- 目前的設定,只要 **重新開機** 目錄就 **不會掛載**
- 如果要 **開機就自動掛載**,要修改 worker 上的 */etc/fstab*
```
sudo nano /etc/fstab
```
- 加入以下內容,路徑要記得修改
```
mpi-mgr:/home/pj/mpi /home/pj/mpi nfs defaults 0 0
```
## 執行 MPI 程式
- 在 Manager,切換到共用目錄
```
cd ~/mpi
```
- 新增一個檔案,儲存所有 nodes 的 hostname
- 檔案名稱、副檔名不限,我把檔案命名成 *hosts.list*
- 檔案內容如下
```
mpi-mgr
mpi-w0
mpi-w1
```
- 新增一個 C 程式碼檔案,貼上下列程式碼
- 名稱不限,我命名為 *hello_mpi.c*
```c
#include <mpi.h>
#include <stdio.h>
int main(int argc, char** argv) {
// Initialize the MPI environment
MPI_Init(NULL, NULL);
// Get the number of processes
int world_size;
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
// Get the rank of the process
int world_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
// Get the name of the processor
char processor_name[MPI_MAX_PROCESSOR_NAME];
int name_len;
MPI_Get_processor_name(processor_name, &name_len);
// Print off a hello world message
printf("Hello world from processor %s, rank %d out of %d processors\n",
processor_name, world_rank, world_size);
// Finalize the MPI environment.
MPI_Finalize();
return 0;
}
```
- 用 `mpicc` 編譯,使用方式和 `gcc` 相同
```
mpicc hello_mpi.c -o hello_mpi
```
- 用 `mpirun` 執行編譯好的檔案,不加任何選項會只在目前機器上執行
```
mpirun hello_mpi
```
**Output**
```
Hello world from processor mpi-mgr, rank 0 out of 2 processors
Hello world from processor mpi-mgr, rank 1 out of 2 processors
```
- 加上 `--hostfile <host_file>`,程式會執行在 host_file 中列出的所有機器上
- 這邊 host_file 就可以是剛剛寫的 *hosts.list*
```
mpirun --hostfile ./hosts.list hello_mpi
```
**Output**
```
Hello world from processor mpi-mgr, rank 0 out of 6 processors
Hello world from processor mpi-mgr, rank 1 out of 6 processors
Hello world from processor mpi-w0, rank 2 out of 6 processors
Hello world from processor mpi-w0, rank 3 out of 6 processors
Hello world from processor mpi-w1, rank 4 out of 6 processors
Hello world from processor mpi-w1, rank 5 out of 6 processors
```