Docker高级使用指南

目录

  1. Docker数据管理
    1. 数据卷
      1. 在容器内创建一个数据卷
      2. 挂载一个主机目录作为数据卷
      3. 挂载一个本地主机文件作为数据卷
    2. 数据卷容器
      1. 利用数据卷容器迁移数据
        1. 备份
        2. 恢复
  2. Docker网络配置
    1. 端口映射实现访问容器
      1. 从外部访问容器应用
      2. 映射所有接口地址
      3. 映射到指定地址的任意端口
      4. 查看映射端口配置
    2. 容器互联实现容器间通信
      1. 容器互联
  3. Dockerfile创建镜像
    1. 环境介绍
    2. 指令介绍
      1. FROM
      2. MAINTAINER
      3. LABEL
      4. ADD
      5. COPY
      6. EXPOSE
      7. ENV
        1. 在Dockerfile中使用环境变量
      8. RUN
      9. CMD
      10. ENTRYPOINT
      11. VOLUME
      12. USER
      13. WORKDIR
      14. ARG
      15. ONBUILD
      16. STOPSIGNAL
      17. HEALTHCHECK

Docker数据管理

用户在使用docker过程中,往往需要查看容器内应用产生的数据,或者需要把容器内的数据进行备份,甚至多个容器之间进行数据的共享,这必然涉及到容器的数据管理操作。

容器的数据管理主要有两种方式:

  • 数据卷(Data Volumes)
  • 数据卷容器(Data Volume Dontainers)

数据卷

数据卷是一个可供容器使用的特殊目录,它绕过文件系统,可以提供很多有用的特性。

  • 数据卷可以在容器间共享使用。
  • 对数据卷的修改会马上生效。
  • 对数据卷的更新,不会影响镜像。
  • 卷会一直存在,直到没有容器使用。
    数据卷的使用,类似Linux下对目录或文件进行mount操作。

在容器内创建一个数据卷

在用docker run命令时,使用-v可以在容器内创建一个数据卷。

1
sudo docker run -d -P --name web -v /webapp training/webapp python app.py

-P 是允许外部访问容器需要暴露的端口。


挂载一个主机目录作为数据卷

使用-v也可以指定挂载一个本地的目录到容器中去作为数据卷。

1
sudo docker run -d -P --name web-2 -v /src/webapp:/opt/webapp training/webapp python app.py

加载主机中的/src/webapp目录到容器的/opt/webapp目录。
本地目录的路径必须是绝对路径,如果目录不存在,docker会自动创建。


docker挂载的数据卷默认权限是读写(rw),用户也可以通过,ro设定只读。

1
sudo docker run -d -P --name web-3 -v /src/webapp:/opt/webapp:ro training/webapp python app.py

挂载一个本地主机文件作为数据卷

-v也可以从主句挂载单个文件到容器中作为数据卷。

1
sudo docker run --rm -it  -v ~/.bash_history:/.bash_history ubuntu /bin/bash

数据卷容器

如果用户需要在容器之间共享一些持续更新的数据,最简单的方式是使用数据卷容器。数据卷容器其实就是一个普通的容器,专门用它提供数据卷供其他容器挂载。

首先创建一个数据卷容器dbdata,并在其中创建一个数据卷挂在到/dbdata。

1
sudo docker run -it -v /dbdata --name dbdata ubuntu

然后,可以在其他容器中使用–volumes-from来挂载dbdata容器中的数据卷。创建db1和db2两个容器,并从dbdata容器挂载数据卷。

1
2
sudo docker run -it --volumes-from dbdata --name db1 ubuntu
sudo docker run -it --volumes-from dbdata --name db2 ubuntu

现在,容器db1和db2都挂载同一个数据卷到相同的/dbdata目录。三个容器任何一方在该目录写入,其他容器都可以看到。

在dbdata容器中创建一个test文件。

1
2
3
4
5
root@33821d0743e1:/# cd dbdata/
root@33821d0743e1:/dbdata# touch test
root@33821d0743e1:/dbdata# ls
test
root@33821d0743e1:/dbdata#

在db1容器内查看。

可以多次使用–volumes-from参数来从多个容器挂载多个数据卷。还可以从其他已经挂载了容器卷的容器来挂载数据卷。

1
sudo docker run -d --volumes-from db1 --name db3 ubuntu

使用–volumes-from参数所挂载数据卷的容器自身并不需要保持运行状态。

如果删除了挂载了的容器(包括dbdata、db1和db2),数据卷并不会自动删除。
如果要删除一个数据卷,必须在删除最后一个还挂载这它的容器时显示使用docker rm -v命令来指定同时删除相关联的容器。

利用数据卷容器迁移数据

可以利用数据卷容器对其中的数据卷进行备份、恢复,以实现数据的迁移。

备份

1
sudo docker run --volumes-from dbdata -v $(pwd):/backup --name worker ubuntu tar cvf /backup/backup.tar /dbdata

首先,利用ubuntu镜像创建一个容器worker。
使用–volumes-from dbdata参数让worker容器挂载dbdata容器的数据卷(即dbdata数据卷)。
使用-v$(pwd):/backup参数来挂载本地的当前目录到worker容器下的/backup目录。
worker容器启动后,使用tar cvf /backup/backup.tar /dbdata命令来将/dbdata下内容备份为容器内的/backup/backup.tar,即主机目录下的backup.tar。


恢复

首先创建一个带有数据卷的容器dbdata2。

1
sudo docker run -v /dbdata --name dbdata2 ubuntu /bin/bash

然后创建另一个新的容器,挂载dbdata2的容器,并使用untar解压备份文件到所挂载的容器卷即可。

1
sudo docker run --volumes-from dbdata2 -v $(pwd):/backup ubuntu tar xvf /backup/backup.tar

Docker网络配置

Docker提供了映射容器端口到宿主主机和容器互联机制来为容器提供网络服务。

端口映射实现访问容器

从外部访问容器应用

在启动容器时,如果不指定对应参数,在容器外部是无法通过网络来访问容器内的网络应用和服务的。

当容器中运行一些网络应用,要让外部访问这些应用,可以通过-P或者-p参数来指定端口映射。当使用-P标记时,Docker会随机映射一个端口至容器内部的开放的网络端口。

1
2
docker run -d -P training/webapp python app.py
docker ps -l

本地主机的32768被映射到了容器5000端口,访问宿主主机的32768端口即可访问容器内Web应用提供的界面。


可以通过docker logs命令来查看应用的信息。

-p则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有ip:hostPort:containerPort | ip::containerPort | hostPort::containerPort。

映射所有接口地址

使用hostPort:containerPort格式将本地的5000端口映射到容器的5000端口。

1
docker run -d -p 5000:5000 training/webapp python app.py

此时默认会绑定本地所有接口上的所有地址。多次使用-p标记可以绑定多个端口。

1
docker run -d -p 5000:5000 -p 3000:80 training/webapp python app.py

映射到指定地址的任意端口

使用ip::containerPort绑定localhost的任意端口到容器的5000端口,本地主机会自动分配一个端口。

1
docker run -d -p 127.0.0.1::5000 training/webapp python app.py

还可以使用udp标记来指定udp端口。

1
docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py

查看映射端口配置

1
docker port CONTAINER [PRIVATE_PORT[/PROTO]]

容器互联实现容器间通信

容器间的连接(linking)系统是除了端口外另一种可以与容器中应用进行交互的方式。它会在源和接收容器之间创建一个隧道,接收容器可以看到源容器指定的信息。

使用–name标记可以为容器自定义命名。

1
docker run -d --name web -P training/webapp python app.py

容器的名称是唯一的。
如果已经命名了一个叫web的容器,要再次使用web命名新容器,需要先用docker rm删除之前的容器。

在执行docker run的使用如果添加–rm标记,则容器会在终止后立即删除。
–rm和-d参数不能同时使用。

容器互联

使用--link参数可以让容器之间安全的进行交互。
先创建一个新的数据库容器。

1
docker run -d --name db training/postgres

删除之前创建的web容器。

1
docker rm -f web

然后创建一个新的web容器,并将它连接到db容器。

1
docker run -d -P --name web --link db:db training/webapp python app.py

–link参数的格式为–link name:alias,其中name是要链接的容器的名称,alias是这个链接的别名。

Docker在两个容器之间创建了一个安全隧道,而且不用映射它们的端口到宿主主机上。在启动db容器的时候没有-p和-P标记,从而避免暴露数据库端口到外部网络上。

Docker通过两种方式为容器公开连接信息。

  • 环境变量。
  • 更新/etc/hosts文件。

