Docker入门基础之Client操作

  1. 1. docker镜像管理
    1. docker build
      1. 基于本地上下文构建
      2. 基于远端上下文构建
      3. 构建参数说明
    2. docker tag
    3. docker save
    4. docker load
    5. docker export
    6. docker import
    7. docker history
    8. docker rmi
    9. docker images
  2. 2. docker容器生命管理
    1. docker run
    2. docker create
    3. docker start
    4. docker stop
    5. docker restart
    6. docker pause/unpause
    7. docker kill
    8. docker rm
  3. 3. docker容器操作
    1. docker ps
    2. docker top
    3. docker attach
    4. docker commit
  4. 4. docker registry操作
    1. docker login
    2. docker pull
    3. docker push
    4. docker search
  5. 5. docker其他操作
    1. docker inspect
    2. docker events
  6. 问题汇总
    1. docker.sock权限问题

在有了之前的Dockerfile基础知识后,我们知道该如何编写一个简单的Dockfile,那么如果通过docker client命令来进行Image的构建呢,本节就来阐述一些docker client的一些使用;

在介绍docker client的各个操作命令之前先说明一下docker client操作的分类,按照操作的对象不同,可以分为以下几个大类:

你会发现docker client的很多命令的功能是重复的,例如docker builddocker image builddocker rundocker container run参考Stack Overflow得知,这是由于历史原因导致的;最早docker的命令并没有按照操作对象进行分类管理,例如docker psdocker build等,后来,docker client变得更加结构化,收敛到了几大类,同时为了向后兼容,所以看上去docker client的命令看上去比较乱;

1. docker镜像管理

docker build

前面在「基于Dockerfile的镜像构建」一节中,已经简单的说明了如何通过docker build进行镜像的构建,这里参考docker build手册再详细说明一下:

docker build 的语法,如下:

1
docker build [OPTIONS] PATH | URL 

docker build通过Dockerfile构建上下文进行镜像的构建;构建的上下文是由PATHURL指定位置的一个文件列表集合;所有构建过程都可以访问构建上下文的任何文件;

基于本地上下文构建

PATH用来指定本地文件系统的路径,会将该路径的文件列表作为构建上下文发送到docker daemon进行构建;如果没有指定Dockerfile会在PATH路径的根路径下查找;构建上下文是递归处理的,所以PATH会包含其所有的子目录,所以一般都是将Dockerfile放入一个空目录中,只添加构建所有需要的文件

不要将根目录/作为PATH来进行构建,这将会把文件系统所有文件全部作为构建上下文传递给Docker daemon进行构建

基于远端上下文构建

URL指向远端的上下文资源,URL可以指向三种资源

  • Git Repo

URL可以指向Git仓库;构建开始之前,Git Repo会被git pull拉取到本地的临时目录,Git仓库的所有的submodule会自动recursively拉取;当然提交历史会被忽略;然后,Git仓库的整个目录会被发送到docker daemon作为上下文进行镜像的构建;

Git Repo的URL的格式可以包含三个部分:仓库地址分支名用于构建上下文的仓库相对路径;如下是Repo URL的语法:

Build Syntax Suffix Commit Used Build Context Used
myrepo.git refs/heads/master /
myrepo.git#mytag refs/tags/mytag /
myrepo.git#mybranch refs/heads/mybranch /
myrepo.git#pull/42/head refs/pull/42/head /
myrepo.git#:myfolder refs/heads/master /myfolder
myrepo.git#master:myfolder refs/heads/master /myfolder
myrepo.git#mytag:myfolder refs/tags/mytag /myfolder
myrepo.git#mybranch:myfolder refs/heads/mybranch /myfolder
  • Tarball 文件

URL可以指向远端的压缩文件,docker build的时候会将URL发送到docker daemon ,由Docker daemon负责压缩包的下载和解压,并将其作为docker build的构建上下文进行构建;Tarbal文件必须是标准的tar文件格式,且压缩格式为xzbzip2gzip或者无压缩;使用语法格式如下:

1
$ docker build http://server/context.tar.gz
  • 文本文件

如果 URL 指向一个纯文本文件,那么该文本文件会被当做Dockerfile发送给docker daemon 用来进行镜像构建,且这种情况的构建是没有任何上下文的

1
$ docker build http://server/Dockerfile.txt

这里和传入文本文件类似的,**docker build支持从STDIN中读取数据**,将其作为Dockerfile进行镜像的构建;如下:

1
2
$ docker build - < Dockerfile
$ cat Dockerfile|docker build -

docker build在将PATH所有文件列表,发送构建上下文的时候,如果根路径有.dockerignore文件,会将其中的文件忽略,不发送到docker daemon,以加速构建过程

构建参数说明

下面简单的介绍一下docker build常用的OPTIONS选项,支持的所有OPTIONS列表可参考官方手册

这里需要注意的是:对container有影响的参数,在docker build过程中只影响构建过程,并不会对最终构建的镜像有何影响;与之相对应的docker run可以对运行时的container的系统参数进行实际修改;

  • -f, --file

指定Dockerfile的路径,如果不指定,默认为PATH/Dockerfile,如果构建上下文为URL,那么Dockerfile就是相对URL构建上下文的根路径;如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ ls .
Dockerfile helloworld helloworld.go
$ docker build -f Dockerfile . # 等价于 docker build .
Sending build context to Docker daemon 2.045MB
Step 1/3 : FROM public.ecr.aws/lts/ubuntu
latest: Pulling from lts/ubuntu
63acfa137832: Pull complete
Digest: sha256:62b8f60c5c8e1717f460bb7af05e558b74feb8ac460ff2abbdd3a98becdc15ce
Status: Downloaded newer image for public.ecr.aws/lts/ubuntu:latest
---> 3003ca74986b
Step 2/3 : COPY helloworld .
---> 36ae209bc43e
Step 3/3 : CMD ./helloworld
---> Running in bb925ccc1708
Removing intermediate container bb925ccc1708
---> 9576343a7c1d
Successfully built 9576343a7c1d
  • -t, --tag

