基础镜像

FROM 基础镜像

基础镜像的选择非常关键:

  • 如果关注的是镜像的安全和大小,那么一般会选择 Alpine;
  • 如果关注的是应用的运行稳定性,那么可能会选择 Ubuntu、Debian、CentOS。

构建上下文与.gitignore

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

“构建上下文”其实与 Dockerfile 并没有直接的关系,它其实指定了要打包进镜像的一些依赖文件。而 COPY 命令也只能使用基于“构建上下文”的相对路径,因为“Docker daemon”看不到本地环境,只能看到打包上传的那些文件。但这个机制也会导致一些麻烦,如果目录里有的文件(例如 readme/.git/.svn 等)不需要拷贝进镜像,docker 也会一股脑地打包上传,效率很低。为了避免这种问题,你可以在“构建上下文”目录里再建立一个 .dockerignore 文件,语法与 .gitignore 类似,排除那些不需要的文件。

# docker ignore
*.swp
*.sh
*.git

指令

每个指令都会生成一个镜像层,所以 Dockerfile 里最好不要滥用指令,尽量精简合并,否则会太多的层会导致镜像臃肿不堪。

COPY

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

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

把这些 Shell 命令集中到一个脚本文件里,用 COPY 命令拷贝进去再用 RUN 来执行:

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

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

RUN 指令实际上就是 Shell 编程,如果你对它有所了解,就应该知道它有变量的概念,可以实现参数化运行,这在 Dockerfile 里也可以做到,需要使用两个指令 ARG 和 ENV。

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

EXPOSE

EXPOSE,它用来声明容器对外服务的端口号,对现在基于 Node.js、Tomcat、Nginx、Go 等开发的微服务系统来说非常有用:

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

Docker多阶段构建

什么是多阶段构建

多阶段构建指在Dockerfile中使用多个FROM语句,每个FROM指令都可以使用不同的基础镜像,并且是一个独立的子构建阶段。使用多阶段构建打包Java/GO应用具有构建安全、构建速度快、镜像文件体积小等优点。

镜像构建的通用问题

镜像构建服务使用Dockerfile来帮助用户构建最终镜像,但在具体实践中,存在一些问题:

  • Dockerfile编写有门槛

    开发者(尤其是Java)习惯了语言框架的编译便利性,不知道如何使用Dockerfile构建应用镜像。

  • 镜像容易臃肿

    构建镜像时,开发者会将项目的编译、测试、打包构建流程编写在一个Dockerfile中。每条Dockerfile指令都会为镜像添加一个新的图层,从而导致镜像层次深,镜像文件体积特别大

  • 存在源码泄露风险

    打包镜像时,源代码容易被打包到镜像中,从而产生源代码泄漏的风险。

多阶段构建优势

针对Java这类的编译型语言,使用Dockerfile多阶段构建,具有以下优势:

  • 保证构建镜像的安全性

    当您使用Dockerfile多阶段构建镜像时,需要在第一阶段选择合适的编译时基础镜像,进行代码拷贝、项目依赖下载、编译、测试、打包流程。在第二阶段选择合适的运行时基础镜像,拷贝基础阶段生成的运行时依赖文件。最终构建的镜像将不包含任何源代码信息。

  • 优化镜像的层数和体积

    构建的镜像仅包含基础镜像和编译制品,镜像层数少,镜像文件体积小。

  • 提升构建速度

    使用构建工具(Docker、Buildkit等),可以并发执行多个构建流程,缩短构建耗时。

使用多阶段构建Dockerfile

以Java Maven项目为例,在Java Maven项目中新建Dockerfile文件,并在Dockerfile文件添加以下内容。

该Dockerfile文件使用了二阶段构建。

  1. 第一阶段:选择Maven基础镜像(Gradle类型也可以选择相应Gradle基础镜像)完成项目编译,拷贝源代码到基础镜像并运行RUN命令,从而构建Jar包。
  2. 第二阶段:拷贝第一阶段生成的Jar包到OpenJDK镜像中,设置CMD运行命令。
# First stage: complete build environment
FROM maven:3.5.0-jdk-8-alpine AS builder # add pom.xml and source code
ADD ./pom.xml pom.xml
ADD ./src src/ # package jar
RUN mvn clean package # Second stage: minimal runtime environment
From openjdk:8-jre-alpine # copy jar from the first stage
COPY --from=builder target/my-app-1.0-SNAPSHOT.jar my-app-1.0-SNAPSHOT.jar EXPOSE 8080 CMD ["java", "-jar", "my-app-1.0-SNAPSHOT.jar"]

go项目两阶段构建示例

# First stage: complete build environment
FROM golang:1.17 AS builder
ENV GOSUMDB=off
ENV GOPROXY=https://goproxy.cn,direct
WORKDIR /go/src # compile
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -o httpserver main.go # Second stage: minimal runtime environment
# copy binary file
FROM alpine:3.9.4
RUN mkdir /app \
# 日志路径,务必和应用中日志路径保持一致
&& mkdir /var/log/httpserver \
&& addgroup -g 10001 httpserver \
&& adduser -S -u 10001 -G httpserver httpserver
COPY --from=builder /go/src/httpserver /app/httpserver
COPY --from=builder /go/src/cert /app/cert/ RUN chown -R 10001:10001 /app \
&& chown -R 10001:10001 /var/log/httpserver USER httpserver
WORKDIR /app
ENTRYPOINT ["./httpserver"]

怎样编写正确、高效的 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. 关于编写性能高效的javascript事件的技术

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

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

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

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

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

  6. 怎样编写高效android代码

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

  7. 如何编写最佳的Dockerfile

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

  8. Dockerfile 编写

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

  9. Swift中编写单例的正确方式

    在之前的帖子里聊过状态管理有多痛苦,有时这是不可避免的.一个状态管理的例子大家都很熟悉,那就是单例.使用Swift时,有许多方法实现单例,这是个麻烦事,因为我们不知道哪个最合适.这里我们来回顾一下单例 ...

随机推荐

  1. kubectl 最新常用命令 --V1.24版本

    Kubectl 自动补全 BASH source <(kubectl completion bash) # 在 bash 中设置当前 shell 的自动补全,要先安装 bash-completi ...

  2. HashSet存储自定义类型元素和LinkedHashSet集合

    HashSet集合存储自定义类型元素 HashSet存储自定义类型元素 set集合报错元素唯一: ~存储的元素(String,Integer,-Student,Person-)必须重写hashCode ...

  3. DENIED Redis is running in protected mode because protected mode is enabled

    DENIED Redis is running in protected mode because protected mode is enabled redisson连接错误 Unable to i ...

  4. IDEA中web项目打成war包并在本地tomcat部署(超细版)

    准备工作:相关软件及插件IDEA(2021.1.3).tomcat(8.5.50)且在IDEA中调用tomcat运行时没有任何错误的,如何下载安装百度都有详细的介绍,这里就不过多的介绍了,版本不同操作 ...

  5. java后端分片上传接口

    文件上传工具--FileUtil package com.youmejava.chun.util; import lombok.Data; import org.apache.tomcat.util. ...

  6. google nexus5x 刷机抓包逆向环境配置(二)

    本文仅供学习交流使用,如侵立删! google nexus5x 刷入永久TWEP和Magisk(面具root) 操作环境 nexus5x kaliLinux win10 准备 下载链接:https:/ ...

  7. 为什么企业不愿意升级ERP系统

    前段时间看到一篇文章讲企业为何不愿意升级内部系统的文章,觉得有意思,也想聊聊为何大部分企业不愿意升级ERP的事情. 老东家用的ERP是QAD系统,92年版本,没有图形界面,用户都是通过NetTerm等 ...

  8. B端产品需求分析与优先级判断

    需求分析是产品经理工作中的重要一部分,而对B端产品经理来说,因为业务的特殊性,所以需求分析更考验产品经理的基础能力比如还原场景中业务调研的能力.需求价值分析中对价值的界定等. B端厂商的产品需求多数来 ...

  9. ajax初识

    Ajax 全称为:"Asynchronous JavaScript and XML"(异步 JavaScript 和 XML) 它并不是 JavaScript 的一种单一技术,而是 ...

  10. 手把手教你springboot集成微信支付

    20220727 最近要做一个微信小程序,需要微信支付,所以研究了下怎么在 java 上集成微信支付功能,特此记录下. 本文完整代码:点击跳转 准备工作 小程序开通微信支付 首先需要在微信支付的官网点 ...