以ubuntu为例,构建一个简单的镜像:
# 新建Dockerfile文件夹,并建文件Dockerfile
$ mkdir Dockerfile
$ cd Dockerfile
$ vi Dockerfile
在Dockerfile文件内写入:
FROM ubuntu
MAINTAINER test-user
RUN echo "测试构建镜像" > /test.txt
第一行指令:本镜像以ubuntu为基础构建;
第二行指令:本镜像维护人员信息;
第三行指令:在 docker build的过程中将“测试构建镜像”写入到test.txt文本文件中。
构建镜像:
$ docker build -t ubuntu:test .
>>>
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM ubuntu
---> ba6acccedd29
Step 2/2 : RUN echo "测试构建镜像" > /test.txt
---> Running in 3eea459154d9
Removing intermediate container 3eea459154d9
---> 5279a6de02d5
Successfully built 5279a6de02d5
Successfully tagged ubuntu:test
可以看到,在step2里面。RUN指令启动了容器 ba6acccedd29 ,运行了RUN指令的命令,之后删除了此镜像。
查看镜像是否构建成功:
$ docker run -it ubuntu:test /bin/bash
>>>
root@fa8a1142b131:/ cat test.txt
测试构建镜像
镜像构建成功。
docker build -t ubuntu:test .
,-t属性是指明构建的镜像的仓库以及标签,.
是指Dockerfile文件就在当前文件夹下,如果在根目录执行构建命令,则路径是 /Dockerfile
,这个路径就是上下文路径,表示的是构建镜像需要的文件所处的路径,默认使用Dockerfile所处的路径。scratch
,即空白镜像,如果FROM scratch
,则意味着接下来的指令为镜像的第一层。每一个RUN指令都是新建一层,然后执行命令,并提交这一层的修改,构成新的镜像。
Dockerfile和shell脚本不同,每一个RUN指令都会构建一层,因此尽量将多个RUN指令的命令写在同一个RUN指令中,不同命令之间可用 \
换行,用 &&
连接,为了减少镜像的大小,在指令中需要删除额外不需要的内容,例如:
FROM debian:stretch
RUN set -x; buildDeps='gcc libc6-dev make wget' \
&& apt-get update \
&& apt-get install -y $buildDeps \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& mkdir -p /usr/src/redis \
&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
&& make -C /usr/src/redis \
&& make -C /usr/src/redis install \
&& rm -rf /var/lib/apt/lists/* \
&& rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps
首先指令的内容是编译、安装 redis 可执行文件,其次,可以看到这一组命令的最后添加了清理工作的命令,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 apt
缓存文件。
用于在构建过程中执行后续的命令,有两种格式:
#shell 格式
RUN echo "test" > ./test.txt
#exec格式
RUN ["echo", "test", "./test.txt"]
COPY <源路径> <目标路径>
功能和用法与COPY类似,官方推荐使用COPY。
优点:在执行 <源文件> 是tar压缩文件的时候,文件格式是bzip\gzip2\xz时,会将源文件复制并解压到目标路径;
缺点:在不解压的前提下无法复制tar压缩文件,会使镜像构建缓存失效,从而使镜像构建变得很缓慢。
类似RUN指令,用于运行程序,但是两者的运行时间不一样:
RUN指令在构建镜像时运行,即docker build时运行;
CMD指令在以构建的镜像启动容器时运行,即docker run时运行。
CMD有三种写法:
# exec模式
CMD ["top"]
# shell模式
CMD top
# 第三种写法是为ENTRYPOINT指令指定的默认程序提供参数
CMD ["param1", "param2"]
CMD的exec模式和shell模式的不同在于容器启动时的1号进程不同,这个详解见"Dockerfile#CMD的exec模式和shell模式不同点.md"。
CMD指令的作用是:指定容器启动时默认要运行的程序,程序运行结束时容器也结束。不过docker run命令中指定的运行的程序可覆盖CMD指定的默认运行程序。
CMD的缺点在于如果在docker run的时候想给CMD指定运行的程序传入其他参数时,需要将CMD的指令从新输入一次,即参数不能传入给构建时编写的CMD指令。
#假设镜像ubuntu:test的Dockerfile中指定了默认程序是运行top
...
CMD ["top"]
...
#构建镜像ubuntu:test
#以ubuntu:test启动容器
docker run -it ubuntu:test /bin/bash
#容器运行后实际运行的是 /bin/bash 命令,而不是默认的 top 命令
功能类似CMD,但是在未使用–entrypoint参数的情况下,docker run命令行指令的指令不会像对CMD指令那样覆盖ENTRYPOINT指令,而是将docker run指定的参数及命令作为参数传给ENTRYPOINT指令。
如果Dockerfile里面有ENTRYPOINT,那么CMD指令内容将作为ENTRYPOINT指令的参数。
docker run --entrypoint
,–entrypoint后面的command会覆盖ENTRYPOINT指令。
优点:不同于docker run时想修改CMD指令只能重新在docker run后面重新制定CMD指令指定的命令,ENTRYPOINT指令支持在docker run时给ENTRYPOINT指令传入参数。
如果Dockerfile中有多个ENTRYPOINT,那么只有最后一个会生效。
编写Dockerfile文件:
FROM ubuntu
MAINTAINER klw
RUN ["echo", "测试镜像",">","./test.txt"]
ENTRYPOINT ["echo", "Hello"]
CMD ["World"]
构建测试镜像 test/ubuntu:v1
$ docker build -t test/ubuntu:v1 .
>>>
Sending build context to Docker daemon 2.048kB
Step 1/5 : FROM ubuntu
---> ba6acccedd29
Step 2/5 : MAINTAINER klw
---> Running in df34ca2e01c0
Removing intermediate container df34ca2e01c0
---> 9c154c5d6c72
Step 3/5 : RUN ["echo", "测试镜像",">","./test.txt"]
---> Running in 66a7bb885da8
测试镜像 > ./test.txt
Removing intermediate container 66a7bb885da8
---> 2f2b89bf850c
Step 4/5 : ENTRYPOINT ["echo", "Hello"]
---> Running in 8fd14a3ae197
Removing intermediate container 8fd14a3ae197
---> 4cb474df91b5
Step 5/5 : CMD ["World"]
---> Running in 5b2381e460b9
Removing intermediate container 5b2381e460b9
---> b26e8bcc53aa
Successfully built b26e8bcc53aa
Successfully tagged test/ubuntu:v1
$ docker images
>>>
REPOSITORY TAG IMAGE ID CREATED SIZE
test/ubuntu v1 b26e8bcc53aa About a minute ago 72.8MB
运行测试镜像:
$ docker run -it test/ubuntu:v1
>>>
Hello World
此时在镜像构建结束后,直接运行容器时,默认运行ENTRYPOINT指令,此时CMD指令是作为ENTRYPOINT指令的参数,即运行的命令是 echo "Hello World"
。
启动容器时加上–entrypoint覆盖ENTRYPOINT指令:
$ docker run -it --entrypoint hostname test/ubuntu:v1 -f
>>>
658aad538c50
–entrypoint 指定的是命令而不是命令行,即是 hostname 而不是 hostaname -f ,如果需要为–entrypoint之后的命令添加参数,参数放在docker run的最后面。
ENV baidu "www.baidu.com"
ENV baidu="www.baidu.com"
作用是设置环境变量,处于ENC指令之后的其他指令,如RUN,或者应用程序都可以访问这里定义的环境变量。
Dockerfile文件:
FROM ubuntu
MAINTAINER klw
ENV c curl
RUN apt-get update && apt-get -y install $c
CMD ["curl", "www.baidu.com"]
构建镜像:
$ docker build -t test/ubuntu:v1 .
>>>
Sending build context to Docker daemon 2.048kB
Step 1/5 : FROM ubuntu
---> ba6acccedd29
Step 2/5 : MAINTAINER klw
---> Running in 8d0cc79cad40
Removing intermediate container 8d0cc79cad40
---> 1b122061de5d
Step 3/5 : ENV c curl
---> Running in 5c889cfc2b9f
Removing intermediate container 5c889cfc2b9f
---> f9c354eb3023
Step 4/5 : RUN apt-get update && apt-get -y install $c
---> Running in 2b3c1e2fd480
Get:1 http://security.ubuntu.com/ubuntu focal-security InRelease [114 kB]
Get:2 http://archive.ubuntu.com/ubuntu focal InRelease [265 kB]
...
Unpacking curl (7.68.0-1ubuntu2.7)
...
Removing intermediate container 2b3c1e2fd480
---> 1db52424a5ff
Step 5/5 : CMD ["curl", "www.baidu.com"]
---> Running in dabb0264466d
Removing intermediate container dabb0264466d
---> 38c76ce0b788
Successfully built 38c76ce0b788
Successfully tagged test/ubuntu:v1
可见RUN指令在运行时识别到了设置的环境变量 ENV c curl。
启动容器:
$ docker run -it --rm --name test test/ubuntu:v1
>>>
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc>