Docker安装
以Ubuntu/Debian为例,Docker安装可以分为如下几个步骤:
- 安装ca-certificates和curl
- 安装Docker GPG密钥
- 导入Docker APT源
- 使用APT安装Docker Engine
一般包括如下命令:
sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
注意:国内访问APT源可能会失败,可以通过设置代理解决,临时设置代理的方法是通过-o参数,如:
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -o Acquire::http::proxy="http://127.0.0.1:7890/"
其中的-o Acquire::http::proxy="http://127.0.0.1:7890/"设置了代理。
由于Docker官方的Registry已经被国内网络封禁,因此为了拉取镜像,还需要为Docker设置代理:
sudo mkdir -p /etc/systemd/system/docker.service.d sudo touch /etc/systemd/system/docker.service.d/http-proxy.conf
在http-proxy.conf中添加如下内容:
[Service] Environment="HTTP_PROXY=http://127.0.0.1:7890/" Environment="HTTPS_PROXY=http://127.0.0.1:7890/" Environment="NO_PROXY=localhost,127.0.0.1,.example.com"
并重启Docker:
sudo systemctl daemon-reload sudo systemctl restart docker
Docker入门
使用docker container run命令可以创建一个容器,如
sudo docker run -i -t ubuntu /bin/bash
其中-i表示开启容器中的stdin流,主要是为了创建交互式容器,通常配合-t使用,-t表示Docker创建容器后为其分配一个类似于tty的终端,ubuntu表示容器所使用的镜像,而/bin/bash表示创建容器后要执行的命令,当docker run创建完容器后就会打开容器下的一个bash shell,此时就可以在容器范围内执行命令。由于创建的是交互式容器,因此一旦退出交互式shell容器就会停止,退出的方法是输入exit命令。
如果不想创建交互式容器,可以创建守护式容器,只需要使用-d参数即可,此时run命令将直接返回容器ID,而不会附加到容器当中。
一般所有docker container开头的命令都可以省略container,如docker container run和docker run是一样的,其它前缀一般也可以省略,如docker image开头的命令可以省略image。
可以使用docker container ps或者docker container ls命令列举已创建的容器,默认情况下ps命令只会列举运行中的容器,使用docker ps -a可以列举所有容器,包括停止状态的容器。docker ps -n x可以列举最后x个容器,包括停止状态的容器。
创建容器时Docker会自动为容器分配一个ID,容器内的主机名(hostname)也被设置为这个ID,同时默认情况下Docker也会为容器生成一个随机的名称,可以通过--name参数来手动指定,如
sudo docker run --name myubuntu -i -t ubuntu /bin/bash
容器的命名是唯一的,如果要创建同名容器,必须通过docker container rm命令删除已有的容器。
为了启动容器,可以使用docker container start命令,此时会按照docker container run的配置运行容器,但不会附着到容器当中,可以使用docker container attach命令附着到容器。
也可以使用docker container restart命令来重启容器。
如果想要查看容器内的输出,可以使用docker container logs命令来获取容器的日志,持续监控日志可以加上-f参数,--tail 10参数表示只获取最后10行,而-t可以为每条日志加上时间戳。
使用docker container top命令可以查看容器内部运行的进程,docker container stats命令可以查看容器的信息和状态,包括CPU、内存、网络IO、存储IO等。
使用docker container exec命令可以在容内额外启动新进程,使用-d参数表示创建后台进程。
为了停止容器,可以使用docker container stop命令。
使用docker container inspect命令可以获取容器的配置信息,其中将包括容器的网络相关配置,容器的运行参数等大量深入的配置。
使用docker container rm命令可以删除停止状态的容器,使用-f参数可以强制删除运行中的容器。
Docker镜像
Docker的机制类似于Git,容器通过镜像来创建,而镜像是一个多层虚拟化系统,最底端是一个引导文件系统bootfs,其上有root文件系统rootfs,可能是某种操作系统,如Debian或者Ubuntu,这一层称为基础镜像,基础镜像之上是一个读写文件系统,我们的程序就是在读写文件系统之上运行的。
Docker镜像使用不可变的思想,任何对镜像的操作都会产生一层新的镜像,Docker采用写时复制的机制来创建新的镜像,这样创建一个容器时就相当于从底向上构建一个镜像栈。
可以使用docker images命令(相当于docker image ls命令)来列举当前的本地镜像,这些镜像保存在/var/lib/docker目录下,当运行docker run命令创建镜像时,首先会查找本地是否存在所需的镜像,如果不存在则从网络上下载,Docker的镜像保存在仓库当中,类似于Git,托管平台称为Docker Registry,目前最主要的是Docker官方运营的公共Registry服务,即Docker Hub。
每个人都可以注册Docker Hub账户,并上传自己的镜像,Docker使用者可以从Docker Hub上拉取所需的镜像。
一个仓库可以拥有多个镜像,为了区分,镜像会附带一个标签(tag),如ubuntu:latest,ubuntu:12.04。
Docker Hub有两种类型的仓库,分别是用户仓库和顶层仓库,用户仓库的镜像都由用户自己创建,而顶层仓库是由Docker内部的人管理的,用户仓库的命名往往为username/reponame格式,而顶级仓库的命名为reponame格式,如ubuntu就是一个顶级仓库。
sudo docker run --name myubuntu -i -t ubuntu /bin/bash
当运行上述命令时,首先会检查本地中是否存在ubuntu镜像,注意这里省略了标签名,因此相当于ubuntu:latest。如果本地中没有这个镜像,那么Docker就会尝试在Registry中下载,默认从Docker Hub中查找,ubuntu是一个顶级仓库,因此将定位到顶级仓库中的镜像ubuntu:latest,并下载到本地,随后使用该镜像创建容器。
可以使用docker search命令来查找Docker Hub上公开的可用镜像,如docker search ubuntu。
可以使用docker image pull命令来拉取Docker Hub上的镜像,如docker pull ubuntu:12.04。
每个人都可以构建自己的镜像,构建的方式有两种:
- 使用docker container commit命令
- 使用docker buildx build命令和Dockerfile文件
目前推荐使用的是第二种。
首先介绍第一种方法,为了构建镜像,只需要从某一个镜像创建一个容器,并根据自己的需要在容器内增加内容,随后使用docker container commit命令在本地提交,如:
docker commit -m"my ubuntu" -a"white" myubuntu white/ubuntu:latest
docker container commit命令将提交当前镜像上的所有修改,并创建一个新的镜像,其中-m参数指定注释,-a参数指定作者名,myubuntu是容器名,而white/ubuntu:latest是创建的镜像名,latest是标签。commit命令仅仅是本地提交,而且是差异化提交,因此速度很快。
创建镜像之后可以通过docker images ls命令列举当前的本地镜像查看。一旦创建镜像之后,就可以上传到Docker Hub。第一步是使用docker login进行登录,推荐使用如下形式:
docker login -u "myusername" -p "mypassword" docker.io
这里使用密码进行登录,-u参数指定用户名,-p参数指定密码,这里docker.io表示登录到Docker Hub,由于Docker Registry服务是开源的,因此企业也可以搭建内部的Registry。
也可以使用Access Token进行登录,只需要到Docker Hub后台创建密钥即可。登录之后可以使用docker logout注销,但在登录状态下才能上传到Docker Hub。
为了上传,需要到Docker Hub创建一个仓库,并记住仓库的名称,如white/ubuntu,并且保持本地镜像的名称和仓库名一致,如果不一致的话可以使用docker image tag命令创建别名,如:
docke image tag white/myubuntu white/ubuntu
命令中省略了标签,因此都指的是latest。
注意这里是为已有的镜像创建别名,和容器是没关系的,之后使用docker image push命令就可以上传到Docker Hub了:
docker image push white/ubuntu:latest
这里指定的是latest标签,因此将会查找本地中的white/ubuntu:latest镜像并上传。
接下来介绍第二种方式,即docker buildx build命令和Dockerfile方式,Dockerfile是一个文本文件,其中描述了一个镜像的创建过程,使用的是Docker的DSL语言,因此比较灵活方便,表达力也更强。
Dockerfile的范围局限在一个文件夹,这个文件夹称为构建上下文(build context),Dockerfile应当直接放置在构建上下文中,而且名称就是Dockerfile:
mkdir myubuntu cd myubuntu touch Dockerfile
Dockerfile的内容例如:
FROM ubuntu:latest MAINTAINER White "white@example.com" RUN apt-get update && apt-get install -y nginx RUN echo 'hello world' > /usr/share/nginx/html/index.html EXPOSE 80
稍后介绍其中各行的含义,写好Dockerfile的内容之后,在构建上下文中通过docker buildx build命令即可构建镜像:
sudo docker build -t="white/myubuntu" . [+] Building 24.0s (7/7) FINISHED docker:default => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 210B 0.0s => WARN: MaintainerDeprecated: Maintainer instruction is deprecated in f 0.0s => [internal] load metadata for docker.io/library/ubuntu:latest 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [1/3] FROM docker.io/library/ubuntu:latest 0.0s => [2/3] RUN apt-get update && apt-get install -y nginx 22.9s => [3/3] RUN echo 'hello world' > /usr/share/nginx/html/index.html 0.5s => exporting to image 0.4s => => exporting layers 0.4s => => writing image sha256:70092e438ae3bf17f1fe49ddc666809e0cb205c309eb2 0.0s => => naming to docker.io/white09060/myubuntu 0.0s 1 warning found (use --debug to expand): - MaintainerDeprecated: Maintainer instruction is deprecated in favor of using label (line 2)
注意build命令需要传入一个路径,这里指定的是'.',表示构建上下文为当前目录,-t="white/myubuntu"表示构建的镜像名称(相当于初始标签)。构建成功之后,将会返回镜像的ID,由于Docker使用不可变镜像的思想,因此Dockerfile中的每一行影响镜像内容的命令都会产生一层新的镜像,如果Dockerfile中的某一行命令执行失败,docker build命令依旧会返回一个镜像ID,代表最后一行成功执行的命令产生的镜像。build命令大体上会按照docker commit的方式提交新镜像。
如果想查看镜像是如何构建出来的,可以使用docker image history命令,如docker image history white/myubuntu
由于Docker的这种分层机制,构建镜像过程中会使用构建缓存来加快构建速度,例如其中的RUN apt-get update && apt-get install -y nginx
命令构建的镜像将被缓存,如果修改了后续的命令再构建,那么这一步就不需要再执行了,这会导致Docker不会刷新APT包缓存,如果不想使用缓存,可以在build命令中使用--no-cache参数。
接下来介绍Dockerfile中各行的含义:
- 以#开头的行都会被认为是注释。
- 每个Dockerfile的第一条指令必须是FROM,指定一个已经存在的镜像,后续指令都在该镜像的基础上执行,这个镜像被称为基础镜像。
- MAINTAINER指令指示镜像作者的名称和联系方式。
- RUN指令在当前镜像中执行指定的命令,执行成功后会创建一个新的镜像层。
- EXPOSE指令指示容器内的应用程序将会使用容器的指定端口,这是一个标记指令,不代表可以允许访问这些端口,出于按照考虑,Docker不会自动打开这些端口。
主要指令:
- RUN:执行构建命令,执行后将创建一个新的镜像层,后续的指令将在新的镜像层上进行。默认情况下RUN指令会被/bin/sh -c包装执行,如
RUN apt-get update
相当于/bin/sh -c apt-get update
,如果想要按照原始命令执行,可以使用数组形式,如:RUN ["apt-get", "update"]
- 参数--mount=type={bind, cache, tmpfs, secret, ssh}指定要挂载的文件系统,默认为--mount=type=bind,表示挂载构建上下文,只读。
- 参数--network={default, none, host}指定所使用的网络。
- 参数--security={sandbox, insecure}指定安全策略。
- CMD:在容器启动时执行命令,类似于docker run中指定要执行的命令,每个Dockerfile只能存在一条CMD指令,如果存在多条也只会执行最后一条,CMD指令的作用是为容器启动时的命令执行提供默认行为,如果docker run中没有指定要执行的命令,那么就会执行CMD指令,否则CMD指令将被忽略。
- ENTRYPOINT:用于将容器配置为可执行文件形式,一旦执行该指令,那么CMD指令和docker run中指定要执行的命令都会传递为ENTRYPOINT的参数,如:
ENTRYPOINT ["/usr/sbin/nginx"] CMD ["-h"] docker run -t -i myubuntu -g "daemon off;"
此时CMD指令中的-h和docker run命令中的-g "daemon off;"都将作为ENTRYPOINT指令的参数,上述docker run命令执行后容器在启动时将执行:
/usr/sbin/nginx -g "daemon off;"
如果docker run命令没有指定要执行的命令,那么将使用CMD指令,相当于/usr/sbin/nginx -h。每个Dockerfile只能存在一条ENTRYPOINT指令,如果存在多条也只会执行最后一条。
- WORKDIR:设置容器的工作目录,工作目录将影响RUN、CMD、ENTRYPOINT、COPY、ADD指令的执行。如果提供相对路径,那么将会根据当前的工作目录进行切换,如果未指定,默认的工作目录为"/",但一般情况下由基础镜像决定。如果指定的工作目录不存在,将自动创建。
- ENV:设置构建时的环境变量,这些环境变量可以在构建过程中使用,还会持久化到构建出的容器当中。如:
ENV A=1 B="2" ENV MYDIR /opt/app WORKDIR $MYDIR
- USER:设置当前用户,用户将影响RUN、CMD、ENTRYOINT指令的执行,可以设置用户名和组,包括用户ID和组ID,如USER user:group或USER uid:gid。如果不指定,默认的用户是root。
- VOLUME:用于在容器中挂载卷,容器可以访问卷的内容,但不必在构建时将卷的内容提交到容器当中,卷的源可以位于容器外部,如运行Docker引擎的系统当中,一个卷可以被多个容器共享和重用,对卷的修改不会对更新镜像产生影响,并且对卷的修改是立即生效的,本质上是一种容器直接访问特定文件源的手段,不需要经过容器内的文件系统。出于安全考虑,Dockerfile中的VOLUME指令是受到限制的,你并不能直接在Dockerfile中指定挂载某个源目录,只能使用VOLUME断言容器内的某个目录是挂载的,而源目录必须在运行时(docker run)指定:
VOLUME /var/log /var/db
上述指令将断言容器内的/var/log和/var/db是挂载的,而挂载的源目录需要在执行docker run时指定,如果没有指定,Docker将创建匿名卷。例如MySQL的Dockerfile中包含:
VOLUME /var/lib/mysql
如果用户在执行docker run时没有指定挂载源,那么Docker将创建匿名卷,匿名卷通常挂载/var/lib/docker/volumes/中的某一个目录,并且会生成一个非常长的目录名。此时这个目录将作为MySQL的数据目录,这对于测试来说是合理的。为了使用生产数据,用户可以在docker run中指定挂载源:
docker run -v /my/own/datadir:/var/lib/mysql mysql:8
- ADD:用于将构建上下文中的文件或目录复制到镜像中,不能对构建上下文之外的文件进行ADD操作,如:
ADD hom* /mydir/ ADD test.txt relativeDir/ ADD test.txt /absoluteDir/
ADD <src> <dest>服从以下规则:
- src可以是一个文件,一个目录或者一个URL。
- 如果src是一个文件或者一个目录,它必须存在于构建上下文中。
- 如果src是一个文件,且dest以/结尾,那么src将被写入到dest目录中,文件名相同。
- 如果src是一个URL,且dest以/结尾,那么将下载该URL文件到dest目录中,文件名由URL推断。
- 如果src是一个目录,那么src内的所有文件都将被复制,但不包括src目录本身。
- 如果src是一个公认的压缩文件(identity、gzip、bzip2、xz),且不是URL,将自动解压,此时src相当于一个目录。
- 如果src是一个文件,而且dest并不以/结尾,那么src将被写入到dest文件中。
- 如果dest不存在,将自动创建,包括缺失的所有目录。
- src支持Git URL,如
ADD git@git.example.com:foo/bar.git /bar
,验证在运行时指定--ssh参数。 - src为Git URL时,--keep-git-dir=true参数可以指定ADD时将.git目录添加进镜像,默认情况下.git目录是排除的。
- src为HTTP URL时,--checksum参数可以提供校验和,如
--checksum=sha256:24454f830cdb571e2c4ad15481119c43b3cafd48dd869a9b2945d1036d1dc68d
- --chown和--chmod参数可以设置文件的拥有者和权限,如
--chown=myuser:mygroup --chmod=644
- --exclude=<path>参数可以按模式排除文件,如
--exclude=*.txt
- COPY:和ADD命令类似,但仅仅复制而不会进行文件提取和解压,而且不支持URL。
- COPY支持ADD命令的所有参数,还包括--from参数,它允许指定复制源,默认情况下复制源是构建上下文,你可以使用其它镜像、构建过程的中间镜像或其它命名上下文作为复制源,如:
FROM alpine AS build RUN clang -o /hello hello.c FROM scratch COPY --from=build /hello / COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
- COPY支持ADD命令的所有参数,还包括--from参数,它允许指定复制源,默认情况下复制源是构建上下文,你可以使用其它镜像、构建过程的中间镜像或其它命名上下文作为复制源,如:
- LABEL:为镜像添加元数据,元数据是一组键值对,元数据可以通过docker inspect命令查看,如:
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"
- STOPSIGNAL:设置停止容器时发送什么系统调用信号给容器,默认是SIGTERM。
- ARG:用于设置容器的构建参数,这些参数可以由用户在执行docker build时指定,这需要通过--build-arg参数。如:
ARG build ARG webapp_user=user docker build --build-arg build=1234 -t myubuntu .
Docker预定义了一组ARG变量:HTTP_PROXY、http_proxy、HTTPS_PROXY、https_proxy、FTP_PROXY、ftp_proxy、NO_PROXY、no_proxy、ALL_PROXY、all_proxy。
- ONBUILD:为镜像设置触发器,当镜像被用作其它镜像的基础镜像时,触发器将会被执行,ONBUILD可以任何构建指令(除了ONBUILD、FROM、MAINTAINER和COPY --from)作为参数,ONBUILD只会触发一次,并不会继承。如:
ONBUILD ADD . /app/src ONBUILD RUN /usr/local/bin/python-build --dir /app/src
- EXPOSE:指定容器在运行时需要监听的网络端口,包括TCP端口和UDP端口,如果不指定默认为TCP,EXPOSE并不会公开这些端口,需要通过docker run命令的-p参数或者-P参数进行公开。如:
EXPOSE 80/tcp EXPOSE 80/udp
- MAINTAINER:指定容器的作者名,该指令已废弃,最好使用LABEL。
- SHELL:用于设置默认的Shell,不指定的情况下Linux下为["/bin/sh", "-c"],Windows下为["cmd", "/S", "/C"],该指令在Windows下作用比较大,因为Windows有cmd和powershell两种Shell。
Docker容器运行
Docker创建并允许容器的核心命令是docker container run,可以简写为docker run,它支持一系列的参数,由于Docker镜像的不可变特点,很多参数对应的配置一旦确定后就无法更改,只能通过重新创建容器来改变,因此docker run命令执行时的参数就尤为重要。
--name:设置容器的名字,如果不设置那么Docker将自动生成一个名字,在许多命令中,容器名可以取代容器ID。
--cidfile:将容器ID输出到文件,类似于PID文件,如--cidfile /tmp/docker_test.cid。
--pid:设置进程命名空间模式,默认情况下容器都会具有自己的进程命名空间,并且系统进程的PID被屏蔽,因此可以重用系统进程所占用的PID。
--pid参数允许配置容器和主机共享同个命名空间(--pid=host),也可以配置该容器加入另一个容器的命名空间(--pid=container:mynginx)。
--privileged:为容器开启特权模式,一旦开启后容器几乎可以做到主机能做的任何事情,包括Linux内核的所有功能,该参数用于特殊用途,例如在Docker中运行Docker。
-w/--workdir:设置容器的工作目录,相当于Dockerfile中的WORKDIR指令,该参数会覆盖WORKDIR指令。
--storage-opt:设置容器文件系统的大小限制,仅适用于btrfs、overlay2、windowsfilter、zfs存储驱动,如--storage-opt size=120G。
--tmpfs:用于挂载tmpfs文件系统,如--tmpfs /run:rw,noexec,nosuid,size=65536k。
-v:用于挂载卷,如-v ./content:/content,分号左边是主机上的源目录,分号右边是容器内的挂载目录,如果源目录不存在Docker将自动创建,此参数可以配合Dockerfile中的VOLUME指令使用。
--read-only:设置容器根文件系统为只读模式,容器将无法对文件系统进行任何写操作,但在挂载卷时可以将特定的卷设置为可读写模式,这样容器就只能在特定的卷写入数据。
--mount:用于挂载文件系统。包括卷、主机目录和tmpfs,如--mount type=bind,src=/data,dst=/data。
-p/--expose:用于公开容器的网络端口,可以将容器内的特定端口映射到主机上的特定端口。
如-p 127.0.0.1:80:8080/tcp表示将容器内的8080端口映射到127.0.0.1(主机)上的80端口,并且是TCP协议。
如果省略IP地址,即-p 80:8080/tcp则表示将容器内的8080端口映射到0.0.0.0上的80端口,此时外部可以访问该端口。
如果不需要映射,可以写成--expose 80表示公开容器内的80端口,但不映射到主机。
-P/--expose-all:用于公开容器内用到的所有网络端口,此参数可以配合Dockfile中的EXPOSE指令使用,EXPOSE指令的所有端口都会因该参数被公开。
--pull:设置镜像构建(运行)时的拉取策略,默认情况下为--pull=mssing表示先查找本地缓存中的镜像,如果不存在才拉取镜像。
其它可选的值为--pull=never表示只查找本地缓存中的镜像,如果不存在也不会拉取,而是抛出错误。
--pull=always表示总是拉取镜像,从不查找本地缓存。
-e/--env/--env-file:设置环境变量,相当于Dockerfile中的ENV指令,如--env VAR1=value1 --env VAR2=value2,如果省略变量值,Docker首先会从主机环境变量中查找,如果不存在则该变量将被忽略。可以从文件中导入环境变量,如--env-file env.list。
-l/--label/--label-file:设置容器的元数据,相当于Dockerfile中的LABEL指令,格式和环境变量类似。
--network:设置容器所连接的网络,如--network=my-net --ip=192.0.2.69。
--volumes-from:用于挂载另一个容器所挂载的所有卷,如--volumes-from 777f7dc92da7 --volumes-from ba8c0c54f0f2:ro。
-d/--detach:以分离模式运行容器,即创建守护式容器。docker run -d命令将直接返回容器的ID,并且不会监控容器内的输入输出,注意不可以在分离模式中运行service x start命令,否则容器会退出,即docker run -d -p 80:80 myimage service nginx start是错误的,应该使用:
docker run -d -p 80:80 my_image nginx -g 'daemon off;'
--device:用于将主机上的设备添加到容器当中,默认情况下容器可以使用read、write、mknod设备,如 --device=/dev/sdc:/dev/xvdc。
-a/--attach:以附加模式运行容器,用于绑定容器内的stdin、stdout、stderr流,如-a stdin。
-i/--interactive:保证容器内的stdin流打开,用于创建交互式容器,通常配合-t使用。
-t/--tty:为容器分配一个TTY伪终端,通常配合-i使用,如docker run -i -t debian passwd root。
如果只使用-i参数而不使用-t参数,那么TTY的相关功能将无法使用,例如上述修改root密码时,密码将会以纯文本显示。
如果只使用-t参数而不使用-i参数,TTY终端将无法写入stdin,一般情况下没有意义。
--restart:设置容器的重启策略,默认为--restart=no,表示从不自动重启。
--restart=on-failure[:max-retries]表示在出现错误时重启(退出码非零),可以设置最大重启次数,如--restart=on-failure:5。
--restart=unless-stopped表示总是重启,但容器被显式停止或Docker引擎被停止或重启除外。
--restart=always表示总是且无限期地重启。
--rm:默认情况下容器的文件系统在容器退出时保持不变,使用--rm参数可以指示Docker在容器退出时清理文件系统中的数据。
--add-host:为容器的hosts文件增加内容,如--add-host=my-hostname=8.8.8.8。
--log-driver:设置容器的日志驱动程序,默认为--log-driver=json-file,如果想关闭容器的日志,可以使用--log-driver=none。
--ulimit:设置容器的ulimit,在默认配置下,容器的权限不足以在容器中设置ulimit。
--stop-signal:设置停止容器时发送什么系统调用信号给容器,相当于Dockerfile中的STOPSIGNAL。
--security-opt:设置容器的安全配置,不述,可见Optional security options。
--stop-timeout:设置停止容器时发送系统调用信号后等待的时间(秒),如果容器在超时后没有退出,将发送SIGKILL信号,Linux容器的默认值为--stop-timeout=10,Windows容器的默认值为--stop-timeout=30。
-m/-memory:设置容器的内存上限,如-m 2GB。
Docker容器存储
默认情况下,Docker容器的数据都保存在可读写的文件系统层之上,当容器被删除时,其中的数据也会丢失,而且让一个进程去访问容器中的数据是比较困难的。Docker为容器数据的可持久化提供了两种选择,分别是卷挂载(Volume mount)和绑定挂载(Bind mount)。
卷通常保存在主机系统中,并由Docker进行管理,非Docker进程不允许修改主机上的卷,而绑定挂载也保存在主机系统中,但可以任意选择目录位置,并且允许非Docker进程修改其中的内容,还有一种临时挂载(tmpfs mount)方式,它保存在主机系统的内存当中,并且从不持久化。
卷挂载和绑定挂载都可以使用-v/--volume参数配置,临时挂载可以使用--tmpfs参数配置,这三种方式都可以使用--mount参数配置,这也是Docker官方推荐的。
卷是由Docker进行管理的,可以通过docker volume开头的命令进行操作,创建卷的命令是docker volume create,也可以在执行docker container run命令的时候创建卷。同个卷可以被多个容器同时使用,即便没有容器使用卷,该卷也不会被删除(除非使用了--rm参数),可以通过docker volume prune命令删除所有未使用的卷。
卷分为命名卷和匿名卷,例如通过Dockerfile中的VOLUME指令断言的卷没有在执行docker run命令时配置挂载源时,Docker会自动为其创建匿名卷,匿名卷会生成一个很长的名字,同样地删除容器时并不会删除所使用的匿名卷(除非使用了--rm参数)。
绑定挂载和卷挂载类似,但功能比较局限,只是简单地将主机上的某个目录挂载到容器当中,主机上的任何进程或者(默认情况下)容器都可以对该目录进行读写,绑定挂载是一次性的,不需要通过Docker管理。相比之下,卷具有如下优点:
- 卷更容易备份和迁移
- 可以使用Docker CLI或者API管理卷
- 卷可以在Linux和Windows容器下工作
- 卷更适合多个容器共享
一般情况下,卷挂载是首选的,而绑定挂载主要用于挂载配置文件,例如Docker默认会将/etc/resolv.conf绑定挂载到容器中以提供DNS服务。
卷的相关命令:
- docker volume create myvol
- docker volume ls
- docker volume inspect myvol
- docker volume rm myvol
- docker volume prune
为了使用所创建的卷,可以使用--mount参数,如--mount source=myvol,target=/app,其中source表示所使用的卷,而target表示挂载的目标目录。
使用docker run命令启动容器的时候,如果创建了新卷且目标目录中有文件,Docker会将这些文件填充到容器中,使用该卷的其它容器也可以使用这些填充的文件。
在挂载卷的时候,可以设置卷的读写模式,例如只读模式:--mount source=nginx-vol,destination=/usr/share/nginx/html,readonly。
--mount参数可以指定挂载的类型,如--mount type=bind/tmpfs,默认情况下为--mount type=volume。
注意,type=bind的时候,如果目标目录中有文件,这些文件会被Docker遮蔽,这种行为十分怪异,和挂载卷不同。
type=tmpfs的时候,容器停止时挂载就会被删除,注意tmpfs是无法在多个容器之间共享的。
Docker容器网络
默认情况下,容器会从其主机的网络以某种方式(驱动)分配,容器内部并不知道关于主机网络的任何信息,它仅仅被分配一个IP地址、网关、路由表和DNS服务。Docker支持以下网络驱动:
- bridge:桥接模式(默认)
- host:主机模式
- none:无网络模式
- overlay
- ipvlan
- macvlan
在执行docker container run命令的时候,可以使用--network container:<name|id>参数将容器附加到另一个容器的网络中。--network参数可以多次设置,以便让容器加入到多个网络,对于正在运行的容器,可以使用docker network connect命令将其连接到特定的网络。
默认情况下容器将使用和主机一样的DNS服务器,可以通过--dns参数设置,如--dns=1.1.1.1。
默认情况下,容器使用bridge模式,即桥接模式,这会导致容器连接到一个默认的软桥,为了提供更好的隔离性,用户可以预定义软桥,通过docker network开头的命令。如:
docker network create -d bridge my-bridge-network
docker network create支持如下参数:
- -d/--driver:所创建的网络所使用的驱动
- --subnet:指定所创建的子网,如--subnet=172.28.0.0/16
- --ip-range:从某个IP范围进行分配,如--ip-range=172.28.5.0/24
- --gateway:设置网关
- --internal:限制对外部的网络访问,仅允许网络中的各个容器之间进行访问
- --ingress:表示创建Swarm集群所使用的routing-mesh网络
- --ipv6:开启对IPV6的支持
使用docker network rm命令可以删除某个特定的网络,但必须将该网络从所有容器中断开,该命令需要传入网络名称或者ID。而docker network prune将删除所有未使用的网络,即没有任何容器连接的网络。使用docker network ls命令可以列举所有预定义的网络。
可以通过docker network connect和docker network disconnect命令动态地将容器连接到某个网络(也可以通过docker run --network实现)或者从某个网络中断开。如:
docker network connect multi-host-network container1
可以使用--ip/--ip6参数指定IP地址:
docker network connect --ip 10.10.36.122 multi-host-network container2
如果使用host网络驱动,即主机模式,那么容器将会和主机共享相同的网络空间,容器不会被分配一个新的IP地址,注意在主机模式下,容器内所有开放的端口都可以通过主机网络中的对应端口进行访问,此时docker run中的-p和-P参数都会被忽略。主机模式通常比桥接模式的性能更高,因为它不需要NAT转换,但目前主机模式并不支持IPV6。
如果使用none网络驱动,即无网络模式,容器将无法访问其它容器以及主机上的网络,完全隔离。