5. dockerfile
2019-09-25 本文已影响0人
epiiplus1equal0
dockerfile
本文基于马哥的docker和k8s视频总结, 在此致谢马哥.
- 惜字如金! 尽量减少新增行, 因为每新增一行, 都会多一层封装, 从而降低容器性能
- 尽量减少不必要的文件以缩小容器体积
![](https://img.haomeiwen.com/i14021735/92b7b196fce13141.png)
![](https://img.haomeiwen.com/i14021735/15f9a09fb0ce4857.png)
指令不区分大小写, 不过一般预定俗成使用大写
指令顺序执行
第一个非注释行必须使用
FROM
指令: 用于指定制作的当前进行基于哪个基础镜像实现
mkdir ~/img1 # 制作镜像时需要使用专门工作目录
vim Dockerfile # Dockfile文件的文件名首字母必须大写!
- 排除名单, 支持通配:
![](https://img.haomeiwen.com/i14021735/036e6e0096b520dc.png)
- 制作镜像时可以使用环境变量:
![](https://img.haomeiwen.com/i14021735/7b2857e3cf9e4a65.png)
${variable:-word} # 如果变量未设置或者变量存在但是值为空, 则将引用默认值word
${variable:+word} # 如果变量有值且非空, 则改值为tom, 无值则不设置, 相当于满足条件才做修改
docker指令制作docker镜像
docker image build --help
Usage: docker build [OPTIONS] PATH | URL | - # PATH: Dockerfile文件所在目录
Build an image from a Dockerfile
Options:
-q, --quiet Suppress the build output and print image ID on success
--rm Remove intermediate containers after a successful build (default true)
-t, --tag list Name and optionally a tag in the 'name:tag' format
docker image build -t tianjunchang/nginx:v0.1 ./ # 用从当前的Dockerfile制作镜像
FROM
- 建议使用hash码的方式制作镜像
![](https://img.haomeiwen.com/i14021735/120fbfe0dd515b6a.png)
FROM nginx.1.16.1
MAINTANIER和LABEL
- 用于标注作者信息,
MAINTAINER
方式已被废弃
![](https://img.haomeiwen.com/i14021735/1c11a49b576ce045.png)
![](https://img.haomeiwen.com/i14021735/0ae3d852b6387322.png)
MAINTAINER "Alex Ti <1054083247@qq.com>"
LABEL maintainer="Alex Ti <1054083247@qq.com>" \
location="Hangzhou Zhejiang China"
COPY
- 目录后面一定要加 "/", 语法强制要求
- 来源目录只能来自于Dockerfile的父目录, 即制作镜像时专门的工作目录及其子目录, 需要使用的文件或目录必须拷贝到工作目录中, Dockerfile中才能引用
![](https://img.haomeiwen.com/i14021735/b9286067edfb022d.png)
COPY "yum.repo.d/" "/etc/yum.repo.d/"
COPY "index.html" "${DOC_ROOT:-/data/web/html/}"
# 将工作目录的子目录yum.repo.d中的所有内容复制到镜像的/etc/yum.repo.d/目录下
# 将index.html复制到变量DOC_ROOT所引用的目录下, 如果目录不存在时复制到/data/web/html目录下
# 目标目录不存在时会被自动创建
ADD
![](https://img.haomeiwen.com/i14021735/7a1c022ee8ff8c4d.png)
ADD "http://nginx.org/download/nginx-1.16.1.tar.gz" "/usr/local/src/"
# 注意下载的gz文件没有被解压! 想要解压归档文件时还需要使用其他命令
WORKDIR /usr/local/src/ # 也可以直接 RUN cd /usr/local/src/
RUN tar xf nginx-1.16.1.tar.gz && \
mv nginx-1.16.1 webserver
WORKDIR
- 相当于shell脚本中通过cd命令进入到某个目录中进行工作
![](https://img.haomeiwen.com/i14021735/ec36c00008699d91.png)
VOLUME
- 只能被用于由docker管理的存储卷的情形中
![](https://img.haomeiwen.com/i14021735/daccea08d3f90b19.png)
VOLUME /data/volume/
EXPOSE
![](https://img.haomeiwen.com/i14021735/a2a1160fc0f5a9a9.png)
expose: vt. 暴露; 揭露; 揭发; 曝光; 使显示
-
docker container run
时加上"-P" 选项才会真正暴露端口, 在Dockerfile文件中写EXPOSE规则只是开启了暴露端口这个功能
ENV
![](https://img.haomeiwen.com/i14021735/c5175249cff3eb54.png)
ENV DOC_ROOT="/data/web/html/" \
WEB_SERVER_PACKAGE="nginx-1.16.1.tar.gz"
# 也可以在运行命令时传递变量
docker container run --help
-e, --env list Set environment variables
--env-file list Read in a file of environment variables
RUN
![](https://img.haomeiwen.com/i14021735/5305237a32b18de0.png)
RUN echo "test" > test.txt # 没加中括号, 会自动运行为shell进程的子进程
- Dockerfile构建镜像时使用的命令
- RUN基于基础镜像的环境运行, 因此基础镜像的环境内没有的命令或程序, RUN命令中指定的命令无法运行!
- json数组中要使用双引号!!!
CMD
![](https://img.haomeiwen.com/i14021735/66327f5398d0eb51.png)
CMD /usr/local/src/webserver/bin/nginx
# 此为第一种CMD语法, 先启动一个shell进程, 然后nginx作为shell进程的子进程启动
# 这种配置方法最简单, 但是有一个极大的坏处, 就是在容器中启动的nginx进程的PID不是1
# 使用docker container stop对应容器时, nginx进程不会被关闭, 因为它接收不到新号,
# 接收到信号的是PID为1的进程
CMD ["<executable>","<param1>","<param2>"]
# 直接启动PID为1的进程, 可接收并处理信号
- CMD用于定义把镜像启动为容器时, 若没有指定命令, 而默认启动的命令
- RUN与CMD的区别:
![](https://img.haomeiwen.com/i14021735/56d4c5b5f92354a1.png)
ENTRYPOINT
![](https://img.haomeiwen.com/i14021735/9239b1b9ea4b0135.png)
docker container run --name tinyweb1 -it --rm -P nginx \
/bin/httpd -f -h /data/web/html/
# 可以自行指定容器启动时运行命令覆盖原有CMD中的命令
# 但由ENTRYPOINT启动的程序不会被命令行启动参数覆盖
# 除非在命令行再指定entrypoint参数,
# 才会覆盖原有ENTRYPOINT指定的内容:
--entrypoint string Overwrite the default ENTRYPOINT of the image
docker container run --name tinyweb2 -it --rm -P \
--entrypoint "ls /data/" \
nginx
- 当CMD与ENTRYPOINT同时存在时, CMD中的内容将被当做默认参数传递给ENTRYPOINT, 但是会被
docker container run
所指定的参数所覆盖 -
这里要特别强调覆盖参数的区别!!!
-
--entrypoint
覆盖的是Dockerfile文件中ENTRYPOINT所指定的参数 - 而使用
docker container run
所覆盖的是CMD传过来的命令默认参数
-
CMD ["/bin/httpd","-f","-h","/data/web/html/"]
ENTRYPOINT /bin/sh -c
# 用这个Dockerfile创建镜像时, 用docker image inspect查看信息时会发现镜像
# 中的Entrypoint信息如下:
"Entrypoint": [
"/bin/sh",
"-c",
"/bin/sh -c"
]
# 之所有有两个/bin/sh -c, 是因为第2行的ENTRYPOINT指定为/bin/sh -c,
# 实际上是先启动了一个/bin/sh -c进程, 然后在shell进程中又创建了/bin/sh -c的子进程
# 如果要运行自定义的/bin/sh -c, 可修改为如下:
ENTRYPOINT ["/bin/sh","-c"]
例: 向脚本传递参数并启动容器
ADD entrypoint.sh /bin/ # 将自定义的脚本放到容器的/bin/目录下
CMD ["/usr/sbin/nginx","-g","daemon off;"]
# 注意一定要用双引号!
# 这里之所以加分号因为运行docker container run时后面还可能有命令参数传过来
ENTRYPOINT ["/bin/entrypoint.sh"]
vi entrypoint.sh
#!/bin/sh
cat > /etc/nginx/conf.d/www.conf << EOF # 后面一定要用两个<
server {
server_name ${HOSTNAME};
lister ${IP:-0.0.0.0}:${PORT:-80};
root ${NGX_DOC_ROOT:-/usr/share/nginx/html/};
}
EOF
exec "$@" # 让CMD中传过来的nginx命令执行的程序替换当前shell进程作为主进程
实例1
- 1.做好准备工作:
mkdir ~/img1/
cd img1
cp -a /etc/yum.repos.d/*
vi index.html # 添加如下内容
<h1>bbox1 httpd server</h1>
- 2.创建并编辑Dockerfile文件, 添加内容如下:
# Description: test Dockerfile
FROM busybox:latest
LABEL maintainer="Alex Ti <1054083247@qq.com>" \
location="Hangzhou Zhejiang China"
ENV DOC_ROOT="/usr/local/src/" \
WEBSERVER_PACKAGE="nginx-1.16.1.tar.gz" \
WEB_DOC_ROOT="/data/web/html/"
COPY yum.repos.d /etc/yum.repos.d/
COPY index.html ${WEB_DOC_ROOT}
ADD "http://nginx.org/download/nginx-1.16.1.tar.gz" ${DOC_ROOT:-/tmp/}
VOLUME ${WEB_DOC_ROOT}
EXPOSE 80/tcp 11211/udp
WORKDIR ${DOC_ROOT}
RUN tar xf ${WEBSERVER_PACKAGE} && \
mv "nginx-1.16.1" "webserver"
CMD /bin/httpd -f -h ${WEB_DOC_ROOT}
CMD ["/bin/httpd","-f","-h","${WEB_DOC_ROOT}"]
# 第20行代码有错, 在最后启动容器时会报错, 因为/bin/httpd没有被运行
# 为shell进程的子进程, 所以无法识别传入的参数${WEB_DOC_ROOT},
# 不过可以修正为如下:
CMD ["/bin/sh","-c","/bin/httpd","-f","-h ${WEB_DOC_ROOT}"]
补充: sh命令
sh命令是shell命令语言解释器,执行命令从标准输入读取或从一个文件中读取。通过用户输入命令,和内核进行沟通!
bash [options] [file]
-c string: 命令从-c后的字符串读取
-i: 实现脚本交互
-n: 进行shell脚本的语法检查
-x: 实现shell脚本逐条语句的跟踪
sh -c "echo 1" # 执行的结果为1, 说明先开了一个shell进程,
# 在shell进程下执行了子进程echo
- 3.创建自制的镜像并用自制的镜像启动容器
docker image build -t tianjunchang/nginx:v0.1 ./ # 注意创建过程中是否有报错
docker image ls # 查看自制的镜像
REPOSITORY TAG IMAGE ID CREATED SIZE
tianjunchang/nginx v0.4 8ab31e28ed44 5 seconds ago 8.47MB
docker container run --name b1 --rm -it tianjunchang/nginx:v0.1
# 运行此命令时只有光标闪烁, 没有交互界面, 虽然Dockerfile文件中指定的CMD,
# 即容器启动时运行的命令/bin/httpd运行为shell进程的子进程, 但是容器启动时会自动
# 做替换(即exec), 将/bin/httpd进程的PID改为1, 此时如果想要使用shell登录容器, 则
docker container exec -it b1 /bin/sh
# 可以在启动容器时指定需要执行的命令, 例如你要运行的服务
docker container run --name b2 --rm tianjunchang/nginx:v0.2 ls -l /usr/local/src/
# docker container exec --help
Usage: docker container exec [OPTIONS] CONTAINER COMMAND [ARG...]
Run a command in a running container
Options:
-d, --detach Detached mode: run command in the background
-e, --env list Set environment variables
-i, --interactive Keep STDIN open even if not attached
-t, --tty Allocate a pseudo-TTY
USER
![](https://img.haomeiwen.com/i14021735/6cd37523fea9c77d.png)
- 容器启动时, 其主进程以USER指定的用户身份运行
HEALTHCHECK
![](https://img.haomeiwen.com/i14021735/037ba87bcab30091.png)
![](https://img.haomeiwen.com/i14021735/44c103544a096fe0.png)
-
--interval
: 健康监测的时间间隔 -
--start-period
: 容器初始化启动成功, 此时容器内的进程可能未启动成功, 因此是否在容器初始化启动成功时立即进行health check还是设定的--start-period
时间之后进行health check -
--timeout
: 检测服务超时的时间, 如果超过这个时间就判定为失败 -
--reties
: 检测失败时重新检测次数 -
注意使用关键词
CMD
!
SHELL
![](https://img.haomeiwen.com/i14021735/c3538159d9fefa2a.png)
STOPSIGNAL
- 容器中PID为1的进程可以接收
docker container stop
命令从而停止主进程, 从而停止容器
![](https://img.haomeiwen.com/i14021735/c8905a5d265fbb44.png)
ARG
- 只在
docker build
中使用 - 此功能使得一个Dockerfile能够适用于多种场景, 尤其是应用程序版本变化时, 直接传递参数即可
[图片上传失败...(image-c2fd94-1567102998883)]
FROM nginx:1.14.1-alpine # 每次版本变化时都需要去修改nginx版本号, 比较麻烦, 修改:
FROM nginx:${NGX_TAG} # 此命令在编译时会出错, 仅用于示例说明该
FROM nginx:1.14.1-alpine
ARG author="Alex Ti <1054083247@qq.com>"
LABEL maintainer="${author}"
# 定义好Dockerfile文件后测试:
docker build --build-arg author="tianjunchang" -t tianjunchang/myweb:v0.1-1
docker image inspect tianjunchang/myweb # 查看作者信息是否被传入的参数修改
ONBUILD
- 做成镜像时, 被别人用作基础镜像, 在别人
docker build
时被触发执行 -
COPY
可能会有问题, 因为别人执行时工作目录可能没有你指定的源文件
![](https://img.haomeiwen.com/i14021735/4a6e7a03a78ebeff.png)
FROM nginx:1.14.1-alpine
HEALTHCHECK --start-period=1m \
CMD wget -O - -q http://${IP:-0.0.0.0}:10080/
ONBUILD ADD "http://nginx.org/download/nginx-1.16.1.tar.gz" \
"/usr/local/src/"
(1) 基于上面这个Dockerfile, docker build
一个名为 myimg:v0.1 的镜像
(2) 再基于 myimg:v0.1 镜像写一个Dockerfile文件, 再次docker build
一个名为 myimg:v0.2 镜像:
FROM myimg:v0.1
RUN mkdir /test
会发现在docker build
的时候会触发第一个Dockerfile中的ONBUILD
, 下载一个nginx的tar包至/usr/local/src/