03_Docker 入门
文章目录
3.1 确保 Docker 已经就绪
查看 Docker 是否能正常工作;然后学习 Docker 基本的工作流:创建并管理容器。学习容器的典型生命周期:从创建、管理到停止,直到最终删除。
第一步,查看 Docker 程序是否存在,功能是否正常
$ sudo docker info
运行 docker 可执行程序的 info 命令,该命令返回所有容器和镜像的数量、Docker 使用的执行驱动和存储驱动,以及 Docker 的基本配置。
Docker 是基于客户端-服务区架构的。它有一个 docker 程序,既能作为客户端,也可以作为服务区端。作为客户端时,docker 程序向 Docker 守护进程发送请求(如请求返回守护进程自身的信息),然后再对返回的
请求结果进行处理。
3.2 运行我们的第一个容器
使用 docker run 命令创建容器,docker run 命令提供了 Docker 容器的创建到启动的功能,使用该命名来创建新容器。
$ sudo docker run -i -t ubuntu /bin/bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
Digest: sha256:27cb6e6ccef575a4698b66f5de06c7ecd61589132d5a91d098f7f3f9285415a9
Status: Downloaded newer image for ubuntu:latest
root@742df0868a92:/#
首先,告诉 Docker 执行 docker run 命令,并指定 -i 和 -t 两个命令行参数。
-i 参数保证容器中 STDIN 是开启的,尽管并没有附着到容器中。持久的标准输入是交互式 shell 的 “半边天”
-t 参数告诉 Docker 为要创建的容器分配一个伪 tty 终端。
这样创建的容器才能提供一个交互式 shell。
若要在命令行下创建一个能与之交互的容器,而不是一个运行后台服务的容器,则这两个参数已经是最基本的参数了。
接下来,告诉 Docker 基于什么镜像来创建容器,示例中使用的是 ubuntu 镜像。ubuntu 镜像是一个常备镜像,也称为基础镜像,它由 Docker 公司提供,保存在 Docker Hub Registry 上。
可以以 ubuntu 基础镜像为基础,在选择的操作系统上构建自己的镜像。到目前为止,基于此基础镜像启动了一个容器,并且没有对容器增加任何东西。
Docker 在执行上面命令的过程中具体做了什么呢?
首先 Docker 会检查本地是否存在 ubuntu 镜像,如果本地还没有该镜像的话,那么 Docker 就会连接到官方维护的 Docker Hub Register,查看Docker Hub 中是否有该镜像。
Docker 一旦找到了该镜像,就会下载该镜像并将其保存到本地宿主机中。
随后,Docker 在文件系统内部用这个镜像创建一个新容器。该容器拥有自己的网络、IP 地址,以及一个用来和宿主机进行通信的桥接网络接口。
最后,我们告诉 Docker 在新容器中要运行什么命令,在本例中我们在容器运行 /bin/bash 命令启动了一个 Bash shell。
当容器创建完毕后, Docker 就会执行容器中的 /bin/bash 命令,这时就可以看到容器内的 shell 了。
3.3 使用第一个容器
上一节中使用 docker run 创建了一个容器并登录到了新容器中,容器的 ID 为: 742df0868a92,这是一个完整的 Ubuntu 系统,可以用它来做任何事情。
可以获取容器的主机名
root@742df0868a92:/# hostname
742df0868a92
可以看到容器的主机名就是该容器的 ID,再来看看 /etc/hosts 文件,
root@742df0868a92:/# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 742df0868a92
Docker 已经在 hosts 文件中为该容器的 IP 地址添加了一条主机配置。再来看看容器的网络配置情况
root@742df0868a92:/# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet)
RX packets 18344 bytes 26635959 (26.6 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 11795 bytes 826442 (826.4 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
可以看到,这里有 lo 环回接口,还有 IP 为 172.17.0.2 的标准 eth0 网络接口,和普通宿主机是完全一样的,我们还可以查看容器中运行的进程,
root@742df0868a92:/# ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 4628 3608 pts/0 Ss 09:08 0:00 /bin/bash
root 257 0.0 0.0 7060 1564 pts/0 R+ 09:18 0:00 ps -aux
在容器中安装一个软件包
root@742df0868a92:/# apt update && apt install vim
通过上面的命令,就在容器中安装了 vim 软件。
用户可以继续在容器中做任何自己想做的事情,当所有工作都结束时,输入 exit ,就可以返回到 Ubuntu 宿主机的命令行提示符了。
当执行 exit 退出容器后,这个容器现在怎么样了?容器现在已经停止运行了!只有在指定的 /bin/bash 命令处于运行状态的时候,我们的容器也才会相应地处于运行状态。
一旦退出容器,/bin/bash 命令也就结束了,这时容器也随之停止了运行。
但容器仍然是存在的,可以用 docker ps -a 命令查看当前系统中容器的列表
列出 Docker 容器
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
742df0868a92 ubuntu "/bin/bash" 15 minutes ago Exited (130) 10 seconds ago eloquent_moser
1f50a1b1b556 ubuntu:vim "/bin/bash" 25 hours ago Exited (0) 15 minutes ago ecstatic_jang
默认情况下,当执行 docker ps 命令时,只能看到正在运行的容器。如果指定了 -a 参数,docker ps 命令会列出所有容器,包括正在运行和已经停止的。
从该命令的输出结果中可以看到关于这个容器的很多信息:ID、用于创建该容器的镜像、容器最后执行的命令、创建时间以及容器的退出状态。还可以看到,每个容器都一个名称。
**注意:有 3 种方式可以唯一指代容器:短 UUID(如742df0868a92)、长 UUID 或者名称(如 eloquent_moser)
3.4 容器命名
Docker 会为创建的每一个容器自动生产一个随机的名称。列如,在上面创建的容器被命名为 eloquent_moser。如果想为容器指定一个名称,而不是使用自动生成的名称,则可以用 --name 标志来实现。
给容器命名
$ sudo docker run --name bob_the_container -i -t ubuntu /bin/bash
root@4a74904fceae:/# exit
上面的命令将创建一个名为 bob_the_container 的容器。
在很多 Docker 命令中,可以用容器的名称来代替容器 ID。容器名称有助于分辨容器,当构建容器和应用程序之间的逻辑连接时,容器的名称也有助于从逻辑上理解连接关系。
具体的名称(如 web、db)比容器 ID 和随机容器名好记多了。推荐使用容器名称,以更加方便第管理容器。
容器的命名必须是唯一的。如果试图创建两个同名的容器,则命名将会失败。如果要使用的容器名称已经存在,可以先用 docker rm 命令删除已有同名容器后,再创建新的容器。
3.5 重新启动已经停止的容器
bob_the_container 容器已经停止了,对一个停止的容器我们可以重新启动它
启动已经停止运行的容器
$ sudo docker start bob_the_container
除了容器名称,也可以使用容器 ID 来指定容器,
通过 ID 启动已经停止运行的容器
$ sudo docker start 4a74904fceae
也可以使用 docker restart 命令来重新启动一个容器
这时使用不带 -a 参数的 docker ps 命令就可以看到容器已经开始运行了
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4a74904fceae ubuntu "/bin/bash" 10 minutes ago Up About a minute bob_the_container
类似的,Docker 也提供了 docker create 命令来创建一个容器,但是并不运行它。这让我们可以在自己的容器工作流中对其进行细粒度的控制。
3.6 附着到容器上
Docker 容器重新启动的时候,会沿用 docker run 命令时指定的参数来运行,因此容器重新启动后会运行一个交互式会话 shell。
此外,也可以用 docker attach 命令重新附着到该容器的会话上
$ sudo docker attach bob_the_container
root@4a74904fceae:/#
也可以使用容器 ID,重新附着到容器会话上。
通过 ID 附着到正在运行的容器
$ sudo docker attach 4a74904fceae
现在,又重新回到了容器的 Bash 提示符
如果退出容器 shell,容器会再次停止运行。
3.7 创建守护式容器
除了这些交互式运行的容器,也可以创建长期运行的容器。守护式容器没有交互式会话,非常适合运行应用程序和服务。大多数时候我们都需要守护式来运行我们的容器。
创建长期运行的容器
$ sudo docker run --name daemon_dave -d ubuntu /bin/bash -c "while true; do echo hello world; sleep 1; done"
2a779e540871ea0a8c09c61c6ab208f89c8584f15154bf71abb5d997f38a34b1
在上面的 docker run 命令使用了 -d 参数,因此 Docker 会将容器放到后台运行。
我们还在容器要运行的命令里使用了一个 while 循环,该循环会一直打印 hello world, 直到容器或其程停止运行。
通过组合使用上面的这些参数,docker run 命令并没有像上一个容器一样将主机的控制台附着到新的 shell 会哈上,而是仅仅返回一个容器 ID 而已,我们还是在主机的命令行之中。
如果执行 docker ps 命令,可以看到一个正在运行的容器
查看正在运行的 daemon_dave 容器
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2a779e540871 ubuntu "/bin/bash -c 'while…" 3 minutes ago Up 3 minutes daemon_dave
3.8 容器内部都在干些什么
现在有了一个在后台运行 while 循环的守护型容器。为了探究该容器内部都在干些什么,可以用 docker logs 命令来获取容器的日志,
获取守护式容器的日志
$ sudo docker logs daemon_dave
hello world
hello world
hello world
hello world
...
可以看到 while 循环正在向日志里打印 hello world。Docker 会输出最后几条日志项并返回。也可以在命令后使用 -f 参数来监控 Docker 日志。这与 tail -f 命令非常相似。
跟踪守护式容器的日志
$ sudo docker logs -f daemon_dave
hello world
hello world
hello world
hello world
hello world
hello world
...
使用 Ctrl + C 退出日志跟踪
也可以跟踪容器日志的某一片段,在 tail 命令后加入 -f --tail 标志即可,例如,可以用 docker logs --tail 10 daemon_dave 获取日志的最后 10 行内容。
另外,也可以用 docker logs --tail 0 -f daemon_dave 命令跟踪某个容器的最新日志而不必读取整个日志文件。
为了让调试更简单,还可以使用 -t 标志为每条日志项加上时间戳
跟踪守护式容器的最新日志
sudo docker logs -ft daemon_dave
3.9 Docker 日志驱动
3.10 查看容器内的进程
除了容器日志,还可以查看容器内运行的进程。使用 docker top 命令可以查看容器内运行的进程
sudo docker top daemon_dave
该命令执行后,可以看到容器内的所有进程,运行进程的用户及进程 ID
UID PID PPID C STIME TTY TIME CMD
root 47659 47639 0 1月10 pts/0 00:00:02 /usr/bin/qemu-aarch64-static /usr/bin/bash
3.11 Docker 统计信息
除了 docker top 命令,还可以使用 docker stats 命令,它用来显示一个或多个容器的统计信息。
sudo docker stats daemon_dave
上面命令将输出一个守护式容器的列表,以及它们的 CPU、内存、网络IO及存储 I/O 的性能和指标。这对快速监控一台主机上的一组容器非常有用。
3.12 在容器内部运行进程
在 Docker 1.3 之后,也可以通过 docker exec 命令在容器内部额外启动新进程。可以在容器内部运行的进程有两种类型:后台任务和交互式任务。
后台任务在容器内运行且没有交互需求,而交互式任务则保持在前台运行。对于需要在容器内部打开 shell 的任务,交互式任务是很实用的。
在容器中运行后台任务
sudo docker exec -d daemon_dave touch /etc/new_config_file
这里的 -d 参数表明需要运行一个后台进程,-d 参数之后,指定的是要在内部执行这个命令的容器的名字以及要执行的命令。
上面的例子中的命令在 daemon_dave 容器内创建了一个空文件,文件名为 /etc/new_config_file。
通过 docker exec 后台命令,可以在正在运行的容器中进行维护、监控及管理任务。
也可以在 daemon_dave 容器中启动一个诸如打开 shell 的交互式任务。
sudo docker exec -t -i daemon_dave /bin/bash
和运行交互式容器时一样,这里的 -t 和 -i 标志为我们执行的进程创建了 TTY 并捕捉 STDIN。接着我们指定了要在内部执行这个命令的容器的名字以及要执行的命令。
在上面的例子中,这条命令会在 daemon_dave 容器内创建一个新的 shell 会话,有了这个会话,我们就可以在该容器中运行其它命令了。
3.13 停止守护式容器
要停止守护式容器,只需要执行 docker stop 命令
sudo docker stop daemon_dave
也可以用容器 ID 来指代容器名称。
sudo docker stop 2a779e540871
注意:docker stop命令会向 Docker 容器进行发送 SIGTERM 信号。如果想快速停止某个容器,也可以使用 docker kill 命令来向容器进程发送 SIGKILL 信号。
要想查看已经停止的容器的状态,则可以使用 docker ps 命令。还有一个很实用的命令 docker ps -n x,该命令会显示最后 x 个容器,不论这些容器正在运行还是已经停止。
3.14 自动重启容器
如果由于某种错误而导致容器停止运行,还可以通过 --restart 标志,让 Docker 自动重新启动该容器。–restart 标志会检查容器的退出代码,并据此来决定是否要重启容器。
默认的行为是 Docker 不会重启容器。
下面示例是在一个 docker run 命令中使用 --restart 参数
sudo docker run --restart=always --name daemon_dave -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
在上面例子中,–restart 标志被设置为 always。无论容器的退出代码是什么,Docker 都会自动重启该容器。
除了 always,还可以将这个标志设置为 on-failure,这样,只有当容器的退出代码为非 0 值的时候,才会自动重启。
另外,on-failure 还接受一个可选的重启次数参数。
--restart=on-failure:5
这样,当容器退出代码为非 0 时,Docker会尝试自动重启该容器,最多重启 5 次。
3.15 深入容器
除了通过 docker ps 命令获取容器的信息外,还可以使用 docker inspect 来获取更多容器信息
sudo docker inspect daemon_dave
docker inspect 命令会对容器进行详细的检查,然后返回其配置信息,包括名称、命令、网络配置以及很多有用的数据。
也可以用 -f 或者 --format 标志来选定查看结果
sudo docker inspect --format='{{ .State.Running }}' daemon_dave
上面这条命令会返回容器的运行状态,我们还能获取其他有用的信息,如容器 IP 地址。
sudo docker inspect --format '{{ .NetworkSettings.IPAddress }}' daemon_dave
也可以同时指定多个容器,并显示每个容器的输出结果。
sudo docker inspect --format '{{.Name}} {{ .State.Running }}' daemon_dave bob_the_container
可以为该参数指定要查询和返回的查看散列中的任意部分。
注意:除了查看容器,还可以通过浏览 /var/lib/docker 目录来深入了解 Docker 的工作原理。
该目录存放着 Docker 镜像、容器及其容器的配置。
所有的容器都保存在 /var/lib/docker/containers 目录下。
3.16 删除容器
如果容器已经不再使用,可以使用 docker rm 命令来删除他们
sudo docker rm 2a779e540871
注意:从 Docker 1.6.2 开始,可以通过给 docker rm 命令传递 -f 标志来删除运行中的 Docker 容器。
这之前的版本必须先使用 docker stop 命令 或 docker kill 命令停止容器,才能将其删除
目前,还没有办法一次删除所有容器,但可以通过组合命令来达到这一目的
删除所有容器
sudo docker rm `sudo docker ps -a -q`
上面的 docker ps 命令会列出现有的全部容器,-a 标志代表列出所有容器,而 -q 标志则表示只需要返回容器的 ID 而不会返回容器的其它信息。
这样就得到了容器 ID 的列表,并传递给了 docker rm 命令,从而达到删除所有容器的目的。