Docker基础
源自摘抄,留个记录,防止再次被禁,不明白公开的纯技术文档也能享受这种待遇。
安装
使用自动脚本安装
# wget -qO- https://get.docker.com/ | sh
or
$ sudo curl -sSL https://get.docker.com/ | sh
国内方式,可以通过 --mirror
选项使用国内源进行安装:
$ curl -fsSL get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh --mirror Aliyun
# $ sudo sh get-docker.sh --mirror AzureChinaCloud
启动 Docker
$ sudo systemctl enable docker
$ sudo systemctl start docker
If you want to avoid typing sudo whenever you run the docker command, add your username to the docker group:
$ sudo groupadd docker
$ sudo usermod -aG docker ${USER}
Ubuntu
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
apt-cache policy docker-ce
sudo apt-get install -y docker-ce
Docker should now be installed, the daemon started, and the process enabled to start on boot. Check that it's running:
$ sudo systemctl status docker
国内方式
$ curl -fsSL https://mirrors.aliyun.com/docker-ce/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://mirrors.aliyun.com/docker-ce/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
测试 Docker 是否安装正确
$ docker run --rm hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete
Digest: sha256:308866a43596e83578c7dfa15e27a73011bdd402185a84c5cd7f32a88b501a24
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
使用镜像
Docker 运行容器前需要本地存在对应的镜像,如果本地不存在该镜像,Docker 会从镜像仓库下载该镜像。
获取镜像
从 Docker 镜像仓库获取镜像的命令是 docker pull
。其命令格式为:
$ docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
具体的选项可以通过 docker pull --help
命令看到,这里我们说一下镜像名称的格式。
- Docker 镜像仓库地址:地址的格式一般是
<域名/IP>[:端口号]
。默认地址是 Docker Hub(docker.io
)。 - 仓库名:如之前所说,这里的仓库名是两段式名称,即
<用户名>/<软件名>
。对于 Docker Hub,如果不给出用户名,则默认为library
,也就是官方镜像。
比如:
$ docker pull ubuntu:18.04
18.04: Pulling from library/ubuntu
92dc2a97ff99: Pull complete
be13a9d27eb8: Pull complete
c8299583700a: Pull complete
Digest: sha256:4bc3ae6596938cb0d9e5ac51a1152ec9dcac2a1c50829c74abd9c4361e321b26
Status: Downloaded newer image for ubuntu:18.04
docker.io/library/ubuntu:18.04
上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub (docker.io
)获取镜像。而镜像名称是 ubuntu:18.04
,因此将会获取官方镜像 library/ubuntu
仓库中标签为 18.04
的镜像。docker pull
命令的输出结果最后一行给出了镜像的完整名称,即: docker.io/library/ubuntu:18.04
。
列出镜像
要想列出已经下载下来的镜像,可以使用 docker image ls
命令。
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest 5f515359c7f8 5 days ago 183 MB
nginx latest 05a60462f8ba 5 days ago 181 MB
mongo 3.2 fe9198c04d62 5 days ago 342 MB
<none> <none> 00285df0df87 5 days ago 342 MB
ubuntu 18.04 329ed837d508 3 days ago 63.3MB
ubuntu bionic 329ed837d508 3 days ago 63.3MB
列表包含了 仓库名
、标签
、镜像 ID
、创建时间
以及 所占用的空间
。
其中仓库名、标签在之前的基础概念章节已经介绍过了。镜像 ID 则是镜像的唯一标识,一个镜像可以对应多个 标签。因此,在上面的例子中,我们可以看到 ubuntu:18.04
和 ubuntu:bionic
拥有相同的 ID,因为它们对应的是同一个镜像。
镜像体积
docker image ls
列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。由于 Docker 使用 Union FS,相同的层只需要保存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。
你可以通过 docker system df
命令来便捷的查看镜像、容器、数据卷所占用的空间。
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 24 0 1.992GB 1.992GB (100%)
Containers 1 0 62.82MB 62.82MB (100%)
Local Volumes 9 0 652.2MB 652.2MB (100%)
Build Cache 0B 0B
虚悬镜像
上面的镜像列表中,还可以看到一个特殊的镜像,这个镜像既没有仓库名,也没有标签,均为 <none>
。:
<none> <none> 00285df0df87 5 days ago 342 MB
这个镜像原本是有镜像名和标签的,原来为 mongo:3.2
,随着官方镜像维护,发布了新版本后,重新 docker pull mongo:3.2
时,mongo:3.2
这个镜像名被转移到了新下载的镜像身上,而旧的镜像上的这个名称则被取消,从而成为了 <none>
。除了 docker pull
可能导致这种情况,docker build
也同样可以导致这种现象。由于新旧镜像同名,旧镜像名称被取消,从而出现仓库名、标签均为 <none>
的镜像。这类无标签镜像也被称为 虚悬镜像(dangling image) ,可以用下面的命令专门显示这类镜像:
$ docker image ls -f dangling=true
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 00285df0df87 5 days ago 342 MB
一般来说,虚悬镜像已经失去了存在的价值,是可以随意删除的,可以用下面的命令删除。
$ docker image prune
删除本地镜像
如果要删除本地的镜像,可以使用 docker image rm
命令,其格式为:
$ docker image rm [选项] <镜像1> [<镜像2> ...]
用 ID、镜像名、摘要删除镜像
其中,<镜像>
可以是 镜像短 ID
、镜像长 ID
、镜像名
或者 镜像摘要
。
## 镜像短 ID
$ docker image rm 501
## 镜像名
$ docker image rm centos
## 摘要
$ docker image ls --digests
REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
node slim sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228 6e0c4c8e3913 3 weeks ago 214 MB
$ docker image rm node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228
Untagged: node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228
用 docker image ls 命令来配合
像其它可以承接多个实体的命令一样,可以使用 docker image ls -q
来配合使用 docker image rm
,这样可以成批的删除希望删除的镜像。
比如,我们需要删除所有仓库名为 redis
的镜像:
$ docker image rm $(docker image ls -q redis)
或者删除所有在 mongo:3.2
之前的镜像:
$ docker image rm $(docker image ls -q -f before=mongo:3.2)
使用 Dockerfile 定制镜像
Dockerfile 内容
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
FROM 指定基础镜像
所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个 nginx
镜像的容器,再进行修改一样,基础镜像是必须指定的。而 FROM
就是指定 基础镜像,因此一个 Dockerfile
中 FROM
是必备的指令,并且必须是第一条指令。
除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch
。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。
FROM scratch
...
如果你以 scratch
为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。
不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 FROM scratch
会让镜像体积更加小巧。使用 Go 语言 开发的应用很多会使用这种方式来制作镜像,这也是有人认为 Go 是特别适合容器微服务架构的语言的原因之一。
RUN 执行命令
RUN
指令是用来执行命令行命令的。由于命令行的强大能力,RUN
指令在定制镜像时是最常用的指令之一。其格式有两种:
-
shell 格式:
RUN <命令>
,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的RUN
指令就是这种格式。
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
-
exec 格式:
RUN ["可执行文件", "参数1", "参数2"]
,这更像是函数调用中的格式。
构建镜像
在 Dockerfile
文件所在目录执行:
$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM nginx
---> e43d811ce2f4
Step 2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
---> Running in 9cdc27646c7b
---> 44aa4490ce2c
Removing intermediate container 9cdc27646c7b
Successfully built 44aa4490ce2c
这里我们使用了 docker build
命令进行镜像构建。其格式为:
docker build [选项] <上下文路径/URL/->
指定了最终镜像的名称 -t nginx:v3
镜像构建上下文(Context)
如果注意,会看到 docker build
命令最后有一个 .
。.
表示当前目录,而 Dockerfile
就在当前目录,因此不少初学者以为这个路径是在指定 Dockerfile
所在路径,这么理解其实是不准确的。
首先我们要理解 docker build
的工作原理。Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 Docker Remote API,而如 docker
命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker
功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。
当我们进行镜像构建的时候,并非所有定制都会通过 RUN
指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY
指令、ADD
指令等。而 docker build
命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?
这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build
命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。
如果在 Dockerfile
中这么写:
COPY ./package.json /app/
这并不是要复制执行 docker build
命令所在的目录下的 package.json
,也不是复制 Dockerfile
所在目录下的 package.json
,而是复制 上下文(context) 目录下的 package.json
。
因此,COPY
这类指令中的源文件的路径都是相对路径。这也是初学者经常会问的为什么 COPY ../package.json /app
或者 COPY /opt/xxxx /app
无法工作的原因,因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去。
现在就可以理解刚才的命令 docker build -t nginx:v3 .
中的这个 .
,实际上是在指定上下文的目录,docker build
命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。
如果观察 docker build
输出,我们其实已经看到了这个发送上下文的过程:
$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
...
客户端命令(docker)
客户端命令选项
-
--config=""
:指定客户端配置文件,默认为~/.docker
; -
-D=true|false
:是否使用 debug 模式。默认不开启; -
-H, --host=[]
:指定命令对应 Docker 守护进程的监听接口,可以为 unix 套接字unix:///path/to/socket
,文件句柄fd://socketfd
或 tcp 套接字tcp://[host[:port]]
,默认为unix:///var/run/docker.sock
; -
-l, --log-level="debug|info|warn|error|fatal"
:指定日志输出级别; -
--tls=true|false
:是否对 Docker 守护进程启用 TLS 安全机制,默认为否; -
--tlscacert=/.docker/ca.pem
:TLS CA 签名的可信证书文件路径; -
--tlscert=/.docker/cert.pem
:TLS 可信证书文件路径; -
--tlscert=/.docker/key.pem
:TLS 密钥文件路径; -
--tlsverify=true|false
:启用 TLS 校验,默认为否。
客户端命令
可以通过 docker COMMAND --help
来查看这些命令的具体用法。
-
attach
:依附到一个正在运行的容器中; -
build
:从一个 Dockerfile 创建一个镜像; -
commit
:从一个容器的修改中创建一个新的镜像; -
cp
:在容器和本地宿主系统之间复制文件中; -
create
:创建一个新容器,但并不运行它; -
diff
:检查一个容器内文件系统的修改,包括修改和增加; -
events
:从服务端获取实时的事件; -
exec
:在运行的容器内执行命令; -
export
:导出容器内容为一个tar
包; -
history
:显示一个镜像的历史信息; -
images
:列出存在的镜像; -
import
:导入一个文件(典型为tar
包)路径或目录来创建一个本地镜像; -
info
:显示一些相关的系统信息; -
inspect
:显示一个容器的具体配置信息; -
kill
:关闭一个运行中的容器 (包括进程和所有相关资源); -
load
:从一个 tar 包中加载一个镜像; -
login
:注册或登录到一个 Docker 的仓库服务器; -
logout
:从 Docker 的仓库服务器登出; -
logs
:获取容器的 log 信息; -
network
:管理 Docker 的网络,包括查看、创建、删除、挂载、卸载等; -
node
:管理 swarm 集群中的节点,包括查看、更新、删除、提升/取消管理节点等; -
pause
:暂停一个容器中的所有进程; -
port
:查找一个 nat 到一个私有网口的公共口; -
ps
:列出主机上的容器; -
pull
:从一个Docker的仓库服务器下拉一个镜像或仓库; -
push
:将一个镜像或者仓库推送到一个 Docker 的注册服务器; -
rename
:重命名一个容器; -
restart
:重启一个运行中的容器; -
rm
:删除给定的若干个容器; -
rmi
:删除给定的若干个镜像; -
run
:创建一个新容器,并在其中运行给定命令; -
save
:保存一个镜像为 tar 包文件; -
search
:在 Docker index 中搜索一个镜像; -
service
:管理 Docker 所启动的应用服务,包括创建、更新、删除等; -
start
:启动一个容器; -
stats
:输出(一个或多个)容器的资源使用统计信息; -
stop
:终止一个运行中的容器; -
swarm
:管理 Docker swarm 集群,包括创建、加入、退出、更新等; -
tag
:为一个镜像打标签; -
top
:查看一个容器中的正在运行的进程信息; -
unpause
:将一个容器内所有的进程从暂停状态中恢复; -
update
:更新指定的若干容器的配置信息; -
version
:输出 Docker 的版本信息; -
volume
:管理 Docker volume,包括查看、创建、删除等; -
wait
:阻塞直到一个容器终止,然后输出它的退出符。