Docker 的安装与使用

Docker 的安装与使用

一、安装 Docker 服务

​ Docker 分为 stable testnightly 三个更新频道,Docker 官网 上有有各种环境下的安装指南,这里基于 CentOS 7 的 stable 版本 进行安装部署

警告:切勿在没有配置 Docker YUM 源的情况下直接使用 yum 命令安装 Docker.

1. 系统要求

​ Docker 支持 64 位版本 CentOS 7/8,并且要求内核版本不低于 3.10。 CentOS 7 满足最低内核的要求,但由于内核版本比较低,部分功能(如 overlay2 存储层驱动)无法使用,并且部分功能可能不太稳定。

2. 使用 yum 安装

​ 首先安装依赖包

1
$ sudo yum install -y yum-utils

​ 如果这里提示一堆错误,可以通过这篇教程来配置 阿里 YUM 源

​ 然后添加安装 Docker 的 Docker YUM 源
​ 这里有两个源,由于国内的网络问题,强烈建议使用国内的 阿里 Docker YUM 源

  1. 阿里 Docker YUM 镜像源

    1
    2
    3
    $ sudo yum-config-manager \
    --add-repo \
    https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
  2. 官方 Docker YUM 源

    1
    2
    3
    $ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
  • 如果需要测试版本的 Docker 请执行以下命令:
1
$ sudo yum-config-manager --enable docker-ce-test

​ 然后安装 Docker,更新 yum 软件源缓存,并安装 docker-ce

1
$ sudo yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

3. 使用脚本自动按照安装

​ 在测试或开发环境中 Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,CentOS 系统上可以使用这套脚本安装,另外可以通过 --mirror 选项使用国内源进行安装

若你想安装测试版的 Docker, 请从 test.docker.com 获取脚本

1
2
3
4
5
6
7
# 以下两者二选一即可

# 官方源
$ curl -fsSL get.docker.com -o get-docker.sh

# 阿里镜像源(国内用户推荐)
$ sudo sh get-docker.sh --mirror Aliyun

​ 执行这个命令之后,脚本就会自动的将一切准备工作做好,并且把 Docker 的 稳定版(stable) 安装在系统中

4. 启动 Docker

1
2
$ sudo systemctl start docker
$ sudo systemctl enable docker

5. 建立 Docker 用户组

​ 默认情况下,docker 命令会使用 Unix socket 与 Docker 引擎通讯。而只有 root 用户和 docker 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 root 用户。因此,更好地做法是将需要使用 docker 的用户加入 docker 用户组。

​ 建立 docker 组:

1
$ sudo groupadd docker

​ 将当前用户加入 docker 组:

1
$ sudo usermod -aG docker $USER

6. 镜像加速

在中国大陆,由于总所都周知的网络原因,我们拉取前文提到的 官方 Docker 仓库—Docker Hub 是十分困难的,所以我们首先要进行国内镜像加速

2024.09.09更新:Docker 官方的镜像仓库地址 https://registry-1.docker.io/ 目前测试发现可在国内直接使用,好像被官方放出来了,是否是永久放出来还不清楚!目前Docker Hub 的官方网站 hub.docker.com 还是无法在国内访问!

​ 进入网站 https://cr.console.aliyun.com/ 登录账号后点击【镜像工具】下的【镜像加速器】并获取自己的 Docker 镜像仓库地址
​ 通常是 https://<your_code>.mirror.aliyuncs.com
​ 然后

1
2
3
4
5
6
7
8
9
10
11
$ sudo mkdir -p /etc/docker
$ sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://<your_code>.mirror.aliyuncs.com",
"https://hub.anitsuri.top"
]
}
EOF
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
  • 上面的 <your_code> 是需要你进入到上面的网站来获取自己的<code>
  • 下面的 https://hub.anitsuri.top 为文章作者本人搭建的 Docker 镜像加速器
  • 一定要保证该文件符合 json 规范,否则 Docker 将不能启动

7. 测试 Docker 是否安装正确

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 使用 Docker 官方的
$ docker run --rm hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:53cc4d415d839c98be39331c948609b659ed725170ad2ca8eb36951288f81b75
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/

​ 若能正常输出以上信息,则说明安装成功

8. 添加内核参数

如果在 CentOS 使用 Docker 看到下面的这些警告信息:

1
2
WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled

请添加内核配置参数以启用这些功能。

1
2
3
4
$ sudo tee -a /etc/sysctl.conf <<-EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

然后重新加载 sysctl.conf 即可

1
$ sudo sysctl -p

二、Docker 基本命令与使用

1. 基本概念

1.1 镜像

​ 我们都知道,操作系统分为 内核用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:18.04 就包含了完整的一套 Ubuntu 18.04 最小系统的 root 文件系统。

Docker 镜像 是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像 不包含 任何动态数据,其内容在构建之后也不会被改变。

  • 分层存储
    因为镜像包含操作系统完整的 root 文件系统,其体积往往是庞大的,因此在 Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。
    ​ 镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。

    ​ 分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。

1.2 容器

​ 镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

​ 容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。

​ 前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为 容器存储层

​ 容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。

​ 按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 数据卷(Volume)、或者 绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。

​ 数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。

1.3 仓库—Docker Registry

​ 镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。

​ 一个 Docker Registry 中可以包含多个 仓库Repository);每个仓库可以包含多个 标签Tag);每个标签对应一个镜像。

​ 通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。

​ 以 Ubuntu 镜像 为例,ubuntu 是仓库的名字,其内包含有不同的版本标签,如,16.04, 18.04。我们可以通过 ubuntu:16.04,或者 ubuntu:18.04 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 ubuntu,那将视为 ubuntu:latest

​ 仓库名经常以 两段式路径 形式出现,比如 jwilder/nginx-proxy,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。

Docker Registry 公开服务

​ Docker Registry 公开服务是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。

​ 最常使用的 Registry 公开服务是官方的 Docker Hub,这也是默认的 Registry,并拥有大量的高质量的 官方镜像。除此以外,还有 Red Hat 的 Quay.io;Google 的 Google Container RegistryKubernetes 的镜像使用的就是这个服务;代码托管平台 GitHub 推出的 ghcr.io

​ 由于某些原因,在国内访问这些服务可能会比较慢。国内的一些云服务商提供了针对 Docker Hub 的镜像服务(Registry Mirror),这些镜像服务被称为 加速器。常见的有 阿里云加速器DaoCloud 加速器 等。使用加速器会直接从国内的地址下载 Docker Hub 的镜像,比直接从 Docker Hub 下载速度会提高很多。在 安装 Docker 一节中有详细的配置方法。

​ 国内也有一些云服务商提供类似于 Docker Hub 的公开服务。比如 网易云镜像服务DaoCloud 镜像市场阿里云镜像库 等。

私有 Docker Registry

​ 除了使用公开服务外,用户还可以在本地搭建私有 Docker Registry。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。在 私有仓库 一节中,会有进一步的搭建私有 Registry 服务的讲解。

​ 开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 docker 命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。

​ 除了官方的 Docker Registry 外,还有第三方软件实现了 Docker Registry API,甚至提供了用户界面以及一些高级功能。比如,HarborSonatype Nexus

2. Docker 基础命令

2.1 Docker 启动/停止容器
2.1.1 Docker Hello World

​ Docker 允许你在容器内运行应用程序, 使用 docker run 命令来在容器内运行一个应用程序
eg. 输出 Hello World

1
2
$ docker run ubuntu:18.04 /bin/echo "Hello World"
Hello World
  • 参数解析:
    • docker: Docker 的二进制执行文件。
    • run: 与前面的 docker 组合来运行一个容器。
    • ubuntu:18.04:指定要运行的镜像,Docker 首先从本地主机上查找镜像是否存在,如果不存在,Docker 就会从镜像仓库 Docker Hub 下载公共镜像。
    • /bin/echo "Hello World": 在启动的容器里执行的命令
  • 以上命令完整的意思可以解释为:Docker 以 ubuntu 18.04 镜像创建一个新容器,然后在容器里执行 bin/echo "Hello World",然后输出结果。

2.1.1

2.1.2 运行交互式的容器

​ 通过 docker 的两个参数 -i-t 来让 docker 运行的容器实现 “与其对话” 的能力

