Dockerfile


Dockfile是一个用于编写docker镜像生成过程的文件,其有特定的语法。在一个文件夹中,如果有一个名字为Dockfile的文件,其内容满足语法要求,在这个文件夹路径下执行命令:docker build --tag name:tag .,就可以按照描述构建一个镜像,name是镜像的名称,tag是镜像的版本或者是标签号。详情可以参考:https://docs.docker.com/engine/reference/builder/#usage

Dockerfile指令

Dockerfile的基本指令:FROM、LABEL、RUN、CMD、EXPOSE、ENV、ADD、COPY、ENTRYPOINT、VOLUME、USER、WORKDIR、ONBUILD。

FROM

第一个指令必须是FROM了,其指定一个构建镜像的基础源镜像,如果本地没有就会从公共库中拉取,没有指定镜像的标签会使用默认的latest标签,可以出现多次,如果需要在一个Dockerfile中构建多个镜像。

尽量使用官方的image作为base image

用法如下:

FROM scratch  
FROM centos  
FROM ubuntu:14.04

LABEL

描述镜像的信息,像代码的注释。用法如下:

LABEL maintainer="heqingliang@163.com"
LABEL version="1.0"
LABEL description="This is description"

RUN

RUN命令是一个常用的命令,执行完成之后会成为一个新的镜像,每执行一条RUN命令就是生成一层镜像。docker的镜像层是只读的,所以你如果第一句安装了软件,用完在后面一句删除是不可能的。所以这种情况要在一句RUN命令中完成,可以通过&符号连接多个RUN语句。

为了美观,复杂的RUN请用反斜杠换行,避免无用分层,合并多条命令成一行

用法如下:

RUN yum update && yum install -y vim python-dev
RUN apt-get update && apt-get install vim && rm -rf \
    /var/lib/apt/lists/*

WORKDIR

用于设置工作目录,后续参数如果是相对路径,则会基于之前的命令指定的路径。如:

WORKDIR /test # 如果没有会自动创建test目录
WORKDIR demo
RUN pwd      # 输出结果是/test/demo

path路径也可以是环境变量,比如有环境变量HOME=/home,WORKDIR $HOME/test也就是/home/test。

ADD

复制本机文件、目录、远程文件,添加到指定的容器目录,支持GO的正则模糊匹配。路径是绝对路径,不存在会自动创建。如果源是一个目录,只会复制目录下的内容,目录本身不会复制。ADD命令会将复制的压缩文件夹自动解压,这也是与COPY命令最大的不同。

用法如下:

ADD hello /
ADD test.tar.gz / # 添加到根目录并解压缩

WORKDIR /root
ADD hello test/ # /root/test/hello

COPY

COPY除了不能自动解压,也不能复制网络文件。其它功能和ADD相同。添加远程文件或目录可以使用curl或者wget。

ENV

设置容器的环境变量,可以让其后面的RUN命令使用,容器运行的时候这个常量也会保留。

ENV MYSQL_VERSION 5.6 # 设置常量
RUN apt-get install -y mysql-server="${MYSQL_VERSION}"

EXPOSE

告诉Docker服务器容器对外映射的容器端口号,在docker run -p的时候生效。

VOLUME

在主机上创建一个挂载,挂载到容器的指定路径。docker run -v命令也能完成这个操作,而且更强大。这个命令不能指定主机的需要挂载到容器的文件夹路径。但docker run -v可以,而且其还可以挂载数据容器。

CMD

设置容器启动后默认执行的命令和参数,如果docker run指定了其他的命令,CMD命令被忽略,如果定义了多个CMD,只有最后一个命令会执行。

如定义了以下的Dcokfile:

[heql@ubuntu dk]$ cat Dockerfile 
FROM centos
ENV name Docker
CMD echo "hello $name"

build后只有用docker run image,则会打印出:hello Docker,如果使用docker run -it image /bin/bash,则不会打印。

ENTRYPOINT

和CMD命令一样,唯一的区别是不能被docker run命令的执行命令覆盖,如果要覆盖需要带上选项–entrypoint,如果有多个选项,只有最后一个会生效。

FROM centos
ENV name Docker
ENTRYPOINT echo ["/bin/bash", "-c", "echo hello $name"]

ONBUILD

配置当前所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。意思就是,这个镜像创建后,如果其它镜像以这个镜像为基础,会先执行这个镜像的ONBUILD命令。

DCOKERFILE 例子

下面的python程序是一个使用flask框架编写的web的程序,文件命名为app.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
flask demo
"""

__author__ = 'heqingliang'



from flask import Flask
from flask import request


app = Flask(__name__)


@app.route('/', methods=['GET', 'POST'])
def hello():
return '<h1>Hello Docker</h1>'


def main():
app.run(host="0.0.0.0", port=5000, debug=True)


if __name__ == '__main__':
main()

使用下面的dockerfile,构建image:

FROM python
LABEL maintainer="heqingliang_gzus@163.com"
RUN pip install flask
COPY app.py /app/
WORKDIR /app
EXPOSE 5000
CMD ["python", "app.py"]

编译成功后,会生成相应的image:

[heql@ubuntu flask-demo]$ docker build -t heqingliang/flask-app .
[heql@ubuntu flask-demo]$ docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
heqingliang/flask-app   latest              ddf38d204760        5 minutes ago 

运行容器:

[heql@ubuntu flask-demo]$ docker run -d -p 5000:5000 --name=flask-app heqingliang/flask-app
79088d8fb9e39fedc9702e33fe7cc08a5c9a22322ffa9c7e1afab0680deafa47
[heql@ubuntu flask-demo]$ docker ps
CONTAINER ID        IMAGE                   COMMAND             CREATED             STATUS              PORTS                    NAMES
79088d8fb9e3        heqingliang/flask-app   "python app.py"     6 seconds ago       Up 4 seconds        0.0.0.0:5000->5000/tcp   flask-app

在浏览器中输入http://127.0.0.1:5000/,可以看到如下结果:

docker_flask_app.png

dockerfile命令行参数

如有以下程序,接收命令行程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

int main(int argc, char *argv[]) {
if (argc < 2) {
printf("usage: <%s args...>\n", argv[0]);
return -1;
}

int i;
for (i = 1; i < argc; i++) {
printf("argv[%d]: %s\n", i, argv[i]);
}

return 0;
}

编译hello.c的程序:

gcc -static hello.c -o hello

使用下面的dockerfile,构建image:

FROM scratch
LABEL maintainer="heqingliang_gzus@163.com"
COPY hello /hello/
WORKDIR /hello
ENTRYPOINT ["./hello"]
CMD []

编译成功后,会生成相应的image:

[heql@ubuntu hello-world]$ docker build -t heqingliang/hello .

运行容器:

[heql@ubuntu hello-world]$ docker run heqingliang/hello
usage: <./hello args...>
[heql@ubuntu hello-world]$ docker run heqingliang/hello aa bb cc dd
argv[1]: aa
argv[2]: bb
argv[3]: cc
argv[4]: dd