Dockerfile是什么

Dockerfile 非常普通,它就是一个纯文本,里面记录了一系列的构建指令,比如选择基础镜像、拷贝文件、运行脚本等等,RUN, COPY, ADD指令都会生成一个 Layer,而 Docker 顺序执行这个文件里的所有步骤,最后就会创建出一个新的镜像出来。

一个简单的 Dockerfile 实例:

# Dockerfile.busybox
FROM busybox # 选择构建使用的基础镜像
CMD echo "hello world" # 启动镜像默认运行的命令

CMD

CMD,它指定 docker run 启动容器时默认运行的命令,上面我们使用了 echo 命令,输出“hello world”字符串。

FROM

构建镜像的第一条指令必须是 FROM,所以基础镜像的选择非常关键。如果关注的是镜像的安全和大小,那么一般会选择 Alpine;如果关注的是应用的运行稳定性,那么可能会选择 Ubuntu、Debian、CentOS。

FROM alpine:3.15                # 选择Alpine镜像
FROM ubuntu:bionic # 选择Ubuntu镜像

我们在本机上开发测试时会产生一些源码、配置等文件,需要打包进镜像里,这时可以使用 COPY 命令,它的用法和 Linux 的 cp 差不多,不过拷贝的源文件必须是“构建上下文”路径里的,不能随意指定文件。也就是说,如果要从本机向镜像拷贝文件,就必须把这些文件放到一个专门的目录,然后在 docker build 里指定“构建上下文”到这个目录才行。

这里有两个 COPY 命令示例,你可以看一下:

COPY ./a.txt  /tmp/a.txt    # 把构建上下文里的a.txt拷贝到镜像的/tmp目录
COPY /etc/hosts /tmp # 错误!不能使用构建上下文之外的文件

RUN

Dockerfile 里最重要的一个指令 RUN ,它可以执行任意的 Shell 命令,比如更新系统、安装应用、下载文件、创建目录、编译程序等等,实现任意的镜像构建步骤,非常灵活。

RUN 通常会是 Dockerfile 里最复杂的指令,会包含很多的 Shell 命令,但 Dockerfile 里一条指令只能是一行,所以有的 RUN 指令会在每行的末尾使用续行符 \,命令之间也会用 && 来连接,这样保证在逻辑上是一行,就像下面这样:

RUN apt-get update \
&& apt-get install -y \
build-essential \
curl \
make \
unzip \
&& cd /tmp \
&& curl -fSL xxx.tar.gz -o xxx.tar.gz\
&& tar xzf xxx.tar.gz \
&& cd xxx \
&& ./config \
&& make \
&& make clean

有的时候在 Dockerfile 里写这种超长的 RUN 指令很不美观,而且一旦写错了,每次调试都要重新构建也很麻烦,所以你可以采用一种变通的技巧:把这些 Shell 命令集中到一个脚本文件里,用 COPY 命令拷贝进去再用 RUN 来执行

COPY setup.sh  /tmp/                # 拷贝脚本到/tmp目录

RUN cd /tmp && chmod +x setup.sh \  # 添加执行权限
&& ./setup.sh && rm setup.sh # 运行脚本然后再删除

ARG、ENV、EXPOSE

ARG 定义了基础镜像的名字(可以在FROM指令中使用),ENV 定义了两个环境变量

ARG:镜像层的环境变量

EVN:镜像层和容器层参数

ARG IMAGE_BASE="node"
ARG IMAGE_TAG="alpine" ENV PATH=$PATH:/tmp
ENV DEBUG=OFF

EXPOSE 声明容器对外服务的端口号

EXPOSE: 暴露容器内部端口给外部使用

EXPOSE 443    # 默认是tcp协议
EXPOSE 53/udp # 可以指定udp协议

RUN, COPY, ADD 会生成新的镜像层,其它指令只会产生临时层,不影响构建大小。所以 Dockerfile 里最好不要滥用这些指令,尽量精简合并,否则太多的层会导致镜像臃肿不堪。

docker build

Dockerfile 必须要经过 docker build 才能生效,命令行“docker”是一个简单的客户端,真正的镜像构建工作是由服务器端的“Docker daemon”来完成的,所以“docker”客户端就只能把“构建上下文”目录打包上传(显示信息 Sending build context to Docker daemon ),这样服务器才能够获取本地的这些文件。

