在有了之前的Dockerfile基础知识后,我们知道该如何编写一个简单的Dockfile,那么如果通过docker client命令来进行Image的构建呢,本节就来阐述一些docker client的一些使用;
在介绍docker client的各个操作命令之前先说明一下docker client操作的分类,按照操作的对象不同,可以分为以下几个大类:
docker image COMMAND:docker的Image对象的相关操作;docker container COMMAND:docker的Container对象的相关操作;docker network COMMAND:docker node COMMAND:
你会发现docker client的很多命令的功能是重复的,例如docker build和docker image build,docker run和docker container run,参考Stack Overflow得知,这是由于历史原因导致的;最早docker的命令并没有按照操作对象进行分类管理,例如docker ps,docker build等,后来,docker client变得更加结构化,收敛到了几大类,同时为了向后兼容,所以看上去docker client的命令看上去比较乱;
1. docker镜像管理
docker build
前面在「基于Dockerfile的镜像构建」一节中,已经简单的说明了如何通过docker build进行镜像的构建,这里参考docker build手册再详细说明一下:
docker build 的语法,如下:
shelldocker build [OPTIONS] PATH | URL
docker build通过Dockerfile和构建上下文进行镜像的构建;构建的上下文是由PATH或URL指定位置的一个文件列表集合;所有构建过程都可以访问构建上下文的任何文件;
基于本地上下文构建
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文件格式,且压缩格式为xz,bzip2,gzip或者无压缩;使用语法格式如下:
text$ docker build http://server/context.tar.gz
- 文本文件
如果 URL 指向一个纯文本文件,那么该文本文件会被当做Dockerfile发送给docker daemon 用来进行镜像构建,且这种情况的构建是没有任何上下文的;
dockerfile$ docker build http://server/Dockerfile.txt
这里和传入文本文件类似的,docker build支持从STDIN中读取数据,将其作为Dockerfile进行镜像的构建;如下:
dockerfile$ 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构建上下文的根路径;如下:
shell$ 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 .构建的镜像的信息:
shell$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 9576343a7c1d 4 minutes ago 79.9MB
如下在构建镜像的时候,指定镜像的仓库名和tag:
shell$ 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系统资源的限制参数,参数的格式如下:
shell<type>=<soft limit>[:<hard limit>]
如下,设置镜像中资源的栈大小,不过变更只影响构建过程,并不会对最终构建的镜像有何影响:
shell$ docker build -t test/helloworld --ulimit stack=10240:10240 .
各种资源的类型名,在系统的/etc/security/limits.conf中有定义,如下:
shell$ 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,如下:
shell$ 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并没有影响到最终的镜像:
shell$ 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会被丢弃,如下:
dockerfileFROM debian AS build-env
# ...
FROM alpine AS production-env
# ...
执行只构建第一个build-envstage的构建指令如下:
shell$ docker build -t mybuildimage --target build-env .
docker tag
docker tag用于为一个Image对象打一个tag,其实就是给一个Image起另一个名字而已,格式如下:
shell$ docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
Image的名字是格式一般有两部分组成:注册中心和镜像名;中间用斜线分隔;注册中心名字是标准的DNS格式,可以包含主机的端口号;
Image的tag必须是由ASCII组成,可以包含:大小写字母,数字,下划线,点号,破折号;但是不能以点号,破折号开始,且长度不能超过128个字符长度;
打tag可以使用source image的Image ID,Image Name,如下简单示例:
shell# 通过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的语法格式如下:
shell$ docker save [OPTIONS] IMAGE [IMAGE...]
docker save默认会将tarred的镜像数据(所有layer的数据)输出到STDOUT中,可以通过-o, --output指定输出的tar文件名,也可以用重定向,管道进行操作,如下:
shell$ 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的语法格式如下:
shell$ docker load [OPTIONS]
docker load支持从tar归档文件(支持gzip,bzip2,xz的压缩格式)或者STDIN进行镜像的恢复,恢复的Images信息包括repo名字和tag信息;docker load支持的OPTIONS有两个,如下:
-i, --input:输入的tar归档文件;-q, --quiet:忽略load过程的输出信息
如下测试:
shell$ 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选项:
shell$ docker export [OPTIONS] CONTAINER
和docker save的差别是:
-
docker save的操作对象是一个Image,docker export的操作对象是一个Container; -
docker save会将Image的所有Layer信息,repo和tag信息都保存为tar归档文件;dokcer export会将Container的平铺开的整个文件系统进行归档打包,打包内容不包含任何的对应Image的结构信息;
下面是docker export测试,我们可以看到导出的归档文件就是整个文件系统,而docker save导出的结果在前面一节我们看了是包含Image层级信息的归档文件;
shell$ 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 save和docker export两类导出的归档数据中进行Image的创建;
docker import的格式如下:
shell$ docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
docker import支持从三种源进行Image的构建,分别是:本地文件,URL,STDIN;
shell# 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的Parent,docker history就可以展示这些Layer信息,及其是如何构建的,如下是命令的格式:
shell$ docker history [OPTIONS] IMAGE
支持的选项有:
--format:格式化输出每一层的Image信息;具体参数值可以参考官方文档;-H, --human:每一层的Image大小的输出用可读格式,默认为true;--no-trunc:输出不截断,显示Image的完整的所有信息;-q, --quiet:只输出每层的Image ID;
如下测试:
shell$ 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的命令格式如下:
shell$ docker rmi [OPTIONS] IMAGE [IMAGE...]
OPTION支持的选项如下:
-f, --force:当Image有关联运行的Container,可以通过此选项强制删除;--no-prune:不删除没有打tag的父镜像;
docker images
docker images用于列出docker host上所有的Image列表;命令格式如下:
shell$ docker images [OPTIONS] [REPOSITORY[:TAG]]
可以指定显示特定repo和tag的Image;默认docker images会显示所有的顶层的Image,以及对应的repo,tags和对应的size;docker 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,其格式语法如下:
shelldocker 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,指定一个名字;如下:
shell$ 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的时候,不指定名字,默认会生成一个随机的名字,但是这个随机的名字,看上去又有点生成规则。如下:
shell# 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生成的默认容器名称是两个英文单词拼接而成的。规则如下:
- 前缀单词,是从Docker内置的一个adjective(形容词)词库中随机选取的。这个词库包含许多常用的形容词,例如:admiring、desperate、silly、jovial、awesome等等。
- 后缀单词则,从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文件来防多开的设计;如下测试:
shell$ 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的工作目录;如下:
shell# 修改镜像的工作目录,会发现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,在一个容器中允许另一个容器,但是有很多安全问题;参考文档;下面是关于特权权限的开启示例:
shell$ 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的使用有两种方法,现在又多了一种,可以挂载本地目录:
docker run的时候不显示挂载volume,对于docker build中的挂载点,会使用匿名的volume,VOLUME指令默认在Container运行的时候创建的匿名Volume,虽然是持久化的,但是无法与其他Container共享;docker run显示指定有名的volume挂载,这种时候会在宿主机的指定路径下/var/lib/docker/volumes/vol_name/_data创建对应的名字的volume挂载到Container中;既做到了持久化,也可以和其他Container共享数据;docker run在volume挂载还可以指定本地目录挂载到Container中,如下:
shell$ 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>组成;
shell--mount '<key>=<value>, <key>=<value>...'
虽然官方没有计划弃用--volume(毕竟要向后兼容),但是官方建议使用--mount来进行volume挂载进行数据持久化;
--read-only:让Container的整个根文件系统变成只读,除了--volume和--mount指定挂载的目录;-p, --expose
用于绑定本地主机端口和Container的端口,详见Docker NetWorking;格式如下:
shell-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设置的同名环境变量,使用如下:
shell$ 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说明;如下:
shell$ 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退出,就一直尝试重启 |
--add-host:在docker build中已经说明这里不再赘述;--ulimit:在docker build中已经说明这里不再赘述;--stop-signal
该选项用来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,如下:
bash$ 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命令来启动容器,如下:
shell$ 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指引;
shell$ docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
docker start
docker start用于启动没有运行(例如Created,Exited状态)的Container ,docker start的格式如下:
shell$ 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,同样支持操作多个容器;命令格式如下:
shell$ docker stop [OPTIONS] CONTAINER [CONTAINER...]
docker stop后,默认会向Container的主进程发送一个SIGTERM信号,如果在规定的优雅退出时间内,容器没有Stop,则会继续发送SIGKILL进行强杀;默认发送的信号在介绍docker run的时候已经介绍,可以通过两种方式进行修改:
STOPSIGNAL在docker build的时候设置--stop-signal在docker run或者docker create的时候设置,会覆盖Dockfile的STOPSIGNAL设置;
docker stop支持的选项只有一个-t, --time,设置Container在收到停止信号后的优雅退出超时时间;我们知道docker run或者docker create的时候,可以通过--stop-timeout选项来设置Container的优雅退出超时时间;docker stop的超时时间设置优先级高于docker run设置的超时时间;如下测试:
shell# 设置退出超时时间为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;格式 如下:
shell$ docker restart [OPTIONS] CONTAINER [CONTAINER...]
目前支持的参数只有一个-t, --time,和docker stop一样,用于指定Container优雅退出的的超时时间;
docker pause/unpause
pause用于暂停容器中的所有进程,格式如下:
shell$ docker pause CONTAINER [CONTAINER...]
$ docker unpause CONTAINER [CONTAINER...]
docker pause,在Linux中,通过cgroups的freezer cgroup来实现进程的暂停和恢复;Linux中,
- 一般暂停进程都是通过发送
SIGSTOP信号进行来实现,此信号会被进程观察到,虽然其不能忽略此信号也不能捕获该信号; - 另外一种方式暂停进程是通过
freeze cgroup来实现,是进程无法感知的,也是不能被捕获的;
如下,登录容器查看输出结果,然后进行pause/unpause,输出如下:
shell$ 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,格式如下:
shell$ docker kill [OPTIONS] CONTAINER [CONTAINER...]
docker kill默认会向Container的主进程发送SIGKILL信号;可以通过--signal选项来设置发送给Container的信号值;这里的CONTAINER可以是Container ID,Container ID的前缀,也可以是Container Name;Container ID前缀匹配如果存在唯一的Container ID,则执行docker kill操作;
docker kill目前只支持--signal选项;
docker kill的使用方式和linux的kill命令很像,--signal可以传递任何预定义的信号值,让Container的主进程决定如何进行信号处理;这里需要注意:ENTERPOINT和CMD指令在shell模式运行的时候,是作为/bin/sh -c的子进程运行的,这种情况是无法接受docker命令的信号的;信号只会发送给Container的主进程(PID=1);
docker rm
docker rm用于删除Container,正常情况下Container在stop之后,是一直存在的,可以被再次运行,我们可以通过docker rm可以最终删除该Container,格式如下:
shell$ 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的Container;docker system prune可以移除整个docker无用的资源,例如stoped的Container, 未使用的images,以及网络;
3. docker容器操作
docker ps
docker ps用于查看Container的信息;名字类似linux中的ps命令,格式如下:
shell$ 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;publish或expose:查找暴露特定端口的Container;health:查找特定健康检测状态的Container,健康状态分为以下几类:startig,healthy,unhealthy,none;is-task:判断是否某个服务的task;
docker top
docker top用于输出指定指定容器中运行的进程,命令格式如下,支持docker ps的选项:
shell$ docker top CONTAINER [ps OPTIONS]
docker attach
docker attach用于attach到运行中的Container本地的标准输入,输出,错误流;格式如下:
shell$ 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任然会继续运行;
shell# 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,命令格式如下:
shell$ 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,命令格式如下:
shell$ docker login [OPTIONS] [SERVER]
支持的选项如下:
-
-u, --username:登录用户名; -
-p, --password:登录密码; -
--password-stdin:从标准输入接收密码;
shell$ 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路径中可以查找;
json{
"credsStore": "osxkeychain"
}
对应的helper程序的配置需要以docker-credential-为前缀,加上helper程序名字,例如上面的helper程序的配置文件需命名为:docker-credential-osxkeychain
docker pull
docker pull用于从registry拉取一个Images或者一整个repo;命令格式如下:
shell$ 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:隐藏详细信息输出;
如下测试,
shell# 默认拉取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
等价于:
shell$ docker pull ubuntu:latest
$ docker pull ubuntu@sha256:b6b83d3c331794420340093eb706a6f152d9c1fa51b262d9bf34594887c2c7ac
默认情况,docker pull从Docker Hub中拉取Images,也可以显示的指定从特定的注册中心拉取镜像,如下从AWS的注册中心拉取ubuntu镜像:
shell$ 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;命令格式如下:
shell$ docker push [OPTIONS] NAME[:TAG]
docker push和docker pull功能是一对,使用格式和语法基本一致,这里不再详述了;
docker push推送的时候Docker daemon默认会同时并发的推送一个Image的五层镜像;如果要修改该设置,需要设置dockerd的--max-concurrent-uploads参数;
docker search
docker search用从Docker Hub中搜索Images;不像docker pull/push可以从其他注册中心进行镜像操作,docker search只能从官方的Docker Hub进行Image的搜索;docker search命令的格式如下:
shell$ docker search [OPTIONS] TERM
支持的OPTION列表如下:
-f, --filter:按照指定的flag进行过滤搜索;--format:按照Go Template进行结果输出;--limit:最大搜索结果,默认25个;--no-trunc:输出结果不截断;
5. docker其他操作
docker inspect
docker inspect用于查看Docker对象的详细底层结构控制信息;指令的格式如下:
shell$ 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权限问题
bash$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文件的权限如下:
bash$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,如下流程:
bash# 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
评论