Docker之Ubuntu上使用Docker的簡易教程
Ubuntu上使用Docker的簡易教程
說在開頭
在天池的比賽中涉及到了docker的使用。經過多番探究,大致搞明白了使用的基本流程。這裡對於關鍵部分做一個簡要總結和連結的跳轉。
Docker是什麼、有什麼優點
關於Docker官方性說明網上有很多,官方文件也很詳細,這裡不細說。
只想說在我目前的體驗中可以感受到的一點是,Docker可以打包一個完整的執行環境。這對於重現程式碼的使用環境來說非常有用。使用者不需要再重新配置環境,只需要直接拉取提供的映象,或者是直接載入打包好的映象,就可以基於該映象建立容器執行程式碼了。
所以我們需要搞清楚的是這樣幾點:
- 什麼是映象(image)和容器(container)
- 如何獲取映象
- 從網路
- 從他人
- 如何使用映象
- 如何使用容器
- 如何生成映象
- 如何分享映象
接下來從這幾個方面依次介紹。 首先說明我的實驗環境:
$ sudo docker --version
Docker version 19.03.6, build 369ce74a3c
什麼是映象(image)和容器(container)
官方說法:https://docs.docker.com/get-started/#images-and-containers
Fundamentally, a container is nothing but a running process, with some added encapsulation features applied to it in order to keep it isolated from the host and from other containers. One of the most important aspects of container isolation is that each container interacts with its own private filesystem; this filesystem is provided by a Docker image. An image includes everything needed to run an application - the code or binary, runtimes, dependencies, and any other filesystem objects required.
從根本上說,容器只不過是一個正在執行的程序,它還應用了一些附加的封裝功能,以使其與主機和其他容器保持隔離。容器隔離的最重要方面之一是每個容器都與自己的私有檔案系統進行互動;該檔案系統由Docker映象提供。映象包含執行應用程式所需的所有內容——程式碼或二進位制檔案、執行時、依賴項以及所需的任何其他檔案系統物件。
實際上在實際使用中,我們使用映象建立容器,非常像使用一個新的系統映象(這裡的映象包含了執行程式的所有內容)來安裝系統(建立容器)。只是這裡更加接近於虛擬機器。對系統本身沒有影響。
我覺的這句話說的很好:映象用來建立容器,是容器的只讀模板。
但是容器和虛擬機器還不一樣,這裡引用官方文件的表述:
A container runs natively on Linux and shares the kernel of the host machine with other containers. It runs a discrete process, taking no more memory than any other executable, making it lightweight.
By contrast, a virtual machine (VM) runs a full-blown “guest” operating system with virtual access to host resources through a hypervisor. In general, VMs incur a lot of overhead beyond what is being consumed by your application logic.
容器在Linux上本地執行,並與其他容器共享主機的核心。它執行一個獨立的程序,佔用的記憶體不超過任何其他可執行檔案,這也使其更加輕量級。相比之下,虛擬機器 (VM) 執行成熟的 “訪客” 作業系統,並通過管理程式虛擬訪問主機資源。通常,虛擬機器會產生超出應用程式邏輯消耗的開銷。
所以歸根到底一句話,docker的容器更加輕便省資源。
文件中給出的容器與虛擬機器的結構差異
如何獲取映象
從前面的描述中我們可以瞭解到,使用docker的基礎是,我們得有初始的映象來作為一切的開始。主要獲取映象的方式有兩種,一種是使用他人打包好,並通過網路(主要是docker官方的docker hub和一些類似的映象託管網站)進行分享的映象,另一種則是在本地將映象儲存為本地檔案,直接使用生成的檔案進行共享。後者在網路首先環境下更加方便。
從網路
這裡主要可以藉助於兩條指令。一個是 docker pull
,另一個是 docker run
。
docker pull
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
顧名思義,就是從倉庫中拉取(pull)指定的映象到本機。
看它的配置項,對於可選項我們暫不需要管,我們重點關注後面的 NAME
和緊跟的兩個互斥的配置項。
對應於這裡的三種構造指令的方式,文件中給出了幾種不同的拉取方式:
- NAME
docker pull ubuntu
- 如果不指定標籤,Docker Engine會使用
:latest
作為預設標籤拉取映象。
- NAME:TAG
docker pull ubuntu:14.04
- 使用標籤時,可以再次
docker pull
這個映象,以確保具有該映象的最新版本。 - 例如,
docker pull ubuntu:14.04
將會拉取Ubuntu 14.04映象的最新版本。
- NAME@DIGEST
docker pull ubuntu@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
- 這個為我們提供了一種指定特定版本映象的方法。
- 為了保證後期我們僅僅使用這個版本的映象,我們可以重新通過指定DIGEST(通過檢視映象託管網站裡的映象資訊或者是之前的
pull
輸出裡的DIGEST資訊)的方式pull
該版本映象。
更多詳見:
- https://docs.docker.com/engine/reference/commandline/pull/#pull-an-image-by-digest-immutable-identifier
- 通過API獲取映象倉庫裡映象的標籤:https://www.jianshu.com/p/a5af4f558b0a
docker run
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
docker run
命令首先在指定的映像上建立一個可寫的容器層,然後使用指定的命令啟動它。
這個實際上會自動從官方倉庫中下載本地沒有的映象。更多是使用會在後面建立容器的部分介紹。
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete
Digest: sha256:49a1c8800c94df04e9658809b006fd8a686cab8028d33cfba2cc049724254202
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest bf756fb1ae65 7 months ago 13.3kB
從他人處
關於如何儲存映象在後面介紹,其涉及到的指令為 docker save
,這裡主要講如何載入已經匯出的映象檔案。
docker load
docker load [OPTIONS]
Load an image or repository from a tar archive (even if compressed with gzip, bzip2, or xz) from a file or STDIN. It restores both images and tags.
從tar歸檔檔案 (即使使用gzip、bzip2或xz壓縮後的),從一個檔案或者STDIN中,載入映象或倉庫。它可以恢復映象和標籤。
例子可見:https://docs.docker.com/engine/reference/commandline/load/#examples
如何使用映象
單純的使用映象實際上就是圍繞指令 docker run
來的。
首先我們通過使用 docker images
來檢視本機中已經儲存的映象資訊列表。
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
通過執行 docker run
獲取映象並建立容器。
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
run包含很多的引數和配置項,這裡放一個我用過的最長的(這裡用到了nvidia-docker:https://github.com/NVIDIA/nvidia-docker#usage):
sudo docker run --rm -it --gpus all --name test -v /home/mydataroot:/tcdata:ro nvidia/cuda:10.0-base /bin/bash
這條指令做的就是:
- 啟動Docker容器時,必須首先確定是要在後臺以 “分離” 模式還是在預設前臺模式下執行容器:
-d
,這裡沒有指定-d
則是使用預設前臺模式執行。兩種模式下,部分引數配置不同,這部分細節可以參考文件:https://docs.docker.com/engine/reference/run/#detached-vs-foreground - 使用映象
nvidia/cuda:10.0-base
建立容器,並對容器起一個別名test
。 - 對於該容器,開啟gpu支援,並且所有GPU都可用,但是前提你得裝好nvidia-docker。
--rm
表示退出容器的時候自動移除容器,在測試環境等場景下很方便,不用再手動刪除已經建立的容器了。-t
和-i
:這兩個引數的作用是,為該docker建立一個偽終端,這樣就可以進入到容器的互動模式。- 後面的
/bin/bash
的作用是表示載入容器後執行bash
。docker中必須要保持一個程序的執行,要不然整個容器啟動後就會馬上kill itself,這樣當你使用docker ps
檢視啟動的容器時,就會發現你剛剛建立的那個容器並不在已啟動的容器佇列中。 這個/bin/bash
就表示啟動容器後啟動bash
。 -v
表示將本地的資料夾以只讀(:ro
,讀寫可以寫為:rw
,如果不加,則預設的方式是讀寫)的方式掛載到容器中的/tcdata
目錄中。
$ sudo docker run -it ubuntu bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
3ff22d22a855: Pull complete
e7cb79d19722: Pull complete
323d0d660b6a: Pull complete
b7f616834fd0: Pull complete
Digest: sha256:5d1d5407f353843ecf8b16524bc5565aa332e9e6a1297c73a92d3e754b8a636d
Status: Downloaded newer image for ubuntu:latest
root@966d5fa519a5:/# # 此時進入了基於ubuntu:latest建立的容器中。
退出容器後,檢視本機的映象資訊列表和容器資訊列表。
root@d9e6e9b9323a:/# exit
exit
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 1e4467b07108 9 days ago 73.9M
$ sudo docker ps # 檢視正在執行的容器資訊
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
$ sudo docker ps -a # 檢視所有容器資訊
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
966d5fa519a5 ubuntu "bash" 38 seconds ago Exited (0) 9 seconds ago relaxed_kirch
當我們想要刪除指定的映象的時候,我們可以使用 sudo docker rmi
來進行處理。
docker rmi [OPTIONS] IMAGE [IMAGE...]
後面可以跟多個映象。這裡支援三種方式:
- 使用IMAGE ID:
docker rmi fd484f19954f
- 使用TAG:
docker rmi test:latest
- 使用DIGEST:
docker rmi localhost:5000/test/busybox@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
如何使用容器
建立容器
從前面可以瞭解到,我們可以通過使用 docker run
建立前臺執行的容器,建立好了容器我們會面臨如何使用的問題。
進入容器
進入容器的方法都是一致的,但是這裡會面臨兩種情況,一種是已經退出的容器,另一種是執行在後臺的分離模式下的容器。
已退出的容器
$ sudo docker run -it --gpus all --name testv3 -v /home/lart/Downloads/:/data nvidia/cuda:10.0-base bash
root@efd722f0321f:/# exit
exit
(pt16) lart@god:~/Coding/RGBSOD_MS$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
efd722f0321f nvidia/cuda:10.0-base "bash" 19 seconds ago Exited (0) 3 seconds ago testv3
可以看到,我這裡存在一個已經退出的容器。我們想要進入退出的容器首先需要啟動已經退出的容器:
(pt16) lart@god:~/Coding/RGBSOD_MS$ sudo docker start testv3
testv3
(pt16) lart@god:~/Coding/RGBSOD_MS$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
efd722f0321f nvidia/cuda:10.0-base "bash" 31 seconds ago Up 1 second testv3
關於重啟容器,使用 docker restart
也是可以的。為了驗證,我們先使用 docker stop
停止指定容器。再進行測試。
$ sudo docker stop testv3
testv3
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
efd722f0321f nvidia/cuda:10.0-base "bash" 2 minutes ago Exited (0) 4 seconds ago testv3
$ sudo docker restart testv3 # restart 不僅可以重啟關掉的容器,也可以重啟執行中的容器
testv3
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
efd722f0321f nvidia/cuda:10.0-base "bash" 2 minutes ago Up 1 second testv3
後臺分離模式執行的容器
- 對於建立分離模式的容器我們可以使用
docker run -d
。 - 另外前面提到的
start
或者restart
啟動的容器會自動以分離模式執行。
進入啟動的容器
對於已經啟動的容器,我們可以使用 attach
或者 exec
進入該容器,但是更推薦後者(https://www.cnblogs.com/niuben/p/11230144.html)。
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
efd722f0321f nvidia/cuda:10.0-base "bash" 16 minutes ago Exited (0) 2 minutes ago testv3
$ sudo docker start testv3
testv3
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
efd722f0321f nvidia/cuda:10.0-base "bash" 16 minutes ago Up 2 seconds testv3
$ sudo docker exec -it testv3 bash
root@efd722f0321f:/# exit
exit
$ sudo docker ps -a # 可以看到exec退出後不會把容器關閉
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
efd722f0321f nvidia/cuda:10.0-base "bash" 17 minutes ago Up 36 seconds testv3
$ sudo docker attach testv3
root@efd722f0321f:/# exit
exit
$ sudo docker ps -a # 可以看到attach退出後會把容器關閉
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
efd722f0321f nvidia/cuda:10.0-base "bash" 18 minutes ago Exited (0) 2 seconds ago testv3
退出容器
$ exit
刪除容器
基於指令 docker rm
,用來移除一個或者多個容器。
docker rm [OPTIONS] CONTAINER [CONTAINER...]
我們可以根據容器的ID或者名字來刪除對應的執行中的或者是已經停止的容器。更多的例子可見https://docs.docker.com/engine/reference/commandline/rm/#examples。
停止正在執行的容器
參考:https://blog.csdn.net/Michel4Liu/article/details/80889977
docker stop
:此方式常常被翻譯為優雅的停止容器。docker stop 容器ID或容器名
-t
:關閉容器的限時,如果超時未能關閉則用kill
強制關閉,預設值10s,這個時間用於容器的自己儲存狀態,docker stop -t=60 容器ID或容器名
docker kill
:直接關閉容器docker kill 容器ID或容器名
從本機與容器中互相拷貝資料
docker cp
:用於容器與主機之間的資料拷貝。
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH
- The docker cp utility copies the contents of SRC_PATH to the DEST_PATH. You can copy from the container's file system to the local machine or the reverse, from the local filesystem to the container.
- If
-
is specified for either the SRC_PATH or DEST_PATH, you can also stream a tar archive from STDIN or to STDOUT. - The CONTAINER can be a running or stopped container.
- The SRC_PATH or DEST_PATH can be a file or directory.
由於容器內的資料與容器外的資料並不共享,所以如果我們想要向其中拷貝一些資料,可以通過這個指令來進行復制。當然,另一個比較直接的想法就是通過掛載的目錄進行檔案共享與複製。
如何生成映象
主要包含兩種方式,一種是基於構建檔案Dockerfile和 docker build
的自動構建,一種是基於 docker commit
提交對於現有容器的修改之後生成映象。
docker build
docker build [OPTIONS] PATH | URL | -
- The docker build command builds Docker images from a Dockerfile and a "context". A build's context is the set of files located in the specified PATH or URL.
- The build process can refer to any of the files in the context. For example, your build can use a COPY instruction to reference a file in the context.
更多細節可見https://docs.docker.com/engine/reference/commandline/build/。
這裡用到了Dockerfile,這些參考資料不錯:
對於已有的Dockerfile檔案,我們可以使用如下指令生成映象:
$ docker build -t vieux/apache:2.0 .
# 使用'.'目錄下的Dockerfile檔案。注意結尾的路徑`.`,這裡給打包的映象指定了TAG
$ docker build -t whenry/fedora-jboss:latest -t whenry/fedora-jboss:v2.1 .
# 也可以指定多個TAG
$ docker build -f dockerfiles/Dockerfile.debug -t myapp_debug .
# 也可以不使用'.'目錄下的Dockerfile檔案,而是使用-f指定檔案
打包完之後,我們就可以在 docker images
中看到新增的映象了。
docker commit
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
- It can be useful to commit a container's file changes or settings into a new image. This allows you to debug a container by running an interactive shell, or to export a working dataset to another server.
- Generally, it is better to use Dockerfiles to manage your images in a documented and maintainable way. Read more about valid image names and tags.
- The commit operation will not include any data contained in volumes mounted inside the container.
- By default, the container being committed and its processes will be paused while the image is committed. This reduces the likelihood of encountering data corruption during the process of creating the commit. If this behavior is undesired, set the --pause option to false.
一般使用指定容器的ID就可以進行提交了。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c3f279d17e0a ubuntu:12.04 /bin/bash 7 days ago Up 25 hours desperate_dubinsky
197387f1b436 ubuntu:12.04 /bin/bash 7 days ago Up 25 hours focused_hamilton
$ docker commit c3f279d17e0a svendowideit/testimage:version3
f5283438590d
$ docker images
REPOSITORY TAG ID CREATED SIZE
svendowideit/testimage version3 f5283438590d 16 seconds ago 335.7 MB
如何分享映象
上傳到線上儲存庫
- 上傳到Docker Hub:https://docs.docker.com/get-started/part3/
- 上傳到阿里雲:https://blog.csdn.net/xiayto/article/details/104133417/
本地匯出分享
這裡基於指令 docker save
docker save [OPTIONS] IMAGE [IMAGE...]
Produces a tarred repository to the standard output stream. Contains all parent layers, and all tags + versions, or specified repo:tag
, for each argument provided.
具體的例子可見:https://docs.docker.com/engine/reference/commandline/save/#examples
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
153999a1dfb2 hello-world "/hello" 2 hours ago Exited (0) 2 hours ago naughty_euler
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest bf756fb1ae65 7 months ago 13.3kB
$ sudo docker save -o hello-world-latest.tar hello-world:latest
$ ls
hello-world-latest.tar
$ sudo docker rmi hello-world:latest
Error response from daemon: conflict: unable to remove repository reference "hello-world:latest" (must force) - container 153999a1dfb2 is using its referenced image bf756fb1ae65
$ sudo docker rmi -f hello-world:latest # 強制刪除映象
Untagged: hello-world:latest
Untagged: hello-world@sha256:49a1c8800c94df04e9658809b006fd8a686cab8028d33cfba2cc049724254202
Deleted: sha256:bf756fb1ae65adf866bd8c456593cd24beb6a0a061dedf42b26a993176745f6b
$ sudo docker ps -a # 課件,強制刪除映象後,原始關聯的容器並不會被刪除
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
153999a1dfb2 bf756fb1ae65 "/hello" 5 hours ago Exited (0) 5 hours ago naughty_euler
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
$ sudo docker load -i hello-world-latest.tar
Loaded image: hello-world:latest
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest bf756fb1ae65 7 months ago 13.3kB
$ sudo docker ps -a # 可見,當重新載入對應的映象時,這裡的IMAGE又對應了回來
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
153999a1dfb2 hello-world "/hello" 5 hours ago Exited (0) 5 hours ago naughty_euler
這裡也有個例子:
- docker 打包本地映象,併到其他機器進行恢復:https://blog.csdn.net/zf3419/article/details/88533274
參考資料
- docker命令列的基本指令都在這裡:https://docs.docker.com/engine/reference/commandline/docker/
- 官方給了一些指導性的文件:https://docs.docker.com/develop/