docker日常使用指南

haoliuhust發表於2021-09-11

docker日常使用指南

前言

在對外提供應用程式時,常常碰到執行環境與開發環境不一致的情況,或雖然作業系統一樣,但部署時可能碰到一些系統庫版本不一樣導致執行問題。或對外提供庫時,沒有客戶需要的環境,需要花時間搭建虛擬機器。這一些坑,大部分情況下都能通過docker這個工具幫我們方便的解決。因此學習docker是比較有價值的,同時,docker的使用也非常的簡單,核心的命令比較少。本文對自己使用中用到的一些方式進行總結,一些理解可能不完全正確,更詳細的可以去看官方文件。

1.基礎知識

1.1 docker是什麼

docker是一種容器技術,是一種沙盒技術。它提供了一種非常遍歷的打包機制,這種機制直接打包了應用執行所需要的整個作業系統,從而能夠保證本地環境(開發環境)和生產環境(執行環境)的高度一致。
想了解docker更底層技術的,可以去擴充套件閱讀:Cgroups和Namespace技術
擴充套件閱讀2:daemon和client
image

1.2 與虛擬機器(VM)的區別

image
圖的左邊,畫出了虛擬機器的工作原理。其中,名為 Hypervisor 的軟體是虛擬機器最主要的部分。它通過硬體虛擬化功能,模擬出了執行一個作業系統需要的各種硬體,比如 CPU、記憶體、I/O 裝置等等。然後,它在這些虛擬的硬體上安裝了一個新的作業系統,即 Guest OS。

跟真實存在的虛擬機器不同,在使用 Docker 的時候,並沒有一個真正的“Docker 容器”執行在宿主機裡面。Docker 專案幫助使用者啟動的,還是原來的應用程式,只不過在建立這些程式時,Docker 為它們加上了各種各樣的 Namespace 引數。

總而言之:Docker其實共享了宿主機作業系統的核心,因此核心不同的情況下,不能很好的使用docker, 比如windows上想用linux的docker就比較麻煩。Docker相對於VM比較輕量。

1.3 映象與容器

映象與容器的關係有點像類與物件的關係,映象是一個靜態概念,容器是一個執行時概念,容器是映象的例項。通俗的講,映象就是放在硬碟上的,而容器是基於映象跑起來後的東西。

2.安裝

以下步驟預設宿主機為Ubuntu, 其它作業系統可以參考官方文件。
快捷安裝指令碼可通過文末的方式去獲得。

2.1 線上安裝

安裝docker: https://docs.docker.com/engine/install/ubuntu/

sudo apt-get update
sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

驗證安裝結果:

sudo docker run --rm hello-world

如果宿主機沒有顯示卡或者我們不使用顯示卡,到這一步就結束了安裝,如果要使用顯示卡,還需要進一步安裝 nvidia-docker2:
https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#install-guide

distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update && sudo apt-get install -y nvidia-docker2
sudo systemctl restart docker
tee /etc/docker/daemon.json <<-'EOF'
{
    "registry-mirrors": ["https://6kx4zyno.mirror.aliyuncs.com"],
    "exec-opts": ["native.cgroupdriver=systemd"],
    "default-runtime": "nvidia",
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}
EOF
 
sudo systemctl restart docker
sudo docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi 可用於驗證能否訪問GPU

2.2 離線安裝

對於宿主機不讓聯網的時候,可以進行離線安裝,也比較方便。
可以參考這篇總結:https://fanfuhan.github.io/2019/11/22/docker_based_use/

3.配置

此節總結幾個常見的可能修改的配置。大部分的配置都可以通過修改/etc/docker/daemon.json完成,也有例外。

3.1 映象儲存位置設定

docker映象預設佔用根目錄空間,有些時候根目錄空間不足,需要指定其他位置。
此時通過修改/etc/docker/daemon.json完成:

{
    "data_root":"myownpath", ### 儲存位置
    "default-runtime": "nvidia",
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}

執行命令使配置生效(修改daemon.json後都需要執行):

systemctl daemon-reload
systemctl restart docker

