docker 配置
dockerd 默认配置见官方文档 daemon.json 示例.且提供了众多命令行参数.下面介绍一些常用配置:
1 | # 进程相关 |
docker 底层原理
docker 使用 Go 语言编写,并利用 Linux 内核的多个功能来实现其功能.
Namespaces 名称空间
docker 使用一种称为 namespaces
的技术来为容器提供运行环境的隔离.运行容器时,docker 引擎会为该容器创建一组如下的名称空间
名称空间 | 描述 |
---|---|
PID | 提供进程隔离.每个容器中都有独立的进程号 |
Network | 提供网络资源隔离.每个容器都有独立的网络设备接口, IPv4, IPv6 协议栈,路由表,防火墙等 |
IPC | 提供进程间通信资源隔离.容器中进程间通信仍然使用 Linux 进程间通信方法,信号量,消息队列,共享内存等 |
Mount | 提供文件系统隔离.每个容器都有独立的 / 根文件系统 |
UTS | 提供容器主机名和域名隔离.每个容器都有独立的主机名和域名,其主机名一般为容器 ID |
User | 提供用户及用户组隔离.每个容器都有独立的用户,用户组及其相关访问权限 |
Control groups
控制组(cgroups)以一组进程为目标进行系统资源分配和控制,它提供了如下功能:
- Resource limitation,资源限制,如内存,CPU 等硬件资源
- Prioritization,优先级控制
- Accounting,审计或统计
- Controll,进程控制,如进程挂起与恢复
系统管理员可更具体地控制对系统资源的分配,优先顺序,拒绝,管理和监控.可更好地根据任务和用户分配硬件资源,提高总体效率.在实践中,系统管理员一般会利用 CGroup 做下面这些事:
- 隔离进程集合,并限制他们所消耗的资源
- 为这组进程分配其足够使用的内存,网络带宽和磁盘存储限制
- 限制访问某些设备
Union file systems
Union file systems(联合文件系统)是通过创建层级进行操作的文件系统,它将对文件系统的修改作为一次提交来一层层的叠加.常用的包含 overlay2
,aufs
.
overlay2 采用三层结构:
- lowerdir: 只读层,镜像层
- uperdir: 读写层.创建容器时创建,所有对容器的改动发生在这里
- merged: 容器挂载点,将以上两层进行合并后看到的内容
docker 几种网络模型
可以使用 docker run --net=xxx
指定容器使用的网络类型
bridge
: 桥接,默认的网络模型.为主机上的容器分配单独的网络名称空间,IP 等,并将容器中的网络接口连接到虚拟网桥上(docker0)host
: 与宿主机共用网络名称空间,容器使用宿主机的 IP 和端口overlay
: 与其它容器共用网络名称空间,使容器间能够通过 lo 进行通信none
: 容器有独立的网络名称空间,但不进行任何网络配置,只有本地地址macvlan
: 为容器分配 MAC 地址,使其在网络上显示为物理设备.可通过 MAC 地址直接将流量路由到容器.
docker 开发最佳实践
保持镜像尽可能的小
尽量使用
ENV
和ARG
让人不改或者少改 Dockerfile 即可做构建对应版本的镜像尽量减少 Dockerfile 中单独的
RUN
命令的数量来减少镜像的层数.在一个 RUN 中清理编译生成文件,安装包的缓存等临时文件
Dockerfile 中指令
RUN
,COPY
,ADD
会创建新的镜像层,之后镜像层的操作不会影响上一层.因此即便 Dockerfile 中包含RUN rm -rf xxx
镜像大小也不会减小.
1 | RUN mkdir /data |
1 | RUN mkdir /data && touch /data/index.html |
- 从适当的基础镜像开始.例如,如果您需要 JDK,请考虑基于正式的 openjdk 镜像,而不是基于 ubuntu 镜像开始,再将 openjdk 的安装作为 Dockerfile 的一部分
- 可以在官方 Dockerfile 里添加一些常见的排错命令,也可以将二进制及其依赖库添加到镜像中,参见为容器镜像定制安装Linux工具.如
1 | # 本示例仅作为示例演示,并没有实际意义 |
- 使用多阶段构建.例如,您可以使用 maven 镜像构建 Java 应用程序,然后使用 tomcat 镜像并将构建的 Java 程序复制到正确的位置.这意味着您最终构建的镜像不包括构建所引入的所有库和依赖项.见如下示例
1 | FROM golang:1.13.6-alpine3.10 as builder |
1 | FROM golang:1.13.6-alpine3.10 as builder |
- 尝试将共享的运行环境或依赖构建为独立的镜像,然后在此基础上构建其它镜像.Docker 只需要加载一次公共层,然后将它们缓存,可以更快的构建.如多个应用都需要自定义的 Tomcat 环境,可以将自定义 Tomcat 构建为单独镜像,而不需要每次构建应用时从最初始自定义 Tomcat 环境开始构建
- 容器时区问题可以在构建镜像的时候安装
tzdate
包,然后声明变量TZ
即可声明容器运行的时区,或者构建的时候复制宿主机的/etc/localtime
或者运行的时候挂载宿主机的/etc/localtime
在何处以及如何保留数据
- 避免将数据存储在容器的可写层中,这会增加容器大小,且效率不如使用 volumes 或 bind 挂载
- bind 挂载多用于开发或测试过程中.对于生产环境,请使用 volumes
Dockerfile
1 | docker build [OPTIONS] PATH | URL | -` |
docker build
命令从 Dockerfile
及上下文构建镜像,构建的上下文是位于 PATH
或 URL
指定的位置的文件集合.PATH
是本地文件系统上的目录,URL
是一个 Git 仓库位置.
上下文是递归处理的.因此 PATH
包括任何子目录,URL
包括仓库及其子模块.
构建过程是 Docker 守护进程进行的.构建的第一件事就是将整个上下文目录及其子目录发送到守护进程中.因此,最好以空目录作为上下文,仅包含 Dockerfile 及 Dockerfile 构建过程中所需要的文件.
.dockerignore
通过在上下文中添加 .dockerignore
文件可以排除构建上下文中包含的文件或目录.
.dockerignore
文件使用 #
作为注释,每行包含一个忽略的文件或目录,支持使用 *
,?
,**
作为通配符匹配.分别表示所有文件,单个字符,目录递归.
在使用 *
忽略所有文件后,可以使用 !
向上下文中添加被忽略的文件
指令详解
ARG
ARG
定义一个变量,用户也可以在构建时使用 --build-arg <varname>=<value>
传入构建参数.如果该参数没有在 Dockerfile 中定义,则输出警告信息.
1 | ARG <name>[=<default value>] |
FROM
FROM
指定构建过程的基础镜像,一个有效的 Dockerfile 必须以 FROM
指令启动.它支持使用 ARG
定义的变量
1 | FROM [--platform=<platform>] <image> [AS <name>] |
RUN
RUN
指令从当前镜像最新层执行命令并提交结果.生成的镜像层用于 Dockerfile 中下一步.
1 | RUN COMMAND |
LABEL
LABEL
指令为镜像打标签
1 | LABEL <key>=<value> <key>=<value> <key>=<value> ... |
EXPOSE
EXPOSE
指令暴露 Docker 容器监听的端口
1 | EXPOSE <port> [<port>/<protocol>...] |
ENV
ENV
指令定义环境变量
1 | ENV <key> <value> |
WORKDIR
WORKDIR
指令定义工作目录,相当于 cd
1 | WORKDIR /path/to/workdir |
USER
USER
指令指定运行镜像时使用的用户名及可选组
1 | USER <user>[:<group>] |
VOLUME
VOLUME
指定挂载点
1 | VOLUME ["/path"] |
ADD
ADD
指令支持拷贝压缩文件到镜像中,并自动解压ADD
指令支持从源文件来自指定URL,构建容器时会自动下载到指定目录
1 | ADD [--chown=<user>:<group>] <src>... <dest> |
COPY
1 | COPY [--chown=<user>:<group>] <src>... <dest> |
ENTRYPOINT
ENTRYPOINT
指令设置容器启动后要执行的命令.使用 --entrypoint
指令进行替换.它有两种形式
--entrypoint
指令指定的命令会覆盖原有所有命令及参数
1 | # exec 形式,是推荐的形式.容器中 `executable` 进程 ID 为 1,可以使用 docker stop 命令优雅的停止容器. |
CMD
CMD
指令一般用来设置容器启动后执行命令的默认参数.如果是参数,则必须指定 ENTRYPOINT
指令.如果包含多个 CMD
指令,以最后一个为准.
docker run [command]
时会覆盖 CMD
指令内容,对 ENTRYPOINT
无影响
CMD
指令有 3 种形式
1 | # exec 形式,是推荐的形式.该形式不会支持管道或变量替换.容器中 `executable` 进程 ID 为 1 |
下表列出了不同 ENTRYPOINT
与 CMD
指令在容器启动时运行的命令:
No ENTRYPOINT | ENTRYPOINT exec_entry p_entry |
ENTRYPOINT ["exec_entry", "p_entry"] |
|
---|---|---|---|
No CMD | error | /bin/sh -c exec_entry p_entry |
exec_entry p_entry |
CMD ["exec_cmd p_cmd"] |
exec_cmd p_c |
/bin/sh -c exec_entry p_entry |
exec_entry p_entry exec_cmd p_cmd |
CMD ["p1_cmd", "p2_cmd"] |
p1_cmd p2_cmd |
/bin/sh -c exec_entry p_entry |
exec_entry p_entry p1_cmd p2_cmd |
CMD exec_cmd p_cmd |
exec_cmd p_cmd |
/bin/sh -c exec_entry p_entry |
exec_entry p_entry /bin/sh -c exec_cmd _cmd |
可以在 GitHub 上查看官方镜像的 Dockerfile