镜像的定制实际上就是定制每一层所添加的配置、文件。

如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。

这个脚本就是 Dockerfile。

  1. Dockerfile 是一个文本文件,其内包含了一条条的 指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

  2. 因为每一条指令构建一层,而且每一层构建好后,就不会再变化。为了使镜像尽可能地小而且层次清晰,每一层都应该围绕一个特定的目标进行构建,并且在构建结束前,要清理掉所有缓存和其他无关的东西!

  3. Docker 现在最多只能支持 127 层,尽量让每一条命令都完成一个完整的目标,不要每条 shell 命令都对应一个 RUN,这是相当糟糕的做法。

在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每一层该如何构建。

一、commit 命令

docker commit 用法类似 git commit,用于将当前容器层的修改,固化成一个新的镜像层。

# 1. 首先启动了一个容器
# 2. 通过 exec 命令登入该容器做一些修改
# 3. 可以使用下列命令查看容器层的具体改动
docker diff <container id> # 4. 使用 commit 命令提交容器层的改动
docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]] # 模板
docker commit \
--author "Tao Wang <twang2218@gmail.com>" \
--message "修改了默认网页" \
webserver \
nginx:v2 # 5. 使用 history 命令查看镜像的构建历史
docker history nginx:v2

利用 Dockerfile 定制镜像,其实就是在基础镜像上启动一个临时容器,然后在该容器上一条条地运行 Dockerfile 内的指令。

每跑完一个指令,就将当前的修改固化层一个新的镜像层(这就类似在此时执行 docker commit)。

指令跑完了,一个分层的镜像也就生成了,这时再清除掉构建用的临时容器。

二 、Dockerfile 指令详解

Dockerfile 常用的有十多个指令:

  1. FROM:指定基础镜像
  2. LABEL:镜像的一些标签,如 maintainer/licence
  3. USER:能用普通用户,就不要用 root 来做。建议使用普通用户来运行不需要 root 权限的服务。
  4. ENV:设置环境变量,可用于设置 PATH 或者其他环境变量。
    • 运行容器时,可以通过 --env XXX=xxx 来设置或者修改环境变量。--env可多次重复使用
    • 密码/密钥等参数的默认值可设置在这里,在运行时自行修改。
  5. ARG:构建时的参数,只在构建期有用。(而 ENV 就相当于运行期参数)
    • 可通过docker build -build-arg <varname>=<value> xxx 来修改构建参数。
    • 一般用于设置一些依赖的版本号、镜像源的地址等。构建时根据这些参数从镜像源下载对应的依赖。
  6. WORKDIR:用于制定下一个镜像层的工作目录(容器内部的),类比 cd xxx
    • 可多次使用,这样每一个镜像层都可以用不同的工作目录。
    • 如果路径不存在,会直接创建该路径
  7. ADD/COPY:都是添加文件的命令,更推荐使用 COPY,ADD 最好只用在 tar.gz/tar.xz等文件的添加上(会自动解压)。
    • 需要下载的文件,建议使用 curl/wget
    • COPY 可以用于从别的镜像复制文件(常用于多阶段构建)
  8. RUN:最常用的构建指令,会创建新的镜像层,所以最好让每条 RUN 命令都完成一个目标的构建,减少层数。
  9. VOLUME:指定数据层挂载点。
    • 常用:VOLUME ["/data", "/var/log/"]
  10. EXPOSE:暴露端口。
    • 该指令只制定了容器需要暴露的端口。在 run 时还需要用 -p xx:xx 做端口映射,才能和本机的端口绑定!
  11. ENTRYPOINT:镜像的“入口”,也就是启动镜像时会执行的命令。
    • 格式:ENTRYPOINT ["executable", "param1", "param2"]
    • docker run 命令的所有其他参数,都会被当作 "入口"命令的参数传入!
  12. CMD:在不使用 ENTRYPOINT 的情况下,它就是镜像的默认命令。
    • 格式:CMD ["executable","param1","param2"]
    • 在使用 ENTRYPOINT 的情况下,CMD 建议设置为 CMD ["--help"],并且紧跟在 ENTRYPOINT 命令之后。
  13. ONBUILD:该指令适合用在基础镜像的构建中。
    • 如果 FROM 一个使用了 ONBUILD 指令的镜像,会先执行该指令,然后才执行 Dockerfile 里面的指令。

需要注意的是,现在只有 RUN/ADD/COPY 这三条指令,才会创建新的镜像层。其他的指令只会在构建过程中创建临时镜像层,它们不会出现在最终的镜像中。

1. FROM 指定基础镜像

所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。

Docker Hub 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 nginxredismongomysqlhttpdphptomcat 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 nodeopenjdkpythonrubygolang 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。

如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如 ubuntudebiancentosfedoraalpine 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。

除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。

FROM scratch
...

如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。

不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,比如 swarmcoreos/etcd。对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 FROM scratch 会让镜像体积更加小巧。使用 Go 语言 开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go 是特别适合容器微服务架构的语言的原因之一。

镜像构建上下文

docker build --tag <image name>:tag . 中的 . 并不仅仅指 Dockerfile 的路径!

build 命令的最后一个参数,是镜像构建上下文的路径,这个路径可以是文件夹路径,可以是一个 tar 压缩包,也可以是一个 url,甚至 git 仓库地址也是支持的。

Docker 是 Client/Server 模式的程序,build 命令会将该 [文件夹/tar 压缩包/url] 的内容发送给 Server 端(Docker 引擎)用于构建,

因此后面构建中的 COPY/ADD 指令,只能使用上下文里面的内容,更不支持 ../xxx 这样的路径。

三、多阶段构建

多阶段构建中,不同的阶段使用不同的基础镜像(因此有多个 FROM),前面的阶段大都是为了生成一些需要的文件(前后端编译等)。

在最后一个阶段,使用 COPY 将需要的文件从前几个阶段生成的镜像中 COPY 过来,这样就得到了一个只包含运行时的镜像。

前端编译基于前端相关的镜像,后端用后端的编译镜像,最后放到只包含运行时的镜像里。

FROM golang:1.9-alpine as builder

RUN apk --no-cache add git

WORKDIR /go/src/github.com/go/helloworld/

RUN go get -d -v github.com/go-sql-driver/mysql

COPY app.go .

RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest as prod

RUN apk --no-cache add ca-certificates

WORKDIR /root/

COPY --from=0 /go/src/github.com/go/helloworld/app .

CMD ["./app"]

一些 Jenkins 构建,可供参考/使用的 Dockerfile:

  1. Slave: Jenkins Slave
  2. Python 3.7
  3. Android 构建环境:Docker Android Build Box
  4. 大合集:mritd/dockerfile

参考

Docker 学习笔记(二):Dockerfile 定制镜像的更多相关文章

  1. docker学习笔记二:常用命令

    docker学习笔记二:常用命令 查看docker常用命令 docker --help 返回结果如下: 其中常用的命令如下: 1.image相关操作 展示所有的image: 删除image: rmi ...

  2. InterSystems Ensemble学习笔记(二) Ensemble创建镜像, 实现自动故障转移

    系列目录 InterSystems Ensemble学习笔记(一) Ensemble介绍及安装InterSystems Ensemble学习笔记(二) Ensemble创建镜像, 实现自动故障转移 一 ...

  3. Docker学习笔记二(linux下安装Docker)

    Docker学习笔记二(linux下安装Docker) 1.在线安装linux Docker 这种方式首先要保证linux 环境下可以上网,当然,小编是在自己的电脑上安装了虚拟机,在虚拟机上安装了,l ...

  4. docker学习笔记(3)- 镜像

    简介 在docker学习笔记(1)- 架构概述一节中可以看到镜像是docker三大组件之一,可以将Docker镜像类比为虚拟机的模版. 镜像由多个层组成,每层叠加之后从外部看就像一个独立的对象,镜像的 ...

  5. Docker学习笔记三 Dockerfile 指令 定制镜像

    本文地址:https://www.cnblogs.com/veinyin/p/10412079.html  镜像是分层存储的,每一层都是独立存在的,修改当前层并不会修改其依赖的上一层,删除某一层也只是 ...

  6. Docker学习笔记二 使用镜像

    本文地址:https://www.cnblogs.com/veinyin/p/10408363.html  Docker运行容器前,需本地存在对应镜像,若没有则Docker从镜像仓库下载该镜像.  镜 ...

  7. Docker学习笔记 - 创建私有的镜像仓库

    一.查找镜像仓库 https://hub.docker.com/ 二.下载镜像仓库 docker pull registry:2.6.2 三.安装镜像仓库 docker run -d -p 6000: ...

  8. Docker学习笔记二:Docker常用命令及提升拉取镜像的速度

    一.Docker命令: 1.docker images   //命令用来查看docker中所包含的镜像信息 2.docker ps -a    //命令用来查看docker中所包含所有容器信息(运行状 ...

  9. docker学习之使用 DockerFile 构建镜像并搭建 swarm+compose 集群

    题目要求 (1)将springboot应用程序打成jar包:Hot.jar (2)利用dockerfile将Hot.jar构建成镜像 (3)构建 Swarm 集群 (4)在 Swarm 集群中使用 c ...

  10. docker学习笔记二

    知识点: 1)手动构建镜像 2)Dockerfile快速构建镜像 阿里云yum源https://opsx.alibaba.com/mirror 镜像制作nginx镜像实例 创建并运行centos容器 ...

随机推荐

  1. Win10 设备管理器一个USB设备描述符请求失败解决方法

    问题:进入设备管理器,发现[通用串行总线控制器]下有一项带有黄色[!]未知USB设备(设备描述符请求失败). 或者 解决方法如下: 1.点击Windows键 +R或者(点击系统桌面左下角[开始],在开 ...

  2. Java_jdbc 基础笔记之五 数据库连接 (ResultSet)

    /** * ResultSet: 结果集. 封装了使用 JDBC 进行查询的结果. * 1. 调用 Statement 对象的 executeQuery(sql)可以得到结果集. * 2. Resul ...

  3. 算力和AI-ZILLIZ

    特征向量检索加速.https://www.zilliz.com/ 公司介绍: MegaWise异构众核加速数据库 MegaWise是ZILLIZ独立自主研发的新一代异构众核加速数据库系统.MegaWi ...

  4. [转]BigDecimal使用(整理)

    原文地址:https://www.jianshu.com/p/2947868d76eb 应用场景 大多数的商业计算中,一般采用java.math.BigDecimal类来进行精确计算.比如:货币 使用 ...

  5. Vue中创建单文件组件 注册组件 以及组件的使用

    <template> <div id="app"> <v-home></v-home> <hr > <br> ...

  6. Java之Object对象中的wait()和notifyAll()用法

    用一个例子来说明Object对象中的wait方法和notifyAll方法的使用. 首先定义一个消息类,用于封装数据,以供读写线程进行操作: /** * 消息 * * @author syj */ pu ...

  7. 使用.whl文件安装torch和pytorch的方法

    当使用pip install torch torchvision命令下载感觉很慢时 可以先到下面的网页将.whl文件下载下来,然后再运行安装即可: torchvision也是 下载好了以后,运行: p ...

  8. 多重if结构

    package com.imooc; import java.util.Scanner; public class TypeExchange { public static void main(Str ...

  9. Spring cloud微服务安全实战-5-1单点登录基本架

    基于微服务架构,前后端分离实现SSO 前后端分离到底是个什么样的架构. 中间不是用Nginx而是用Node JS. node.js在服务端的页面渲染,这样搜索引擎爬虫 来爬的时候 爬的就是html页面 ...

  10. Clean Code 代码整洁之道

    军规:让营地比你来时更干净. 整洁代码 Leblanc : Later equals never. (勒布朗法则:稍后等于永不) 对代码的每次修改都影响到其他两三处代码. 修改无小事. 如同医生不能遵 ...