3.2 設定映象源

映象源是來獲取映象的地方,類似pip源,有時候官方源速度不行或根本訪問不了。這時候可以通過設定代理(下節)或設定映象源的方式去改善。

{
    "data_root":"myownpath", ### 儲存位置
     "registry-mirrors": ["https://6kx4zyno.mirror.aliyuncs.com"], ### 映象源,可以設定多個
    "default-runtime": "nvidia",
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}

3.3 代理設定

有些時候需要掛代理去下載程式碼或者映象。代理的設定分為對daemon的設定(主要影響映象下載)和client(影響的是容器執行中的)。

3.3.1 daemon代理

docker是一個C/S架構,我們執行的docker命令實際是一種客戶端,它會發起REST API到daemon(Server端),由daemon去拉取需要的映象。此節設定的就是daemon的代理。幾乎所有的daemon相關設定都可以在daemon.json中完成,但代理是個例外,這個設定需要建立:
/etc/systemd/system/docker.service.d/http-proxy.conf 檔案。

[Service]
Environment="HTTP_PROXY=http://proxy.example.com:80"
Environment="HTTPS_PROXY=https://proxy.example.com:443"
Environment="NO_PROXY=localhost,127.0.0.1,docker-registry.example.com,.corp"  ### 設定一些ip跳過代理

3.3.2 容器代理

建立~/.docker/config.json:

{
 "proxies":
 {
   "default":
   {
     "httpProxy": "http://192.168.1.12:3128",
     "httpsProxy": "http://192.168.1.12:3128",
     "noProxy": "*.test.example.com,.example2.com,127.0.0.0/8"
   }
 }
}

還有一種方式是在Dockerfile或啟動容器的時候設定環境變數:
image

4.docker的使用

4.1 基礎使用

4.1.1 啟動容器(docker run)

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

docker run有比較多的引數可以設定,完整的參見:https://docs.docker.com/engine/reference/commandline/run/
比較常用的是下面一些:

-d: 後臺執行容器,並返回容器ID;
-i: 以互動模式執行容器,通常與 -t 同時使用;
-P: 隨機埠對映,容器內部埠隨機對映到主機的埠
-p: 指定埠對映,格式為:主機(宿主)埠:容器埠
-t: 為容器重新分配一個偽輸入終端,通常與 -i 同時使用;
--name="nginx-lb": 為容器指定一個名稱;
--cpuset="0-2" or --cpuset="0,1,2": 繫結容器到指定CPU執行;
-m :設定容器使用記憶體最大值;
--net=bridge: 指定容器的網路連線型別,支援 bridge/host/none/container: 四種型別;
--expose=[]: 開放一個埠或一組埠;
--volume , -v: 繫結一個卷
--rm ,退出容器後刪除名字 
--restart ,重啟選項,有no/always/on-failure/unless-stopped
--entrypoint ,重寫容器程式的入口

比如我們執行sudo docker run -it --name test ubuntu:16.04就是以前臺互動形式,以ubuntu:16.04映象啟動一個容器,第一次執行大概會輸出下面這些內容:

Unable to find image 'ubuntu:16.04' locally
16.04: Pulling from library/ubuntu
58690f9b18fc: Pull complete 
b51569e7c507: Pull complete 
da8ef40b9eca: Pull complete 
fb15d46c38dc: Pull complete 
Digest: sha256:454054f5bbd571b088db25b662099c6c7b3f0cb78536a2077d54adc48f00cd68
Status: Downloaded newer image for ubuntu:16.04
root@d8324be2d956:/# 

也就是第一次執行時,會去拉取映象,然後啟動容器並進入容器終端(也可以通過docker pull 去自己拉取)。進入容器終端,我們就可以像普通終端一樣去安裝工具,編譯程式碼等等了。
而如果以後臺形式執行,則是:

sudo docker run -d --name test ubuntu:16.04
a64fea9800522c5347eaa9feb87ee6c9d67762d81519cc58dbdeec5a8a786066

容器將以後臺形式執行。那麼我們怎麼進入在後臺執行的容器呢?