使用env命令查看web容器的环境变量。

1
docker exec web env

其中DB_开头的环境变量是供web容器连接db容器使用,前缀采用大写的连接别名。

除了环境变量,Docker还添加host信息到父容器的/etc/hosts的文件。

1
docker exec web cat /etc/hosts 

这里有两个hosts信息,第一个是db容器的ip和主机名,第二个是web容器,web容器用自己的id作为默认主机名。可以在web容器中用ping命令测试与db容器的连通。

1
docker exec web ping db

可以连接多个子容器到父容器。

Dockerfile创建镜像

Dockerfile是一个文本格式的配置文件,用户可以用Dockerfile快速创建自定义的镜像。

环境介绍

  1. Dockerfile中所用的文件一定是和Dockerfile文件在同一级目录下,可以为Dockerfile文件的父目录的子目录。
  2. Dockerfile中相对路径默认是Dockerfile所在的目录。
  3. Dockerfile中每一条指令被视为一层,分层构建,联合挂载。所以,能写到一行的指令,一定要写到一行。
  4. Dockerfile中指令不区分大小写,但习惯上将它大写,以便和参数区分开来。

指令介绍

FROM

功能为指定基础镜像,必须是第一条指令。
同时意味着接下来所写的指令将作为镜像的第一层开始。

1
2
3
FROM <image>
FROM <image>:<tag>
FROM <image>:<digest>

三种写法,其中是可选项,如果没有选择,那么默认latest。
如果不以任何镜像为基础,那么写法为:FROM scratch。

MAINTAINER

指定作者

1
MAINTAINER <name>

新版docker中使用LABEL指明。

LABEL

为镜像指定标签。

1
2
3
4
5
6
7
8
9
10
11
12
13
LABEL <key>=<value> <key>=<value> <key>=<value>

一个Dockerfile可以有多个LABEL
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates that label-values \
can span multiple lines."

但不建议这么写,最好写成一行,可以使用\换行。
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"

LABEL会继承基础镜像的LABEL,遇到相同key的,则值被覆盖。

ADD

复制文件到镜像中。

1
2
ADD <src> ... <dest>
ADD ["<src>" ... "<dest>"]

路径可以是容器内的绝对路径,也可以是相对于工作目录的相对路径,推荐写成绝对路径。
可以是一个本地文件,还可以是一个url,相当于wget。

1
2
3
ADD test relativeDir/
ADD test /relativeDir
ADD http://example.com/test /

scr为一个目录时,会自动把目录下的文件复制过去,目录本身不会复制。
如果scr为多个文件,dest一个是一个目录。

COPY

复制文件到镜像中。

1
2
COPY <src> ... <dest>
COPY ["<src>" ... "<dest>"]

与ADD的区别:COPY只能是本地文件,其他用法一致。

EXPOSE

暴露容器运行时的监听端口给外部。

1
EXPOSE <port>/<tcp/udp>

EXPOSE不会使容器访问主机的端口,如果想使容器和主机的端口有映射关系,必须在容器启动的时候加上-p参数。

ENV

设置环境变量。

1
2
ENV <key> <value>
ENV <key>=<value> ...

两者的区别:第一种一次设置一个,第二种一次设置多个。

在Dockerfile中使用环境变量

  • $varname
  • ${varname}
  • ${varname:-default}
  • ${varname:+default}

第一种和第二种相同。
第三种表示当变量不存在时,使用-号后面的值。
第四种表示无论变量存不存在,都使用+后面的值。

RUN

运行指定命令。

1
2
RUN <command>
RUN ["executable", "param1", "param2"]

第一种后面直接跟shell命令。

  • 在linux上默认/bin/sh -c
  • 在windows上默认cmd /S /C

第二种类似函数调用。

  • 可以将executable理解为可执行文件,后面就是两个参数。

CMD

容器启动时要运行的命令。

1
2
3
CMD ["executable", "param1", "param2"]
CMD ["param1", "param2"]
CMD command param1 param2

示例:

1
2
CMD ["sh", "-c", "echo $HOME"]
CMD ["echo", "$HOME"]

参数一定要用双引号包括。原因是参数传递后,docker解析的是一个JSON array。

RUN和CMD的却别:

  • RUN是构建容器镜像时就会运行的命令以及提交运行结果。
  • CMD是容器启动时执行的命令,在构建时不运行,构建时仅仅指定了这个命令。

ENTRYPOINT

启动时的默认命令。

1
2
ENTRYPOINT ["executable", "param1", "param2"]  
ENTRYPOINT command param1 param2

ENTRYPOINT与CMD比较:

  • 相同点:只能写一条,如果写了多条,那么只有最后一条会生效;容器启动时才会运行,运行时机相同。
  • 不同点:ENTRYPOINT不会被运行的command覆盖,而CMD则会被覆盖。

如果我们在Dockerfile中同时写了ENTRYPOINT和CMD,并且CMD指令不是一个完整可执行的命令,那么CMD指定的内容将会作为ENTRYPOINT的参数。

1
2
3
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]

如果我们在Dcokerfile中同时写了ENTRYPOINT和CMD,并且CMD是一个完整的指令,那么他们会相互覆盖,谁在最后谁生效。

1
2
3
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ls -al

那么容器内将执行ls -al,top -b将不会被执行。

VOLUME

实现挂载功能,可将宿主机目录挂载到容器中。

1
VOLUME ["/data"]

[“/data”]可以是一个JsonArray,也可以是多个值。

1
2
3
VOLUME ["/var/log"]
VOLUME /var/log
VOLUME /var/log /var/db

容器使用的是AUFS,这种文件系统不能持久化数据,当容器关闭后,所有的更改都会丢失。所以当数据需要持久化时使用这个命令。

USER

设置启动容器的用户,可以是用户名或UID。

1
2
USER king
USER UID

如果设置了容器以king用户去运行,那么RUN,CMD和ENTRYPOINT都会以这个用户去运行,使用这个命令一定要确认容器中拥有这个用户,并且拥有足够的权限。

WORKDIR

设置工作目录,对RUN,CMD,ENTRYPOINT,COPY和ADD生效。如果目录不存在会创建,也可以设置多次。

1
WORKDIR /path/to/workdir
1
2
3
4
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

pwd的执行结果是/a/b/c。
WORKDIR也可以解析环境变量。

1
2
3
ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

pwd的执行结果是/path/$DIRNAME。

ARG

设置变量。ARG命令定义一个变量,在docker build创建镜像的时候,使用**–build-arg=**来指定参数。

1
ARG <name>[=<default value>]

如果用户在build镜像时指定了一个参数没有定义在Dockerfile中,那么将会有一个warning,提示如下:
[Warning] One or more build-args [path] were not consumed.

可以定义一个或者多个参数.

1
2
3
FROM ubuntu
ARG user
ARG buildinfo

还可以给参数一个默认值。

1
2
3
FROM ubuntu
ARG user=king
ARG buildinfo=1

如果我们给了ARG定义的参数默认值,那么当build镜像时没有指定参数值,将会使用这个默认值。

ONBUILD

1
ONBUILD [INSTRUCTION]

这个命令只对当前镜像的子镜像生效。
比如当前镜像为A,在Dockerfile中添加:

1
ONBUILD RUN ls -al

这个ls -al命令不会在A镜像构建或启动的时候执行。此时有一个镜像B是基于A镜像构建的,那么这个ls -al命令会在镜像B构建的时候被执行。

STOPSIGNAL

当容器停止时给系统发送什么样的指令,默认是15.

1
STOPSIGNAL signal

HEALTHCHECK

1
2
HEALTHCHECK [OPTIONS] CMD command
HEALTHCHECK NONE
  1. 在容器内部运行一个命令来检查容器的健康状况。
  2. 在基础镜像中取消健康检查命令。

[OPTIONS]支持以下三种选项:

  • –interval=DURATION 两次检查默认的时间间隔,默认为30秒。
  • –timeout=DURATION 健康检查命令运行超时时长,默认为30秒。
  • -reties=N 当连续失败指定次数后,则容器被认为是不健康的,状态日为unhealthy,默认次数是3。

HEALTHCHECK命令只能出现一次,如果出现了多次,只有最后一次生效。

CMD后面的命令的返回值决定了本次健康检查是否成功,具体的返回值如下:

  • 0:success。表示容器是健康的。
  • 1:unhealthy。表示容器已经不能工作了。
  • 2:reserved。保留值。
1
2
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1

健康检查的命令是:curl -f http://localhost/ || exit 1
两次检查的间隔是5分钟,命令超时时间是3秒。