1
2
$ docker run -i -t ubuntu:18.04 /bin/bash
root@ae8c819e4626:/#
  • 参数解析:

    • -i:允许你对容器内的标准输入 (STDIN) 进行交互
      • 标准输入(Standard Input,简称 STDIN)是指从终端、键盘或其他设备读取数据的过程。它允许用户将数据输入到计算机系统中,供程序进行处理。通常情况下,我们通过键盘输入数据时,数据会被缓存起来,直到用户按下回车键才会传输到程序中进行处理。
    • -t:在新容器里指定一个伪终端或者终端
  • 注意第 2 行 **root@2331009fcf4e:/#**,此时我们已经进入了一个 ubuntu 18.04 系统的容器
    我们尝试在容器中运行命令 cat /proc/version/ls 试一下

    1
    2
    3
    4
    5
    root@ae8c819e4626:/# cat /proc/version 
    Linux version 3.10.0-1160.119.1.el7.x86_64 (mockbuild@kbuilder.bsys.centos.org) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC) ) #1 SMP Tue Jun 4 14:43:51 UTC 2024

    root@ae8c819e4626:/# ls
    bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
    • 此时,我们可以使用 exit 命令 或者是使用 Ctrl+D 来退出容器
      1
      2
      3
      root@ae8c819e4626:/# exit
      exit
      [root@localhost ~]#
      注意第三行中 root@runoob:~# 表明我们已经退出了当前的容器,返回到当前的主机中

2.1.2

2.1.3 启动容器(让它在后台运行)

​ 使用以下命令创建一个以进程方式运行的容器

1
2
$ docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo hello worldl; sleep 1; done"
342d416fa405c1d4ecf438bea88e2bbb598e43ccb06d83f7cda478d661b26382
  • 命令解析:

    • -d:以 分离模式 运行容器,也就是说会在后台运行容器

    • /bin/sh -c:使用 /bin/sh 运行一个命令

    • "while true; do echo hello world; sleep 1; done"

      :这是一个无限循环的 shell 脚本,内容如下:

      • while true; do ...; done:表示一个无限循环。
      • echo hello world:每次循环时输出 “hello world”
      • sleep 1:然后等待 1 秒钟
  • 以上命令完整的意思可以解释为:启动一个基于 Ubuntu 18.04 镜像的 Docker 容器,并在容器中运行一个无限循环的脚本,每秒钟输出一次 “hello world”

  • 输出的 342d416fa405c1d4ecf438bea88e2bbb598e43ccb06d83f7cda478d661b26382 是 容器ID,对每个容器来说都是唯一的

    • 首先,我们要确认容器有在运行,可以通过 docker ps 来查看:

      1
      2
      3
      $ docker ps
      CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
      342d416fa405 ubuntu:18.04 "/bin/sh -c 'while t…" 26 minutes ago Up 26 minutes great_ellis
      • 输出详情介绍:

        • CONTAINER ID: 容器 ID

        • IMAGE: 使用的镜像

        • COMMAND: 启动容器时运行的命令

        • CREATED: 容器的创建时间

        • STATUS: 容器状态

        • PORTS: 容器的端口信息和使用的连接类型(tcp\udp)。

          NAMES: 自动分配的容器名称。

      • 这其中的 状态 (STATUS) 有 7 种:

        • created(已创建)
        • restarting(重启中)
        • running 或 Up(运行中)
        • removing(迁移中)
        • paused(暂停)
        • exited(停止)
        • dead(死亡)
    • 在宿主主机内使用 docker logs 命令,查看容器内的日志:

      1
      $ docker logs 342d416fa405

      2.1.3

2.1.4 停止容器

​ 我们使用 docker stop 命令来停止容器

1
2
$ docker stop 342d416fa405
342d416fa405

​ 通过 docker ps 查看,容器已经停止工作

1
2
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

2.1.4

2.2 Docker 容器使用

​ docker 客户端非常简单 ,我们可以直接输入 docker 命令来查看到 Docker 客户端的所有命令选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
$ docker

Usage: docker [OPTIONS] COMMAND

A self-sufficient runtime for containers

Common Commands:
run Create and run a new container from an image
exec Execute a command in a running container
ps List containers
build Build an image from a Dockerfile
pull Download an image from a registry
push Upload an image to a registry
images List images
login Log in to a registry
logout Log out from a registry
search Search Docker Hub for images
version Show the Docker version information
info Display system-wide information

Management Commands:
builder Manage builds
buildx* Docker Buildx
compose* Docker Compose
container Manage containers
context Manage contexts
image Manage images
manifest Manage Docker image manifests and manifest lists
network Manage networks
plugin Manage plugins
system Manage Docker
trust Manage trust on Docker images
volume Manage volumes

Swarm Commands:
swarm Manage Swarm

Commands:
attach Attach local standard input, output, and error streams to a running container
commit Create a new image from a container's changes
cp Copy files/folders between a container and the local filesystem
create Create a new container
diff Inspect changes to files or directories on a container's filesystem
events Get real time events from the server
export Export a container's filesystem as a tar archive
history Show the history of an image
import Import the contents from a tarball to create a filesystem image
inspect Return low-level information on Docker objects
kill Kill one or more running containers
load Load an image from a tar archive or STDIN
logs Fetch the logs of a container
pause Pause all processes within one or more containers
port List port mappings or a specific mapping for the container
rename Rename a container
restart Restart one or more containers
rm Remove one or more containers
rmi Remove one or more images
save Save one or more images to a tar archive (streamed to STDOUT by default)
start Start one or more stopped containers
stats Display a live stream of container(s) resource usage statistics
stop Stop one or more running containers
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
top Display the running processes of a container
unpause Unpause all processes within one or more containers
update Update configuration of one or more containers
wait Block until one or more containers stop, then print their exit codes

Global Options:
--config string Location of client config files (default "/root/.docker")
-c, --context string Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with "docker context use")
-D, --debug Enable debug mode
-H, --host list Daemon socket to connect to
-l, --log-level string Set the logging level ("debug", "info", "warn", "error", "fatal") (default "info")
--tls Use TLS; implied by --tlsverify
--tlscacert string Trust certs signed only by this CA (default "/root/.docker/ca.pem")
--tlscert string Path to TLS certificate file (default "/root/.docker/cert.pem")
--tlskey string Path to TLS key file (default "/root/.docker/key.pem")
--tlsverify Use TLS and verify the remote
-v, --version Print version information and quit

Run 'docker COMMAND --help' for more information on a command.

For more help on how to use Docker, head to https://docs.docker.com/go/guides/

2.2.0_1

​ 可以通过命令 docker <command> --help 更深入的了解指定的 Docker 命令用法
​ 比如我们要查看 docker stars 指令的具体使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ docker stats --help

Usage: docker stats [OPTIONS] [CONTAINER...]

Display a live stream of container(s) resource usage statistics

Aliases:
docker container stats, docker stats

Options:
-a, --all Show all containers (default shows just running)
--format string Format output using a custom template:
'table': Print output in table format with column headers (default)
'table TEMPLATE': Print output in table format using the given Go template
'json': Print in JSON format
'TEMPLATE': Print output using the given Go template.
Refer to https://docs.docker.com/go/formatting/ for more information about
formatting output with templates
--no-stream Disable streaming stats and only pull the first result
--no-trunc Do not truncate output

2.2.0_2

2.2.1 启动容器

​ 有了镜像后,我们就能够以这个镜像为基础启动并运行一个容器

​ 启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(exited)的容器重新启动