4.1.2 進入容器

進入容器有2種方式,一種是attach, 一種是

docker exec -it 容器ID/名稱 bash

推薦使用這種。進入後,可輸入exit退出容器終端(只是退出終端,容器並不停止)

4.1.3 停止/刪除/啟動/重啟容器

docker stop 容器ID/名稱
docker rm 容器ID/名稱    ### 當容器發生重名時,我們就得刪除以前的或者把新的改名
docker start 容器ID/名稱
docker restart 容器ID/名稱

還是非常好理解的,基本上就是英文加容器即可。
還有一個docker update可用於修改docker run時指定的引數。
基本上學會以上操作,就能跑起來一個現場的容器,建立一個隔離的環境了。但僅僅有這些還不夠,因為一旦刪除容器了,我們在容器裡建立的內容都不在了。

4.2 進階使用

4.2.1 持久化(掛載主機硬碟)

啟動時通過-v 主機目錄:容器目錄選項即可將主機的目錄掛載到容器中。

sudo docker run -d --name test -v /home/xxx:/root/xxx ubuntu:16.04

4.2.2 埠對映

有時候容器內啟動的是一個網路服務,這個服務去監聽一個介面。但它監聽的實際上是容器的內部埠,直接去訪問是不行的,需要對映一個主機埠到容器的埠。
通過-p 主機埠:容器埠或直接使用主機網路--net=host

docker run -d -p 5000:5000 ubuntu:16.04 
docker run -d --net=host ubuntu:16.04 

4.2.3 自定義啟動命令

截止到目前,我們都沒有指定過容器啟動後執行什麼命令,其實run的最後一個引數可以用於在啟動容器後執行的命令:

docker run -d --name test ubuntu:16.04  /bin/bash
docker run -d --name test ubuntu:16.04  sh -c “/run.sh && /bin/bash” ### 多條命令拼接

4.2.4 容器狀態/日誌檢視

> docker ps ###列出當前容器(包括已經停止的)
CONTAINER ID   IMAGE                     COMMAND                  CREATED       STATUS       PORTS                                            NAMES
b8862fc5d600   6e4bffa46d70              "kube-controller-man…"   13 days ago   Up 13 days                                                    k8s_kube-controller-manager_kube-controller-manager-alg-dev01_kube-system_5af433af822e5ae2fb8825ecf24ac394_11
8ed05c4c9210   ebac1ae204a2              "kube-scheduler --au…"   13 days ago   Up 13 days                                                    k8s_kube-scheduler_kube-scheduler-alg-dev01_kube-system_8d0b3537ceaac4d2c6bbcb377f490c26_10
f36f7818738b   66f781e54201              "nvidia-device-plugin"   2 weeks ago   Up 2 weeks                                                    k8s_nvidia-device-plugin-ctr_nvidia-device-plugin-daemonset-d2ct6_kube-system_09c990d8-83a5-4bc5-b8ad-c7bf00079f59_158850
c60e540d83d6   k8s.gcr.io/pause:3.1      "/pause"                 2 weeks ago   Up 2 weeks                          

> docker logs [-f等選項] 容器名/ID

4.2.5 對容器修改的提交

很多時候我們基於一個映象啟動了容器,在容器中我們安裝了我們需要的軟體,想在容器刪除後也能夠使用,而不是再裝一次。這時就需要我們能夠提交這個修改。和git類似,也是通過commit指令去提交。

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

Create a new image from a container's changes

Options:
  -a, --author string    Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")
  -c, --change list      Apply Dockerfile instruction to the created image
  -m, --message string   Commit message
  -p, --pause            Pause container during commit (default true)
  

例如在我們上述啟動的ubuntu16.04的容器基礎上,我們裝了一些軟體:

docker commit test ubuntu:my16.04

即可生成一個新的映象(注意不是容器)。那麼下次我們啟動時直接用我們生成的這個映象,啟動的容器就包含了我們已經安裝過的環境了。

docker run -d --name test ubuntu:my16.04  /bin/bash

4.3 製作映象(Dockerfile)

