Dockerfile详解

本文基于win10 docker 讲述Dockerfile的书写,并举例示范。

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。

基本步骤

  • 创建名为Dockerfile的文件
  • 书写Dockerfile文件
  • 构建镜像,如:docker build -t nginx:v3 . (.代表当前目录,nginx:v3是Dockerfile中定义的镜像标签)
  • 运行docker镜像:docker run -d nginx:v3

命令介绍

FROM

引用最基础的镜像,即构建的基础镜像。

1
FROM nginx:latest

RUN

用于执行后面跟着的命令行命令。

1
2
3
4
5
6
RUN <命令行命令>
RUN go build ./main.go

或者:
RUN ["可执行文件", "参数1", "参数2"]
RUN ["go", "build", "./main.go"]

&& 可用于连接命令,如:wget http://redis.tar && tar -xzvf redis.tar

COPY

复制指令,从上下文目录中复制文件或者目录到容器里指定路径。

1
2
3
4
COPY [--chown=<user>:<group>] ["<源路径1>",...  "<目标路径>"]

# 将当前文件夹下所有的文件复制到镜像/data/myapp文件夹下
COPY . /data/myapp

ADD

ADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY。

与COPY的区别是,若 <源文件> 为 tar 压缩文件的话,会自动复制并解压到 <目标路径>!!!

CMD

类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:

  • CMD 在docker run 时运行。
  • RUN 是在 docker build。

作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。

注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效

1
2
3
4
5
CMD ["<可执行文件或命令>","<param1>","<param2>",...] 
或:
CMD <shell 命令>

CMD java -jar server.jar

ENTRYPOINT

类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。

优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。

注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

此命令适合搭配CMD使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参。

如构建以下的镜像:

1
2
3
FROM nginx
ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参
  1. 不传参数,默认运行dockerfile中的CMD参数。

    1
    docker run  nginx:test
  2. docker run 传参数,覆盖运行run 的参数。

    1
    docker run  nginx:test -c /etc/nginx/new.conf

以上两种方式最后运行的都是:nginx -c /etc/nginx/new.conf,推荐使用数组的方式书写ENTRYPOINT。

ENV

设置环境变量。

1
2
3
4
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

ENV NODE_VERSION 7.2.0

VOLUME

定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。

作用:

  • 避免重要的数据,因容器重启而丢失,这是非常致命的。
  • 避免容器不断变大。
1
2
3
4
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>

VOLUME /data/log

在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点!!!

EXPOSE

声明端口,需要在运行时搭配 -p端口映射使用。

1
2
3
EXPOSE <端口1> [<端口2>...]

EXPOSE 8080

WORKDIR

指定工作目录(项目当前路径)。

1
2
3
WORKDIR <工作目录路径>

WORKDIR /data

更多命令信息参考:https://docs.docker.com/engine/reference/builder/

案例示范

使用dockerfile部署一个简单的gin项目应用。

  1. 创建docker文件夹

  2. 书写gin程序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    package main

    import "github.com/gin-gonic/gin"

    func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
    c.JSON(200, gin.H{
    "message": "pong",
    })
    })

    r.Run(":8080")
    }
  3. 生成go mod 依赖文件

    1
    go mod init docker
  4. 拉取go依赖包

    1
    go mod download
  5. 测试应用程序,访问localhost:8080/ping

    1
    go run main.go
  6. 书写Dockerfile文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    From golang:latest

    # 创建文件夹
    RUN mkdir /app

    # 当前工作目录
    WORKDIR /app

    # 将当前文件夹的所有文件复制到容器中
    COPY . /app

    # 设置go代理
    ENV GO111MODULE=on GOPROXY=https://goproxy.io,direct

    # 下载go的依赖
    RUN go mod tidy

    # 暴露端口
    EXPOSE 8080

    # 运行
    CMD go run main.go
  7. 构建镜像

    1
    docker build -t gin-app .

  8. 运行容器:

    1
    docker run -d -p 8080:8080  gin-app

正常运行。

项目结构示意图

参考:

Dockerfile菜鸟教程

Dockerfile官方文档