​ 因为 Docker 的容器实在太轻量级了,很多时候用户都是随时删除和新创建容器

  1. 新建并启动
    所需要的命令主要为 docker run
    比如下面的命令输出一个 “Hello World”,之后终止容器

    1
    2
    $ docker run ubuntu:18.04 /bin/echo 'Hello world'
    Hello world

    ​ 这跟在本地直接执行 /bin/echo 'Hello World' 看起来差不多
    ​ 下面的命令则启动一个 bash 终端,允许用户进行交互

    1
    2
    $ docker run -i -t ubuntu:18.04 /bin/bash
    root@9b7553a5ada9:/#

    ​ 其中,-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开
    ​ 在交互模式下,用户可以通过所创建的终端来输入命令,比如

    1
    2
    3
    4
    root@9b7553a5ada9:/# pwd
    /
    root@9b7553a5ada9:/# ls
    bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var

    当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:

    • 检查本地是否存在指定的镜像,不存在就从 Registry 下载
    • 利用镜像创建并启动一个容器
    • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
    • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
    • 从地址池配置一个 ip 地址给容器
    • 执行用户指定的应用程序
    • 执行完毕后容器被终止

    2.2.2_1

  2. 启动已终止容器
    可以利用 docker container start 命令,直接将一个已经终止(exited)的容器启动运行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 查看所有容器
    # Exited 是 已终止 的意思
    $ docker container ls -a
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    9b7553a5ada9 ubuntu:18.04 "/bin/bash" 12 seconds ago Exited (0) 5 seconds ago happy_jemison

    # 启动 ID 为 9b7553a5ada9 的容器
    $ docker container start 9b7553a5ada9
    9b7553a5ada9

    $ docker container ls
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    9b7553a5ada9 ubuntu:18.04 "/bin/bash" About a minute ago Up 10 seconds happy_jemison

    2.2.2_2

    ​ 容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。除此之外,并没有其它的资源。可以在伪终端中利用 pstop 来查看进程信息

    1
    2
    3
    4
    root@9b7553a5ada9:/# ps
    PID TTY TIME CMD
    1 pts/0 00:00:00 bash
    11 pts/0 00:00:00 ps

    2.2.2_3

    ​ 可见,容器中仅运行了指定的 bash 应用。这种特点使得 Docker 对资源的利用率极高,是货真价实的轻量级虚拟化

    • 注:

      • 可以通过以下命令来删除所有处于已经终止(exited)状态的容器

        1
        $ docker container prune

        ​ 这个命令会删除所有停止的容器,确保 docker container ls -a 中只显示运行中的容器

      • 可以通过以下命令来删除特定的容器

        1
        $ docker rm <container_id>
2.2.2 后台运行

​ 更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 -d 参数来实现

​ 下面举两个例子来说明一下

  1. 如果不使用 -d 参数运行容器

    1
    2
    3
    4
    5
    $ docker run ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
    hello world
    hello world
    hello world
    hello world

    容器会把输出的结果 (STDOUT) 打印到宿主机上面
    2.2.3_1

  2. 如果使用了 -d 参数运行容器

    1
    2
    $ docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
    8e0094dd2070c6b392c8608921f4a45b69e956724279392d5d127834def80364

    此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用 docker logs 查看)

    • 注: 容器是否会长久运行,是和 docker run 指定的命令有关,和 -d 参数无关

    • 使用 -d 参数启动后会返回一个唯一的 id,也可以通过 docker container ls 命令来查看容器信息

      1
      2
      3
      $ docker container ls
      CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
      8e0094dd2070 ubuntu:18.04 "/bin/sh -c 'while t…" 21 seconds ago Up 20 seconds eloquent_cohen
    • 要获取容器的输出信息,可以通过 docker container logs [container ID or NAMES] 命令

      1
      2
      3
      4
      $ docker container logs ce83d7a7d9d74db1689b398a54cf43e72d63a894a4483b8547afb0feae38144a
      hello world
      hello world
      hello world

    2.2.3_2

2.2.3 终止容器

​ 可以使用 docker container stop 来终止一个运行中的容器

​ 此外,当 Docker 容器中指定的应用终结时,容器也自动终止

​ 例如对于上面中只启动了一个终端的容器,用户通过 exit 命令或 Ctrl+d 来退出终端时,所创建的容器立刻终止

​ 终止状态的容器可以用 docker container ls -a 命令看到

1
2
3
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
04e64308f1d8 ubuntu:18.04 "/bin/bash" 10 seconds ago Exited (0) 5 seconds ago funny_diffie

​ 处于终止状态的容器,可以通过 docker container start [container ID or NAMES] 命令来重新启动
​ 此外,docker container restart [container ID or NAMES] 命令会将一个运行态的容器终止,然后再重新启动它
2.2.4

2.2.4 进入容器

​ 在使用 -d 参数时,容器启动后会进入后台

​ 某些时候需要进入容器进行操作,包括使用 docker attach 命令或 docker exec 命令,推荐使用 docker exec 命令,原因会在下面说明

  1. attach 命令

    举个例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 以后台运行且可以交互的方式运行 Ubuntu 容器
    $ docker run -dit ubuntu
    6a78bfcb96683d5df69875136708fcb3b45d0e869936859b5ba3221dee2c641a

    # 查看所有容器
    $ docker container ls
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    6a78bfcb9668 ubuntu "/bin/bash" 17 seconds ago Up 16 seconds gifted_elbakyan

    # 用 attach 的方法进入(交互式的)容器
    $ docker attach 6a78bfcb9668
    root@6a78bfcb9668:/#
    • 注意:如果从这个标准输入 (stdin) 中 exit,会导致容器的停止

    2.2.5.1

  2. exec 命令

    -i -t 参数
    docker exec 后面可以跟多个参数,这里主要说明 -i -t 参数
    ​ 只用 -i 参数时,由于没有分配标准输入(stdin),界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回

    ​ 当 -i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符

1
2
3
4
5
6
7
8
9
10
11
12
$ docker run -dit ubuntu
fa2296ba7e4b20e080c7fe39582f1ad10fb5fec31a5f4ceb64f842b39e9aec53

$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fa2296ba7e4b ubuntu "/bin/bash" 8 seconds ago Up 7 seconds hardcore_shockley

$ docker exec -i fa2296ba7e4b bash
# 可能由于没有附带伪终端,这里就导致长时间没有响应

$ docker exec -it fa2296ba7e4b bash
root@fa2296ba7e4b:/#

​ 如果从这个标准输入 (stdin) 中 exit,不会导致容器的停止。这就是为什么推荐使用 docker exec 的原因。

更多参数说明请使用 docker exec --help 查看

2.2.5 导出容器

​ 如果要导出本地某个容器,可以使用 docker export 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 首先查看一下所有容器,发现有一个容器还"活着"(Up)
# *活着(Up) 的容器是不能导出的
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fa2296ba7e4b ubuntu "/bin/bash" 27 minutes ago Up 27 minutes hardcore_shockley

# 终止容器
$ docker stop fa2296ba7e4b
fa2296ba7e4b

# 再次查看一下所有容器,发现那个容器已经"死了"(Exited)
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fa2296ba7e4b ubuntu "/bin/bash" 30 minutes ago Exited (137) 2 minutes ago hardcore_shockley

# 然后导出容器
$ docker export fa2296ba7e4b > ubuntu.tar
$ ls
anaconda-ks.cfg ubuntu.tar

​ 这样将导出容器快照到本地文件

2.2.6

2.2.6 导入容器快照

​ 可以使用 docker import 从容器快照文件中再导入为镜像

1
2
3
4
5
$ cat ubuntu.tar | docker import - test/ubuntu:v1.0
sha256:e11195c373c054190f3dda6349b10a08ba9accfb7c9365fa44296ad25cb9ae8f
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
test/ubuntu v1.0 e11195c373c0 10 seconds ago 78.1MB
  • 命令解析:

    • cat ubuntu.tar:该命令会将 ubuntu.tar 文件的内容输出到标准输出(也就是命令行显示的地方),但这里通过管道(|)将输出重定向给下一个命令。

      docker import:Docker 的 import 命令用于将一个文件系统(通常是一个 .tar 压缩文件)导入为 Docker 镜像。

      -:这个符号在 docker import 中表示从标准输入(stdin)读取数据。它告诉 Docker 从前面的管道中接收数据,而不是直接从一个文件读取。

      test/ubuntu:v1.0:这是你为导入的 Docker 镜像指定的名称和标签。在这个例子中,镜像的名称为 test/ubuntu,标签为 v1.0

2.2.7

​ 此外,也可以通过指定 URL 或者某个目录来导入,举个例子

1
$ docker import http://example.com/exampleimage.tgz example/imagerepo
  • 注:用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息
2.2.7 删除容器

​ 可以使用 docker container rm [container ID or NAMES] 来删除一个处于终止状态的容器

1
2
3
4
5
6
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fa2296ba7e4b ubuntu "/bin/bash" About an hour ago Exited (137) 59 minutes ago hardcore_shockley

$ docker container rm hardcore_shockley
hardcore_shockley
  • 如果要删除一个运行中的容器
    • 可以添加 -f 参数,Docker 会发送 SIGKILL 信号给容器
    • 也可以先使用 docker stop [container ID or NAMES] 来先停止容器,然后删除

2.2.8

2.2.8 清理所有处于终止状态的容器

​ 用 docker container ls -a 命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用下面的命令可以清理掉所有处于终止状态的容器

1
$ docker container prune
2.2.9 运行一个 Web 应用

​ 接下来我们将在docker容器中运行一个 Nginx 应用来运行一个web应用

1
2
3
# 载入 nginx 镜像
$ docker pull nginx:latest
$ docker run -d -p 80:80 --name nginx nginx:latest

