docker(2)

ENTRYPOINT 入口点

ENTRYPOINT 的格式和 RUN 指令格式一样,分为 exec 格式和 shell 格式。需要通过 docker run 的参数 --entrypoint

1
<ENTRYPOINT> "<CMD>"

有cmd为什么要entrypoint

1
2
3
4
5
FROM ubuntu:18.04
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
CMD [ "curl", "-s", "http://myip.ipip.net" ]

在使用参数时,参数会直接替换掉默认的指令,这里的 -i 替换了原来的 CMD,而不是添加在原来的 curl -s http://myip.ipip.net 后面。而 -i 根本不是命令,所以自然找不到,使用entrypoint可以因为当存在 ENTRYPOINT 后,CMD 的内容将会作为参数传给 ENTRYPOINT,而这里 -i 就是新的 CMD,因此会作为参数传给 curl

2.可以写一个脚本

ENV 设置环境变量

1
2
ENV VERSION=1.0 DEBUG=on \
NAME="Happy Feet"

这个例子中演示了如何换行,以及对含有空格的值用双引号括起来的办法,这和 Shell 下的行为是一致的。

ARG 构建参数

构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是,ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用 ARG 保存密码之类的信息,因为 docker history 还是可以看到所有值的

VOLUME 定义匿名卷

数据库文件应该保存于卷(volume)中,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。

1
VOLUME /data

EXPOSE 暴露端口

EXPOSE 指令是声明容器运行时提供服务的端口,这只是一个声明,在容器运行时并不会因为这个声明应用就会开启这个端口的服务以方便配置映射

另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口

WORKDIR 指定工作目录

使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录

1
2
RUN cd /app
RUN echo "hello" > world.txt

每一个run都是不同的容器,改变文件路径要使用workdir

USER 指定当前用户

USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN, CMD 以及 ENTRYPOINT 这类命令的身份注意,USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。

HEALTHCHECK 健康检查

HEALTHCHECK 指令是告诉 Docker 应该如何进行判断容器的状态是否正常,这是 Docker 1.12 引入的新指令。

ONBUILD 为他人作嫁衣裳

ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行

LABEL 为镜像添加元数据

LABEL 指令用来给镜像以键值对的形式添加一些元数据(metadata 我们还可以用一些标签来申明镜像的作者、文档地址等

容器

启动方式1.删了重新构建 2.重新启动

所需要的命令主要为 docker run

1
2
$ docker run ubuntu:18.04 /bin/echo 'Hello world'
Hello world
1
2
$ docker run -t -i ubuntu:18.04 /bin/bash
root@af8bae53bdd3:/#

启动一个bash终端,-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。

docker run 后台启动步骤

检查本地是否存在指定的镜像,不存在就从 registry 下载

利用镜像创建并启动一个容器

分配一个文件系统,并在只读的镜像层外面挂载一层可读写层

从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去

从地址池配置一个 ip 地址给容器

执行用户指定的应用程序

执行完毕后容器被终止

可以利用 docker container start 命令,直接将一个已经终止(exited)的容器启动运行

守护态运行

更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 -d 参数来实现

1
2
$ docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a

此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用 docker logs 查看)。

使用 -d 参数启动后会返回一个唯一的 id,也可以通过 docker container ls 命令来查看容器信息。

1
2
3
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
77b2dc01fe0f ubuntu:18.04 /bin/sh -c 'while tr 2 minutes ago Up 1 minute agitated_wright

要获取容器的输出信息,可以通过 docker container logs 命令

1
2
3
4
5
$ docker container logs [container ID or NAMES]
hello world
hello world
hello world
. . .

终止

docker container stop 来终止一个运行中的容器

可以通过 docker container start 命令来重新启动。

此外,docker container restart 命令会将一个运行态的容器终止,然后再重新启动它。

进入容器

某些时候需要进入容器进行操作,包括使用 docker attach 命令或 docker exec 命令,推荐大家使用 docker exec 命令,原因会在下面说明。

docker attach 不需要参数直接跟id就行但是在bash界面的时候exit会直接导致整个容器关闭,

docker exec 后面可以多个参数 如-i,-t,且exit时不会关闭容器

导出和导入

如果要导出本地某个容器,可以使用 docker export 命令

可以使用 docker import 从容器快照文件中再导入为镜像,例如

此外,也可以通过指定 URL 或者某个目录来导入,例如

1
$ docker import http://example.com/exampleimage.tgz example/imagerepo

用户既可以使用 *docker load* 来导入镜像存储文件到本地镜像库,也可以使用 *docker import* 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。

删除

可以使用 docker container rm 来删除一个处于终止状态的容器。例如

1
docker container prune

可以清理所有处于终止的容器

仓库

