一、FROM 语法
scratch -- 从头开始
尽量来使用官方提供的image
FROM 指定基础镜像,最好挑一些apline,slim之类的基础小镜像. scratch镜像是一个空镜像,常用于多阶段构建 如何确定我需要什么要的基础镜像? Java应用当然是java基础镜像(SpringBoot应用)或者Tomcat基础镜像(War应用) JS模块化应用一般用nodejs基础镜像 其他各种语言用自己的服务器或者基础环境镜像,如python、golang、java、php等
编辑
二、label语法
meta信息,类似于代码的注释。
LABEL multi.label1="value1" multi.label2="value2" other="value3" LABEL multi.label1="value1" \ multi.label2="value2" \ other="value3"
三、run语法
没run一次image上会有新的一层。因此有必要使用&&合并执行,避免layer 过多看起来很混乱。
编辑
四、workdir 语法
使用workdir不要使用run cd来替代
workdir尽量使用绝对路径。增强dockerfile的可移植性。
编辑
五、add 和copy 语法
- 区别:add 还具有解压缩的功能,例如add test.tat.gz / 会直接将test.tar.gz解压出来
下面以reids源代码为例进行演示
Dockerfile如下
FROM alpine ADD ./redis-7.0.8.tar.gz /redis/ RUN ls -l RUN cd /redis && ls -l
结果
- workdir 和add的结合
- 添加远程文件/目录使用curl 或者 wget
编辑
copy文件夹时候的坑
https://www.jianshu.com/p/9b7da9aacd8a
六、ENV语法
定义常量,增加可维护性,和shell中定义变量一个意思。
编辑
七、volume 和expose 语法
八、run、cmd 和entrypoint
编辑例子:
shell 格式的docker file build的image
[root@iZ2vceej7yes1abpm7yec2Z dockerfile]# more Dockerfile FROM centos ENV name Docker ENTRYPOINT echo "hello $name" [root@iZ2vceej7yes1abpm7yec2Z dockerfile]# docker build -t ninesun0318/centos-entrypoint-shell . Sending build context to Docker daemon 2.048kB Step 1/3 : FROM centos ---> 0d120b6ccaa8 Step 2/3 : ENV name Docker ---> Running in 00b2f71e6d9f Removing intermediate container 00b2f71e6d9f ---> 4be9a34323b3 Step 3/3 : ENTRYPOINT echo "hello $name" ---> Running in cfc67b613f7a Removing intermediate container cfc67b613f7a ---> 865aaaacd1ee Successfully built 865aaaacd1ee Successfully tagged ninesun0318/centos-entrypoint-shell:latest [root@iZ2vceej7yes1abpm7yec2Z dockerfile]# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE ninesun0318/centos-entrypoint-shell latest 865aaaacd1ee 17 seconds ago 215MB centos latest 0d120b6ccaa8 2 months ago 215MB hello-world latest bf756fb1ae65 10 months ago 13.3kB [root@iZ2vceej7yes1abpm7yec2Z dockerfile]# docker run 865aaaacd1ee hello Docker
很完美这个dockerfile没有问题。
exec格式的docker file build的image
[root@iZ2vceej7yes1abpm7yec2Z dockerfile]# more Dockerfile FROM centos ENV name Docker ENTRYPOINT ["/bin/echo", "hello $name"] [root@iZ2vceej7yes1abpm7yec2Z dockerfile]# docker build -t ninesun0318/centos-entrypoint-exec . Sending build context to Docker daemon 2.048kB Step 1/3 : FROM centos ---> 0d120b6ccaa8 Step 2/3 : ENV name Docker ---> Using cache ---> 4be9a34323b3 Step 3/3 : ENTRYPOINT ["/bin/echo", "hello $name"] ---> Running in 61c84954936b Removing intermediate container 61c84954936b ---> 2292313abbf0 Successfully built 2292313abbf0 Successfully tagged ninesun0318/centos-entrypoint-exec:latest [root@iZ2vceej7yes1abpm7yec2Z dockerfile]# [root@iZ2vceej7yes1abpm7yec2Z dockerfile]# [root@iZ2vceej7yes1abpm7yec2Z dockerfile]# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE ninesun0318/centos-entrypoint-exec latest 2292313abbf0 5 seconds ago 215MB ninesun0318/centos-entrypoint-shell latest 865aaaacd1ee 8 minutes ago 215MB centos latest 0d120b6ccaa8 2 months ago 215MB hello-world latest bf756fb1ae65 10 months ago 13.3kB [root@iZ2vceej7yes1abpm7yec2Z dockerfile]# docker run ninesun0318/centos-entrypoint-exec hello $name [root@iZ2vceej7yes1abpm7yec2Z dockerfile]#
奇怪exec 并没有把name 这个ENV打印出来。为何?
只有shell 中才会执行name才会被解释为变量,exec 就是简单的执行。
如何修改呢?
[root@iZ2vceej7yes1abpm7yec2Z dockerfile]# more Dockerfile FROM centos ENV name Docker ENTRYPOINT ["/bin/bash","-c","echo hello $name"] [root@iZ2vceej7yes1abpm7yec2Z dockerfile]#
重新build 然后run
[root@iZ2vceej7yes1abpm7yec2Z dockerfile]# docker build -t ninesun0318/centos-entrypoint-exec-new . Sending build context to Docker daemon 2.048kB Step 1/3 : FROM centos ---> 0d120b6ccaa8 Step 2/3 : ENV name Docker ---> Using cache ---> 4be9a34323b3 Step 3/3 : ENTRYPOINT ["/bin/bash","-c","echo hello $name"] ---> Running in 5ee34add9195 Removing intermediate container 5ee34add9195 ---> cf961663ddc1 Successfully built cf961663ddc1 Successfully tagged ninesun0318/centos-entrypoint-exec-new:latest [root@iZ2vceej7yes1abpm7yec2Z dockerfile]# docker run ninesun0318/centos-entrypoint-exec-new hello Docker [root@iZ2vceej7yes1abpm7yec2Z dockerfile]# more Ddo more: stat of Ddo failed: No such file or directory
这个问题得到了解决。
cmd推荐写法
Dockerfile 测试
CMD为ENTRYPOINT提供默认参数
FROM alpine LABEL mantainer=ninesun CMD ["testvm"] CMD ["uuuidd"] ENTRYPOINT ["echo"]
docker build -t demo:test -f Dockerfile3 . docker run --rm --name test demo:test uuuidd # 指定其他命令 docker run --rm --name test demo:test 22222 22222
entrypoint推荐写法
例子说明:entrypoint 一定会被执行,cmd 会被覆盖。
Dockerfile列子
1. FROM alpine 2. LABEL mantainer=ninesun 3. CMD ["testvm"] 4. CMD ["uuuidd"] 5. ENTRYPOINT ["echo","hahhahah"]
# docker run指定参数 覆盖cmd,但是entrypoint一定被执行 docker run --rm --name test demo:test 222 hahhahah 222 # docker run 不覆盖cmd,但是entrypoint一定被执行 docker run --rm --name test demo:test hahhahah uuuidd
CMD和ENTRYPOINT两者都可以作为容器的入口
Dockerfile中只能有一条CMD指令。 如果列出多个CMD,则只有最后一个CMD才会生效。
CMD的主要目的是为执行中的容器提供默认值。 这些默认值可以包含可执行文件,也可以省略可执行文件,在这种情况下,您还必须指定ENTRYPOINT指令。
ARG和ENV的区别
- ARG 是构建时变量
- ENV是运行时变量
1、ARG
ARG指令定义了一个变量,用户可以在构建时使用--build-arg = 传递,docker build命令会将其传递给构建器。--build-arg 指定参数会覆盖Dockerfile 中指定的同名参数
如果用户指定了 未在Dockerfile中定义的构建参数 ,则构建会输出 警告 。
ARG只在构建期有效,运行期无效
不建议使用构建时变量来传递诸如github密钥,用户凭据等机密。因为构建时变量值使用docker
history是可见的。
ARG变量定义从Dockerfile中定义的行开始生效。
2、ENV
- 在构建阶段中所有后续指令的环境中使用,并且在许多情况下也可以内联替换。
- 引号和反斜杠可用于在值中包含空格。
- ENV 可以使用key value的写法,但是这种不建议使用了,后续版本可能会删除
ENV MY_MSG hello ENV MY_NAME="John Doe" ENV MY_DOG=Rex\ The\ Dog ENV MY_CAT=fluffy #多行写法如下 ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \ MY_CAT=fluffy
- docker run --env 可以修改这些值
- 容器运行时ENV值可以生效
- ENV在image阶段就会被解析并持久化(docker inspect image查看),参照下面示例
FROM alpine ENV arg=1111111 ENV runcmd=$arg RUN echo$runcmd CMD echo$runcmd #ENV的固化问题: 改变arg,会不会改变 echo的值,会改变哪些值,如何修改这些值?
测试案例
FROM alpine ARG arg1=22222 ENV arg2=1111111 ENV runcmd=$arg1 RUN echo$arg1$arg2$runcmd CMD echo$arg1$arg2$runcmd
实战
java-springboot 镜像
# syntax=docker/dockerfile:1 FROM 10.50.10.185/qms/openjdk:8-jdk-alpine LABEL maintainer="ninesun@126.com" WORKDIR /opt/app COPY target/*.jar ./ #Alpine Linux 下的包管理工具 #RUN apk add mysql-client # 设定时区UTC -> CST RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone && touch EDA_ETL-0.0.51-SNAPSHOT.jar ENV JAVA_OPTS="" ENV PARAMS="" ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom $JAVA_OPTS -jar EDA_ETL-0.0.51-SNAPSHOT.jar $PARAMS" ]
python 程序镜像
FROM submitty/python:3.7 COPY ./redis_key_mig /app WORKDIR /app RUN /bin/bash -c 'pwd;ls -l /app' #RUN /usr/local/bin/python -m pip install --upgrade pip RUN pip3 install redis==3.5.3 cmd ["python", "/app/redisMigrate.py"]