Docker 使用教學
檢視
- 檢視所有 image
- 檢視所有 container
$ docker container ls -a
或
- 檢視所有正在執行的 container
$ docker container ls
或
停止與刪除
Container
-
停止 container
$ docker stop {containerID(s)}
-
強制停止 container
$ docker kill {containerID(s)}
-
刪除 container (PS: container 要是處於停止
狀態)
$ docker rm {containerID(s)}
-
刪除所有已停止的 container
$ docker container prune
-
停止全部 container (Not recommended)
$ docker stop $(docker ps -aq)
-
刪除全部 container (Not recommended)
$ docker rm $(docker ps -aq)
-
強制刪除全部 container (Not recommended)
$ docker rm -f $(docker ps -aq)
docker ps -q
是只列出 container ID
{containerID(s)} 不用全打,只要打到不會和其他 ID 重複的部分就可以抓的到了
Image
-
刪除 image (PS: 要完全沒有任何 container 使用該 image)
$ docker rmi {imageID(s)}
-
刪除所有沒在用的 image
$ docker image prune -a
-
刪除沒有 tag 的 image (Not recommended)
$ docker rmi $(docker images -f "dangling=true" -q)
-
刪除全部 image (Not recommended)
$ docker rmi $(docker images -q)
docker images -q
是只列出 image ID
在 rmi
後加入 -f
是強制刪除,一樣也是不建議使用
{image_ID(s)} 不用全打,只要打到不會和其他 ID 重複的部分就可以抓的到了
建立與執行
Create image
$ docker build -t {image_repository}:{image_tag} {daemon_path} -f {path_to_dockfile} --build-arg {ARG_param_name}={ARG_value}
-t
: 也可以寫成 --tag
,給創立的 image tag name
- {image_repository}: image repository
- {image_tag}: image tag
- {daemon_path}: 如果
Dockerfile
有些檔案是從外部找會從該路徑下找 (一般很少用,所以通常都是用 .
)
-f
: 也可以寫成 --file
,用來指定要用哪個路徑下的 Dockerfile
來建立 image,Dockerfile
名字可以取其他名字
- {path_to_dockfile}:
Dockerfile
的路徑
--build-arg
: 由外部給予內部 ARG
值,如果要給多個值,每個都要給前綴 --build-arg
,不能只寫一次給多個 ARG
如果在只有 Dockerfile
的路徑下,且 Dockerfile
的名字也是叫 Dockerfile
,可以不用加上 -f
有關 Dockerfile
的相關寫法可以看 Dockerfile 建立教學
Run container
舊版(docker-ce < 19.03)
使用 nvidia-docker run container
$ nvidia-docker run -it -h {container_hostname} --name {container_name} -v {local_path_to_volume}:{container_path} --shm-size {shm_size} -d {imageID_or_repo:Tag}
-it
: 代表在執行 Docker 虛擬容器環境時,開啟虛擬終端機,以互動的模式執行
-h
: 指定 container hostname
--name
: 指定該 container 名字
- {container_name}: 指定的 container 名字
--shm-size
: 指定該 container 可以使用的 shared memerory
- {shm_size}: 指定的 shared memerory 大小,如
1G
, 4G
如果 shared memerory 不足會出現 ERROR: Unexpected bus error encountered in worker. This might be caused by insufficient shared memory (shm).
-d
: 背景執行 container
- {imageID_or_repo:Tag}: 用指定 image 來跑 container,可以用 image 的 id 或是 {image_repository}:{image_tag}
-v
: 執行 container 時,把目前的某個位置掛載到 container 中
- {local_path_to_volume}: 要掛載進 container 的外部 local 絕對路徑
- {container_path}: 掛載進 container 內的指定路徑 (預設路徑是在
/
下,可以使用相對於 /
的相對路徑,或是絕對路徑,若是資料夾不存在則會自動建立資料夾)
掛載多個資料夾則要多加幾個 -v
像是 -v /on/my/host/1:/on/the/container/1 -v /on/my/host/2:/on/the/container/2
更多資訊請看這裡
新版 (docker-ce >= 19.03)
$ docker run -it --gpus {gpu_cond} -h {container_hostname} --name {container_name} -v {local_path_to_volume}:{container_path} --shm-size {shm_size} -d {imageID_or_repo:Tag}
-it
: 代表在執行 Docker 虛擬容器環境時,開啟虛擬終端機,以互動的模式執行
--gpus
: 指定要使用的 GPU
- {gpu_cond}: 常用的是
device=0,2
代表只用指定的 0
和 2
號 GPU,或是使用 all
代表使用全部 GPU
使用時是 --gpus device=0,2
(等價於 --gpus 0,2
)來指定只使用 0
和 2
號 GPU,或是使用 --gpus all
使用全部 GPU
-h
: 指定 container hostname,一般是會顯示於 bash 中的 {user_name}@{host_name}:{path}#
的 {host_name}
中
--name
: 指定該 container 名字
- {container_name}: 指定的 container 名字
--shm-size
: 指定該 container 可以使用的 shared memerory
- {shm_size}: 指定的 shared memerory 大小,如
1G
, 4G
如果 shared memerory 不足會出現 ERROR: Unexpected bus error encountered in worker. This might be caused by insufficient shared memory (shm).
-d
: 背景執行 container
- {imageID_or_repo:Tag}: 用指定 image 來跑 container,可以用 image 的 id 或是 {image_repository}:{image_tag}
-v
: 執行 container 時,把目前的某個位置掛載到 container 中
- {local_path_to_volume}: 要掛載進 container 的外部 local 絕對路徑
- {container_path}: 掛載進 container 內的指定路徑 (預設路徑是在
/
下,可以使用相對於 /
的相對路徑,或是絕對路徑,若是資料夾不存在則會自動建立資料夾)
掛載多個資料夾則要多加幾個 -v
像是 -v /on/my/host/1:/on/the/container/1 -v /on/my/host/2:/on/the/container/2
更多資訊請看這裡
Execute container
$ docker exec -it {containerID_or_name} {run_cmd}
-it
: 代表在執行 Docker 虛擬容器環境時,開啟虛擬終端機,以互動的模式執行
- {containerID_or_name}: 指定執行的 container ID 或名字
- {run_cmd}: 執行 container 時自動執行的 command,一般不需要設就寫
bash
就好
更多資訊請看這裡
More command
More Usage
建立一個可以 SSH 連線的 docker container
可以透過
- 修改
Dockerfile
- 或是 建立好 container 後再進入 container 中安裝
By docker file
Add to Dockerfile
在 Dockerfile
最後 加入以下程式碼在最後片段
# ssh setting
RUN echo "$ssh_usr:$ssh_pwd" >> ~/passwdfile && \
chpasswd -c SHA512 < ~/passwdfile && \
rm ~/passwdfile && \
sed -i "s/#Port.*/Port 22/" /etc/ssh/sshd_config && \
sed -i "s/#PermitRootLogin.*/PermitRootLogin yes/" /etc/ssh/sshd_config && \
sed -i "s/#PasswordAuthentication.*/PasswordAuthentication yes/" /etc/ssh/sshd_config
# expose the port 22(which is the default port of ssh)
EXPOSE 22
# set entrypoint to restart ssh automatically
ENTRYPOINT service ssh restart && bash
預設會自動啟動 SSH
(藉由最後面的 ENTRYPOINT
)
Run container
在 docker run
contianer 時,加入 -p {remote_container_port}:{container_ssh_port}
來設定連到 container 的 port
-p
: 設定 local<->container 的 port mapping
- {remote_container_port}: 外部連線的 port
- {container_ssh_port}: container 內部的 SSH 設定 port
例如 docker run -it --name test -p 5678:22 -d test_img
就是用 test_img
的 image 建立一個名字叫做 test
的 container,並且將 container 外部(就是 local)的 5678 port 對應到 container 內部的 port 22
container 內部 port(在這個例子是 22) 必須要是 container 裡 SSH 設定的 port
更多關於 docker 使用可以看這篇
By install SSH after access to container
- Run container 的方式和上面一樣
- 進入 container
$ docker exec -it {containerID_or_name} bash
-it
: 代表在執行 Docker 虛擬容器環境時,開啟虛擬終端機,以互動的模式執行
- {containerID_or_name}: 指定執行的 container ID 或名字
以上面的例子來說就是 test
bash
: 因為要安裝 SSH 所以是以 bash 開啟
- 進入 container 後,安裝 SSH
$ apt-get update && apt-get install -y ssh
- 修改 container 內的 SSH config
$ vim /etc/ssh/sshd_config
- 修改 port
文件的前幾行有個 Port 可以設定,預設是 22
這個 port 是對應到上述的 {container_ssh_port}
- 修改授權
將文件中的 PasswordAuthentication XXX
取消註解並修改成 PasswordAuthentication yes
- 啟用 root 登入授權
將文件中的 PermitRootLogin XXX
取消註解並修改成 PermitRootLogin yes
- 設定使用者密碼
因為 container 預設使用者是 root,而我們也想讓之後連進來的權限為 root,但又不想讓任何知道 ip 和 port 的外部人員可以任意連進來,所以需要設定 container 內 root 的密碼
- 重啟 container
設完密碼後需要重啟 container 才會生效
- 在 container 外重啟 container
$ docker restart {containerID_or_name}
- 重新進入 container 並重啟 SSH 使其生效
- 進入 container
$ docker exec -it {containerID_or_name} bash
- 重啟 SSH
$ /etc/init.d/ssh restart
每次重新啟動 container 後,都必須得進入 container 重新開啟 SSH
更多關於 SSH 安裝與設定可以看這篇
SSH 連線進入 container
以 SSH 連線進剛剛設定的 container 的方式就是以和剛剛 server local 同樣 ip 連線,但 port 使用一開始設定的 {remote_contianer_port}
Docker 使用 tensorboard
和 jupyter
Run container
$ docker run -it -p 5566:6006 -p 7788:8888 -d docker_image
-p 5566:6006
: 將 container 內部的 6006
port 連到外部系統的 5566
port
tensorboard
預設 port 為 6006
-p 7788:8888
: 將 container 內部的 8888
port 連到外部系統的 7788
port
port 值當然都可以自己設定,不過不可使用 < 1024 的 privileged ports 或是已被使用的 port
更多 docker 使用方式請看這裡
Run tensorboard
- 啟動 tensorboard
$ tensorboard --logdir runs/ --host 0.0.0.0
--logdir
: 設定要顯示 tensorboard 的資料夾
--host
: 設定使用的主機網路
預設為 localhost
,也就是 127.0.0.1
,會是連到本機(container)的一個虛擬 IP,而設為 0.0.0.0
才可真的 mapping 到系統給的 port
- 進入
XXX.XXX.XXX:5566
XXX.XXX.XXX
: 外部主機實體 ip
5566
: 會 mapping 到 container 的 6006
port(也就是預設的 tensorboard port)
Run jupyter
- 啟動 jupyter
$ jupyter notebook --ip 0.0.0.0 --no-browser --allow-root
--ip
: 設定使用的主機網路
預設為 localhost
,也就是 127.0.0.1
,會是連到本機(container)的一個虛擬 IP,而設為 0.0.0.0
才可真的 mapping 到系統給的 port
--no-browser
: 不啟動瀏覽器
--allow-root
: 如果 container 內使用者為 root
則需要允許使用 root
開啟 jupyter
- 進入
XXX.XXX.XXX:5566
XXX.XXX.XXX
: 外部主機實體 ip
7788
: 會 mapping 到 container 的 8888
port(也就是預設的 jupyter port)
Docker 中使用 GUI
-
run container 時加入 DISPLAY
、 XAUTHORITY
環境變數,設定 net
參數,將 x server
相關檔案掛載進 container
docker run -it -h $(hostname) -e DISPLAY=$DISPLAY -e XAUTHORITY=/tmp/xauth -v ~/.Xauthority:/tmp/xauth -v /tmp/.X11-unix:/tmp/.X11-unix --net=host -d {imageID_or_repo:Tag}
-h
: 將 container 的 host 設為和 local 相同 (因為 ~/.Xauthority
會和主機名稱綁在一起)
-e
: 設定 container 環境變數指令
DISPLAY=$DISPLAY
: 將 container 內部的 DISPLAY
參數使用的顯示 port
設成和外部使用者同一個
XAUTHORITY=/tmp/xauth
: 將 container 內部 X server
授權文件的 XAUTHORITY
參數設成和外部使用者同一個顯示授權文件
-v
: 外部授權相關文件掛載進 container
- 將
~/.Xauthority
掛載進 container 的 /tmp/xauth
- 將
/tmp/.X11-unix
掛載進 container 的 /tmp/.X11-unix
--net
: 設定 container 連線方式,
-
進入 container 後,使用 su {user_name}
切換為外部使用者 (會在 container 內開啟新 shell)
如果一開始 container 沒有外部使用者,則需要照 container 外面(local)的 $USER 跟 $UID, 在 docker 裡面也建立一個同名同代號的 user: useradd -m -s /bin/bash -u {$UID} {$USER}
Docker Issue
檔案 / 資料夾權限
錯誤說明
container 內如果使用者群組和 container 外部使用者群組不同而造成離開 container 後沒有修改權限時使用以下指令修改擁有者和擁有者群組
解決方式
修改檔案
權限
$ sudo chown {owner_name}:{owner_group} {filename}
- chown: 更改擁有者的指令 (change owner 的縮寫)
- {owner_name}: 檔案的新擁有者
- {owner_group}: 檔案的新擁有者群組
- {filename}: 要變更權限的檔案
- 如果只要變更擁有者可以不用加後面的
:{owner_group}
- 如果只要變更擁有者群組只要寫
:{owner_group}
不用加前面的 {owner_name}
例如
$ sudo chown jimmy:jimmy_group file.txt
就是把 file.txt
這個檔案的擁有者改變為 jimmy
,並把擁有者群組改變為 jimmy_group
$ sudo chown jimmy file.txt
就是把 file.txt
這個檔案的擁有者改變為 jimmy
$ sudo chown :jimmy_group file.txt
就是把 file.txt
這個檔案的擁有者群組改變為 jimmy_group
,特別要注意前面的 :
不能少
修改 資料夾及以下所有檔案
權限
$ sudo chown -R {owner_name}:{owner_group} {folder_name}
- chown: 更改擁有者的指令 (change owner 的縮寫)
- {owner_name}: 資料夾的新擁有者
- {owner_group}: 資料夾的新擁有者群組
- {folder_name}: 要變更權限的資料夾
- 如果只要變更擁有者可以不用加後面的
:{owner_group}
- 如果只要變更擁有者群組只要寫
:{owner_group}
不用加前面的 {owner_name}
例如
$ sudo chown -R jimmy:jimmy_group folder
就是把 folder
這個資料夾的擁有者改變為 jimmy
,並把擁有者群組改變為 jimmy_group
$ sudo chown -R jimmy folder
就是把 folder
這個資料夾的擁有者改變為 jimmy
$ sudo chown -R :jimmy_group folder
就是把 folder
這個資料夾的擁有者群組改變為 jimmy_group
,特別要注意前面的 :
不能少
如果遇到 No CMAKE_CXX_COMPILER could be found.
錯誤說明
缺乏相依套件
解決方式
$ apt-get update && apt-get -y install build-essential
錯誤說明
因為是使用 runtime
的輕量執行版本,沒有指定的 NVCC 套件
解決方式
有 2 種方式:
- 安裝
nvidia-cuda-toolkit
(不推薦,安裝 devel
才是比較好的解決方式)
$ apt-get update && apt-get install nvidia-cuda-toolkit
- 使用
nvidia-docker
的 devel
版本 (Recommended)
如果安裝 opencv-python
出現問題
錯誤說明
缺乏相依套件
解決方式
$ apt-get update && apt-get install -y libgl1-mesa-glx libsm6 libxrender1 libxext-dev
如果更新 nvidia-smi
後執行 container 出現 Error response from daemon: Cannot restart container XXX: could not select device driver "" with capabilities: [[gpu]]
錯誤說明
此錯誤是因為更新 nvidia driver 後 docker 仍是套用之前舊的 nvidia driver,重新安裝 nvidia container toolkit 並重啟 docker 即可
解決方式
- 重新安裝
nvidia-container-toolkit
$ sudo apt-get update && sudo apt-get -y install nvidia-container-toolkit
$ sudo systemctl restart docker