拉取镜像

你可以通过 docker search 命令来查找官方仓库中的镜像,并利用 docker pull 命令来将它下载到本地。

其中包括镜像名字、描述、收藏数(表示该镜像的受关注程度)、是否官方创建(OFFICIAL)、是否自动构建 (AUTOMATED

数据卷

1
2
$ docker volume create my-vol
创建一个数据卷

查看所有的 数据卷

1
$ docker volume ls

在主机里使用以下命令可以查看指定 数据卷 的信息

1
2
3
4
5
6
7
8
9
10
11
$ docker volume inspect my-vol
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]

启动一个挂载数据卷的容器

在用 docker run 命令的时候,使用 --mount 标记来将 数据卷 挂载到容器里。在一次 docker run 中可以挂载多个 数据卷

1
2
3
4
5
6
$ docker run -d -P \
--name web \
# -v my-vol:/usr/share/nginx/html \
--mount source=my-vol,target=/usr/share/nginx/html \
nginx:alpine

source=my-volsource参数指定了数据卷的名称。在这个例子中,数据卷的名称是 my-vol。如果这个数据卷在执行docker run命令时不存在,Docker 会自动创建。

target=/usr/share/nginx/htmltarget参数定义了在Docker容器中的挂载点路径。在这个例子中,挂载点路径是 /usr/share/nginx/html,这通常是nginx服务器托管静态文件的目录。

  • -d:这是 docker run 命令中的选项,全称是 --detach,用于将Docker容器在后台运行,并打印出容器ID。换句话说,使用 -d选项启动的容器将 “脱离” 控制台,以守护进程的方式在后台运行。
  • -p:这是 docker run 命令中的选项,全称是 --publish,用于绑定容器的网络端口到主机的网络端口。格式如 -p <host-port>:<container-port>,例如 -p 8080:80 表示将容器的 80 端口映射到主机的 8080 端口。 这样,你就可以通过访问主机的 8080 端口从外部访问容器的服务。

删除数据卷

1
$ docker volume rm my-vol

挂载主机目录

使用 --mount 标记可以指定挂载一个本地主机的目录到容器中,本地和容器里面的目录是共用

1
2
3
4
5
$ 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

这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作。本地目录的路径必须是绝对路径

1
2
3
4
5
$ 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

增加只读权限

--mount 标记也可以从主机挂载单个文件到容器中

1
2
3
4
5
6
7
8
9
$ docker run --rm -it \
# -v $HOME/.bash_history:/root/.bash_history \
--mount type=bind,source=$HOME/.bash_history,target=/root/.bash_history \
ubuntu:18.04 \
bash

root@2affd44b4667:/# history
1 ls
2 diskutil list

rm是退出后自己删除

使用网络

外部访问容器

容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P-p 参数来指定端口映射。

当使用 -P 标记时,Docker 会随机映射一个端口到内部容器开放的网络端口

1
2
3
4
5
$ docker run -d -P nginx:alpine

$ docker container ls -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fae320d08268 nginx:alpine "/docker-entrypoint.…" 24 seconds ago Up 20 seconds 0.0.0.0:32768->80/tcp bold_mcnulty

本地主机的 32768 被映射到了容器的 80 端口。此时访问本机的 32768 端口即可访问容器内 NGINX 默认页面

通过docker logs去查看访问记录

映射所有接口地址

不加-p的话,只能开发端口,但是没有映射

1
$ docker run -d -p 80:80 nginx:alpine

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

1
$ docker run -d -p 127.0.0.1:80:80 nginx:alpine

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

1
$ docker run -d -p 127.0.0.1::80 nginx:alpine

查看映射端口配置

使用 docker port 来查看当前映射的端口配置,也可以查看到绑定的地址

1
2
$ docker port fa 80
0.0.0.0:32768

容器互联

下面先创建一个新的 Docker 网络

1
$ docker network create -d bridge my-net

-d 参数指定 Docker 网络类型,有 bridge overlay。其中 overlay 网络类型用于 Swarm mode,在本小节中你可以忽略它

如果你只是在单个主机上运行Docker容器,使用bridge网络即可;如果你在设置Docker Swarm集群,或者需要在多个主机上运行的容器之间进行网络通信,那么你应该使用overlay网络。

连接容器

运行一个容器并连接到新建的 my-net 网络

1
$ docker run -it --rm --name busybox1 --network my-net busybox sh

打开新的终端,再运行一个容器并加入到 my-net 网络

1
$ docker run -it --rm --name busybox2 --network my-net busybox sh

能够相互ping通就行

image-20240114171357380

Docker Compose

如果你有多个容器之间需要互相连接,推荐使用 Docker Compose

配置 DNS