2.2.10

  • 参数说明:
    • -d:让容器在后台运行,上面也有提到
    • -p:将本地主机的 80 端口,映射到容器的端口 80

​ 然后查看正在运行的容器,使用 docker ps 命令

1
2
3
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e21bdba7bf00 nginx:latest "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp nginx

​ 这里多了端口信息 0.0.0.0:80->80/tcp, :::80->80/tcp

​ 我们还可以使用 docker top 来查看容器内运行的进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
$ docker inspect nginx
docker inspect nginx
[
{
"Id": "e21bdba7bf00e73aeb1140d105406fb574483e99aa439644c518e8761f3fb5e6",
"Created": "2024-09-06T05:10:20.122787067Z",
"Path": "/docker-entrypoint.sh",
"Args": [
"nginx",
"-g",
"daemon off;"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 1932,
"ExitCode": 0,
"Error": "",
"StartedAt": "2024-09-06T05:10:21.117830247Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:39286ab8a5e14aeaf5fdd6e2fac76e0c8d31a0c07224f0ee5e6be502f12e93f3",
"ResolvConfPath": "/var/lib/docker/containers/e21bdba7bf00e73aeb1140d105406fb574483e99aa439644c518e8761f3fb5e6/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/e21bdba7bf00e73aeb1140d105406fb574483e99aa439644c518e8761f3fb5e6/hostname",
"HostsPath": "/var/lib/docker/containers/e21bdba7bf00e73aeb1140d105406fb574483e99aa439644c518e8761f3fb5e6/hosts",
"LogPath": "/var/lib/docker/containers/e21bdba7bf00e73aeb1140d105406fb574483e99aa439644c518e8761f3fb5e6/e21bdba7bf00e73aeb1140d105406fb574483e99aa439644c518e8761f3fb5e6-json.log",
"Name": "/nginx",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "bridge",
"PortBindings": {
"80/tcp": [
{
"HostIp": "",
"HostPort": "80"
}
]
},
......
2.3 Docker 镜像使用
2.3.1 获取镜像

​ 之前提到过,Docker Hub 上有大量的高质量的镜像可以用,这里我们就说一下怎么获取这些镜像。
​ 从 Docker 镜像仓库获取镜像的命令是 docker pull。其命令格式为:

1
$ docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
  • 具体的选项可以通过 docker pull --help 命令看到,这里我们说一下镜像名称的格式。
    • Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub(docker.io),但是我们上面配置了 Docker 镜像加速服务,所以我们这里是阿里云的 Docker 加速服务(<your_code>.mirror.aliyuncs.com)(虽然最后也是 Docker Hub 的仓库)
    • 仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像

​ 比如

1
2
3
4
5
6
7
$ docker pull ubuntu:18.04
Using default tag: latest
latest: Pulling from library/ubuntu
31e907dcc94a: Pull complete
Digest: sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e34a9ab63ee
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest

​ 上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub (docker.io) 获取镜像。而镜像名称是 ubuntu:18.04,因此将会获取官方镜像 library/ubuntu 仓库中标签为 18.04 的镜像。docker pull 命令的输出结果最后一行给出了镜像的完整名称,即: docker.io/library/ubuntu:18.04

​ 从下载过程中可以看到我们之前提及的分层存储的概念,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后,给出该镜像完整的 sha256 的摘要,以确保下载一致性

​ 在使用上面命令的时候,你可能会发现,你所看到的层 ID 以及 sha256 的摘要和这里的不一样。这是因为官方镜像是一直在维护的,有任何新的 bug,或者版本更新,都会进行修复再以原来的标签发布,这样可以确保任何使用这个标签的用户可以获得更安全、更稳定的镜像

2.3.2 查找镜像

我们可以从 Docker Hub 网站来搜索镜像

我们也可以使用 docker search 命令来搜索镜像。比如我们需要一个 httpd 的镜像来作为我们的 web 服务。我们可以通过 docker search 命令搜索 httpd 来寻找适合我们的镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ docker search httpd
NAME DESCRIPTION STARS OFFICIAL
httpd The Apache HTTP Server Project 4789 [OK]
jitesoft/httpd Apache httpd on Alpine linux. 0
hipache DEPRECATED (upstream); use "traefik", "nginx… 85 [OK]
openquantumsafe/httpd Demo of post-quantum cryptography in Apache … 14
openeuler/httpd 0
betterweb/httpd 0
vulhub/httpd 0
paketobuildpacks/httpd 0
dockette/apache Apache / HTTPD 1
skyven/httpd httpd(buysbox httpd) 0
zarra/httpd httpd 0
rogerlino94/httpd httpd 0
casus71/httpd HTTPD 0
ardeshir/httpd httpd 0
umyoooon/httpd httpd 0
lichnelei/httpd httpd 0
sanjay8939/httpd httpd 0
1543777187/httpd httpd 0
manojmane1294/httpd httpd 0
sohampatil08/httpd httpd images >> httpd containers 0
medolika/httpd httpd 0
sjlife/httpd httpd 0
nasoym/httpd httpd 0
447523269/httpd httpd 0
yrx2010/httpd httpd 0
  • NAME: 镜像仓库源的名称
  • DESCRIPTION: 镜像的描述
  • OFFICIAL: 是否 docker 官方发布
  • stars: 类似 Github 里面的 star,表示点赞、喜欢的意思。
  • AUTOMATED: 自动构建。
2.3.3 列出镜像列表

我们可以使用 docker image ls 来列出本地主机上的镜像

  • docker images 也可以,但是从功能上来说,它默认显示的内容包括镜像仓库名、标签、镜像 ID、创建时间和大小,而 docker image ls 可以对 image 进行其他操作,如 docker image rm(删除镜像)、docker image inspect(查看详细信息)等
1
2
3
4
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 39286ab8a5e1 3 weeks ago 188MB
ubuntu 18.04 f9a80a55f492 15 months ago 63.2MB

2.3.1

  • 选项说明:

    • REPOSITORY:镜像的仓库源
    • TAG:镜像的标签
    • IMAGE ID:镜像的 ID
    • CREATED:镜像创建时间
    • SIZE:镜像大小
  • 同一仓库源可以有多个 TAG,代表这个仓库源的不同个版本,如 ubuntu 仓库源里,有 15.10、14.04 等多个不同的版本,我们使用 REPOSITORY:TAG 来定义不同的镜像
    eg. 使用 18.04 的 Ubuntu 的镜像来运行容器的时候 be like:

    1
    2
    $ docker run -it ubuntu:18.04 /bin/bash
    root@42820000f93d:/#

    ​ 如果要使用版本为 最新版(latest) 的 ubuntu 系统镜像来运行容器时 be like:

    1
    2
    $ docker run -it ubuntu:latest /bin/bash
    root@39e968165990:/#
  • 镜像体积:

    ​ 如果仔细观察,会注意到,这里标识的所占用空间和在 Docker Hub 上看到的镜像大小不同。比如,ubuntu:18.04 镜像大小,在这里是 63.3MB,但是在 Docker Hub 显示的却是 25.47 MB。这是因为 Docker Hub 中显示的体积是压缩后的体积。在镜像下载和上传过程中镜像是保持着压缩状态的,因此 Docker Hub 所显示的大小是网络传输中更关心的流量大小。而 docker image ls 显示的是镜像下载到本地后,展开的大小,准确说,是展开后的各层所占空间的总和,因为镜像到本地后,查看空间的时候,更关心的是本地磁盘空间占用的大小

    ​ 另外一个需要注意的问题是,docker image ls 列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。由于 Docker 使用 Union FS,相同的层只需要保存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多

    • 可以通过 docker system df 命令来便捷的查看镜像、容器、数据卷所占用的空间
      1
      2
      3
      4
      5
      6
      $ docker system df
      TYPE TOTAL ACTIVE SIZE RECLAIMABLE
      Images 2 2 250.9MB 0B (0%)
      Containers 2 1 1.095kB 0B (0%)
      Local Volumes 0 0 0B 0B
      Build Cache 0 0 0B 0B
      2.3.2_1
  • 虚悬镜像:

    ​ 上面的镜像列表中,还可以看到一个特殊的镜像,这个镜像既没有仓库名,也没有标签,均为 <none>

    1
    <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) ,可以用下面的命令专门显示这类镜像:

    1
    2
    3
    $ docker image ls -f dangling=true
    REPOSITORY TAG IMAGE ID CREATED SIZE
    <none> <none> 00285df0df87 5 days ago 342 MB

    ​ 一般来说,虚悬镜像已经失去了存在的价值,是可以随意删除的,可以用下面的命令删除

    1
    $ docker image prune
  • 中间层镜像:

    ​ 为了加速镜像构建、重复利用资源,Docker 会利用 中间层镜像 。所以在使用一段时间后,可能会看到一些依赖的中间层镜像。默认的 docker image ls 列表中只会显示顶层镜像,如果希望显示包括中间层镜像在内的所有镜像的话,需要加上 -a 参数

    1
    $ docker image ls -a

    ​ 这样会看到很多无标签的镜像,与之前的虚悬镜像不同,这些无标签的镜像很多都是中间层镜像,是其它镜像所依赖的镜像。这些无标签镜像不应该删除,否则会导致上层镜像因为依赖丢失而出错。实际上,这些镜像也没必要删除,因为之前说过,相同的层只会存一遍,而这些镜像是别的镜像的依赖,因此并不会因为它们被列出来而多存了一份,无论如何你也会需要它们。只要删除那些依赖它们的镜像后,这些依赖的中间层镜像也会被连带删除

  • 列出部分镜像:

    ​ 不加任何参数的情况下,docker image ls 会列出所有顶层镜像,但是有时候我们只希望列出部分镜像。docker image ls 有好几个参数可以帮助做到这个事情。

    ​ 根据仓库名列出镜像

    1
    2
    3
    4
    $ docker image ls ubuntu
    REPOSITORY TAG IMAGE ID CREATED SIZE
    ubuntu 18.04 329ed837d508 3 days ago 63.3MB
    ubuntu bionic 329ed837d508 3 days ago 63.3MB

    ​ 列出特定的某个镜像,也就是说指定仓库名和标签

    1
    2
    3
    $ docker image ls ubuntu:18.04
    REPOSITORY TAG IMAGE ID CREATED SIZE
    ubuntu 18.04 329ed837d508 3 days ago 63.3MB

    ​ 除此以外,docker image ls 还支持强大的过滤器参数 --filter,或者简写 -f。之前我们已经看到了使用过滤器来列出虚悬镜像的用法,它还有更多的用法。比如,我们希望看到在 mongo:3.2 之后建立的镜像,可以用下面的命令:

    1
    2
    3
    4
    $ docker image ls -f since=mongo:3.2
    REPOSITORY TAG IMAGE ID CREATED SIZE
    redis latest 5f515359c7f8 5 days ago 183 MB
    nginx latest 05a60462f8ba 5 days ago 181 MB

    ​ 想查看某个位置之前的镜像也可以,只需要把 since 换成 before 即可

    ​ 此外,如果镜像构建时,定义了 LABEL,还可以通过 LABEL 来过滤。

    1
    2
    $ docker image ls -f label=com.example.version=0.1
    ...
  • 以特定格式显示:

    ​ 默认情况下,docker image ls 会输出一个完整的表格,但是我们并非所有时候都会需要这些内容。比如,刚才删除虚悬镜像的时候,我们需要利用 docker image ls 把所有的虚悬镜像的 ID 列出来,然后才可以交给 docker image rm 命令作为参数来删除指定的这些镜像,这个时候就用到了 -q 参数

    1
    2
    3
    4
    5
    6
    7
    $ docker image ls -q
    5f515359c7f8
    05a60462f8ba
    fe9198c04d62
    00285df0df87
    329ed837d508
    329ed837d508

    --filter 配合 -q 产生出指定范围的 ID 列表,然后送给另一个 docker 命令作为参数,从而针对这组实体成批的进行某种操作的做法在 Docker 命令行使用过程中非常常见,不仅仅是镜像,将来我们会在各个命令中看到这类搭配以完成很强大的功能。因此每次在文档看到过滤器后,可以多注意一下它们的用法。

    ​ 另外一些时候,我们可能只是对表格的结构不满意,希望自己组织列;或者不希望有标题,这样方便其它程序解析结果等,这就用到了 Go 的模板语法

    ​ 比如,下面的命令会直接列出镜像结果,并且只包含镜像ID和仓库名:

    1
    2
    3
    4
    5
    6
    7
    $ docker image ls --format "{{.ID}}: {{.Repository}}"
    5f515359c7f8: redis
    05a60462f8ba: nginx
    fe9198c04d62: mongo
    00285df0df87: <none>
    329ed837d508: ubuntu
    329ed837d508: ubuntu

    ​ 或者打算以表格等距显示,并且有标题行,和默认一样,不过自己定义列:

    1
    2
    3
    4
    5
    6
    7
    8
    $ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"
    IMAGE ID REPOSITORY TAG
    5f515359c7f8 redis latest
    05a60462f8ba nginx latest
    fe9198c04d62 mongo 3.2
    00285df0df87 <none> <none>
    329ed837d508 ubuntu 18.04
    329ed837d508 ubuntu bionic
2.3.4 删除本地镜像

​ 如果要删除本地的镜像,可以使用 docker image rm 命令

1
$ docker image rm [选项] <镜像1> [<镜像2>...]
  • 用 ID、镜像名、摘要 来删除镜像

    比如有这么一些镜像

    1
    2
    3
    4
    5
    6
    $ docker image ls
    REPOSITORY TAG IMAGE ID CREATED SIZE
    nginx latest 39286ab8a5e1 3 weeks ago 188MB
    ubuntu latest edbfe74c41f8 5 weeks ago 78.1MB
    redis alpine 7d06252fad43 5 weeks ago 41.2MB
    ubuntu 18.04 f9a80a55f492 15 months ago 63.2MB

    ​ 我们可以用镜像的完整 ID,也称为 “长 ID”,来删除镜像。使用脚本的时候可能会用长 ID,但是人工输入就太累了,所以更多的时候是用 “短 ID” 来删除镜像。docker image ls 默认列出的就已经是短 ID 了,一般取前3个字符以上,只要足够区分于别的镜像就可以了

    比如这里,如果我们要删除 redis:alpine 镜像

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $ docker image rm 7d0
    Untagged: redis:alpine
    Untagged: redis@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098
    Deleted: sha256:7d06252fad43690f61cbb858628b933fd85f048aaba4c05e577274b2c77cd7cb
    Deleted: sha256:f84d060cad0ab71233833c557876af2f215f0fefec722460626113bf1b752266
    Deleted: sha256:3e0fdfa6c9211180cd33f8545d7e2190a791ee23e69ae2e1017e082db07fdb85
    Deleted: sha256:95f5267d1a49d63f3ada2c6c7234cb31d68ce6b0d4c7ea53bea0f9f1de1e3067
    Deleted: sha256:e85af1712aa07e2732cad887d8027efaeb98d4579cdc2d55d3f6ca9dd729bf34
    Deleted: sha256:c37a6e8374e357826817411abebbb6febe86f61d9624b31d8d5ddf0654b05544
    Deleted: sha256:71aeab11bf660ddfb7e60541a8902bcb1eae50d11ad3095f66f087993a4ec5b5
    Deleted: sha256:9efb0ed04a8a6b06d559156cc0a765af5fd90ef8045508af90c40fa15c4a50ce
    Deleted: sha256:63ca1fbb43ae5034640e5e6cb3e083e05c290072c5366fcaa9d62435a4cced85

    2.3.3_1

  • 我们也可以用镜像名,也就是 <仓库名>:<标签>,来删除镜像

    1
    2
    3
    4
    5
    $ docker image rm ubuntu:latest
    Untagged: ubuntu:latest
    Untagged: ubuntu@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e34a9ab63ee
    Deleted: sha256:edbfe74c41f8a3501ce542e137cf28ea04dd03e6df8c9d66519b6ad761c2598a
    Deleted: sha256:f36fd4bb7334b7ae3321e3229d103c4a3e7c10a263379cc6a058b977edfb46de
  • 当然,更精确的是使用 “镜像摘要” 来删除镜像

    1
    2
    3
    4
    5
    6
    $ docker image ls --digests
    REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
    ubuntu 18.04 sha256:152dc042452c496007f07ca9127571cb9c29697f42acbfad72324b2bb2e43c98 f9a80a55f492 15 months ago 63.2MB

    $ docker image rm ubuntu@sha256:152dc042452c496007f07ca9127571cb9c29697f42acbfad72324b2bb2e43c98
    Untagged: ubuntu@sha256:152dc042452c496007f07ca9127571cb9c29697f42acbfad72324b2bb2e43c98

    ![3.2.3_4](E:\GongFang\3rd Week\Zabbix\3.2.3_4.png)

  • Untagged 和 Deleted

    ​ 如果观察上面这几个命令的运行输出信息的话,你会注意到删除行为分为两类,一类是 Untagged,另一类是 Deleted。我们之前介绍过,镜像的唯一标识是其 ID 和摘要,而一个镜像可以有多个标签。

    ​ 因此当我们使用上面命令删除镜像的时候,实际上是在要求删除某个标签的镜像。所以首先需要做的是将满足我们要求的所有镜像标签都取消,这就是我们看到的 Untagged 的信息。因为一个镜像可以对应多个标签,因此当我们删除了所指定的标签后,可能还有别的标签指向了这个镜像,如果是这种情况,那么 Delete 行为就不会发生。所以并非所有的 docker image rm 都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已。

    ​ 当该镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除行为。镜像是多层存储结构,因此在删除的时候也是从上层向基础层方向依次进行判断删除。镜像的多层结构让镜像复用变得非常容易,因此很有可能某个其它镜像正依赖于当前镜像的某一层。这种情况,依旧不会触发删除该层的行为。直到没有任何层依赖当前层时,才会真实的删除当前层。这就是为什么,有时候会奇怪,为什么明明没有别的标签指向这个镜像,但是它还是存在的原因,也是为什么有时候会发现所删除的层数和自己 docker pull 看到的层数不一样的原因。

    ​ 除了镜像依赖以外,还需要注意的是容器对镜像的依赖。如果有用这个镜像启动的容器存在(即使容器没有运行),那么同样不可以删除这个镜像。之前讲过,容器是以镜像为基础,再加一层容器存储层,组成这样的多层存储结构去运行的。因此该镜像如果被这个容器所依赖的,那么删除必然会导致故障。如果这些容器是不需要的,应该先将它们删除,然后再来删除镜像。

  • 使用 docker image ls 来配合

    ​ 像其它可以承接多个实体的命令一样,可以使用 docker image ls -q 来配合使用 docker image rm,这样可以成批的删除希望删除的镜像

    ​ 比如,我们需要删除所有仓库名为 redis 的镜像:

    1
    $ docker image rm $(docker image ls -q redis)

    ​ 或者删除所有在 mongo:3.2 之前的镜像:

    1
    $ docker image rm $(docker image ls -q -f before=mongo:3.2)
2.4 Dockerfile
2.4.1 什么是 Dockerfile

​ Dockerfile 是一个用来构建 Docker 镜像的文本文件,它包含了一系列的指令和参数。每一条指令都会创建一个新的镜像层,并对镜像进行提交。这些指令描述了镜像的构建过程,包括基础映像、软件包安装、文件拷贝、环境变量设置等。

​ 通过编写 Dockerfile,你可以将应用程序、环境和依赖项打包成一个独立的容器镜像,使其可以在不同的环境和平台上运行,实现应用程序的可移植性和可扩展性。

​ Dockerfile 包括以下几个部分:

  • FROM:使用 FROM 指令指定基础映像,作为构建镜像的起点。
  • RUN:在构建过程中在镜像中执行命令。
  • CMD:指定容器创建时的默认命令。
  • COPY:将文件或目录复制到镜像中。
  • ADD:将文件、目录或远程URL复制到镜像中。
  • ENV:在容器内部设置环境变量。
  • EXPOSE:声明容器运行时监听的特定网络端口。
  • WORKDIR:设置后续指令的工作目录。
  • USER:指定后续指令的用户上下文。
  • VOLUME:为容器创建挂载点或声明卷。
2.4.2 Dockerfile 的使用
  1. FROM:用于指定基础镜像

    1
    2
    FROM [基于的镜像名称]:[版本(tag)]
    *版本(tag)可不写,默认latest
  2. 注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大,例如:

    1
    2
    3
    4
    FROM centos
    RUN yum -y install wget
    RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
    RUN tar -xvf redis.tar.gz

    以上执行会创建 3 层镜像。可简化为以下格式:

    1
    2
    3
    4
    FROM centos
    RUN yum -y install wget \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
    && tar -xvf redis.tar.gz

    如上,以 && 符号连接命令,这样执行后,只会创建 1 层镜像。

  3. WORKDIR:指定接下来的 shell 语句运行在哪个目录下(可自动创建)

    1
    WORKDIR /opt/dockerfile
  4. COPY:将我们当前机器的文件 copy 到 docker 镜像中去

    1
    2
    COPY [--chown=<user>:<group>] <源路径1>...  <目标路径>
    COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
    • **<源路径>**:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:

      1
      2
      3
      4
      5
      # 将所有以 hom 开头的文件复制到镜像的 /mydir/ 目录下
      COPY hom* /mydir/

      # 将所有名字为 hom 后跟 一个任意字符 并以 .txt 结尾的文件复制到镜像的 /mydir/ 目录下。这里的 ? 是一个通配符,它只匹配一个字符
      COPY hom?.txt /mydir/
    • **<目标路径>**:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。

  5. ADD:和 COPY 相似(且相同需求下官方更推荐 RUN),不同之处如下

    • ADD 优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>
    • ADD 缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定
    • 总结:所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD
  6. RUN:执行后面跟着的命令行命令

    • shell 格式:

      1
      2
      RUN <命令行命令>
      # <命令行命令> 等同于,在终端操作的 shell 命令。
    • exec 格式:

      1
      2
      3
      RUN ["可执行文件", "参数1", "参数2"]
      # 例如:
      # RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline
  7. CMD:和 RUN 相似(但是 RUN 是在构建的时候就会运行,而 CMD 只会在容器真正运行的时候才会运行的脚本),但是 CMD 是为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 RUN 中指定要运行的程序所覆盖

    • 注:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效
    1
    2
    3
    CMD <shell 命令>
    CMD ["<可执行文件或命令>","<param1>","<param2>",...]
    CMD ["<param1>","<param2>",...] # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数

    ​ 推荐使用第二种格式,执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种格式运行,并且默认可执行文件是 sh

  8. ENTRYPOINT:和 CMD 类似

  9. ENV:设置环境变量(如果定义了环境变量,那么在后续的指令中就可以使用这个环境变量)

    1
    2
    ENV <key> <value>
    ENV <key1>=<value1> <key2>=<value2>...

    eg. 设置 NODE_VERSION = 7.2.0,在后续的指令中可以通过 $NODE_VERSION 引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 设置环境变量 NODE_VERSION 并赋值为 7.2.0 
    ENV NODE_VERSION 7.2.0

    # 使用 curl 通过 Nodejs 官方那里下载 NODE_VERSION 版的 Node.js 二进制包和对应的 SHA256 校验和文件
    # -S 表示只有在错误的时候显示信息
    # -L 表示自动追随新链接并进行下载
    # -O 表示保存到本地
    RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
    && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"
  10. ARG:和 ENV 作用一致,但是作用域不一样
    ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量
    构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖

    1
    ARG <参数名>[=<默认值>]
  11. VOLUME:指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据

    1
    2
    VOLUME ["<路径1>", "<路径2>"...]
    VOLUME <路径>

    eg.

    1
    VOLUME /data
    • 这里的 /data 目录就会在容器运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化

    在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点:

    1
    $ docker run -d -v mydata:/data xxxx
    • 在这行命令中,就使用了 mydata 这个命名卷挂载到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置
  12. EXPOSE:声明容器运行时提供服务的端口,但仅仅只是声明,在容器运行时并不会因为这个声明应用就会开启这个端口的服务。
    在 Dockerfile 中写入这样的声明有两个好处:

    • 一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射
    • 另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
    1
    EXPOSE <端口1> [<端口2>...]
    • 注意:要将 EXPOSE 和在运行时使用 -p <宿主端口>:<容器端口> 区分开来。-p,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。
  13. USER:指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)

    1
    USER <用户名>[:<用户组>]
    • 注:如果以 root 执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来运行某个服务进程,不要使用 su 或者 sudo,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。官方文档建议使用 gosu
  14. HEALTHCHECK:用于指定某个程序或者指令来监控 docker 容器服务的运行状态

    1
    2
    3
    4
    5
    HEALTHCHECK [选项] CMD <命令> # 设置检查容器健康状况的命令

    HEALTHCHECK NONE # 如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令

    HEALTHCHECK [选项] CMD <命令> # 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。

    当在一个镜像指定了 HEALTHCHECK 指令后,用其启动容器,初始状态会为 starting,在 HEALTHCHECK 指令检查成功后变为 healthy,如果连续一定次数失败,则会变为 unhealthy

    • HEALTHCHECK 支持下列选项:

      • --interval=<间隔>:两次健康检查的间隔,默认为 30 秒;

      • --timeout=<时长>:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;

      • --retries=<次数>:当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。

    • 注意:和 CMD, ENTRYPOINT 一样,HEALTHCHECK 只可以出现一次,如果写了多个,只有最后一个生效。

  15. ONBUILD:用于延迟构建命令的执行(官方文档:为他人做嫁衣裳)

    • 简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这时执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令
    1
    ONBUILD <其它指令>

    eg. 我们要制作 Node.js 所写的应用的镜像。我们都知道 Node.js 使用 npm 进行包管理,所有依赖、配置、启动信息等会放到 package.json 文件里。在拿到程序代码后,需要先进行 npm install 才可以获得所有需要的依赖。然后就可以通过 npm start 来启动应用。因此,一般来说会这样写 Dockerfile

    1
    2
    3
    4
    5
    6
    7
    FROM node:slim
    RUN mkdir /app
    WORKDIR /app
    COPY ./package.json /app
    RUN [ "npm", "install" ]
    COPY . /app/
    CMD [ "npm", "start" ]

    然后我们再在这个上面添加点东西,然后想要让别人使用我修改后的版本(我创建的基础镜像)
    假设这个基础镜像的名字为 my-node 的话,各个项目内的自己的 Dockerfile 就变为:

    1
    2
    3
    4
    5
    6
    7
    FROM node:slim
    RUN mkdir /app
    WORKDIR /app
    ONBUILD COPY ./package.json /app
    ONBUILD RUN [ "npm", "install" ]
    ONBUILD COPY . /app/
    CMD [ "npm", "start" ]

    就这样小小的把原来 COPY RUN COPY 三行的前面添加上 ONBUILD ,就能够实现“别人使用的软件是加了我所维护的功能的版本”

  16. LABEL:给镜像添加一些元数据(metadata),以键值对的形式

    1
    LABEL <key>=<value> <key>=<value> <key>=<value> ...

    我们还可以用一些标签来申明镜像的作者、文档地址等:

    1
    2
    3
    LABEL org.opencontainers.image.authors="AnitsuriW"

    LABEL org.opencontainers.image.documentation="https://docker.anitsuri.top"
2.5 数据卷
2.5.1 什么是数据卷

数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

  • 数据卷 可以在容器之间共享和重用
  • 数据卷 的修改会立马生效
  • 数据卷 的更新,不会影响镜像
  • 数据卷 默认会一直存在,即使容器被删除

注意:数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会复制到数据卷中(仅数据卷为空时会复制)

2.5.2 数据卷的使用
  1. 创建数据卷

    1
    $ docker volume create <数据卷的名字>
  2. 查看所有数据卷

    1
    2
    3
    $ docker volume ls
    DRIVER VOLUME NAME
    local moka

    在主机里使用以下命令可以查看指定 数据卷 的信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $ docker volume inspect moka
    [
    {
    "CreatedAt": "2024-09-09T11:58:00+08:00",
    "Driver": "local",
    "Labels": null,
    "Mountpoint": "/var/lib/docker/volumes/moka/_data",
    "Name": "moka",
    "Options": null,
    "Scope": "local"
    }
    ]
  3. 启动一个挂载数据卷的容器

    ​ 在用 docker run 命令的时候,使用 --mount 标记来将 数据卷 挂载到容器里。在一次 docker run 中可以挂载多个 数据卷

    ​ 下面创建一个名为 web 的容器,并加载一个 数据卷 到容器的 /usr/share/nginx/html 目录

    1
    2
    3
    4
    $ docker run -d -P \
    --name web \
    --mount source=moka,target=/usr/share/nginx/html \
    nginx:alpine
  4. 查看数据卷的具体信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    $ docker inspect web

    # 数据卷信息在 "Mount" Key 下面

    "Mounts": [
    {
    "Type": "volume",
    "Name": "moka",
    "Source": "/var/lib/docker/volumes/moka/_data",
    "Destination": "/usr/share/nginx/html",
    "Driver": "local",
    "Mode": "z",
    "RW": true,
    "Propagation": ""
    }
    ],
  5. 删除数据卷

    1
    $ docker volume rm moka

    数据卷 是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个命令

    ​ 无主的数据卷可能会占据很多空间,要清理请使用以下命令

    1
    $ docker volume prune
2.6 挂载主机目录
2.6.1 挂载一个主机目录作为数据卷

​ 使用 --mount 标记可以指定挂载一个本地主机的目录到容器中去。

1
2
3
4
5
$ docker run -d -P \
--name web \
# -v /src/webapp:/usr/share/nginx/html \
--mount type=bind,source=/src/webapp,target=/usr/share/nginx/html \
nginx:alpine

​ 上面的命令加载主机的 /src/webapp 目录到容器的 /usr/share/nginx/html目录。这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作。本地目录的路径必须是绝对路径,以前使用 -v 参数时如果本地目录不存在 Docker 会自动为你创建一个文件夹,现在使用 --mount 参数时如果本地目录不存在,Docker 会报错。

​ Docker 挂载主机目录的默认权限是 读写,用户也可以通过增加 readonly 指定为 只读

1
2
3
4
$ docker run -d -P \
--name web \
--mount type=bind,source=/src/webapp,target=/usr/share/nginx/html,readonly \
nginx:alpine

​ 加了 readonly 之后,就挂载为 只读 了。如果你在容器内 /usr/share/nginx/html 目录新建文件,会显示如下错误

1
2
/usr/share/nginx/html # touch new.txt
touch: new.txt: Read-only file system
2.6.2 查看数据卷的具体信息

​ 在主机里使用以下命令可以查看 web 容器的信息

1
$ docker inspect web

挂载主机目录 的配置信息在 “Mounts” Key 下面

1
2
3
4
5
6
7
8
9
10
"Mounts": [
{
"Type": "bind",
"Source": "/src/webapp",
"Destination": "/usr/share/nginx/html",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
2.6.3 挂载一个本地主机文件作为数据卷

--mount 标记也可以从主机挂载单个文件到容器中

1
2
3
4
5
6
7
8
$ docker run --rm -it \
--mount type=bind,source=$HOME/.bash_history,target=/root/.bash_history \
ubuntu:18.04 \
bash

root@2affd44b4667:/# history
1 ls
2 diskutil list

​ 这样就可以记录在容器输入过的命令了

3. Docker Compose

3.1 什么是 Docker Compose

Compose 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。从功能上看,跟 OpenStack 中的 Heat 十分类似。

​ 其代码目前在 https://github.com/docker/compose 上开源。

Compose 定位是 「定义和运行多个 Docker 容器的应用(Defining and running multi-container Docker applications)」,其前身是开源项目 Fig。

​ 通过上面的介绍,我们知道使用一个 Dockerfile 模板文件,可以让用户很方便的定义一个单独的应用容器。然而,在日常工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个 Web 项目,除了 Web 服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等。

Compose 恰好满足了这样的需求。它允许用户通过一个单独的 docker-compose.yml 模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。

Compose 中有两个重要的概念:

  • 服务 (service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。
  • 项目 (project):由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。

Compose 的默认管理对象是项目,通过子命令对项目中的一组容器进行便捷地生命周期管理。

Compose 项目由 Python 编写,实现上调用了 Docker 服务提供的 API 来对容器进行管理。因此,只要所操作的平台支持 Docker API,就可以在其上利用 Compose 来进行编排管理。

3.2 安装 Docker Compose

​ 从 官方 GitHub Release 处直接下载编译好的二进制文件

1
2
3
4
5
6
7
8
$ DOCKER_CONFIG=/usr/local/lib/docker/cli-plugins
$ sudo mkdir -p $DOCKER_CONFIG/cli-plugins
$ sudo curl -SL https://github.com/docker/compose/releases/download/v2.29.1/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
$ sudo chmod +x $DOCKER_CONFIG/cli-plugins
$ docker compose version

# 国内用户可以使用以下方式加快下载
$ sudo curl -SL https://moka.anitsuri.top/images/Registry+Harbor/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
3.3 Docker Compose 的使用
术语

首先介绍几个术语:

  • 服务 (service):一个应用容器,实际上可以运行多个相同镜像的实例。
  • 项目 (project):由一组关联的应用容器组成的一个完整业务单元。

可见,一个项目可以由多个服务(容器)关联而成,Compose 面向项目进行管理

场景

​ 最常见的项目是 web 网站,该项目应该包含 web 应用和缓存

​ 下面我们用 Python 来建立一个能够记录页面访问次数的 web 网站

  • Web 应用
    新建文件夹,在该目录中编写 app.py 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from flask import Flask
    from redis import Redis

    app = Flask(__name__)
    redis = Redis(host='redis', port=6379)

    @app.route('/')
    def hello():
    count = redis.incr('hits')
    return 'Hello World! 该页面已被访问 {} 次。\n'.format(count)

    if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)
  • Dockerfile

    ​ 编写 Dockerfile 文件,内容为

    1
    2
    3
    4
    5
    FROM python:3.6-alpine
    ADD . /code
    WORKDIR /code
    RUN pip install redis flask
    CMD ["python", "app.py"]
  • docker-compose.yml

    ​ 编写 docker-compose.yml 文件,这个是 Compose 使用的主模板文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    version: '3'
    services:

    web:
    build: .
    ports:
    - "5000:5000"

    redis:
    image: "redis:alpine"
  • 运行 compose 项目

    1
    $ docker compose up

    此时访问本地 5000 端口,每次刷新页面,计数就会加 1

3.4 Docker Compose 命令说明

​ 对于 Compose 来说,大部分命令的对象既可以是项目本身,也可以指定为项目中的服务或者容器。如果没有特别的说明,命令对象将是项目,这意味着项目中所有的服务都会受到命令影响。

​ 执行 docker compose [COMMAND] --help 可以查看具体某个命令的使用格式。

docker compose 命令的基本的使用格式是

1
docker compose [-f=<arg>...] [options] [COMMAND] [ARGS...]
  1. [options]

    • -f, --file FILE 指定使用的 Compose 模板文件,默认为 docker-compose.yml,可以多次指定。
    • -p, --project-name NAME 指定项目名称,默认将使用所在目录名称作为项目名。
    • --verbose 输出更多调试信息。
    • -v, --version 打印版本并退出。
  2. [COMMAND]

    • **build**:构建(重新构建)项目中的服务容器
      格式为 docker compose build [options] [SERVICE...]
      服务容器一旦构建后,将会带上一个标记名,例如对于 web 项目中的一个 db 容器,可能是 web_db

      可以随时在项目目录下运行 docker compose build 来重新构建服务
      选项包括:

      • --force-rm 删除构建过程中的临时容器。
      • --no-cache 构建镜像过程中不使用 cache(这将加长构建过程)。
      • --pull 始终尝试通过 pull 来获取更新版本的镜像。
    • **config**:验证 Compose 文件格式是否正确,若正确则显示配置,若格式错误显示错误原因

    • **down**:此命令将会停止 up 命令所启动的容器,并移除网络

    • **exec**:进入指定的容器

    • **help**:获得帮助

    • **images**:列出 Compose 文件中包含的镜像

    • **kill**:通过发送 SIGKILL 信号来强制停止服务容器

      ​ 格式为 docker compose kill [options] [SERVICE...]

      支持通过 -s 参数来指定发送的信号,例如通过如下指令发送 SIGINT 信号

      1
      $ docker compose kill -s SIGINT
    • **pause**:暂停一个服务容器

      ​ 格式为 docker compose pause [SERVICE...]

    • **port**:打印某个容器端口所映射的公共端口

      ​ 格式为 docker compose port [options] SERVICE PRIVATE_PORT
      选项:

      • --protocol=proto 指定端口协议,tcp(默认值)或者 udp。
      • --index=index 如果同一服务存在多个容器,指定命令对象容器的序号(默认为 1)。
    • **ps**:列出项目中目前的所有容器

      ​ 格式为 docker compose ps [options] [SERVICE...]

      选项:

      • -q 只打印容器的 ID 信息
    • **pull**:拉取服务依赖的镜像

      ​ 格式为 docker compose pull [options] [SERVICE...]

      选项:

      • --ignore-pull-failures 忽略拉取镜像过程中的错误
    • **push**:推送服务依赖的镜像到 Docker 镜像仓库

    • **restart**:重启项目中的服务

      ​ 格式为 docker compose restart [options] [SERVICE...]

      选项:

      • -t, --timeout TIMEOUT 指定重启前停止容器的超时(默认为 10 秒)
    • **rm**:删除所有(停止状态的)服务容器。推荐先执行 docker compose stop 命令来停止容器

      ​ 格式为 docker compose rm [options] [SERVICE...]

      选项:

      • -f, --force 强制直接删除,包括非停止状态的容器。一般尽量不要使用该选项。
      • -v 删除容器所挂载的数据卷。
    • **run**:在指定服务上执行一个命令

      ​ 格式为 docker compose run [options] [-p PORT...] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]

      比如:

      1
      $ docker compose run ubuntu ping docker.com

      ​ 将会启动一个 ubuntu 服务容器,并执行 ping docker.com 命令。

      ​ 默认情况下,如果存在关联,则所有关联的服务将会自动被启动,除非这些服务已经在运行中。

      ​ 该命令类似启动容器后运行指定的命令,相关卷、链接等等都将会按照配置自动创建。

      两个不同点:

      • 给定命令将会覆盖原有的自动运行命令;

      • 不会自动创建端口,以避免冲突。

        如果不希望自动启动关联的容器,可以使用 --no-deps 选项,例如

      1
      $ docker compose run --no-deps web python manage.py shell

      ​ 将不会启动 web 容器所关联的其它容器。

      选项:

      • -d 后台运行容器。
      • --name NAME 为容器指定一个名字。
      • --entrypoint CMD 覆盖默认的容器启动指令。
      • -e KEY=VAL 设置环境变量值,可多次使用选项来设置多个环境变量。
      • -u, --user="" 指定运行容器的用户名或者 uid。
      • --no-deps 不自动启动关联的服务容器。
      • --rm 运行命令后自动删除容器,d 模式下将忽略。
      • -p, --publish=[] 映射容器端口到本地主机。
      • --service-ports 配置服务端口并映射到本地主机。
      • -T 不分配伪 tty,意味着依赖 tty 的指令将无法运行。
    • **start**:启动已经存在的服务容器

      ​ 格式为 docker compose start [SERVICE...]

    • **start**:停止已经处于运行状态的容器,但不删除它。通过 docker compose start 可以再次启动这些容器。

      ​ 格式为 docker compose stop [options] [SERVICE...]

      选项:

      • -t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。
    • **top**:查看各个服务容器内运行的进程

    • **unpause**:恢复处于暂停状态中的服务

      ​ 格式为 docker compose unpause [SERVICE...]

    • **up**:

      ​ 格式为 docker compose up [options] [SERVICE...]

      ​ 该命令十分强大,它将尝试自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器的一系列操作。

      ​ 链接的服务都将会被自动启动,除非已经处于运行状态。

      ​ 可以说,大部分时候都可以直接通过该命令来启动一个项目。

      ​ 默认情况,docker compose up 启动的容器都在前台,控制台将会同时打印所有容器的输出信息,可以很方便进行调试。

      ​ 当通过 Ctrl-C 停止命令时,所有容器将会停止。

      ​ 如果使用 docker compose up -d,将会在后台启动并运行所有的容器。一般推荐生产环境下使用该选项。

      ​ 默认情况,如果服务容器已经存在,docker compose up 将会尝试停止容器,然后重新创建(保持使用 volumes-from 挂载的卷),以保证新启动的服务匹配 docker-compose.yml 文件的最新内容。如果用户不希望容器被停止并重新创建,可以使用 docker compose up --no-recreate。这样将只会启动处于停止状态的容器,而忽略已经运行的服务。如果用户只想重新部署某个服务,可以使用 docker compose up --no-deps -d <SERVICE_NAME> 来重新创建服务并后台停止旧服务,启动新服务,并不会影响到其所依赖的服务。

      选项:

      • -d 在后台运行服务容器。
      • --no-color 不使用颜色来区分不同的服务的控制台输出。
      • --no-deps 不启动服务所链接的容器。
      • --force-recreate 强制重新创建容器,不能与 --no-recreate 同时使用。
      • --no-recreate 如果容器已经存在了,则不重新创建,不能与 --force-recreate 同时使用。
      • --no-build 不自动构建缺失的服务镜像。
      • -t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。
    • **version**:打印版本信息

      ​ 格式为 docker compose version

至此,Docker 的基础命令与使用就结束了


Docker 的安装与使用
https://moka.anitsuri.top/2024/09/05/Docker_Basic_Install/
作者
アニつり
发布于
2024年9月5日
许可协议