4.3.1 Dockerfile編寫

通過在容器內修改再提交的方式雖然能夠生成映象,但手動操作太多,而且不便於自動化。更常用的製作映象的方式是Dockerfile。Dockerfile的基本使用比較簡單,只需要掌握幾個關鍵字:

FROM ubuntu:16.04 ### FROM: 基礎映象
ENV LANG C.UTF-8 
ENV TZ=Asia/Shanghai ### 設定容器的時區, ENV用於設定環境變數

RUN mkdir /opt/alg ### RUN: 執行一條命令,多個命令可以通過&&

ADD config/ /opt/alg/config/ ### ADD: 除有COPY的功能外,還能通過URL下載檔案,並且會自動解壓縮
COPY Dependency/ /opt/alg/Dependency/ ### COPY: 拷貝宿主機的檔案或資料夾到映象
COPY bin/ /opt/alg/bin/
COPY models/ /opt/alg/models/

ENTRYPOINT ["/opt/alg/config/start_service.sh" ]  ### 設定容器啟動的入口,類似於main函式,在docker run中可以通過 --entrypoint=XXX 覆蓋,如果有這個,那麼docker run時設定的command就會被當作它的引數

除了用ENTRYPOINT去指定入口,還可以用CMD去指定,這2者也可能混用。它們之間的差異參考:https://blog.csdn.net/wuce_bai/article/details/88997725

4.3.2 映象生成

有點類似於我們編譯程式碼,docker提供的生成映象的命令也是build:
在Dockerfile所在目錄執行:

docker build . -t 映象名:標籤
例如:
docker build . -t myapp:v1

映象生成後,我們就可以使用前文的方式去啟動容器了。

4.4 映象的儲存、載入

映象既可以上傳至官方的DockerHub供人pull,也可以自行搭建私有化的映象倉庫(如harbor)。但對於普通人或日常使用,更多的可能是想將映象儲存成一個可傳輸的檔案,然後放到其他機器,再載入。這個docker也是有對應命令支援的。

docker save [OPTIONS] IMAGE [IMAGE...]
> docker save -o my_ubuntu_v3.tar runoob/ubuntu:v3 ###將映象runoob/ubuntu:v3 儲存成my_ubuntu_v3.tar
docker load
--input , -i : 指定匯入的檔案,代替 STDIN。
--quiet , -q : 精簡輸出資訊。
> docker load -i my_ubuntu_v3.tar

也可以結合其他壓縮軟體的命令,直接儲存出壓縮包:

docker save <myimage>:<tag> | gzip > <myimage>_<tag>.tar.gz
gunzip -c <myimage>_<tag>.tar.gz | docker load

4.5 顯示卡的使用

對於深度學習部署,很多可能需要顯示卡,使用docker時,需要保證顯示卡驅動安裝,同時按上述步驟安裝了nvidia-docker2。
啟動容器時,增加--gpus選項即可:

sudo docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi ### all: 所有顯示卡都可用
sudo docker run --rm --gpus device=0,2 nvidia/cuda:11.0-base nvidia-smi ### 0,2 卡可用
也可以用下列方式:
sudo docker run --rm --gpus '"device=0"' nvidia/cuda:11.0-base nvidia-smi ### 0卡可用

4.6 其他常用指令

docker images 列出所有映象
docker rmi 刪除映象
docker cp 宿主機和容器間拷貝檔案

還有一個不錯的工具叫runlike,可以用來檢視容器啟動時的引數,可自行安裝。

5. 使用場景

docker即可以用來發布應用,服務,這可能也是它的一種主要使用場景,也可以用來方便的建立不同的開發編譯環境,比如在我們的ubuntu16.04機器上,去開發centos SDK, 甚至它也可以通過vnc去連線,獲得圖形化的開發環境。

一鍵快捷安裝指令碼:

連結:https://pan.baidu.com/s/1tb9tWEMzs6Ms1WDCzf1fqQ
提取碼:58q3
若以上鍊接失效,關注 老司機的視覺屋,回覆dockertool即可獲取連結