<!-- 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 記憶體就行 ![](https://hackmd.io/_uploads/BJaV5vs5n.png) - 啟用兩張網卡 - 選擇要使用的機器,按 "設定" ![](https://hackmd.io/_uploads/Skh55wscn.png) - 選擇網路 ![](https://hackmd.io/_uploads/ByxkjPic3.png) - 點 "介面卡1",勾選 "啟用網路卡",並選擇附加到 **NAT** ![](https://hackmd.io/_uploads/rkQAjvj5n.png) - 點 "介面卡2",勾選 "啟用網路卡",並選擇附加到 **Host-only** (僅限主機介面卡) ![](https://hackmd.io/_uploads/H1Scjvjcn.png) - 按確定儲存設定 - 安裝 Ubuntu Server - 安裝時不需要特別的設定,使用預設選項就行 - 設定 Server's name (hostname) 時盡量讓每台機器有不同的名稱,以利辨識 - 名稱沒有限制,方便自己辨識就行,我使用的名稱是 *mpi-mgr*, *mpi-w0*, *mpi-w1* - 不過 Hostname 也可以在安裝完成後修改,如果採用先裝好一台再複製的方法,可以在複製機器後修改 ![](https://hackmd.io/_uploads/H1HvAwj9n.png) - 安裝過程中可以勾選安裝 Open-ssh server - 如果沒裝到也沒關係,之後可以透過指令安裝 ![](https://hackmd.io/_uploads/HJsZaPiqn.png) > 裝好 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`,查詢目前的網卡資訊 ![](https://hackmd.io/_uploads/SkjJSdic2.png) - 通常 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 ```