但这个机制也会导致一些麻烦,如果目录里有的文件(例如 readme/.git/.svn 等)不需要拷贝进镜像,docker 也会一股脑地打包上传,效率很低。为了避免这种问题,你可以在“构建上下文”目录里再建立一个 .dockerignore 文件,语法与 .gitignore 类似,排除那些不需要的文件。

gitignore

下面是一个简单的示例,表示不打包上传后缀是“swp”“sh”的文件:

# docker ignore
*.swp
*.sh

另外关于 Dockerfile,一般应该在命令行里使用 -f 来显式指定。但如果省略这个参数,docker build 就会在当前目录下找名字是 Dockerfile 的文件。所以,如果只有一个构建目标的话,文件直接叫“Dockerfile”是最省事的。

此时构建出来的镜像只有“IMAGE ID”没有名字,不是很方便。为此你可以加上一个 -t 参数,也就是指定镜像的标签(tag),这样 Docker 就会在构建完成后自动给镜像添加名字。当然,名字必须要符合一定的命名规范,用 : 分隔名字和标签,如果不提供标签默认就是“latest”。

docker inspect

命令 docker inspect 来查看镜像的分层信息,Docker 会检查是否有重复的层,如果本地已经存在就不会重复下载,如果层被其他镜像共享就不会删除,这样就可以节约磁盘和网络成本。

容器镜像是由多个只读的 Layer 构成的,同一个 Layer 可以被不同的镜像共享,减少了存储和传输的成本。

镜像分层的好处:可以重复使用未被改动的 layer,每次修改打包镜像,只需重新构建被改动的部分。

完整的 Dockerfile 示例

# Dockerfile
# docker build -t ngx-app .
# docker build -t ngx-app:1.0 . ARG IMAGE_BASE="nginx"
ARG IMAGE_TAG="1.21-alpine" FROM ${IMAGE_BASE}:${IMAGE_TAG} COPY ./default.conf /etc/nginx/conf.d/ RUN cd /usr/share/nginx/html \
&& echo "hello nginx" > a.txt EXPOSE 8081 8082 8083

总结

  1. 创建镜像需要编写 Dockerfile,写清楚创建镜像的步骤,每个指令都会生成一个 Layer
  2. Dockerfile 里,第一个指令必须是 FROM,用来选择基础镜像,常用的有 Alpine、Ubuntu 等。其他常用的指令有:COPYRUNEXPOSE,分别是拷贝文件,运行 Shell 命令,声明服务端口号。
  3. docker build 需要用 -f 来指定 Dockerfile,如果不指定就使用当前目录下名字是“Dockerfile”的文件。
  4. docker build 需要指定“构建上下文”,其中的文件会打包上传到 Docker daemon,所以尽量不要在“构建上下文”中存放多余的文件。
  5. 创建镜像的时候应当尽量使用 -t 参数,为镜像起一个有意义的名字,方便管理。
  6. Dockerfile 里的构建命令不区分大小写,例如“FROM”也可以写成“from”,但习惯上都使用大写形式。

https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#minimize-the-number-of-layers