为构建生成的镜像指定仓库名和tag,格式为name:tag, tag不写默认是latest;如果在镜像构建的时候不指定-t选项,构建的镜像的仓库名和tag都是<none>,如下,是上面执行docker build .构建的镜像的信息:

1
2
3
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 9576343a7c1d 4 minutes ago 79.9MB

如下在构建镜像的时候,指定镜像的仓库名和tag:

1
2
3
4
5
6
7
8
9
10
11
$ docker build -t ccr.ccs.tencentyun.com/ns_gz_walkerdu/helloworld:v1 .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ccr.ccs.tencentyun.com/ns_gz_walkerdu/helloworld v1 9576343a7c1d 41 minutes ago 79.9MB

# 不写tag,默认构建的tag为latest
$ docker build -t ccr.ccs.tencentyun.com/ns_gz_walkerdu/helloworld .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ccr.ccs.tencentyun.com/ns_gz_walkerdu/helloworld latest 9576343a7c1d 45 minutes ago 79.9MB
ccr.ccs.tencentyun.com/ns_gz_walkerdu/helloworld v1 9576343a7c1d 45 minutes ago 79.9MB
  • --ulimit

用来将构建过程中的每个step的container都设置ulimit系统资源的限制参数,参数的格式如下:

1
<type>=<soft limit>[:<hard limit>]

如下,设置镜像中资源的栈大小,不过变更只影响构建过程,并不会对最终构建的镜像有何影响

1
$ docker build -t test/helloworld --ulimit stack=10240:10240 .

各种资源的类型名,在系统的/etc/security/limits.conf中有定义,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ vim /etc/security/limits.conf
#<item> can be one of the following:
# - core - limits the core file size (KB)
# - data - max data size (KB)
# - fsize - maximum filesize (KB)
# - memlock - max locked-in-memory address space (KB)
# - nofile - max number of open file descriptors
# - rss - max resident set size (KB)
# - stack - max stack size (KB)
# - cpu - max CPU time (MIN)
# - nproc - max number of processes
# - as - address space limit (KB)
# - maxlogins - max number of logins for this user
# - maxsyslogins - max number of logins on the system
# - priority - the priority to run user process with
# - locks - max number of file locks the user can hold
# - sigpending - max number of pending signals
# - msgqueue - max memory used by POSIX message queues (bytes)
# - nice - max nice priority allowed to raise to values: [-20, 19]
# - rtprio - max realtime priority
  • --build-arg

前面在构建指令里有一个ARG指令,用来申明一个变量可以在后面的构建过程中使用的,该变量值,可以通过docker build命令在Image构建过程中进行修改; --build-arg传入的变量必须已经在Dockfile中通过ARG 指令定义,否则会在构建过程中触发Warning;具体可见ARG指令介绍的一节;

  • --add-host

在构建过程中,向构建的container的/etc/hosts中写入一个静态的host解析记录,注意:这里host的变更只影响构建过程,并不会对最终构建的镜像有何影响;可以参考github issue,如下:

1
2
3
4
5
6
7
8
$ docker build -t test/helloworld --add-host=walkerdu:1.1.1.1 .

Step 5/6 : RUN cat /etc/hosts
---> Running in 85c90a53ae05
127.0.0.1 localhost
...
1.1.1.1 walkerdu
...

运行容器后,查看/etc/hosts的结果如下,docker build添加的host并没有影响到最终的镜像:

1
2
3
4
5
6
7
8
9
$ docker run -d test/helloworld
$ docker exec -it 676c35929eed 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
192.168.10.4 db7c217d1b36
  • --target

在含有多个构建stage的Dockerfile构建的时候,可以使用--target选项来指定只进行某个stage的构建,其他的构建stage会被丢弃,如下:

1
2
3
4
FROM debian AS build-env
# ...
FROM alpine AS production-env
# ...

执行只构建第一个build-envstage的构建指令如下:

1
$  docker build -t mybuildimage --target build-env .

docker tag

docker tag用于为一个Image对象打一个tag,其实就是给一个Image起另一个名字而已,格式如下:

1
$ docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]

Image的名字是格式一般有两部分组成:注册中心和镜像名;中间用斜线分隔;注册中心名字是标准的DNS格式,可以包含主机的端口号;

Image的tag必须是由ASCII组成,可以包含:大小写字母,数字,下划线,点号,破折号;但是不能以点号,破折号开始,且长度不能超过128个字符长度;

打tag可以使用source image的Image ID,Image Name,如下简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 通过Image Name进行打tag操作
$ docker tag test/helloworld helloworld_tag1:v1
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
helloworld_tag1 v1 156571ba9cfc 7 days ago 79.9MB
test/helloworld latest 156571ba9cfc 7 days ago 79.9MB

# 通过Image ID进行打tag操作
$ docker tag 156571ba9cfc helloworld_tag2:v2
REPOSITORY TAG IMAGE ID CREATED SIZE
helloworld_tag1 v1 156571ba9cfc 7 days ago 79.9MB
helloworld_tag2 v2 156571ba9cfc 7 days ago 79.9MB
test/helloworld latest 156571ba9cfc 7 days ago 79.9MB

# tag指向私有注册中心
$ docker tag 156571ba9cfc walkerdu.com:5050/test/helloworld:v1

docker save

docker的Image是由docker daemon集中化进行管理的,我们可以通过docker save/docker load进行镜像的迁移工作;docker save用于将Image的各个层的数据输出到一个tar的归档文件中;然后在需要的地方进行恢复;docker save的语法格式如下:

1
$ docker save [OPTIONS] IMAGE [IMAGE...]

