Try   HackMD
tags: tutorials docker ubuntu linux cuda GPU NVIDIA python deep learning AI

Docker 使用教學

更多資訊請參考 https://docs.docker.com/reference/

檢視

  • 檢視所有 image
    ​​​​$ docker images
  • 檢視所有 container
    ​​​​$ docker container ls -a
    ​​​​$ docker ps -a
  • 檢視所有正在執行的 container
    ​​​​$ docker container ls
    ​​​​$ docker ps

停止與刪除

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 代表只用指定的 02 號 GPU,或是使用 all 代表使用全部 GPU

    使用時是 --gpus device=0,2 (等價於 --gpus 0,2)來指定只使用 02 號 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

  • 將 conatiner 外的檔案(或資料夾)複製進 container 內

    ​​​​$ docker cp {local_path} {containerID_or_name}:{container_path}
    • {local_path}: 要複製進 container 的外部 local 檔案路徑
    • {containerID_or_name}: 要複製進的 container ID 或名字
    • {container_path}: 複製檔案到的 container 內的指定路徑
  • 重新命名 container

    ​​​​$ docker rename {containerID_or_name} {new_container_name}
    • {containerID_or_name}: 要重新命名的 container ID 或名字
    • {new_container_name}: container 的指定名字
  • 重新啟動 container

    ​​​​$ docker restart {containerID_or_name}
    • {containerID_or_name}: 要重啟的 container ID 或名字
  • 用現有的 container 建立 image

    ​​​​$ docker commit {containerID_or_name} {image_repository}:{image_tag}
    • {containerID_or_name}: 現有的 container ID 或名字
    • {image_repository}: image repository
    • {image_tag}: image tag
  • 將 image 存成檔案

    ​​​​docker save -o {path_for_generated_tar_file} {image_repository}:{image_tag}
    • {path_for_generated_tar_file}: 存放檔案的 path
    • {image_repository}: image repository
    • {image_tag}: image tag
  • 將 image 檔案讀取成 image

    ​​​​docker load -i {path_to_image_tar_file}
    • {path_to_image_tar_file}: image 檔案的 path

More Usage

tags: tutorials ubuntu linux SSH docker

建立一個可以 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
    1. 修改 port
      文件的前幾行有個 Port 可以設定,預設是 22

      這個 port 是對應到上述{container_ssh_port}

    2. 修改授權
      將文件中的 PasswordAuthentication XXX 取消註解並修改成 PasswordAuthentication yes
    3. 啟用 root 登入授權
      將文件中的 PermitRootLogin XXX 取消註解並修改成 PermitRootLogin yes
    4. 設定使用者密碼
      因為 container 預設使用者是 root,而我們也想讓之後連進來的權限為 root,但又不想讓任何知道 ip 和 port 的外部人員可以任意連進來,所以需要設定 container 內 root 的密碼
      ​​​​​​​​$ passwd root
    5. 重啟 container
      設完密碼後需要重啟 container 才會生效
      • 離開 container
      ​​​​​​​​$ exit
      • 在 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}


tags: tutorials ubuntu linux docker python

Docker 使用 tensorboardjupyter

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

    jupter 預設 port 為 8888

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)

tags: tutorials docker ubuntu linux cuda GPU NVIDIA python deep learning AI

Docker 中使用 GUI

  1. run container 時加入 DISPLAYXAUTHORITY 環境變數,設定 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 連線方式,
      • host: 和主機使用相同連線方式
  2. 進入 container 後,使用 su {user_name} 切換為外部使用者 (會在 container 內開啟新 shell)

    如果一開始 container 沒有外部使用者,則需要照 container 外面(local)的 $USER 跟 $UID, 在 docker 裡面也建立一個同名同代號的 user: useradd -m -s /bin/bash -u {$UID} {$USER}


tags: issue docker

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

如果遇到 Could NOT find CUDA (missing: CUDA_TOOLKIT_ROOT_DIR CUDA_NVCC_EXECUTABLE CUDA_INCLUDE_DIRS CUDA_CUDART_LIBRARY)

錯誤說明

因為是使用 runtime 的輕量執行版本,沒有指定的 NVCC 套件

解決方式

有 2 種方式:

  1. 安裝 nvidia-cuda-toolkit (不推薦,安裝 devel 才是比較好的解決方式)
    ​​​​$ apt-get update && apt-get install nvidia-cuda-toolkit
  2. 使用 nvidia-dockerdevel 版本 (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
  • 重新啟動 docker
$ sudo systemctl restart docker