返回顶部
首页 > 资讯 > 精选 >怎么看待Dockerfile最佳实践
  • 140
分享到

怎么看待Dockerfile最佳实践

2023-06-19 09:06:53 140人浏览 安东尼
摘要

怎么看待Dockerfile最佳实践,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。虽然 Dockerfile 简化了镜像构建的过程,并且把这个过程可以进行版本控制,但是不正

怎么看待Dockerfile最佳实践,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

虽然 Dockerfile 简化了镜像构建的过程,并且把这个过程可以进行版本控制,但是不正当的 Dockerfile 使用也会导致很多问题:

  • docker 镜像太大。如果你经常使用镜像或者构建镜像,一定会遇到那种很大的镜像,甚至有些能达到 数G

  • docker 镜像的构建时间过长。每个 build 都会耗费很长时间,对于需要经常构建镜像(比如单元测试)的地方这可能是个大问题

  • 重复劳动。多次镜像构建之间大部分内容都是完全一样而且重复的,但是每次都要做一遍,浪费时间和资源

一般指导方针和建议

容器应该是短暂的

容器模型是进程而不是机器,不需要开机初始化。在需要时运行,不需要时停止,能够删除后重建,并且配置和启动的最小化。

使用.dockerignore文件

在 docker build 的时候,忽略部分无用的文件和目录可以提高构建的速度。比如.git目录。dockerignore的定义类似gitignore。具体使用方式可以参考链接。

避免安装不必要的安装包

为了减少镜像的复杂性、镜像大小和构建时间,应该避免安装无用的包。

每个容器只运行一个进程

一个容器只运行一个进程。容器起到了隔离应用隔离数据的作用,不同的应用运行在不同的容器让集群的纵向扩展以及容器的复用都变的更加简单。需要多个应用交互时请使用 link 命令进行组合或者使用docker-compose。

最小化层数

需要掌握好Dockerfile的可读性和镜像层数之间的平衡。不推荐使用过多的镜像层。

多行命令按字母排序

命令行按字母顺序排序有助于避免重复执行和提高Dockerfile可读性。apt-get update 应与 apt-get install 组合,换行使用反斜杠(\)。例如:

RUN apt-get update && apt-get install -y \  bzr \  cvs \  git \  mercurial \  subversion

构建缓存

Dockerfile的每条指令都会将结果提交为新的镜像。下一条指令基于上一条指令的镜像进行构建。如果一个镜像拥有相同的父镜像和指令(除了 ADD ),Docker将会使用镜像而不是执行该指令,即缓存。

因此,为了有效的利用缓存,尽量保持Dockerfile一致,并且将不变的放在前面而经常改变放在末尾。

如不希望使用缓存,在执行 docker build 的时候加上参数 --no-cache=true 。

Docker匹配镜像决定是否使用缓存的规则如下:

  • 从缓存中存在的基础镜像开始,比较所有子镜像,检查它们构建的指令是否和当前的是否完全一致。如果不一致则缓存不匹配。

  • 多数情况中,比较Dockerfile中的指令是足够的。然而,特定的指令需要做更多的判断。

  • ADD COPY 指令中,将要添加到镜像中的文件也要被检查。通常是检查文件的校验和(checksum)。

  • 缓存匹配检查并不检查容器中的文件。例如,当使用 RUN apt-get -y update 命令更新了容器中的文件,并不会被缓存检查策略作为缓存匹配的依据。

时区

官方Image 使用的时区基本上都是标准的 UTC 时间,如果容器想使用中国标准时间,基于Debian的系统在Dockerfile中加入

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtimeRUN echo "Asia/Shanghai" >> /etc/timezone

基于Centos的系统在Dockerfile中加入

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

修改默认源

有时候你可能感觉官方的源更新或者安装软件比较慢,可以在Dockerfile修改官方默认源,例如alpine想使用阿里的源可以在Dockerfile中加入:

RUN echo -e "Http://mirrors.aliyun.com/alpine/v3.5/main\nhttp://mirrors.aliyun.com/alpine/v3.5/commUnity" > /etc/apk/repositories

Dockerfile指令

FROM

推荐使用官方仓库中的镜像作为基础镜像。

RUN

把复杂的或过长的 RUN 语句写成以 \ 结尾的多行的形式,以提高可读性和可维护性。

apt-get update 和 apt-get install 一起执行,否则 apt-get install 会出现异常。