如何自定义配置容器的主机名和 DNS 呢?秘诀就是 Docker 利用虚拟文件来挂载容器的 3 个相关配置文件。自动挂载

1
2
3
4
$ mount
/dev/disk/by-uuid/1fec...ebdf on /etc/hostname type ext4 ...
/dev/disk/by-uuid/1fec...ebdf on /etc/hosts type ext4 ...
tmpfs on /etc/resolv.conf type tmpfs ...

这种机制可以让宿主主机 DNS 信息发生更新后,所有 Docker 容器的 DNS 配置通过 /etc/resolv.conf 文件立刻得到更新。

  1. /etc/resolv.conf : 记录了容器的 DNS 配置信息,DNS服务器的地方。
  2. /etc/hostname : 记录了容器的主机名。
  3. /etc/hosts : 记录了容器的 IP 地址和主机名的映射

配置全部容器的 DNS ,也可以在 /etc/docker/daemon.json 文件中增加以下内容来设置

如果在容器启动时没有指定最后两个参数,Docker 会默认用主机上的 /etc/resolv.conf 来配置容器

高级网络配置

Docker 中的 “docker0” 虚拟网桥是 Docker 网络功能的核心组件之一,它负责在 Docker 主机和 Docker 容器之间以及 Docker 容器之间传递网络数据包。

这是 “docker0” 虚拟网桥的主要职责:

  1. 连接容器网络:每个 Docker 容器都会有一个网卡与 “docker0” 虚拟网桥连接,使得互相之间可以通信。
  2. 与主机网络交互:”docker0” 虚拟网桥还可以与 Docker 主机的其他网络接口(例如物理网卡)进行交互,使得 Docker 容器可以与 Docker 主机或 Docker 主机所在网络的其他设备通信。
  3. 分配 IP 地址:当一个新的 Docker 容器启动时,”docker0” 虚拟网桥会为该容器的网卡分配一个 IP 地址。

通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。Docker 就创建了在主机和所有容器之间一个虚拟共享网络

快速配置指南

下面是一个跟 Docker 网络相关的命令列表。其中有些命令选项只有在 Docker 服务启动的时候才能配置,而且不能马上生效

-b BRIDGE--bridge=BRIDGE 指定容器挂载的网桥

1
dockerd --bridge=my_bridge

--bip=CIDR 定制 docker0 的掩码

-H SOCKET...--host=SOCKET... Docker 服务端接收命令的通道:Docker 服务端接收命令的通道,即 Docker API

--icc=true|false 是否支持容器之间进行通信

--ip-forward=true|false 请看下文容器之间的通信

--iptables=true|false 是否允许 Docker 添加 iptables 规则:iptables 规则被用来实现网络隔离、端口映射等功能。例如,当你使用 -p 选项启动一个 Docker 容器并映射端口时,Docker 后台会自动添加 iptables 规则来将主机的端口流量转发到 Docker 容器的端口

--mtu=BYTES 容器网络中的 MTU:MTU(Maximum Transmission Unit,最大传输单元)

下面2个命令选项既可以在启动服务时指定,也可以在启动容器时指定。在 Docker 服务启动的时候指定则会成为默认值,后面执行 docker run 时可以覆盖设置的默认值。

--dns=IP_ADDRESS... 使用指定的DNS服务器

--dns-search=DOMAIN... 指定DNS搜索域,如果你在一个宿主机上输入 ping myhost,而 DNS 无法直接找到 “myhost” 这个主机名的记录,如果你的搜索域包含 example.com,那么 DNS 将尝试查询 myhost.example.com

1
docker run --dns-search=example.com --dns-search=another.com -d my_image

最后这些选项只有在 docker run 执行时使用,因为它是针对容器的特性内容。

-h HOSTNAME--hostname=HOSTNAME 配置容器主机名

1
docker run -h myhostname my_image

--link=CONTAINER_NAME:ALIAS 添加到另一个容器的连接

--net=bridge|none|container:NAME_or_ID|host 配置容器的桥接模式

-p SPEC--publish=SPEC 映射容器端口到宿主主机

-P or --publish-all=true|false 映射容器所有端口到宿主主机

容器访问控制

iptables 防火墙来进行管理和实现。iptables 是 Linux 上默认的防火墙软件,在大部分发行版中都自带。

容器访问外部网络

容器要想访问外部网络,需要本地系统的转发支持。在Linux 系统中,检查转发是否打开

转发,容器原本不能够访问外网,但是通过主机转发就可以进行访问

1
2
$sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

容器之间访问

容器之间相互访问,需要两方面的支持。

容器的网络拓扑是否已经互联。默认情况下,所有容器都会被连接到 docker0 网桥上。

本地系统的防火墙软件 – iptables 是否允许通过。