如何编写正确高效的Dockerfile的更多相关文章

  1. 如何编写正确且高效的 OpenResty 应用

    本文内容,由我在 OpenResty Con 2018 上的同名演讲的演讲稿整理而来. PPT 可以在 这里 下载,因为内容比较多,我就不在这里一张张贴出来了.有些内容需要结合 PPT 才能理解,请多 ...

  2. C#7.2——编写安全高效的C#代码 c# 中模拟一个模式匹配及匹配值抽取 走进 LINQ 的世界 移除Excel工作表密码保护小工具含C#源代码 腾讯QQ会员中心g_tk32算法【C#版】

    C#7.2——编写安全高效的C#代码 2018-11-07 18:59 by 沉睡的木木夕, 123 阅读, 0 评论, 收藏, 编辑 原文地址:https://docs.microsoft.com/ ...

  3. 怎样编写正确、高效的 Dockerfile

    基础镜像 FROM 基础镜像 基础镜像的选择非常关键: 如果关注的是镜像的安全和大小,那么一般会选择 Alpine: 如果关注的是应用的运行稳定性,那么可能会选择 Ubuntu.Debian.Cent ...

  4. 关于编写性能高效的javascript事件的技术

    如何能做出高效的web前端程序是我每次做前端开发都会不自觉去考虑的问题.几年前雅虎里牛逼的前端工程师们出了一本关于提升web前端性能的书籍,轰动了整个web开发技术界,让神秘的web前端优化问题成为了 ...

  5. 【转】关于编写性能高效的javascript事件的技术

    原文转自:http://blog.jobbole.com/80170/ 如何能做出高效的web前端程序是我每次做前端开发都会不自觉去考虑的问题.几年前雅虎里牛逼的前端工程师们出了一本关于提升web前端 ...

  6. C#7.2——编写安全高效的C#代码

    原文地址:https://docs.microsoft.com/zh-cn/dotnet/csharp/write-safe-efficient-code?view=netcore-2.1 值类型的优 ...

  7. 怎样编写高效android代码

    基于Android相关设备作为嵌入式设备范畴,在书写App应用的时候要格外关注效率.而且受电池电量的限制.这就导致嵌入式设备有诸多考虑.有限处理能力.因此就要求我们尽量去写高效的代码. 本文讨论了非常 ...

  8. 如何编写最佳的Dockerfile

    译者按: Dockerfile 的语法非常简单,然而如何加快镜像构建速度,如何减少 Docker 镜像的大小却不是那么直观,需要积累实践经验.这篇博客可以帮助你快速掌握编写 Dockerfile 的技 ...

  9. Dockerfile 编写

    转: https://blog.fundebug.com/2017/05/15/write-excellent-dockerfile/如何编写最佳的Dockerfile 译者按: Dockerfile ...

  10. 使用 NineData 高效编写 SQL

    SQL 是 Structured Query Language 的缩写,中文翻译为"结构化查询语言".它是关系型数据库的标准语言,所有的关系型数据库管理系统(RDBMS),比如 M ...

随机推荐

  1. Qt编写物联网管理平台34-地图按钮

    一.前言 地图按钮很常见,这也是用户给钱就干的一个模块.设备现场提供了对应的地图文件,其实就是图片文件,做的简单点就是直接CAD图纸转成jpg,做的精致点就是搞了3D风格的立体样式图片,其实还是图片, ...

  2. 利用idea开发环境进行Spring Boot开发时maven同步更新jar依赖包时提示:sync:Cannot resolve xxx 的解决方案

    idea maven sync Cannot resolve xxx 的解决方案 经常会出现这种奇葩情况,提示找不到包 其实是因为网络波动或者突然断掉,导致包更新出现问题 直接去maven的仓库目录 ...

  3. TNN编译及使用

    要使用 CMake 和 TNN 库基于 C++ 实现神经网络模型的推理预测,你需要按照以下步骤进行操作: 准备环境 确保已安装 CMake 和 C++ 编译器.并从 TNN 的 GitHub 仓库下载 ...

  4. 【狂神说Java】Java零基础学习笔记-Java流程控制

    [狂神说Java]Java零基础学习笔记-Java流程控制 Scanner对象 之前我们学的基本语法中我们并没有实现程序和人的交互,但是Java给我们提供了这样一个工具类,我们可以获取用户的输入.ja ...

  5. vue基础3

    1.watch 案例:百度搜索框 注释的是用watch实现的 然后这个我用的是oninput事件 a.深浅监听 浅监听 深监听(不建议使用) 2.过滤器 全局: Vue.fliter('过滤器名字', ...

  6. Jenkins使用问题汇总

    1. 禁止job出现403问题 解决方法: 在系统管理 –> Configure Global Security中调整设置:取消"启用安全(Enable security)" ...

  7. 第二章 dubbo源码解析目录

    6.1 如何在spring中自定义xml标签 dubbo自定义了很多xml标签,例如<dubbo:application>,那么这些自定义标签是怎么与spring结合起来的呢?我们先看一个 ...

  8. 理解Java的接口和抽象类

    深入理解Java的接口和抽象类   对于面向对象编程来说,抽象是它的一大特征之一.在Java中,可以通过两种形式来体现OOP的抽象:接口和抽象类.这两者有太多相似的地方,又有太多 不同的地方.很多人在 ...

  9. React、Angular、Vue.js三者比较指南

    Vue-React-Angular三者区别   1. 基本概念Angular 是一个应用设计框架与开发平台,用于创建高效.复杂.精致的单页面应用. React 是一个用于构建用户界面的 JavaScr ...

  10. docker网络配置:bridge模式、host模式、container模式、none模式

    在docker平台里有四种网络模式,今天继续分享一下它们的常用知识,进一步加深对docker技术的理解. 1.docker网络模式分类 docker run创建Docker容器时,可以用--net选项 ...