避免运行 apt-get upgrade 或 dist-upgrade ,在无特权的容器中,很多 必要 的包不能正常升级。如果基础镜像过时了,应当联系维护者。 推荐 apt-get update && apt-get install -y package-a package-b 这种方式,先更新,之后安装最新的软件包。

RUN apt-get update && apt-get install -y \    aufs-tools \    automake \    build-essential \    curl \    dpkg-sig \    libcap-dev \    libsqlite3-dev \    mercurial \    reprepro \    ruby1.9.1 \    ruby1.9.1-dev \    s3cmd=1.1.* \ && rm -rf /var/lib/apt/lists/*

此外,你可以通过移除/var/lib/apt/lists减少镜像大小。

注意:官方的ubuntu和Debian会自动运行apt-get clean,所以不需要显式的调用

CMD

推荐使用 CMD ["executable","param1","param2"] 这样的格式。如果镜像是用来运行服务,需要使用 CMD["apache2","-DFOREGROUND"] ,这种格式的指令适用于任何服务性质的镜像。

ENTRYPOINT

ENTRYPOINT 应该用于 镜像的主命令,并使用 CMD 作为默认设置,以 s3cmd 为例:

ENTRYPOINT ["s3cmd"]CMD ["--help"]

获取帮助:

docker run s3cmd

或者执行命令:

docker run s3cmd ls s3://mybucket

这在镜像名与程序重名时非常有用。

ENTRYPOINT 也可以启动自定义脚本: 定义脚本:

#!/bin/bashset -eif [ "$1" = 'postgres' ]; then    chown -R postgres "$PGDATA"    if [ -z "$(ls -A "$PGDATA")" ]; then        Gosu postgres initdb    fi    exec gosu postgres "$@"fiexec "$@"

注意:这段脚本使用了exec命令以确保最终应用程序在容器内启动的PID为1。这段脚本允许容器接收Unix signals。

COPY ./docker-entrypoint.sh /ENTRYPOINT ["/docker-entrypoint.sh"]

这段脚本为用户提供了多种和 Postgres 交互的途径:

你可以简单地启动 Postgres:

docker run postgres。

或者运行 postgres 并传入参数:

docker run postgres postgres --help。

你甚至可以从镜像中启动一个完全不同的程序,比如 Bash:

docker run --rm -it postgres bash

EXPOSE

应该尽可能地使用默认端口。例如Apache WEB服务使用EXPOSE 80,MongoDB使用EXPOSE 27017。

ENV

  • 可以使用ENV更新PATH环境变量。例如,ENV PATH /usr/local/Nginx/bin:$PATH可以确保CMD [“nginx”]正常运行。

  • ENV还可以提供程序所需要的环境变量,例如Postgres的 PGDATA。

  • ENV可以设置版本等信息。使版本信息更易于维护。

ENV PG_MAJOR 9.3ENV PG_VERSION 9.3.4RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

ADD or COPY

虽然 ADD 与 COPY 功能类似,但推荐使用 COPY 。 COPY 只支持基本的文件拷贝功能,更加的可控。而 ADD 具有更多特定,比如tar文件自动提取,支持URL。 通常需要提取tarball中的文件到容器的时候才会用到 ADD 。

如果在Dockerfile中使用多个文件,每个文件应使用单独的 COPY 指令。这样,只有出现文件变化的指令才会不使用缓存。

为了控制镜像的大小,不建议使用 ADD 指令获取URL文件。正确的做法是在 RUN 指令中使用 wget 或 curl 来获取文件,并且在文件不需要的时候删除文件。

RUN mkdir -p /usr/src/things \    && curl -SL http://example.com/big.tar.gz \    | tar -xJC /usr/src/things \    && make -C /usr/src/things all

VOLUME

VOLUME 通常用作数据卷,对于任何可变的文件,包括数据库文件、代码库、或者容器所创建的文件/目录等都应该使用 VOLUME 挂载。

USER

如果服务不需要特权来运行,使用 USER 指令切换到非root用户。使用 **RUN groupadd -r mysql && useradd -r -g Mysql mysql **之后用 USER mysql 切换用户

要避免使用 sudo 来提升权限,因为它带来的问题远比它能解决的问题要多。如果你确实需要这样的特性,那么可以选择使用 gosu 。

最后,不要反复的切换用户。减少不必要的layers。

WORKDIR

为了清晰和可维护性,应该使用WORKDIR来定义工作路径。推荐使用WORKDIR来代替RUN cd … && do-something 这样的指令。

ONBUILD

Dockerfile 中的其它指令都是为了定制当前镜像而准备的,唯有 ONBUILD 是为了帮助别人定制自己而准备的。

ONBUILD指令用来设置一些触发的指令,用于在当该镜像被作为基础镜像来创建其他镜像时(也就是Dockerfile中的FROM为当前镜像时)执行一些操作,ONBUILD中定义的指令会在用于生成其他镜像的Dockerfile文件的FROM指令之后被执行,上述介绍的任何一个指令都可以用于ONBUILD指令,可以用来执行一些因为环境而变化的操作,使镜像更加通用。

注意:

  • ONBUILD中定义的指令在当前镜像的build中不会被执行。

  • 可以通过查看docker inspect <image>命令执行结果的OnBuild键来查看某个镜像ONBUILD指令定义的内容。

  • ONBUILD中定义的指令会当做引用该镜像的Dockerfile文件的FROM指令的一部分来执行,执行顺序会按ONBUILD定义的先后顺序执行,如果ONBUILD中定义的任何一个指令运行失败,则会使FROM指令中断并导致整个build失败,当所有的ONBUILD中定义的指令成功完成后,会按正常顺序继续执行build。

  • ONBUILD中定义的指令不会继承到当前引用的镜像中,也就是当引用ONBUILD的镜像创建完成后将会清除所有引用的ONBUILD指令。

  • ONBUILD指令不允许嵌套,例如ONBUILD ONBUILD ADD . /data是不允许的。

  • ONBUILD指令不会执行其定义的FROM或MAINTAINER指令。

例如,Dockerfile使用如下的内容创建了镜像 image-A :

[...]ONBUILD ADD . /app/srcONBUILD RUN /usr/local/bin/python-build --dir /app/src[...]

如果基于 image-A 创建新的镜像时,新的Dockerfile中使用FROM image-A指定基础镜像时,会自动执行ONBUILD指令内容,等价于在后面添加了两条指令。

FROM image-A#Automatically run the followingADD . /app/srcRUN /usr/local/bin/Python-build --dir /app/src
使用场景
node.js

假设我们要制作 node.js 所写的应用的镜像。我们都知道 Node.js 使用 npm 进行包管理,所有依赖、配置、启动信息等会放到 package.JSON 文件里。在拿到程序代码后,需要先进行 npm install 才可以获得所有需要的依赖。然后就可以通过 npm start 来启动应用。因此,一般来说会这样写 Dockerfile:

FROM node:slimRUN mkdir /appWORKDIR /appCOPY ./package.json /appRUN [ "npm", "install" ]COPY . /app/CMD [ "npm", "start" ]

把这个 Dockerfile 放到 Node.js 项目的根目录,构建好镜像后,就可以直接拿来启动容器运行。但是如果我们还有第二个 Node.js 项目也差不多呢?好吧,那就再把这个 Dockerfile 复制到第二个项目里。那如果有第三个项目呢?再复制么?文件的副本越多,版本控制就越困难,让我们继续看这样的场景维护的问题。

如果第一个 Node.js 项目在开发过程中,发现这个 Dockerfile 里存在问题,比如敲错字了、或者需要安装额外的包,然后开发人员修复了这个 Dockerfile,再次构建,问题解决。第一个项目没问题了,但是第二个项目呢?虽然最初 Dockerfile 是复制、粘贴自第一个项目的,但是并不会因为第一个项目修复了他们的 Dockerfile,而第二个项目的 Dockerfile 就会被自动修复。

那么我们可不可以做一个基础镜像,然后各个项目使用这个基础镜像呢?这样基础镜像更新,各个项目不用同步 Dockerfile 的变化,重新构建后就继承了基础镜像的更新?好吧,可以,让我们看看这样的结果。那么上面的这个 Dockerfile 就会变为:

FROM node:slimRUN mkdir /appWORKDIR /appCMD [ "npm", "start" ]

这里我们把项目相关的构建指令拿出来,放到子项目里去。假设这个基础镜像的名字为 my-node 的话,各个项目内的自己的 Dockerfile 就变为:

FROM my-nodeCOPY ./package.json /appRUN [ "npm", "install" ]COPY . /app/

基础镜像变化后,各个项目都用这个 Dockerfile 重新构建镜像,会继承基础镜像的更新。

那么,问题解决了么?没有。准确说,只解决了一半。如果这个 Dockerfile 里面有些东西需要调整呢?比如 npm install 需要统一加一些参数,那怎么办?这一行 RUN 是不可能放入基础镜像的,因为涉及到了当前项目的 ./package.json,难道又要一个个修改么?所以说,这样制作基础镜像,只解决了原来的 Dockerfile 的前4条指令的变化问题,而后面三条指令的变化则完全没办法处理。

ONBUILD 可以解决这个问题。让我们用 ONBUILD 重新写一下基础镜像的 Dockerfile:

FROM node:slimRUN mkdir /appWORKDIR /appONBUILD COPY ./package.json /appONBUILD RUN [ "npm", "install" ]ONBUILD COPY . /app/CMD [ "npm", "start" ]

这次我们回到原始的 Dockerfile,但是这次将项目相关的指令加上 ONBUILD,这样在构建基础镜像的时候,这三行并不会被执行。然后各个项目的 Dockerfile 就变成了简单地:

FROM my-node

是的,只有这么一行。当在各个项目目录中,用这个只有一行的 Dockerfile 构建镜像时,之前基础镜像的那三行 ONBUILD 就会开始执行,成功的将当前项目的代码复制进镜像、并且针对本项目执行 npm install,生成应用镜像。

Maven

类似Java,Go等编译型项目,可以使用ONBUILD指令进行优化Dockerfile。

编写onbuild Dockerfile如下:

FROM maven:3-jdk-8RUN mkdir -p /usr/src/appWORKDIR /usr/src/appONBUILD ADD . /usr/src/appONBUILD RUN mvn install

然后所有依赖maven编译的项目Dockerfile可以简化成如下形式:

FROM maven:3.3-jdk-8-onbuildCMD ["java","-jar","/usr/src/app/target/demo-1.0-SNAPSHOT-jar-with-dependencies.jar"]

关于怎么看待Dockerfile最佳实践问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注编程网精选频道了解更多相关知识。

--结束END--

本文标题: 怎么看待Dockerfile最佳实践

本文链接: https://lsjlt.com/news/295385.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 怎么看待Dockerfile最佳实践
    怎么看待Dockerfile最佳实践,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。虽然 Dockerfile 简化了镜像构建的过程,并且把这个过程可以进行版本控制,但是不正...
    99+
    2023-06-19
  • MSSQL - 最佳实践
    MSSQL - 最佳实践 - 使用SSL加密连接 author: 风移 摘要 在SQL Server安全系列专题月报分享中,往期我们已经陆续分享了:如何使用对称密钥实现SQL Server列加密技术、使用非对称密钥实现SQL Server列...
    99+
    2016-06-06
    MSSQL - 最佳实践
  • 怎么进行Linux 运维最佳实践
    本篇文章为大家展示了怎么进行Linux 运维最佳实践,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。我们面对的是一个不断变化的世界,业务需求在变,技术架构在变,开源工具与商业系统异构部署,新工具和技术...
    99+
    2023-06-19
  • Web 开发最佳实践
    要创建高效、安全且对搜索引擎友好的网站,遵循最佳 Web 开发实践至关重要。以下指南将为您提供建立高性能、可访问且易于维护的网站的最佳做法。 1. 性能优化 最小化资源大小:压缩 HTML、CSS 和 JavaScript 文件,以减...
    99+
    2024-03-03
    Web 开发、最佳实践、SEO、性能、安全性
  • PHP 安全最佳实践
    php安全最佳实践包括:输入验证,如使用filter_sanitize_*过滤数据。xss防御,如使用htmlspecialchars()转义输出。sql注入防御,如使用预处理语句。弱口...
    99+
    2024-04-30
    安全 php laravel composer lsp
  • ASP开发者必看!Windows环境下最佳实践!
    ASP开发者必看!Windows环境下最佳实践! ASP(Active Server Pages)是一种基于服务器端的脚本语言,用于动态生成HTML页面。ASP的开发环境是Windows,因此在Windows环境下进行ASP开发时,有一些最...
    99+
    2023-06-27
    windows numpy leetcode
  • VMware 服务器的最佳实践:实现最佳性能
    ...
    99+
    2024-04-02
  • MySQL 的 20+ 条最佳实践
    数据库操作是当今 Web 应用程序中的主要瓶颈。 不仅是 DBA(数据库管理员)需要为各种性能问题操心,程序员为做出准确的结构化表,优化查询性能和编写更优代码,也要费尽心思。 在本文中,我列出了一些针对程序员的...
    99+
    2022-05-10
    MySQL MySQL
  • RAT最佳实践之 - 简介
    Real Application Testing为Oracle 11g测试套件,在此不做过多描述,它包含如下两个组件:SQL Performance Analyzer   &n...
    99+
    2024-04-02
  • HTML Noscript标签:最佳实践
    浏览器兼容性 Noscript 标签支持所有现代浏览器,包括 Chrome、Firefox、Safari 和 Edge。但是,在使用 Noscript 标签时,需要考虑一些兼容性问题。例如,在 Internet Explorer 8 及...
    99+
    2024-02-06
    HTML Noscript JavaScript 浏览器兼容性 替代内容 语义 可访问性
  • Golang接口的最佳实践
    在Golang中,接口是一种定义对象行为的类型。接口提供了一种方式来指定对象应该具有的方法,并且让不同的类型实现这些方法。使用接口能够使代码更加灵活和可扩展,同时也符合面向对象编程中的...
    99+
    2024-02-24
    实践 golang 接口实现
  • Windows用户必看:Python实时编程算法的最佳实践!
    Python作为一种高效且易用的编程语言,已经成为了数据科学、机器学习以及人工智能领域的标准语言。它的广泛应用使得Python的实时编程算法变得越来越重要,因为这些算法能够使我们更好地处理数据、预测趋势以及做出更好的决策。本文将介绍一些P...
    99+
    2023-06-15
    windows 实时 编程算法
  • Oracle实例管理及最佳实践
    Oracle实例管理及最佳实践 在Oracle数据库管理中,Oracle实例是非常重要的概念之一。Oracle实例是Oracle数据库的运行环境,每个数据库实例管理一个数据库,它包含了...
    99+
    2024-03-08
    oracle 最佳实践 实例管理 sql语句
  • PHP 函数的最佳实践是什么?
    php 函数最佳实践:遵循命名约定、明确参数类型、声明返回类型、使用文档注释、追求可重用性、优化性能、编写单元测试,例如:sum() 函数清晰命名、声明 int 类型参数和返回类型,并通...
    99+
    2024-04-18
    php 函数最佳实践 代码可读性
  • PHP函数的最佳实践是什么?
    php 函数最佳实践包括:使用描述性且符合命名规范的函数名称优先传入参数、指定类型注解、设置默认参数返回有意义且类型注解的值异常处理以正确处理错误提取通用功能并考虑使用函数库优化性能以减...
    99+
    2024-04-21
    php 最佳实践 代码可读性
  • Go 协程的最佳实践是什么?
    go 协程最佳实践:启动合理数量的协程:限制协程数量以避免资源耗尽。使用 goroutine 池:预创建协程并根据需要重用以提高效率。注意协程泄漏:关闭不再需要的协程以防止内存泄漏。使用...
    99+
    2024-05-22
    go 协程
  • Android异常处理最佳实践
    一个好的app 异常处理机制 我认为应该至少包含以下几个功能: 1.能把错误信息上传到服务器  让开发者可以持续改进app 2.错误信息至少应该包含 是否在主进程 是...
    99+
    2022-06-06
    Android
  • Android夜间模式最佳实践
    由于Android的设置中并没有夜间模式的选项,对于喜欢睡前玩手机的用户,只能简单的调节手机屏幕亮度来改善体验。目前越来越多的应用开始把夜间模式加到自家应用中,没准不久goog...
    99+
    2022-06-06
    Android
  • oracle dataguard网络最佳实践一
    oracle dataguard redo 网络最佳实践(简译)oracle dataguard好处:1 对系统性能影响最小这里有两个最高可用架构(MAA)场景配置,在有足够带宽的情况下,得出如下结论:1 ...
    99+
    2024-04-02
  • RAT最佳实践之 - DBReplay简介
    DBReplay用途   精准捕获在线生产系统负载,并在测试系统灵活回放,以真实评估系统变更(软件 Or 硬件)对生产系统的影响。 支持的数据库版...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作