DockerFile详解
前言
我们使用Docker
镜像, 一般都是从远程Registry
仓库pull
下来的.
1 | docker pull hello-world |
但是实际开发过程中, 经常会需要把自己的应用程序打包成一个images
镜像. 这个镜像是需要自己打包的.
打包的方式一般有两种, Docker File
和Docker Compose
.
如何在已有 image 上做修改
官方虽然提供了很多的Image
, 但是我们总会要做一些定制化需求.
比如往Image
里面塞一些应用程序. 下面列举一个不推荐使用的修改Image
的方法.
1 | # 1. 拉取基础image |
但是这种方法不推荐, 也不安全.
如果是有恶意的人, 在第二步创建了一个恶意脚本, 然后发布到官方仓库. 使用者根本不知道上传者做了什么操作, 无法进行Review
.
为了能进行Review
代码审查, Docker
提供了一个叫DockerFile
的文件. 用来记录修改了哪些操作.
使用 Dockerfile 改造上述例子
1 | # 1. 基于 alpine 镜像构建 |
我们创建了一个Dockerfile
文件, 然后执行构建命令, 输出了hello
1 | # 1. 在当前目录根据 Dockerfile 构建 Docker 镜像 |
Dockerfile 语法
下面讲讲Dockerfile
的一些命令的语法和使用, 不过建议还是看官方文档的好.
FROM
FROM
表示当前镜像是基于哪个镜像做的.
比如之前用的FROM alpine
, 表示当前镜像是基于alpine
来做定制化需求.
推荐使用官方的Image
来作为FROM
基础镜像.
LABEL
LABEL
用来标记一些元数据metadata
, 相当于镜像的注释.
虽然可以没有, 但是建议要有. 不然就是一个三无产品Image
, 谁敢放心用呢?
1 | # 1. 基于 alpine 镜像构建 |
WORKDIR
WORKDIR
相当于linux
的cd
命令, 用于进入某个目录中, 如果目录不存在会自动创建目录.
有两点需要注意
- 千万不要使用
RUN cd
, 因为每一次RUN
都会产生一层Image Layer
. - 尽量使用绝对目录, 避免弄混淆了
1 | WORKDIR /test |
ENV
ENV
用于设置环境变量, 提高可维护性.
比如我要升级MySQL
版本, 改变量就可以了.
1 | ENV MYSQL_VERSION 5.6 |
除了上面这种简单用法还有一些高级用法
1 | FROM alpine |
${VAR:-default}
的意思是, 当VAR
没有被定义的时候, 使用后面的default
值${VAR:+default}
的意思是, 当VAR
被定义的时候, 使用后面的value
值, 否则使用空字符串
当重复定义一个变量的时候, 当前Image Layer
会使用上一层Image Layer
定义的变量.
1 | FROM alpine |
ARG
ARG <name>[=<default value>]
在docker build
镜像的时候使用--build-arg <varname>=<value>
参数, 可以将外部参数传入Dockerfile
文件中, 用来构建镜像.
1 | ARG VERSION=latest |
构建镜像
1 | # 1. 构建基于 最新alpine 的镜像 |
值得注意的是, 官方文档提到, ARG
不要传递敏感数据, 不安全.
还有就是同名变量ENV
会覆盖ARG
. 根据这个特性, 可以做一些默认的操作.
1 | FROM alpine |
COPY
ADD
和COPY
都是在构建镜像的时候, 将上下文的文件拷贝到镜像中.
1 | docker build -t ahaochan/ahao-alpine . |
关于上下文, 其实我们一直有在用, 在构建的时候会传入一个参数.
, 表示将当前目录作为上下文.
构建的时候, docker
客户端会把上下文中的所有文件发送给docker daemon
.
那么如果docker
客户端和docker daemon
不在同一台机器上, docker daemon
是获取不到除了上下文之外的文件的.
所以Dockerfile
里面的文件, 都要基于这个上下文来访问.
如果只是简单的将文件复制到镜像中, 直接使用COPY
就可以了
1 | FROM alpine |
构建完毕后, 我们进去看看
1 | # 1. 准备文件 |
另外, COPY
还能在多阶段构建中使用, 这是区别于ADD
的一个用法.
多阶段构建常用在编译打包阶段, 这里不细讲.
1 | FROM alpine |
ADD
ADD
和COPY
差不多, 也是将上下文的文件拷贝到镜像中.ADD
除了不能应用在多阶段构建的场景之外, ADD
比COPY
多了两个功能.
- 自动解压缩文件并添加到镜像中
- 从
url
拷贝文件并添加到镜像中. (官方不推荐, 使用curl
或wget
替代)
1 | FROM alpine |
1 | # 1. 准备压缩包 |
RUN
RUN
有2种形式
shell
形式:RUN <command>
exec
形式:RUN ["executable", "param1", "param2"]
1 | FROM alpine |
可以看到, exec
形式的RUN
命令(其实不止RUN
, 还有CMD
和ENTRYPOINT
), 不会去解析${VAR}
.
RUN
命令一般用来安装一些依赖, 删除缓存等操作.
但是每一次RUN
都会产生一层Image Layer
, 所以需要尽可能的少用RUN
, 安装依赖尽量一行代码搞定.
1 | # yum 安装例子 |
CMD
CMD
有3种形式
shell
形式:CMD <command>
exec
形式:CMD ["executable", "param1", "param2"]
exec
形式:CMD ["param1","param2"]
, 作为ENTRYPOINT
的默认参数.CMD
是container
启动时默认执行的命令.
使用CMD
有几个需要注意的地方.- 如果
Dockerfile
有多个CMD
, 就只会执行最后一个. - 外部命令会覆盖内部的
CMD
.
1 | # 1. 基于 alpine 镜像构建 |
编写完Dockerfile
后, 我们来构建它.
1 | # 1. 构建镜像 |
ENTRYPOINT
ENTRYPOINT
有2种形式
shell
形式:ENTRYPOINT command param1 param2
exec
形式:ENTRYPOINT ["executable", "param1", "param2"]
, 推荐使用.
ENTRYPOINT
让container
以应用程序或者服务的形式在后台运行.
等价于docker run --entrypoint "command"
1 | FROM alpine |
1 | docker build -t ahaochan/ahao-alpine . |
Docker Compose 语法
我们在进行容器间的通信的时候, 要分别启动容器, 关闭还要一个个关闭.
比如我要在ahao-alpine2
向ahao-alpine1
发送请求, 需要先启动ahao-alpine1
, 然后再启动ahao-alpine2
.
1 | docker run -d --name ahao-alpine1 alpine /bin/ping 127.0.0.1 |
为了解决这个繁琐的操作, Docker
提供了一个工具Docker Compose
.
1 | # 不推荐使用 apt install, 这安装的是旧版本 |
我们创建一个docker-compose.yml
文件
1 | version: "3" |
一个Service
代表一个Container
, 启动类似docker run
, 可以为其指定network
和volume
.
然后执行以下命令
1 | # 在后台启动 compose |
搭建 wordpress
1 | version: "3" |