docker save默认会将tarred的镜像数据(所有layer的数据)输出到STDOUT中,可以通过-o, --output指定输出的tar文件名,也可以用重定向,管道进行操作,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ docker save test/helloworld > helloworld.tar
$ tar -tvf helloworld.tar
-rw-r--r-- 0/0 2170 2022-06-27 13:50 156571ba9cfca54ccb1492a20e9adc95318920559b679fcc40305e02b6c1e10e.json
drwxr-xr-x 0/0 0 2022-06-27 13:50 68c3a70ebf6e9af074b0b891cfb9fe6b417d2fc074cc29c90f842868296335a7/
-rw-r--r-- 0/0 3 2022-06-27 13:50 68c3a70ebf6e9af074b0b891cfb9fe6b417d2fc074cc29c90f842868296335a7/VERSION
-rw-r--r-- 0/0 406 2022-06-27 13:50 68c3a70ebf6e9af074b0b891cfb9fe6b417d2fc074cc29c90f842868296335a7/json
-rw-r--r-- 0/0 80342528 2022-06-27 13:50 68c3a70ebf6e9af074b0b891cfb9fe6b417d2fc074cc29c90f842868296335a7/layer.tar
drwxr-xr-x 0/0 0 2022-06-27 13:50 d3d1560b519c71f54e868a3a0818f0b844c938103707543661e0ec39175b3a47/
-rw-r--r-- 0/0 3 2022-06-27 13:50 d3d1560b519c71f54e868a3a0818f0b844c938103707543661e0ec39175b3a47/VERSION
-rw-r--r-- 0/0 1301 2022-06-27 13:50 d3d1560b519c71f54e868a3a0818f0b844c938103707543661e0ec39175b3a47/json
-rw-r--r-- 0/0 2060800 2022-06-27 13:50 d3d1560b519c71f54e868a3a0818f0b844c938103707543661e0ec39175b3a47/layer.tar
-rw-r--r-- 0/0 288 1970-01-01 08:00 manifest.json
-rw-r--r-- 0/0 98 1970-01-01 08:00 repositories

docker load

docker save相对的是docker load,用于从tar归档文件中加载Image到docker host中进行集中化管理;docker load的语法格式如下:

1
$ docker load [OPTIONS]

docker load支持从tar归档文件(支持gzip,bzip2,xz的压缩格式)或者STDIN进行镜像的恢复,恢复的Images信息包括repo名字和tag信息;docker load支持的OPTIONS有两个,如下:

  • -i, --input:输入的tar归档文件;
  • -q, --quiet:忽略load过程的输出信息

如下测试:

1
2
$ docker load -i helloworld.tar
Loaded image: test/helloworld:latest

当load的时候出现Image的repo和tag和docker host存在重复的时候,会覆盖之前旧的Images

docker export

docker export是将一个Container的文件系统导出为一个tar归档文件,命令格式如下,OPTION只支持-o, --output选项:

1
$ docker export [OPTIONS] CONTAINER

docker save的差别是:

  • docker save的操作对象是一个Imagedocker export的操作对象是一个Container

  • docker save会将Image的所有Layer信息,repo和tag信息都保存为tar归档文件;dokcer export会将Container的平铺开的整个文件系统进行归档打包,打包内容不包含任何的对应Image的结构信息;

下面是docker export测试,我们可以看到导出的归档文件就是整个文件系统,而docker save导出的结果在前面一节我们看了是包含Image层级信息的归档文件;

1
2
3
4
5
6
7
8
9
10
11
12
13
$ docker export 7bd9f0152dff > helloworld_container.tar
# 可以看到归档文件是整个文件系统
$ tar -tvf helloworld_container.tar |head
-rwxr-xr-x 0/0 0 2022-07-06 07:18 .dockerenv
lrwxrwxrwx 0/0 0 2022-06-11 19:49 bin -> usr/bin
drwxr-xr-x 0/0 0 2022-04-18 18:28 boot/
drwxr-xr-x 0/0 0 2022-07-06 07:18 dev/
-rwxr-xr-x 0/0 0 2022-07-06 07:18 dev/console
drwxr-xr-x 0/0 0 2022-07-06 07:18 dev/pts/
drwxr-xr-x 0/0 0 2022-07-06 07:18 dev/shm/
drwxr-xr-x 0/0 0 2022-07-06 07:18 etc/
-rw------- 0/0 0 2022-06-11 19:49 etc/.pwd.lock
-rw-r--r-- 0/0 3028 2022-06-11 19:50 etc/adduser.conf

docker export导出的时候,不会将Container的volumes挂载的数据进行归档,只会归档底层挂载的目录;可以参考如何备份,恢复和迁移数据volumes

docker import

docker import用于从tar归档文件或者标准输入中创建一个新的Image;这里和docker load的区别是docker save & docker export之间的区别导致的;stackoverflow上也有关于docker load and import difference的讨论;

  • docker load会加载归档文件中的镜像,包括repo和tag名字,不做任何修改,而docker import会创建新的Image,如果不指定repo和tag名字,会默认为none;
  • docker image支持从docker savedocker export两类导出的归档数据中进行Image的创建

docker import的格式如下:

1
$ docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]

docker import支持从三种源进行Image的构建,分别是:本地文件,URL,STDIN

