Dockerfile 是一个文本文件,我们可以通过组合一条条的指令 (Instruction),来构建满足我们需求的 Docker 镜像

文档

Best practices for writing Dockerfiles

Reference

Dockerfile 指令详解

简单上手

使用 Dockerfile 构建SpringBoot 工程的镜像

  1. 新建 SpringBoot 项目,默认的端口是 8080 ,新建 Controller 和 Mapping
  1. @RestController
  2. public class HelloController {
  3. @GetMapping("hello")
  4. public String hello() {
  5. return "hello world!";
  6. }
  7. }

启动项目,访问 http://localhost:8080/hello 测试

  1. 打 jar 包

    注意,需要在 pom 中添加 spring-boot-maven-plugin 插件,否则运行 jar 包时会提示:没有主清单属性
  1. <build>
  2. <plugins>
  3. <plugin>
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-maven-plugin</artifactId>
  6. </plugin>
  7. </plugins>
  8. </build>
  1. #打包
  2. mvn package

target 目录下就可以找到 .jar 文件,我这里的文件名为:demo-0.0.1-SNAPSHOT.jar

在 Linux 新建 ~/springboot 文件夹,并将 jar 包上传到这个文件夹下

  1. 新建 Dockerfile

    在这个文件下新建 Dockerfile 文件
  1. # 基于 openjdk:8-jre 这个基础镜像进行构建
  2. FROM openjdk:8-jre
  3. # 这里的 demo-0.0.1- SNAPSHOT.jar 要对应上传的 jar 包名称
  4. # 将 本地 jar包 复制到容器内
  5. COPY demo-0.0.1-SNAPSHOT.jar app.jar
  6. # 开放 8080 端口
  7. EXPOSE 8080
  8. # 运行命令、参数
  9. ENTRYPOINT ["java","-jar"]
  10. CMD ["app.jar"]

保存文件,退出编辑器

  1. 编译 Docker 镜像
  1. # build 是构建 Docker 镜像的命令
  2. # -t 指定镜像的 tag
  3. # 名称:demo 版本:v1.0
  4. # 最后的 . 表示 build context 目录为当前目录,目的是为了找到 所需的 jar 包
  5. docker build -t demo:v1.0 .
  1. 启动容器
  1. # 前台启动刚构建的 SpringBoot 容器
  2. # -p 映射容器8080端口 到宿主机的 8080 上
  3. docker run -p 8080:8080 demo:v1.0
  1. 测试

    访问 Linux 的8080 端口,注意替换为自己的 Linux 的地址,并开放 8080 端口

http://192.168.43.161:8080/hello

build context

Dockerfile 默认会使用它自己所在的目录作为 context,通过 docker 执行构建命令后,Docker daemon 会拷贝 context 目录下的所有文件,所以 context 目录不要放置项目无关的文件,或者可以使用 .dockerignore 定义忽略文件,也可以指定 context 路径

  1. # build 命令通过 Dockerfile 构建镜像
  2. # 指定 ~/dockerfile 为 build context
  3. docker build ~/dockerfile
  4. # 不需要添加文件到 context 可以使用 -
  5. docker build -

可以通过 stdin 的方式,避免生产 Dockerfile 文件,直接 build 镜像

  1. docker build -t myimage:latest -<<EOF
  2. FROM busybox
  3. RUN echo "hello world"
  4. EOF

除了可以指定 context外,还可以通过-f 指定 Dockerfile 所在的路径

  1. docker build -f dockerfiles/Dockerfile .

最佳实践

非常推荐官方的 Dockerfile最佳实践:Best practices for writing Dockerfiles

  1. 每个容器单一职责,有利于横向拓展和复用
  2. 旧版强调减少层数以提高性能,现在只有 RUN, COPY, ADD 这几个命令会创建层,其他命令只会创建中间层。并且只有使用到资源最终会被拷贝到最终镜像
  3. 多个参数按字母顺序排列,并使用空格和 \ 进行分割,提高可读性
  4. --no-cache 不使用缓存,默认 build 过程中如果检查到有可重用的镜像层则使用。从基础镜像开始,每一条命令逐一检查,如果命令不一样则缓存失效。使用 ADDCOPY 则会校验使用到的文件校验和是否相同,除了这两个命令,其他则不会通过文件变化来决定是否匹配缓存,而是仅通过命令本身是否一致来判断是否匹配缓存,比如:RUN apt-get -y update会改变容器内的文件,但是也只使用这个命令匹配缓存,而不会通过文件的变动。一旦缓存失效,后续都会产生新的镜像层

Dockerfile 指令 (instructions)

FROM

Dockerfile 的第一个命令一般都是 FROM,通过这个指定该镜像的 Base Image,推荐基础镜像:alpine,因为它完整且轻量,如果不需要 Base Image 可以用 FROM scratch,代表该镜像基于一个空镜像进行构建

RUN

由于上面提到的缓存匹配原则,RUN apt-get update 命令可能会导致直接使用了原来缓存的镜像层,而没有执行该命令获取最新的软件列表,可以使用 RUN apt-get update && apt-get install -y 来使缓存失效

可以使用 \ 分割,提高可读性:

  1. RUN apt-get update && apt-get install -y \
  2. curl

CMD

指定容器启动时运行的命令,通常默认采用的格式:CMD ["executable", "param1", "param2"…],如:

  1. CMD ["perl", "-de0"]

这样使用 docker run -it 命令进入容器时,就会默认进入 shell 界面

EXPOSE

指定容器需要监听的端口

ENV

可以使用 ENV 更新 PATH 环境变量,例如

  1. ENV PATH=/usr/local/nginx/bin:$PATH

注意!每一个 ENV 指令都会创建一个新的中间层 (intermediate layer),如果使用 ENV 设置了变量,在未来的层 unset 了变量,那么它在 unset 之前依然是可用的。为了防止这种情况,我们应该用 RUN 进行环境变量的 设置和取消

  1. ENV ADMIN_USER="mark"
  2. RUN echo $ADMIN_USER > ./mark
  3. RUN unset ADMIN_USER

ADD or COPY

两个命令功能相似,优先使用COPY,它的作用只是将本地文件拷贝到容器内,而 ADD 则有其他特性,比如:自动将本地 tar 文件提取到镜像中、远程URL

如果多个步骤需要使用不同的文件,应该单独 COPY,而不是一次性 COPY,这样部分文件变化不会导致所有的缓存都失效

避免使用 ADD 通过 URL 获取包,可以使用 curl 或者 wget,这样可以在提取后删除文件,避免镜像多一层,还可以通过管道,就不需要再手动删除中间文件

  1. RUN mkdir -p /usr/src/things \
  2. && curl -SL https://example.com/big.tar.xz \
  3. | tar -xJC /usr/src/things \
  4. && make -C /usr/src/things all

ENTRYPOINT

使用 ENTRYPOINT 设置主命令,还可以用 CMD 设置默认的可选参数

  1. ENTRYPOINT ["s3cmd"]
  2. CMD ["--help"]

运行编译镜像,指定名称为:s3cmd,运行容器

  1. docker run s3cmd

默认会运行 s3cmd 并带上 --help 参数,即:显示该命令的帮助

运行下面命令:

  1. docker run s3cmd ls s3://mybucket

ls s3://mybucket 会覆盖默认可选参数 --help

如果需要覆盖 ENTRYPOINT,需要使用 --entrypoint 参数

VOLUME

暴露镜像中可变和用户可修改的数据,比如:存储文件、配置文件,比如:

  1. VOLUME /data

设置的目录会在容器运行时自动挂载为匿名卷,如果没有设置,就会写入容器存储层

USER

如果不需要使用 sudo ,可以通过 USER 切换到非 root 用户,例如:

  1. RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres

WORKDIR

WORKDIR 指令可以来指定工作目录,不存在会自动创建

Dockerfile 不同于 Shell,下面的命令其实是不同的层,第一条的 cd 不会影响第二条命令,最终运行结束会导致在 /app 下找不到 world.txt 文件

  1. RUN cd /app
  2. RUN echo "hello" > world.txt

应该使用:

  1. WORKDIR /app
  2. RUN echo "hello" > world.txt

参考资料

使用 Dockerfile 定制镜像

利用构建缓存机制缩短Docker镜像构建时间

Dockerfile: ENTRYPOINT和CMD的区别

Dockerfile 实践及梳理的更多相关文章

  1. Docker Compose 实践及梳理

    Docker Compose 可以实现 Docker 容器集群的编排,可以通过 docker-compose.yml 文件,定义我们的服务及其需要的依赖,轻松地运行在测试.生产等环境 文档 Produ ...

  2. [转]docker之Dockerfile实践

    本文转自:https://www.cnblogs.com/jsonhc/p/7767669.html 上一篇介绍了Dockerfile中使用的指令,现在开始进行指令实践 先查看下本地的镜像,选一个作为 ...

  3. docker之Dockerfile实践

    上一篇介绍了Dockerfile中使用的指令,现在开始进行指令实践 先查看下本地的镜像,选一个作为base image: [root@docker ~]# docker images REPOSITO ...

  4. 【转】docker之Dockerfile实践

    转自:https://www.cnblogs.com/jsonhc/p/7767669.html 上一篇介绍了Dockerfile中使用的指令,现在开始进行指令实践 先查看下本地的镜像,选一个作为ba ...

  5. <创新思维与实践>总结梳理

    2017年12月3-4号 培训了两天的创新思维与实践,有时间要补充总结一下. ---todo

  6. Dockerfile极简入门与实践

    前文中,罗列了docker使用中用到的基本命令 此文,将会对怎样使用Dockerfile去创建一个镜像做简单的介绍 Dockerfile命令 要开始编写Dockerfile,首先要对相关的命令有个清晰 ...

  7. Docke--Dockerfile实践

    Dockerfile 实践 nginx镜像构建 先查看下本地的镜像,选取官网的centos作为base image: [root@server ~]# docker images REPOSITORY ...

  8. docker:Dockerfile构建LNMP平台

    docker:Dockerfile构建LNMP平台   1.dockerfile介绍  Dockerfile是Docker用来构建镜像的文本文件,包含自定义的指令和格式.可以通过docker buil ...

  9. Docker 入门指南——Dockerfile 指令

    COPY 复制文件 格式: COPY [--chown=<user>:<group>] <源路径>... <目标路径> 源路径可以是多个,甚至可以使通配 ...

随机推荐

  1. node.js背后的引擎V8及优化技术

    本文将挖掘V8引擎在其它方面的代码优化,如何写出高性能的代码,及V8的性能诊断工具.V8是chrome背后的javascript引擎,因此本文的相关优化经验也适用于基于chrome浏览器的javasc ...

  2. 一次搞懂JavaScript对象

    索引 目录 索引 1. 对象与类 2.对象使用 2.1 语法 2.2 属性 3.对象特性 4.对象的创建 4.1 字面量 4.2 工厂函数 4.3 构造函数 4.4 class类 4.5 对象与单例模 ...

  3. 【Uva1025 A Spy in the Metro】动态规划

    题目描述 某城市地铁是线性的,有n(2≤n≤50)个车站,从左到右编号1~n.有M1辆列车从第1站开始往右开,还有M2辆列车从第n站开始往左开.列车在相邻站台间所需的运行时间是固定的,因为所有列车的运 ...

  4. dva的effect那么难用,自己造一个轮子吧

    背景 对于dva这个开发框架,国内从事react的前端工程师多半不会感到陌生,dva完善的开发体系和简单的api,让其被广泛运用到实际工作中.我所在的公司也是长期使用dva作为基础的开发框架,虽然好用 ...

  5. decimal和float的区别

    场景 今天在开发的时候,在mongodb中有个字段保存的数据结构是decimal,然后需要对这个字段的值进行范围的查询.结果却怎么查询值范围都是空. 解决 如图中看到的,利用Navicat,可以明显的 ...

  6. js 数据存入数组

    var tag = []; $('.deltag').each(function(){ if($(this).attr("checked")== 'checked'){ tag.p ...

  7. 硬核万字长文,深入理解 Java 字节码指令(建议收藏)

    Java 字节码指令是 JVM 体系中非常难啃的一块硬骨头,我估计有些读者会有这样的疑惑,"Java 字节码难学吗?我能不能学会啊?" 讲良心话,不是我谦虚,一开始学 Java 字 ...

  8. Elasticsearch(9300、9200)未授权访问

    下载地址https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.5.0.zip 检测 http://localhost ...

  9. JAVA虚拟机的组成>从零开始学java系列

    目录 JAVA虚拟机的组成 什么是虚拟机? JAVA虚拟机的组成部分 堆区(堆内存) 方法区 虚拟机栈 本地方法栈 程序计数器 字符串常量池 JAVA虚拟机的组成 什么是虚拟机? 虚拟机是运行在隔离环 ...

  10. 使用 Assimp 库加载 3D 模型

    前言 要想让自己的 3D 之旅多一点乐趣,肯定得想办法找一些有意思一点的 3D 模型.3D 模型有各种各样的格式,obj的,stl的,fbx的等等不一而足.特别是 obj 格式的 3D 模型,完全是纯 ...