# Docker安裝與使用步驟
ver_2024.03.24
modified_2024.09.08 - LEE
## Server安裝
#### 1. 安裝Docker-Engine
在Server上先安裝Docker-Engine,照著網站步驟Install using the apt repository安裝
[[連結]](<https://docs.docker.com/engine/install/ubuntu/>)
#### 2. 安裝Nvidia的套件
安裝Nvidia的套件才能在container中使用到GPU資源,照著網站的步驟,install with apt安裝即可
[[連結]](<https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html>)
**<font color=red>!!!安裝完server記得要reboot!!!</font>**
```
重啟指令二選一
sudo systemctl restart docker
sudo service docker restart
```
## 來建立並使用Docker吧
#### 1. Pull Image: 下載前人建好的Image
Docker Hub中有很多前人創建的Image File,在網站搜尋下載合用的就對了。在Tag中會有不同的版本跟檔案,這次使用的nvidia/cuda的ubuntu20.04有兩種
選有devel那個12.3.1-devel-ubuntu20.04並pull下來
(titan53 我選 11.6 的版本 - 易修)
>devel : Develope開發者版本,套件完整
>runtime : 執行版本,環境中套件比較簡略
[[連結]](<https://hub.docker.com/r/nvidia/cuda/tags?page=1&name=ubuntu20.4>)
```
sudo docker pull nvidia/cuda:12.3.1-devel-ubuntu20.04
```
查看目前有哪些已經pull下來的Image可以使用
```
sudo docker images
```
刪掉不要的Image
```
sudo docker rmi {ImageID}
```
#### 2. 建立Container
利用剛剛pull的Image建立container
```
sudo docker run -it -p 8888:9999 --gpus all --name {要取的名子} {ImageName:ImageTag}
or
sudo docker run -it -p 8888:9999 --gpus all --name {要取的名子} {ImageID}
or
sudo docker run -it -p 8888:9999 --shm-size="1g" --gpus all --name {要取的名子} {ImageID}
```
`-i`互動式介面
`-t` 終端介面(terminal)
`-p` 設定連結的port
`--shm-size` 設定share memory, 預設為64MB
<font color=red>注意: 8888換成要對外連線的port,9999換成對內連線的port</font>
如果要連結host的資料夾與container的資料夾使用`-v`參數
<font color=red>注意: 硬碟內的folder記得不能重複,不然container會互相干擾</font>
```
docker run -v /host/user/data1:/mnt/data1 -it your_docker_image
```
列出目前所有container, 確認container建立成功
```
sudo docker ps -a
```
#### 3. 啟用剛建立的Container
```
sudo docker start {已建立的容器名子}
```
#### 4. 進入Container
```
sudo docker attach {正在執行的容器名子}
```
#### 5. 退出容器,但不關閉(會在背景執行)
```
crtl+P後ctrl+Q
```
#### 6. 這個container我不要了!!
第一步要先停止container的執行
```
sudo docker stop {容器名子}
```
再來移除他
```
sudo docker rm {容器名子}
```
#### 7. 查看container們的使用情形
```
sudo docker stats
```
# Docker的進階使用
## Image的管理與轉移
#### 1. commit : 將建立或是修改過的容器轉換成Image
產生的新image會原封不動紀錄原本的系統,包含環境設定、硬體、檔案...
<font color = 'red'>注意 : 如果有使用docker -v,掛載到硬碟上的東西不會被記錄</font>
利用`sudo docker images`可以查看新產生的Image
```
sudo docker commit {舊容器ID} {新IMAGE名稱}:{IMAGE_TAG}
```
#### 2. save : 將Image檔案匯出成tar方便使用隨身碟轉換
```
sudo docker save -o {取名子}.tar {ImageName:Tag}
```
#### 3. load : 複製到指定裝置後如何安裝Image.tar檔
```
sudo docker load -i {ImageName}.tar
```
## DockerFile與Entrypoint
簡單來說,DockerFile是指定Container建立過程的指令檔
可以將額外功能或指令包裝進Image中
#### 1. 首先要先準備好Dockerfile,基本範例如下
!!!注意,Dockerfile檔名必須為Dockerfile!!!
```
FROM ubuntu:20.04
RUN apt-get update && apt-get upgrade
```
> `FROM`代表要使用的image,`RUN`讓我們在建立的container中執行的shell指令
#### 2. 接著用這個Dockerfile建立image吧
```
sudo docker build -t {產生的Image名稱} .
```
> `-t`代表target後面加上image的名稱,`.`是Dockerfile所在位置
#### 3. 成功建立Image
成功建立image可以使用`sudo docker images`查看,並使用`sudo docker run`建立容器
#### 4. 那什麼是Entrypoint呢......
只有Dockerfile是不夠的,如果想客製化每個容器,可以透過使用`ENTRYPOINT`指定創建容器時執行特定bash檔,並且利用`-e`傳入環境變數提供bash檔使用
```
FROM nvidia-11.4.3-ssh:latest
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
```
創建容器時傳入bash檔`entrypoint.sh`對應的環境變數
```
docker run -it --name {容器名} -e {環境變數名}={想傳入變數} {ImageID}
```
# 常見問題
## 從 Putty 連進 Container 的前置作業
#### 1. 在 container 裡面裝好 vim 以及 ssh
```
apt install vim -y
apt install openssh-server -y
```
> `-y`不用在`yes/no`時輸入 => 直接`yes`
#### 2. 於 container 內更改參數
```
vim /etc/ssh/sshd_config
```
文件內更改 3 項後存檔:
```
取消註解 PORT XX
PermitRootLogin yes
PasswordAuthentication yes
```
#### 3. 設置密碼
```
passwd
```
#### 4. 將 ssh 重啟
```
/etc/init.d/ssh restart
```
<font color = 'red'> 完成! </font>
## 在連線時的環境變數問題
舉個栗子🌰, 你要使用pytorch, 開開心心的pull一個官方image下來, 然後建立好container, 接著你在裡面安裝了ssh, 然後在外部透過putty連進來後發現, 無法執行python,
```
python hello.py
# -bash: command not found: python.
```
這時候你就懷疑自我了, 剛剛我用docker attach {container_id}進去之後, 明明可以順利執行阿?
這邊要提到的是, 用putty連線linux時, 系統會預設去執行/etc/profile, 所以你要做的事情是把python的位置加入。
在使用attach進入container的本地端可以利用下面指令取得python路徑
```
which python
```
首先, 把python的位置加入環境變數,
```
export PATH={/opt/conda/lib/}:$PATH // {裡面放python的路徑}
```
然後, 在一個地方創建env.txt, 把環境變數存入
```
env > env.txt
```
接著,把所有在env.txt裡面的字, 複製到/etc/profile裡面(注藥要保留原本的內容, 貼在後面就好)
最後, source它
```
source /etc/profile
```
之後你用putty連進container也可以找到正確的python了!
另解如下
```
1. 在本地端找到python路徑
which python
2. 在~/.bashrc最後加入連結路徑
export PATH={/opt/conda/lib/}:$PATH
3. 啟動bashrc
source ~/.bashrc
```
> ./bashrc是一個在用戶登入時會執行的設定檔,因此我們將python執行路徑加入就會在登入同時重新連結
> 不能運作的原因,有可能是透過遠端連線使用root登入時,會執行的設定檔為`/root/.profile`,若檔案不存在可以自行建立,內容為`source ~/.bashrc`,就能正常呼叫.bashrc設定檔了
## container無法聯網
- container中無法順利聯網,連`apt update`都不行 >> 重啟docker服務
```
sudo systemctl restart docker
```
## 其他
#### 學習資源
[[開源電子書 Docker--從入門到實踐]](<https://philipzheng.gitbook.io/docker_practice/>)
#### 筆記: 創立平行計算docker指令
```
sudo docker run -it -p 8888:22 -e USERNAME=test123 --gpus all --name test123 my-ubuntu:latest
```
> `docker run`的時候,`--rm` 退出將會自動刪除容器,對於測試階段相當好用
#### 紀錄使用過的Image hub
- cnstark/pytorch映像檔不錯可以直接使用torch和GPU
```
sudo docker pull cnstark/pytorch:2.0.0-py3.9.12-cuda11.8.0-devel-ubuntu22.04
```
#### commit vs. build 傻傻分不清楚
- commit : 從container創建Image
- build : 透過Dockerfile為Image添加資訊後建立新的Image
> GPT:
簡而言之,Dockerfile中的指令主要用於建立image,而不是在每次attach container時執行。 若要在container運行時執行特定命令,可以使用docker exec。
<!--
### 未整理與待解決
... -->