1
2
3
4
5
# docker import helloworld.tar 
sha256:b8341bd5729572eca48873962f63bb4307ec1369c59d18263d4526c4728f1565
[root@VM-131-137-centos /data/home/walkerdu/soft/docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> b8341bd57295 8 seconds ago 82.4MB

docker history

docker history用于查看一个Image的各个Layer的信息,Image是由一个个的Layer叠加而成的;每一层Layer都是上层Layer的Parentdocker history就可以展示这些Layer信息,及其是如何构建的,如下是命令的格式:

1
$ docker history [OPTIONS] IMAGE

支持的选项有:

  • --format:格式化输出每一层的Image信息;具体参数值可以参考官方文档
  • -H, --human:每一层的Image大小的输出用可读格式,默认为true;
  • --no-trunc:输出不截断,显示Image的完整的所有信息;
  • -q, --quiet:只输出每层的Image ID;

如下测试:

1
2
3
4
5
6
7
8
$ docker history  test/helloworld
IMAGE CREATED CREATED BY SIZE COMMENT
156571ba9cfc 8 days ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "./he… 0B
a8531a366e53 8 days ago /bin/sh -c cat /etc/security/limits.conf 0B
7a7dec1369d8 9 days ago /bin/sh -c #(nop) COPY file:e562b4187dbcae3e… 2.06MB
3003ca74986b 3 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 3 weeks ago /bin/sh -c #(nop) ADD file:7b14f19bdf02562d0… 77.8MB
<missing> 3 weeks ago /bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH 0B

docker rmi

docker rmi用于从docker host的本地镜像仓库中删除指定Image,如果一个Image含有多个tags,必须指定对应的repo:tag进行指定删除;如果一个image只有一个tag,那么可以直接通过ImageID进行删除;docker rmi不会删除远端注册中心的镜像;docker rmi的命令格式如下:

1
$ docker rmi [OPTIONS] IMAGE [IMAGE...]

OPTION支持的选项如下:

  • -f, --force:当Image有关联运行的Container,可以通过此选项强制删除;
  • --no-prune:不删除没有打tag的父镜像;

docker images

docker images用于列出docker host上所有的Image列表;命令格式如下:

1
$ docker images [OPTIONS] [REPOSITORY[:TAG]]

可以指定显示特定repo和tag的Image;默认docker images会显示所有的顶层的Image,以及对应的repo,tags和对应的sizedocker images支持的选项列表如下:

  • -a, --all:显示所有Images,包括中间层的Image,默认只会显示顶层的Image,不会展示中间层的Image,我们现在知道image的构造是一层一层堆叠的,为了加速Image的构建过程和减少Image的所占用大小;
  • --digests:显示Image的digest信息,也就是hash值;
  • -f, --filter:用于过滤特殊条件的Image,--filter支持的参数格式为”key=value”对,匹配多个参数需要多个--filter选项来标识,例如--filter "foo=bar" --filter "bob=alice",目前--filter支持的选项参数有以下几种:
    • dangling:取值为true/false;用来筛选没有打tag的Image;
    • label:使用方式为:label=<key>或者label=<key>=<value>,用来筛选特定label的Image;
    • before:使用方式为:before=<image-name>[:<tag>]或者before=<image-id>或者before=<image-name@digest>,用来过滤创建比指定Image早的Image;
    • after:使用方式和before一样,用来过滤比指定Image创建晚的Image;
    • reference:用来匹配特定repo和tag的Image;
  • --format:格式化输出Image的信息;
  • --no-trunc:输出不截断,显示Image的完整的所有信息;
  • -q, --quiet:只输出所有的Image ID;

2. docker容器生命管理

docker run

docker run用来运行一个container,其格式语法如下:

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

docker run命令运行一个container,其过程其实分了两步:

  • 基于一个image之上,创建一个可修改的container layer;
  • 然后通过特殊命令启动该container layer;

docker run等价于先调用API/containers/create,然后再调用/containers/(id)/start;且已经stop的container,可以通过docker start进行恢复,包括只之前running时发生的修改;具体相关docker run的手册说明可以详见官方指引

docker run的构建参数OPTIONS选项列表,具体可参考官方文档,有一些是和docker build重合的,区别在于,他们在docker build中只影响构建镜像的过程,而docekr run的修改会影响运行的container;下面介绍一些比较重要的参数:

  • --name

给要运行的container,指定一个名字;如下:

1
2
3
4
$ docker run -d --name walker test/helloworld 
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4113dceb42e0 test/helloworld "/bin/sh -c ./hellow…" 3 seconds ago Up 3 seconds walker

如果运行container的时候,不指定名字,默认会生成一个随机的名字,但是这个随机的名字,看上去又有点生成规则。如下:

1
2
3
4
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9faf038fe924 test/helloworld "/bin/sh -c ./hellow…" 3 seconds ago Up 1 second hardcore_stonebraker
5f7f11848962 test/helloworld "/bin/sh -c ./hellow…" 26 seconds ago Up 25 seconds infallible_carson

经过请教Claude.AI得知,Docker生成的默认容器名称是两个英文单词拼接而成的。规则如下:

  1. 前缀单词,是从Docker内置的一个adjective(形容词)词库中随机选取的。这个词库包含许多常用的形容词,例如:admiring、desperate、silly、jovial、awesome等等。
  2. 后缀单词则,从Docker的另一个著名科学家名字词库中随机选取。这个词库包含众多著名科学家的名字,比如:einstein、feynman、hawking、curie等。

具体实现可以参考Docker的官方实现:moby names-generator.go

  • -it, --interactive, --tty

-it这两个选项一般会一起使用,--interactive选项是保持container的STDIN开启,这样运行的container才可以进行shell指令交互,进行相关调试;--tty选项为container提供一个伪终端(pseudo-TTY)绑定STDIN,这样才可以进行容器登录,并通过shell交互进行操作;

  • -d, -detach:后台运行容器,一般服务都是以后台daemon的方式运行;
  • --cidfile

运行container的时候,尝试创建一个新文件,并将container id写入其中,如果文件已经存在,将会返回错误,不会启动container,此cidfile在docker run执行结果后,就会关闭;该功能的目的是为了防止container多开,类似于进程通过pid文件来防多开的设计;如下测试:

1
2
3
4
5
6
$ docker run --cidfile hw.cid -d test/helloworld
$ cat hw.cid
35777669385a31e7d382a616b6b6d5363e600537de3f125854f83703dac75de2
# 再次running
$ docker run --cidfile hw.cid -d test/helloworld
docker: Container ID file found, make sure the other container isn't running or delete hw.cid.
  • -w, --workdir

设置container的工作目录,如果路径不存在,则会自动创建一个;这个参数会覆盖docker build镜像构建时,指定的container的工作目录;如下:

1
2
3
4
5
6
7
# 修改镜像的工作目录,会发现CMD找不到容器启动要执行的exec文件了
$ docker run -w /tmp/hw_dir -it test/helloworld
/bin/sh: 1: ./helloworld: not found

# 修改镜像的工作目录,执行pwd命令
$ docker run -w /tmp/hw_dir -it test/helloworld pwd
/tmp/hw_dir

此选项的用途是创建一个特权容器;默认权限的容器运行时对宿主机来说是一个普通用户,无法访问很多资源,例如硬件资源;特权权限允许容器拥有宿主机的root权限;常用在Docker-in-Docker,在一个容器中允许另一个容器,但是有很多安全问题;参考文档;下面是关于特权权限的开启示例:

1
2
3
4
5
6
7
8
9
10
11
$ docker run  -it test/helloworld  bash
root@5b12bfd00f49:/# mount -t tmpfs none /mnt
mount: /mnt: permission denied.

# 开启特权权限
$ docker run --privileged -it test/helloworld bash
root@d35cd8731f2d:/# mount -t tmpfs none /mnt
root@d35cd8731f2d:/# df -h
Filesystem Size Used Avail Use% Mounted on
...
none 16G 0 16G 0% /mnt
  • -v, --volume

将宿主机的目录挂载到Container的对应的目录中;在前面docker build的VOLUME指令中,我们知道VOLUME指令用来持久化数据的;前已经介绍了关于volume的使用有两种方法,现在又多了一种,可以挂载本地目录:

  1. docker run的时候不显示挂载volume,对于docker build中的挂载点,会使用匿名的volume,VOLUME指令默认在Container运行的时候创建的匿名Volume,虽然是持久化的,但是无法与其他Container共享
  2. docker run显示指定有名的volume挂载,这种时候会在宿主机的指定路径下/var/lib/docker/volumes/vol_name/_data创建对应的名字的volume挂载到Container中;既做到了持久化,也可以和其他Container共享数据
  3. docker run在volume挂载还可以指定本地目录挂载到Container中,如下:
1
2
3
4
5
6
$ docker run -v /tmp/helloworld_dir:/walkerdu -w /walkerdu -it test/helloworld bash
root@acd7b86b4bce:/walkerdu# touch balabala


$ ls /tmp/helloworld_dir/
balabala

当要挂载的本地目录不存在时候会自动创建,我们可以看到通过volume来挂载目录到Container中的方式,也可以进行数据的持久化

  • --mount

mount选项支持在Container中挂载volumes,宿主机目录,tmpfs;它的功能覆盖了上面介绍的--volume选项,只是语法格式不一样而已;详见官方关于VOLUME的说明;如下,mount指令由一组用逗号分隔的<key>=<value>组成;

1
--mount '<key>=<value>, <key>=<value>...'

虽然官方没有计划弃用--volume(毕竟要向后兼容),但是官方建议使用--mount来进行volume挂载进行数据持久化

  • --read-only让Container的整个根文件系统变成只读,除了--volume--mount指定挂载的目录;
  • -p, --expose

用于绑定本地主机端口和Container的端口,详见Docker NetWorking;格式如下:

1
-p [host_ip:]host_port:container_port

主机IP可以不填,默认对所有网卡生效;在前面EXPOSE指令也有介绍,这里不再赘述;

--pull选项用于指定在准备运行Container的时候,如何拉取所需的镜像,如下三个选项参数:

参数值 说明
missing 如果docker deamon中不存在需要的Image,则进行拉取,否则使用已经cache的Image;默认采用此种方式
never 不拉取Image,如果docker deamon中没有,则报错
always 创建Container前,总是拉取最新的Image

用来在Container中设置环境变量,可以用来覆盖Image中docker build设置的同名环境变量,使用如下:

1
2
3
4
5
6
7
8
9
10
$ cat env.txt 
ENV3=val3

$ docker run -e ENV1=val1 --env ENV2=val2 --env-file env.txt -it test/helloworld bash
root@7affea67ff79:/# env
...
ENV2=val2
ENV3=val3
ENV1=val1
...

注意:当选项只设置了环境变量的名字,没有设置value,不会覆盖Container中的环境变量

用来设置Container的元数据即标签;语法格式和环境变量类似;详细参考官方Label说明;如下:

1
2
3
4
5
6
7
8
9
10
11
12
$ cat label.txt 
label3=val3

$ docker run -l label1=val1 --label label2=val2 --label-file label.txt -d test/helloworld
$ docker inspect 0f6861f0984e
...
"Labels": {
"label1": "",
"label2": "val2",
"label3": "val3"
}
...

注意:label可以只设置key,没有value,默认label的值为空串””,如果label的key有重复,那么后面的label会覆盖前面设置的label;

该参数用于设置Container在退出之后的重启策略,如下:

参数 说明
no Container退出后,不自动重启,默认选项
on-failure[:max-retries] 在Container退出后的exit code非0时,尝试进行重启,可以设置重试次数
unless-stopped 除非显示的stop Container,其他情况都重启
always 只要Container退出,就一直尝试重启

该选项用来docker stop Container的时候,发送给Container的信号,可以是信号名SIG<NAME>,也可以是信号值;如果不设置,默认是SIGTERM

该选项用来设置在向Container发送stop信号后,等待多少秒超时还没有完成stop的话,就执行强杀,即发送SIGKILL信号;

如果--stop-timeout被设置为-1,那么会永远等待Container的结束;默认缺省值是有docker deamon决定的,Linux Container 10s,Windows Container 30s

此参数用于限制Container最大允许使用的内存,Linux上是通过设置cgroup来实现;可以查看/sys/fs/cgroup/memory/memory.limit_in_bytes

  • --cpus:使用的cpu核数
  • --sysctl:设置Container运行时的一些内核参数;

docker create

docker create用于创建一个Container,在前面docker run介绍时,我们知道运行一个Container分为两步,第一步就是调用/containers/create的API在一个image之上,创建一个可修改的container layer;docker create就是干这个事情的:创建一个containr layer,然后不运行;

docker create创建完Container后,会在标准输出输出一个Container ID,此Container在创建完后的状态为Created,如下:

1
2
3
4
$ docker create -e A=B test/helloworld
e05febc25ab27f127f2fe754ab17a5b2f5cef984fb8d9f694b1fdcb6d8babda0
$ docker ps -a|grep e05febc25ab2
e05febc25ab2 test/helloworld "/bin/sh -c ./hellow…" 14 seconds ago Created cranky_lichterman

docker create等价于docker run -d,除了它创建的Container不会自动启动运行;此种方式对于想要事先配置好Container,然后按需启动很有用处;

后面可以通过docker start命令来启动容器,如下:

1
2
3
4
$ docker start -ai e05febc25ab2
2022-07-04 07:18:06 Hello World.
2022-07-04 07:18:07 Hello World.
...

docker create的命令格式如下,和docker run的OPTIONS基本是一样的,具体OPTIONS列表可以参考前一节,或者docker run指引

1
$ docker create [OPTIONS] IMAGE [COMMAND] [ARG...]

docker start

docker start用于启动没有运行(例如Created,Exited状态)的Container ,docker start的格式如下:

1
$ docker start [OPTIONS] CONTAINER [CONTAINER...]

docker start的OPTIONS比较少,因为Container的相关参数在其创建的时候已经写入了Container中,docker start相对只是负责启动,有一些简单的输入输出的选项,可以参考docker start的手册说明;

需要注意的是:如上面命令格式,**docker start后面除了OPTIONS的参数,只支持传入Container的名字**,不支持其他的参数传入,可以传入多个Container,批量操作;

docker stop

docker stop用于stop处于running状态的Container,同样支持操作多个容器;命令格式如下:

1
$ docker stop [OPTIONS] CONTAINER [CONTAINER...]

docker stop后,默认会向Container的主进程发送一个SIGTERM信号,如果在规定的优雅退出时间内,容器没有Stop,则会继续发送SIGKILL进行强杀;默认发送的信号在介绍docker run的时候已经介绍,可以通过两种方式进行修改:

  • STOPSIGNALdocker build的时候设置
  • --stop-signaldocker run或者docker create的时候设置,会覆盖DockfileSTOPSIGNAL设置;

docker stop支持的选项只有一个-t, --time,设置Container在收到停止信号后的优雅退出超时时间;我们知道docker run或者docker create的时候,可以通过--stop-timeout选项来设置Container的优雅退出超时时间;docker stop的超时时间设置优先级高于docker run设置的超时时间;如下测试:

1
2
3
4
5
6
7
8
9
# 设置退出超时时间为20s
$ docker run --stop-timeout=20 -d test/helloworld
68c0539616cb4f8b458c7fe09436874a41061e17dcfbe67f2c87d0eb905661a0

# stop的时候设置超时退出时间为2s,可以看到2s后就强杀了
$ date && docker stop -t 2 68c0539616cb && date
Mon Jul 4 16:54:47 CST 2022
68c0539616cb
Mon Jul 4 16:54:49 CST 2022

docker restart

docker restart用于重启一个Container,等价于先docker stop,然后再docker start;格式 如下:

1
$ docker restart [OPTIONS] CONTAINER [CONTAINER...]

目前支持的参数只有一个-t, --time,和docker stop一样,用于指定Container优雅退出的的超时时间;

docker pause/unpause

pause用于暂停容器中的所有进程,格式如下:

1
2
$ docker pause CONTAINER [CONTAINER...]
$ docker unpause CONTAINER [CONTAINER...]

docker pause,在Linux中,通过cgroups的freezer cgroup来实现进程的暂停和恢复;Linux中,

  • 一般暂停进程都是通过发送SIGSTOP信号进行来实现,此信号会被进程观察到,虽然其不能忽略此信号也不能捕获该信号
  • 另外一种方式暂停进程是通过freeze cgroup来实现,是进程无法感知的,也是不能被捕获的;

如下,登录容器查看输出结果,然后进行pause/unpause,输出如下:

1
2
3
4
5
6
$ docker exec -it 68c0539616cb bash
root@68c0539616cb:/# tail -f helloworld.txt
2022-07-04 11:44:14 Hello World.
2022-07-04 11:44:15 Hello World.
2022-07-04 11:44:32 Hello World.
2022-07-04 11:44:33 Hello World.

docker kill

docker kill用于kill正在运行的Container,格式如下:

1
$ docker kill [OPTIONS] CONTAINER [CONTAINER...]

docker kill默认会向Container的主进程发送SIGKILL信号;可以通过--signal选项来设置发送给Container的信号值;这里的CONTAINER可以是Container ID,Container ID的前缀,也可以是Container NameContainer ID前缀匹配如果存在唯一的Container ID,则执行docker kill操作;

docker kill目前只支持--signal选项;

docker kill的使用方式和linux的kill命令很像,--signal可以传递任何预定义的信号值,让Container的主进程决定如何进行信号处理;这里需要注意:ENTERPOINTCMD指令在shell模式运行的时候,是作为/bin/sh -c的子进程运行的,这种情况是无法接受docker命令的信号的;信号只会发送给Container的主进程(PID=1);

docker rm

docker rm用于删除Container,正常情况下Container在stop之后,是一直存在的,可以被再次运行,我们可以通过docker rm可以最终删除该Container,格式如下:

1
$ docker rm [OPTIONS] CONTAINER [CONTAINER...]

docker rm支持的选项有如下三种:

  • -f, --force:强制(通过发送SIGKILL信号)将running的Container销毁;
  • -l, --link:用于删除Container之间默认的underlying桥接网络;
  • -v, --volumes:删除Container的时候,删除匿名volumes;

和git版本管理类似,可以通过docker container prune命令来移除所有已经stop的Containerdocker system prune可以移除整个docker无用的资源,例如stoped的Container, 未使用的images,以及网络;

3. docker容器操作

docker ps

docker ps用于查看Container的信息;名字类似linux中的ps命令,格式如下:

1
$ docker ps [OPTIONS]

docker ps支持的OPTIONS列表如下:

  • -a, --all:列出所有Containers,默认只会列出running状态的Containers
  • --foramt:格式化输出Container的信息;
  • -n, --last:最新的n个创建的Container;
  • -l, --latest:最新创建的容器;
  • --no-trunc:输出的容器信息不截断;
  • -q, --quiet:只输出Container ID信息;
  • -s, --size:显示总的文件大小;
  • -f, --filter:根据条件过滤出特定的Container列表,和docker images--filter功能是一样的,不过Container的ps支持的filter参数值更多,同样--filter选项的参数格式为”key=value”对,匹配多个参数需要多个--filter选项来标识,例如--filter "foo=bar" --filter "bob=alice"docker ps --filter支持的选项参数有以下几种:如下:
    • id:显示指定的Container ID的Container;
    • name:显示指定的Container Name的Container;
    • label:过滤指定label的Container,label的值可以是一个key,也可以是key=value,例如--filter "label=color"或者--filter "label=color=blue"
    • exited:显示特定exit code的Container,需要结合--all选项一起使用;
    • status:显示特定状态的Container,Container的状态包括:created, restarting, running, removing, paused, exited, dead
    • ancestor:查找祖先为给定Image的Containers,Image的参数可以为:<image-name>[:<tag>]或者<image-id>或者<image-name@digest>
    • before :在特定Container之前创建的容器;
    • since:在特定Container之后创建的容器;
    • volume:查找挂载特定volume的Containers;
    • network:查找连接特定网络的的Containers;
    • publishexpose:查找暴露特定端口的Container;
    • health:查找特定健康检测状态的Container,健康状态分为以下几类:startig, healthy, unhealthy, none
    • is-task:判断是否某个服务的task;

docker top

docker top用于输出指定指定容器中运行的进程,命令格式如下,支持docker ps的选项:

1
$ docker top CONTAINER [ps OPTIONS]

docker attach

docker attach用于attach到运行中的Container本地的标准输入,输出,错误流;格式如下:

1
$ docker attach [OPTIONS] CONTAINER

通过docker attach可以查看到正在运行Container的输出,也可以通过交互的方式控制Container的1号进程;可以对同一个Container进行同时多个attach操作;

attach命令只会显示ENTRYPOINT/CMD入口进程的输出,attach命令挂载到Container上后,看上去会把自己挂住,其实只是没有和终端交互而已

可以通过CTRL-C来停止一个Container,CTRL-C会默认向Container发送SIGKILL信号,如果--sig-proxy选项置为true,那么CTRL-C会发送SIGINT信号;如果Container以-it方式启动,那么可以通过发送CTRL-p CTRL-q来detach一个Container,Container任然会继续运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# attach到容器后,CTRL-C终止容器
$ docker run -d test/helloworld
9c9a3ed3979b3aa5ca9cc5bb93644f494ee77e6caba61dec7bb34809e76b47ef
$ docker attach 9c9a3ed3979b
2022-07-11 08:09:48 Hello World.
2022-07-11 08:09:49 Hello World.
2022-07-11 08:09:50 Hello World.
^C

# attach到-it运行的容器后,通过CTRL-p CTRL-q来detach,不影响容器的运行
$ docker run -d -it test/helloworld
513f8ba87e0c1d96febb8ccab3a24eadefe01125ed223772c3f9358ccf4063aa
$ docker attach 513f8ba87e0c
2022-07-11 09:46:03 Hello World.
2022-07-11 09:46:04 Hello World.
2022-07-11 09:46:05 Hello World.
read escape sequence

docker attach支持的选项列表如下:

  • --detach-keys:覆盖detach的Container时候的按键序列;
  • --no-stdin:attach的时候不attach STDIN;
  • sig-proxy:attach后,将所有终端的控制信号直接发送给Container的1号进程;例如CTRL-C按照正常的SIGINT信号发送给Container;

docker commit

docker commit用于从Container中创建一个新的Image,命令格式如下:

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

docker commit可以从Writeable Layer的Container上进行Image的构建,基于此创建的Image会包含Container中的文件修改和设置,这样可以很方便的以交互的方式进行Container的调试,或者将工作环境切换到其他服务器;

需要注意的是:基于Container构建的Image,并不会包含挂载的volumes;

默认情况下:在执行docker commit构建的时候,Container会被暂停,以降低数据损坏的可能性;可以通过选项--pause=false来不暂停Container;

docker commit支持的选项有以下几种:

  • -a, --author,指定作者;
  • -c, --change:在创建Image的时候,通过该参数来使用Dockerfile支持的指令进行Image构建结果的修改;,支持的Dockerfile的指令有:CMD|ENTRYPOINT|ENV|EXPOSE|LABEL|ONBUILD|USER|VOLUME|WORKDIR
  • -m, --message:commit消息;
  • -p, --pause:构建进项的时候是否暂停Container,默认为true;

4. docker registry操作

下面是一些对于Docker的注册中心的相关操作;

docker login

docker login用于登录指定的docker registry,命令格式如下:

1
$ docker login [OPTIONS] [SERVER]

支持的选项如下:

  • -u, --username:登录用户名;

  • -p, --password:登录密码;

  • --password-stdin:从标准输入接收密码;

1
$ cat ~/my_password.txt | docker login --username foo --password-stdin

默认不指定SERVER,会登录到官方的Docker Hub注册中心;我们可以指定登录到任何公开或者私有的注册中心,登录后默认会将凭证以base64编码方式保存在$HOME/.docker/config.json (Linux)对应的auth字段中;Docker支持将登录的凭证存储在其他的凭证仓库,例如:操作系统本地的keychain,相对于Docker的本地config,将凭证存储在远端的仓库中会更加安全;

如果需要将凭证存储在远端仓库中,需要对应的远端仓库的helper或者说client程序来负责和Docker client进行存储和读取交互,目前支持的第三方凭证工具列表如下:

凭证的远端helper程序的配置如下:$HOME/.docker/config.json,对应的helper程序需要在$PATH路径中可以查找;

1
2
3
{
"credsStore": "osxkeychain"
}

对应的helper程序的配置需要以docker-credential-为前缀,加上helper程序名字,例如上面的helper程序的配置文件需命名为:docker-credential-osxkeychain

docker pull

docker pull用于从registry拉取一个Images或者一整个repo;命令格式如下:

1
$ docker pull [OPTIONS] NAME[:TAG|@DIGEST]

我们大部分的Images的最顶层都是从 Docker Hub注册中心拉取获得的,Docker Hub中包含了很多已经构建好的基础Images,我们都可以进行拉取使用,不需要我们自己进行单独的构建;docker pull拉取的时候Docker daemon默认会同时并发的拉取一个Image的三层镜像;如果要修改该设置,需要设置dockerd的--max-concurrent-downloads参数;

docker pull支持的选项如下:

  • -a, --all-tags拉取一个repo下面的所有tag的Image
  • --disable-content-trust:忽略Image的校验;
  • --platform:拉取特定平台的镜像,默认是根据Docker daemon的OS来自动匹配拉取;
  • -q, --quiet:隐藏详细信息输出;

如下测试,

1
2
3
4
5
6
7
# 默认拉取ubuntu:latest的镜像
$ docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
405f018f9d1d: Pull complete
Digest: sha256:b6b83d3c331794420340093eb706a6f152d9c1fa51b262d9bf34594887c2c7ac
Status: Downloaded newer image for ubuntu:latest

等价于:

1
2
$ docker pull ubuntu:latest
$ docker pull ubuntu@sha256:b6b83d3c331794420340093eb706a6f152d9c1fa51b262d9bf34594887c2c7ac

默认情况,docker pull从Docker Hub中拉取Images,也可以显示的指定从特定的注册中心拉取镜像,如下从AWS的注册中心拉取ubuntu镜像:

1
2
3
4
5
6
$ docker pull public.ecr.aws/lts/ubuntu
Using default tag: latest
latest: Pulling from lts/ubuntu
25dab5ae6e34: Pull complete
Digest: sha256:3cd697a1c2c7bae4958052fd5a225a3227f5af5ab89cd608036c3ee210cd4fcf
Status: Downloaded newer image for public.ecr.aws/lts/ubuntu:latest

Docker daemon通过https://协议和注册中心通信,除非注册中心被设置允许通过不安全的http协议进行通信;具体可以参考如何设置insecure registries

docker push

docker push用于向registry推送一个Images或者一整个repo;命令格式如下:

1
$ docker push [OPTIONS] NAME[:TAG]

docker pushdocker pull功能是一对,使用格式和语法基本一致,这里不再详述了;

docker push推送的时候Docker daemon默认会同时并发的推送一个Image的五层镜像;如果要修改该设置,需要设置dockerd的--max-concurrent-uploads参数;

docker search用从Docker Hub中搜索Images;不像docker pull/push可以从其他注册中心进行镜像操作,docker search只能从官方的Docker Hub进行Image的搜索docker search命令的格式如下:

1
$ docker search [OPTIONS] TERM

支持的OPTION列表如下:

  • -f, --filter:按照指定的flag进行过滤搜索;
  • --format:按照Go Template进行结果输出;
  • --limit:最大搜索结果,默认25个;
  • --no-trunc:输出结果不截断;

5. docker其他操作

docker inspect

docker inspect用于查看Docker对象的详细底层结构控制信息;指令的格式如下:

1
$ docker inspect [OPTIONS] NAME|ID [NAME|ID...]

docker inspect默认输出的信息是JSON数组格式化的;它支持的OPTIONS列表如下:

  • -f, --format:按照指定的Go模板进行格式化输出;这个还是比较常用的,关于Go模块,这篇翻译Go Template不错,可以参考;
  • -s, --size:针对Container对象,输出总文件的大小;
  • --type:如果出现相同名的对象,需要指定要查看对象的类型,例如--type=container或者--type=image,当然最好的还是通过归类的对象的inspect命令进行信息的查看,例如docker container inspect

docker events

docker events用于从docker deamon中获取实时的事件消息;对于不同的Docker对象会有不同种类的事件类型,具体的事件类型可以参考官方手册关于各种对象的事件列表

问题汇总

docker.sock权限问题

1
2
3
$docker run -d -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management

docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/create?name=rabbitmq: dial unix /var/run/docker.sock: connect: permission denied.

我们看一下docker.sock文件的权限如下:

1
2
$ll /var/run/docker.sock
srw-rw---- 1 root docker 0 Jul 15 12:51 /var/run/docker.sock

该文件是docker安装时创建的sock文件,它的用途是:

Docker.sock is a Unix socket that enables the Docker server-side daemon, dockerd, to communicate with its command-line interface via a REST API.

比较安全的解决方法是,参考Stack Overflow,docker默认只能以root权限运行,如果想以普通用户运行docker进程,需要将该用户加入docker group,如下流程:

1
2
3
4
5
6
7
8
# Create the docker group if it does not exist
$ sudo groupadd docker
# Add your user to the docker group.
$ sudo usermod -aG docker $USER
# Run the following command or Logout and login again and run (that doesn't work you may need to reboot your machine first)
$ newgrp docker
# Check if docker can be run without root
$ docker run hello-world