-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
docker 学习笔记 #2
Comments
Compose 命令说明命令对象与格式对于 Compose 来说,大部分命令的对象既可以是项目本身,也可以指定为项目中的服务或者容器。如果没有特别的说明,命令对象将是项目,这意味着项目中所有的服务都会受到命令影响。 执行
docker compose [-f=<arg>...] [options] [COMMAND] [ARGS...] 命令选项
命令使用说明
|
一、入门文档
docker 新手练习
https://www.jianshu.com/p/bd5a8945e071
Dockerfile示例
Dockerfile 中每一个指令都会建立一层镜像
构建镜像
在
Dockerfile
文件所在目录执行docker build -t nginx:v3 .
来构建 docker 镜像。后有一个.
指定 上下文路径。当构建镜像的时候,用户指定构建镜像上下文的路径,docker build
命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。如果在
Dockerfile
中这么写:COPY ./package.json /app/
这并不是要复制执行
docker build
命令所在的目录下的package.json
,也不是复制Dockerfile
所在目录下的package.json
,而是复制 上下文(context) 目录下的package.json
。一般来说,应该将
Dockerfile
置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给 Docker 引擎,可以用.dockerignore
,剔除不需要不需要的文件。那么为什么会有人误以为
.
是指定Dockerfile
所在目录呢?这是因为在默认情况下,如果不额外指定Dockerfile
的话,会将上下文目录下的名为Dockerfile
的文件作为 Dockerfile。这只是默认行为,实际上
Dockerfile
的文件名并不要求必须为Dockerfile
,而且并不要求必须位于上下文目录中,比如可以用-f ../Dockerfile.php
参数指定某个文件作为Dockerfile
。当然,一般大家习惯性的会使用默认的文件名
Dockerfile
,以及会将其置于镜像构建上下文目录中。使用用 Git repo 进行构建
这行命令指定了构建所需的 Git repo,并且指定分支为
master
,构建目录为/amd64/hello-world/
,然后 Docker 就会自己去git clone
这个项目、切换到指定分支、并进入到指定目录后开始构建。其他构建方式
如果所给出的 URL 不是个 Git repo,而是个
tar
压缩包,那么 Docker 引擎会下载这个包,并自动解压缩,以其作为上下文,开始构建。docker build - < Dockerfile
或
cat Dockerfile | docker build -
如果标准输入传入的是文本文件,则将其视为
Dockerfile
,并开始构建。这种形式由于直接从标准输入中读取 Dockerfile 的内容,它没有上下文,因此不可以像其他方法那样可以将本地文件COPY
进镜像之类的事情。docker build - < context.tar.gz
如果发现标准输入的文件格式是
gzip
、bzip2
以及xz
的话,将会使其为上下文压缩包,直接将其展开,将里面视为上下文,并开始构建。Dockerfile 指令详解
COPY
格式:
COPY [--chown=<user>:<group>] <源路径>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
和
RUN
指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。COPY
指令将从构建上下文目录中<源路径>
的文件/目录复制到新的一层的镜像内的<目标路径>
位置。比如:COPY package.json /usr/src/app/
<源路径>
可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的filepath.Match
规则,如:<目标路径>
可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用WORKDIR
指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。此外,还需要注意一点,使用
COPY
指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候。在使用该指令的时候还可以加上
--chown=<user>:<group>
选项来改变文件的所属用户及所属组。如果源路径为文件夹,复制的时候不是直接复制该文件夹,而是将文件夹中的内容复制到目标路径。
ADD
ADD
指令和COPY
的格式和性质基本一致。但是在COPY
基础上增加了一些功能。比如
<源路径>
可以是一个URL
,这种情况下,Docker 引擎会试图去下载这个链接的文件放到<目标路径>
去。下载后的文件权限自动设置为600
,如果这并不是想要的权限,那么还需要增加额外的一层RUN
进行权限调整,另外,如果下载的是个压缩包,需要解压缩,也一样还需要额外的一层RUN
指令进行解压缩。所以不如直接使用RUN
指令,然后使用wget
或者curl
工具下载,处理权限、解压缩、然后清理无用文件更合理。因此,这个功能其实并不实用,而且不推荐使用。如果
<源路径>
为一个tar
压缩文件的话,压缩格式为gzip
,bzip2
以及xz
的情况下,ADD
指令将会自动解压缩这个压缩文件到<目标路径>
去。在某些情况下,这个自动解压缩的功能非常有用,比如官方镜像
ubuntu
中:但在某些情况下,如果我们真的是希望复制个压缩文件进去,而不解压缩,这时就不可以使用
ADD
命令了。在 Docker 官方的 Dockerfile 最佳实践文档 中要求,尽可能的使用
COPY
,因为COPY
的语义很明确,就是复制文件而已,而ADD
则包含了更复杂的功能,其行为也不一定很清晰。最适合使用ADD
的场合,就是所提及的需要自动解压缩的场合。另外需要注意的是,
ADD
指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。因此在
COPY
和ADD
指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用COPY
指令,仅在需要自动解压缩的场合使用ADD
。在使用该指令的时候还可以加上
--chown=<user>:<group>
选项来改变文件的所属用户及所属组。CMD
CMD
指令的格式和RUN
相似,也是两种格式:shell
格式:CMD <命令>
exec
格式:CMD ["可执行文件", "参数1", "参数2"...]
CMD ["参数1", "参数2"...]
。在指定了ENTRYPOINT
指令后,用CMD
指定具体的参数。之前介绍容器的时候曾经说过,Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。
CMD
指令就是用于指定默认的容器主进程的启动命令的。在运行时可以指定新的命令来替代镜像设置中的这个默认命令,比如,
ubuntu
镜像默认的CMD
是/bin/bash
,如果我们直接docker run -it ubuntu
的话,会直接进入bash
。我们也可以在运行时指定运行别的命令,如docker run -it ubuntu cat /etc/os-release
。这就是用cat /etc/os-release
命令替换了默认的/bin/bash
命令了,输出了系统版本信息。在指令格式上,一般推荐使用
exec
格式,这类格式在解析时会被解析为 JSON 数组,因此一定要使用双引号"
,而不要使用单引号。如果使用
shell
格式的话,实际的命令会被包装为sh -c
的参数的形式进行执行。比如:CMD echo $HOME
在实际执行中,会将其变更为:
这就是为什么我们可以使用环境变量的原因,因为这些环境变量会被 shell 进行解析处理。
提到
CMD
就不得不提容器中应用在前台执行和后台执行的问题。这是初学者常出现的一个混淆。Docker 不是虚拟机,容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样,用
systemd
去启动后台服务,容器内没有后台服务的概念。一些初学者将
CMD
写为:CMD service nginx start
然后发现容器执行后就立即退出了。甚至在容器内去使用
systemctl
命令结果却发现根本执行不了。这就是因为没有搞明白前台、后台的概念,没有区分容器和虚拟机的差异,依旧在以传统虚拟机的角度去理解容器。对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。
而使用
service nginx start
命令,则是希望 upstart 来以后台守护进程形式启动nginx
服务。而刚才说了CMD service nginx start
会被理解为CMD [ "sh", "-c", "service nginx start"]
,因此主进程实际上是sh
。那么当service nginx start
命令结束后,sh
也就结束了,sh
作为主进程退出了,自然就会令容器退出。正确的做法是直接执行
nginx
可执行文件,并且要求以前台形式运行。比如:ENTRYPOINT
ENTRYPOINT
的格式和RUN
指令格式一样,分为exec
格式和shell
格式。ENTRYPOINT
的目的和CMD
一样,都是在指定容器启动程序及参数。ENTRYPOINT
在运行时也可以替代,不过比CMD
要略显繁琐,需要通过docker run
的参数--entrypoint
来指定。当指定了
ENTRYPOINT
后,CMD
的含义就发生了改变,不再是直接的运行其命令,而是将CMD
的内容作为参数传给ENTRYPOINT
指令,换句话说实际执行时,将变为:那么有了
CMD
后,为什么还要有ENTRYPOINT
呢?这种<ENTRYPOINT> "<CMD>"
有什么好处么?让我们来看几个场景。场景一:让镜像变成像命令一样使用
假设我们需要一个得知自己当前公网 IP 的镜像,那么可以先用
CMD
来实现:假如我们使用
docker build -t myip .
来构建镜像的话,如果我们需要查询当前公网 IP,只需要执行:docker run myip # 当前 IP:61.148.226.66 来自:北京市 联通
嗯,这么看起来好像可以直接把镜像当做命令使用了,不过命令总有参数,如果我们希望加参数呢?比如从上面的
CMD
中可以看到实质的命令是curl
,那么如果我们希望显示 HTTP 头信息,就需要加上-i
参数。那么我们可以直接加-i
参数给docker run myip
么?docker run myip -i # docker: Error response from daemon: invalid header field value "oci runtime error: container_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable file not found in $PATH\"\n".
我们可以看到可执行文件找不到的报错,
executable file not found
。之前我们说过,跟在镜像名后面的是command
,运行时会替换CMD
的默认值。因此这里的-i
替换了原来的CMD
,而不是添加在原来的curl -s http://myip.ipip.net
后面。而-i
根本不是命令,所以自然找不到。那么如果我们希望加入
-i
这参数,我们就必须重新完整的输入这个命令:这显然不是很好的解决方案,而使用
ENTRYPOINT
就可以解决这个问题。现在我们重新用ENTRYPOINT
来实现这个镜像:这次我们再来尝试直接使用
docker run myip -i
:可以看到,这次成功了。这是因为当存在
ENTRYPOINT
后,CMD
的内容将会作为参数传给ENTRYPOINT
,而这里-i
就是新的CMD
,因此会作为参数传给curl
,从而达到了我们预期的效果。场景二:应用运行前的准备工作
启动容器就是启动主进程,但有些时候,启动主进程前,需要一些准备工作。
比如
mysql
类的数据库,可能需要一些数据库配置、初始化的工作,这些工作要在最终的 mysql 服务器运行之前解决。此外,可能希望避免使用
root
用户去启动服务,从而提高安全性,而在启动服务前还需要以root
身份执行一些必要的准备工作,最后切换到服务用户身份启动服务。或者除了服务外,其它命令依旧可以使用root
身份执行,方便调试等。这些准备工作是和容器
CMD
无关的,无论CMD
为什么,都需要事先进行一个预处理的工作。这种情况下,可以写一个脚本,然后放入ENTRYPOINT
中去执行,而这个脚本会将接到的参数(也就是<CMD>
)作为命令,在脚本最后执行。比如官方镜像redis
中就是这么做的:可以看到其中为了 redis 服务创建了 redis 用户,并在最后指定了
ENTRYPOINT
为docker-entrypoint.sh
脚本。该脚本的内容就是根据
CMD
的内容来判断,如果是redis-server
的话,则切换到redis
用户身份启动服务器,否则依旧使用root
身份执行。比如:ENV
格式有两种:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
这个指令很简单,就是设置环境变量而已,无论是后面的其它指令,如
RUN
,还是运行时的应用,都可以直接使用这里定义的环境变量。这个例子中演示了如何换行,以及对含有空格的值用双引号括起来的办法,这和 Shell 下的行为是一致的。
定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。比如在官方
node
镜像Dockerfile
中,就有类似这样的代码:在这里先定义了环境变量
NODE_VERSION
,其后的RUN
这层里,多次使用$NODE_VERSION
来进行操作定制。可以看到,将来升级镜像构建版本的时候,只需要更新7.2.0
即可,Dockerfile
构建维护变得更轻松了。下列指令可以支持环境变量展开:
ADD
、COPY
、ENV
、EXPOSE
、FROM
、LABEL
、USER
、WORKDIR
、VOLUME
、STOPSIGNAL
、ONBUILD
、RUN
。可以从这个指令列表里感觉到,环境变量可以使用的地方很多,很强大。通过环境变量,我们可以让一份
Dockerfile
制作更多的镜像,只需使用不同的环境变量即可。ARG
格式:
ARG <参数名>[=<默认值>]
构建参数和
ENV
的效果一样,都是设置环境变量。所不同的是,ARG
所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用ARG
保存密码之类的信息,因为docker history
还是可以看到所有值的。Dockerfile
中的ARG
指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令docker build
中用--build-arg <参数名>=<值>
来覆盖。灵活的使用
ARG
指令,能够在不修改 Dockerfile 的情况下,构建出不同的镜像。ARG 指令有生效范围,如果在
FROM
指令之前指定,那么只能用于FROM
指令中。使用上述 Dockerfile 会发现无法输出
${DOCKER_USERNAME}
变量的值,要想正常输出,你必须在FROM
之后再次指定ARG
对于多阶段构建,尤其要注意这个问题
对于上述 Dockerfile 两个
FROM
指令都可以使用${DOCKER_USERNAME}
,对于在各个阶段中使用的变量都必须在每个阶段分别指定:VOLUME
格式为:
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
之前我们说过,容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中,后面的章节我们会进一步介绍 Docker 卷的概念。为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在
Dockerfile
中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。VOLUME /data
这里的
/data
目录就会在容器运行时自动挂载为匿名卷,任何向/data
中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行容器时可以覆盖这个挂载设置。比如:在这行命令中,就使用了
mydata
这个命名卷挂载到了/data
这个位置,替代了Dockerfile
中定义的匿名卷的挂载配置。EXPOSE
格式为
EXPOSE <端口1> [<端口2>...]
。EXPOSE
指令是声明容器运行时提供服务的端口,这只是一个声明,在容器运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是docker run -P
时,会自动随机映射EXPOSE
的端口。要将
EXPOSE
和在运行时使用-p <宿主端口>:<容器端口>
区分开来。-p
,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而EXPOSE
仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。WORKDIR
格式为
WORKDIR <工作目录路径>
。使用
WORKDIR
指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR
会帮你建立目录。之前提到一些初学者常犯的错误是把
Dockerfile
等同于 Shell 脚本来书写,这种错误的理解还可能会导致出现下面这样的错误:如果将这个
Dockerfile
进行构建镜像运行后,会发现找不到/app/world.txt
文件,或者其内容不是hello
。原因其实很简单,在 Shell 中,连续两行是同一个进程执行环境,因此前一个命令修改的内存状态,会直接影响后一个命令;而在Dockerfile
中,这两行RUN
命令的执行环境根本不同,是两个完全不同的容器。这就是对Dockerfile
构建分层存储的概念不了解所导致的错误。之前说过每一个
RUN
都是启动一个容器、执行命令、然后提交存储层文件变更。第一层RUN cd /app
的执行仅仅是当前进程的工作目录变更,一个内存上的变化而已,其结果不会造成任何文件变更。而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完全没关系,自然不可能继承前一层构建过程中的内存变化。因此如果需要改变以后各层的工作目录的位置,那么应该使用
WORKDIR
指令。如果你的
WORKDIR
指令使用的相对路径,那么所切换的路径与之前的WORKDIR
有关:RUN pwd
的工作目录为/a/b/c
。USER
格式:
USER <用户名>[:<用户组>]
USER
指令和WORKDIR
相似,都是改变环境状态并影响以后的层。WORKDIR
是改变工作目录,USER
则是改变之后层的执行RUN
,CMD
以及ENTRYPOINT
这类命令的身份。注意,
USER
只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。如果以
root
执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来运行某个服务进程,不要使用su
或者sudo
,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用gosu
。HEALTHCHECK
格式:
HEALTHCHECK [选项] CMD <命令>
:设置检查容器健康状况的命令HEALTHCHECK NONE
:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令HEALTHCHECK
指令是告诉 Docker 应该如何进行判断容器的状态是否正常,这是 Docker 1.12 引入的新指令。在没有
HEALTHCHECK
指令前,Docker 引擎只可以通过容器内主进程是否退出来判断容器是否状态异常。很多情况下这没问题,但是如果程序进入死锁状态,或者死循环状态,应用进程并不退出,但是该容器已经无法提供服务了。在 1.12 以前,Docker 不会检测到容器的这种状态,从而不会重新调度,导致可能会有部分容器已经无法提供服务了却还在接受用户请求。而自 1.12 之后,Docker 提供了
HEALTHCHECK
指令,通过该指令指定一行命令,用这行命令来判断容器主进程的服务状态是否还正常,从而比较真实的反应容器实际状态。当在一个镜像指定了
HEALTHCHECK
指令后,用其启动容器,初始状态会为starting
,在HEALTHCHECK
指令检查成功后变为healthy
,如果连续一定次数失败,则会变为unhealthy
。HEALTHCHECK
支持下列选项:--interval=<间隔>
:两次健康检查的间隔,默认为 30 秒;--timeout=<时长>
:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;--retries=<次数>
:当连续失败指定次数后,则将容器状态视为unhealthy
,默认 3 次。和
CMD
,ENTRYPOINT
一样,HEALTHCHECK
只可以出现一次,如果写了多个,只有最后一个生效。在
HEALTHCHECK [选项] CMD
后面的命令,格式和ENTRYPOINT
一样,分为shell
格式,和exec
格式。命令的返回值决定了该次健康检查的成功与否:0
:成功;1
:失败;2
:保留,不要使用这个值。假设我们有个镜像是个最简单的 Web 服务,我们希望增加健康检查来判断其 Web 服务是否在正常工作,我们可以用
curl
来帮助判断,其Dockerfile
的HEALTHCHECK
可以这么写:这里我们设置了每 5 秒检查一次(这里为了试验所以间隔非常短,实际应该相对较长),如果健康检查命令超过 3 秒没响应就视为失败,并且使用
curl -fs http://localhost/ || exit 1
作为健康检查命令。使用
docker build
来构建这个镜像:docker build -t myweb:v1 .
构建好了后,我们启动一个容器:
当运行该镜像后,可以通过
docker container ls
看到最初的状态为(health: starting)
:docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 03e28eb00bd0 myweb:v1 "nginx -g 'daemon off" 3 seconds ago Up 2 seconds (health: starting) 80/tcp, 443/tcp web
在等待几秒钟后,再次
docker container ls
,就会看到健康状态变化为了(healthy)
:docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 03e28eb00bd0 myweb:v1 "nginx -g 'daemon off" 18 seconds ago Up 16 seconds (healthy) 80/tcp, 443/tcp web
如果健康检查连续失败超过了重试次数,状态就会变为
(unhealthy)
。为了帮助排障,健康检查命令的输出(包括
stdout
以及stderr
)都会被存储于健康状态里,可以用docker inspect
来查看。LABEL
LABEL
指令用来给镜像以键值对的形式添加一些元数据(metadata)。LABEL <key>=<value> <key>=<value> <key>=<value> ...
我们还可以用一些标签来申明镜像的作者、文档地址等:
SHELL
格式:
SHELL ["executable", "parameters"]
SHELL
指令可以指定RUN
ENTRYPOINT
CMD
指令的 shell,Linux 中默认为["/bin/sh", "-c"]
两个
RUN
运行同一命令,第二个RUN
运行的命令会打印出每条命令并当遇到错误时退出。当
ENTRYPOINT
CMD
以 shell 格式指定时,SHELL
指令所指定的 shell 也会成为这两个指令的 shell启动容器
启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(
exited
)的容器重新启动。因为 Docker 的容器实在太轻量级了,很多时候用户都是随时删除和新创建容器。
新建并启动
例如,下面的命令输出一个 “Hello World”,之后终止容器。
$ docker run ubuntu:18.04 /bin/echo 'Hello world' Hello world
这跟在本地直接执行
/bin/echo 'hello world'
几乎感觉不出任何区别。下面的命令则启动一个 bash 终端,允许用户进行交互。
$ docker run -t -i ubuntu:18.04 /bin/bash root@af8bae53bdd3:/#
其中,
-t
选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上,-i
则让容器的标准输入保持打开。在交互模式下,用户可以通过所创建的终端来输入命令,例如
启动已终止容器
可以利用
docker container start
命令,直接将一个已经终止(exited
)的容器启动运行。可以在伪终端中利用
ps
或top
来查看进程信息。可见,容器中仅运行了指定的 bash 应用。这种特点使得 Docker 对资源的利用率极高,是货真价实的轻量级虚拟化。
后台运行
更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加
-d
参数来实现。下面举两个例子来说明一下。
如果不使用
-d
参数运行容器。$ 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) 打印到宿主机上面
如果使用了
-d
参数运行容器。$ docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done" 77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a
此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用
docker logs
查看)。注: 容器是否会长久运行,是和
docker run
指定的命令有关,和-d
参数无关。使用
-d
参数启动后会返回一个唯一的 id,也可以通过docker container ls
命令来查看容器信息。要获取容器的输出信息,可以通过
docker container logs
命令。终止容器
可以使用
docker container stop
来终止一个运行中的容器。此外,当 Docker 容器中指定的应用终结时,容器也自动终止。
例如对于上一章节中只启动了一个终端的容器,用户通过
exit
命令或Ctrl+d
来退出终端时,所创建的容器立刻终止。终止状态的容器可以用
docker container ls -a
命令看到。例如$ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ba267838cc1b ubuntu:18.04 "/bin/bash" 30 minutes ago Exited (0) About a minute ago trusting_newton
处于终止状态的容器,可以通过
docker container start
命令来重新启动。此外,
docker container restart
命令会将一个运行态的容器终止,然后再重新启动它。进入容器
在使用
-d
参数时,容器启动后会进入后台。某些时候需要进入容器进行操作,包括使用
docker attach
命令或docker exec
命令,推荐大家使用docker exec
命令,原因会在下面说明。attach
命令下面示例如何使用
docker attach
命令。注意: 如果从这个 stdin 中 exit,会导致容器的停止。
exec
命令docker exec
后边可以跟多个参数,这里主要说明-i
-t
参数。只用
-i
参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。当
-i
-t
参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。使用
docker attach
命令进入 container(容器)有一个缺点,那就是每次从 container 中退出到前台时,container 也跟着退出了。要想退出 container 时,让 container 仍然在后台运行着,可以使用
docker exec -it
命令。每次使用这个命令进入 container,当退出container 后,container 仍然在后台运行,命令使用方法如下:docker exec -it container1 /bin/bash
这样输入
exit
或者按键Ctrl + C
退出 container 时,这个 container 仍然在后台运行,通过:docker ps
就可以查找到。通过可以终止 container1。
导出和导入容器
导出容器
如果要导出本地某个容器,可以使用
docker export
命令。这样将导出容器快照到本地文件。
导入容器快照
可以使用
docker import
从容器快照文件中再导入为镜像,例如$ cat ubuntu.tar | docker import - test/ubuntu:v1.0 $ docker image ls REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE test/ubuntu v1.0 9d37a6082e97 About a minute ago 171.3 MB
此外,也可以通过指定 URL 或者某个目录来导入,例如
注:用户既可以使用
docker load
来导入镜像存储文件到本地镜像库,也可以使用docker import
来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。删除容器
可以使用
docker container rm
来删除一个处于终止状态的容器。例如如果要删除一个运行中的容器,可以添加
-f
参数。Docker 会发送SIGKILL
信号给容器。清理所有处于终止状态的容器
用
docker container ls -a
命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用下面的命令可以清理掉所有处于终止状态的容器。镜像仓库管理
Docker 官方公共仓库 Docker Hub,其中已经包括了数量超过 2,650,000 的镜像。
常用命令:
$ docker login $ docker logout $ docker search centos $ docker search centos --filter=stars=N $ docker pull centos $ docker tag ubuntu:18.04 username/ubuntu:18.04 $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu 18.04 275d79972a86 6 days ago 94.6MB username/ubuntu 18.04 275d79972a86 6 days ago 94.6MB $ docker push username/ubuntu:18.04 $ docker search username NAME DESCRIPTION STARS OFFICIAL AUTOMATED username/ubuntu
自动构建
自动构建(
Automated Builds
)可以自动触发构建镜像,方便升级镜像。有时候,用户构建了镜像,安装了某个软件,当软件发布新版本则需要手动更新镜像。
而自动构建允许用户通过 Docker Hub 指定跟踪一个目标网站(支持 GitHub 或 BitBucket)上的项目,一旦项目发生新的提交 (
commit
)或者创建了新的标签(tag
),Docker Hub 会自动构建镜像并推送到 Docker Hub 中。Docker 数据管理
在容器中管理数据主要有两种方式:
数据卷(Volumes)
挂载主机目录 (Bind mounts)
数据卷
数据卷
是一个可供一个或多个容器使用的特殊目录,它绕过 UnionFS,可以提供很多有用的特性:数据卷
可以在容器之间共享和重用对
数据卷
的修改会立马生效对
数据卷
的更新,不会影响镜像数据卷
默认会一直存在,即使容器被删除创建数据卷
查看所有的
数据卷
$ docker volume ls DRIVER VOLUME NAME local my-vol
在主机里使用以下命令可以查看指定
数据卷
的信息启动挂载数据卷的容器
在用
docker run
命令的时候,使用--mount
标记来将数据卷
挂载到容器里。在一次docker run
中可以挂载多个数据卷
。下面创建一个名为
web
的容器,并加载一个数据卷
到容器的/usr/share/nginx/html
目录。$ docker run -d -P \ --name web \ # -v my-vol:/usr/share/nginx/html \ --mount source=my-vol,target=/usr/share/nginx/html \ nginx:alpine
查看数据卷
在主机里使用以下命令可以查看
web
容器的信息数据卷
信息在 "Mounts" Key 下面删除数据卷
数据卷
是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除数据卷
,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的数据卷
。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用docker rm -v
这个命令。无主的数据卷可能会占据很多空间,要清理请使用以下命令
挂载主机目录
挂载一个主机目录作为数据卷
使用
--mount
标记可以指定挂载一个本地主机的目录到容器中去。$ 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
指定为只读
。$ docker run -d -P \ --name web \ # -v /src/webapp:/usr/share/nginx/html:ro \ --mount type=bind,source=/src/webapp,target=/usr/share/nginx/html,readonly \ nginx:alpine
加了
readonly
之后,就挂载为只读
了。如果你在容器内/usr/share/nginx/html
目录新建文件,会显示如下错误/usr/share/nginx/html # touch new.txt touch: new.txt: Read-only file system
查看数据卷的具体信息
在主机里使用以下命令可以查看
web
容器的信息挂载主机目录
的配置信息在 "Mounts" Key 下面挂载一个本地主机文件作为数据卷
--mount
标记也可以从主机挂载单个文件到容器中这样就可以记录在容器输入过的命令了。
提供 web 服务
暂时略过
Docker Compose
Docker Compose
是 Docker 官方编排(Orchestration)项目之一,负责快速的部署分布式应用。目前不再需要安装
docker-compose
,并使用docker compose
命令代替docker-compose
。首先介绍几个术语。
服务 (
service
):一个应用容器,实际上可以运行多个相同镜像的实例。项目 (
project
):由一组关联的应用容器组成的一个完整业务单元。可见,一个项目可以由多个服务(容器)关联而成,
Compose
面向项目进行管理。最常见的项目是 web 网站,该项目应该包含 web 应用和缓存。
下面我们用
Python
来建立一个能够记录页面访问次数的 web 网站。web 应用
新建文件夹,新建
app.py
Dockerfile
新建
Dockerfile
docker-compose.yml
新建
docker-compose.yml
运行 compose 项目
此时访问本地
5000
端口,每次刷新页面,计数就会加 1。The text was updated successfully, but these errors were encountered: