欢迎光临
我的个人博客网站

Dockerfile 实践及梳理

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

文档

Best practices for writing Dockerfiles

Reference

Dockerfile 指令详解

简单上手

使用 Dockerfile 构建SpringBoot 工程的镜像

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

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

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

target 目录下就可以找到 .jar 文件,我这里的文件名为:demo-0.0.1-SNAPSHOT.jar
在 Linux 新建 ~/springboot 文件夹,并将 jar 包上传到这个文件夹下

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

保存文件,退出编辑器

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

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

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

docker build -t myimage:latest -<<EOF FROM busybox RUN echo "hello world" EOF 

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

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 来使缓存失效
可以使用 分割,提高可读性:

RUN apt-get update && apt-get install -y      curl 

CMD

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

CMD ["perl", "-de0"] 

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

EXPOSE

指定容器需要监听的端口

ENV

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

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

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

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

ADD or COPY

两个命令功能相似,优先使用COPY,它的作用只是将本地文件拷贝到容器内,而 ADD 则有其他特性,比如:自动将本地 tar 文件提取到镜像中、远程URL
如果多个步骤需要使用不同的文件,应该单独 COPY,而不是一次性 COPY,这样部分文件变化不会导致所有的缓存都失效
避免使用 ADD 通过 URL 获取包,可以使用 curl 或者 wget,这样可以在提取后删除文件,避免镜像多一层,还可以通过管道,就不需要再手动删除中间文件

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

ENTRYPOINT

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

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

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

docker run s3cmd 

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

运行下面命令:

docker run s3cmd ls s3://mybucket 

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

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

VOLUME

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

VOLUME /data 

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

USER

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

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

WORKDIR

WORKDIR 指令可以来指定工作目录,不存在会自动创建
Dockerfile 不同于 Shell,下面的命令其实是不同的层,第一条的 cd 不会影响第二条命令,最终运行结束会导致在 /app 下找不到 world.txt 文件

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

应该使用:

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

参考资料

使用 Dockerfile 定制镜像

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

Dockerfile: ENTRYPOINT和CMD的区别

赞(0) 打赏
未经允许不得转载:张拓的天空 » Dockerfile 实践及梳理
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

专业的IT技术经验分享 更专业 更方便

联系我们本站主机

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