创建容器镜像

120 阅读5分钟

创建容器镜像

镜像的内部机制

镜像层Layer

容器镜像会将一些重复的部分抽取出来,形成镜像层,术语叫“Layer”,以某种方式来共享这部分数据。

容器镜像内部是由多个镜像层组成的,每层都是只读不可修改的一组文件,相同的层可以在镜像之间共享,然后多个层像搭积木一样堆叠起来,再使用一种叫“Union FS 联合文件系统”的技术把它们合并在一起,就形成了容器最终看到的文件系统。

image.png

docker inspect

查看镜像的组成,镜像的分层信息在“RootFS"部分

docker inspect nginx:alpine

image.png

镜像层操作的逻辑

Docker在执行docker pull、docker rmi等命令操作镜像的时候,会检查是否有重复的层,如果本地已经存在就不会重复下载,如果层被其他镜像共享就不会删除。

Dockerfile

Dockerfile是用来规定如何构建容器镜像的文件,相当于建筑施工的“施工图纸”。

一个简单的Dockerfile

# Dockerfile.busybox
FROM busybox                  # 选择基础镜像
CMD echo "hello world"        # 启动容器时默认运行的命令

第一条指令是 FROM,所有的 Dockerfile 都要从它开始,表示选择构建使用的基础镜像,相当于“打地基”

第二条指令是 CMD,它指定 docker run 启动容器时默认运行的命令

构建镜像

docker build -f Dockerfile.busybox .

image.png

-f 参数指定 Dockerfile 文件名,后面必须跟一个文件路径,叫做“构建上下文”(build’s context),这里只是一个简单的点号,表示当前路径的意思

查看和运行镜像

docker inspect 115d
docker run 115d
docker images

image.png

编写正确、高效的Dockerfile

FROM

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

FROM alpine:3.15                # 选择Alpine镜像
FROM ubuntu:bionic              # 选择Ubuntu镜像

COPY

本机开发测试上的一些源码、配置等文件,需要打包到镜像,可以使用COPY命令,不过拷贝的源文件必须是“构建上下文”路径里的。

COPY ./a.txt  /tmp/a.txt    # 把构建上下文里的a.txt拷贝到镜像的/tmp目录
COPY /etc/hosts  /tmp       # 错误!不能使用构建上下文之外的文件

RUN

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

超长的RUN指令很不美观,可以把一些Shell命令集中到一个脚本文件中,用COPY命令拷贝进去再用RUN来执行

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

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

ARG和ENV

RUN 指令实际上就是 Shell 编程,它有变量的概念,可以实现参数化运行,这在 Dockerfile 里也可以做到,需要使用两个指令 ARG 和 ENV

ARG IMAGE_BASE="node"
ARG IMAGE_TAG="alpine"

ENV PATH=$PATH:/tmp
ENV DEBUG=OFF

区别:

  • ARG:只在镜像构建过程中可见,容器运行时不可见
  • ENV:不仅能够在构建镜像过程中使用,在容器运行时,也能以环境变量的形式被应用程序使用。

EXPOSE

用来声明容器对外服务的端口号。

对现在基于 Node.js、Tomcat、Nginx、Go 等开发的微服务系统来说非常有用

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

因为每个指令都会生成一个镜像层,所以 Dockerfile 里最好不要滥用指令,尽量精简合并 只有 RUN, COPY, ADD 会生成新的镜像层,其它指令只会产生临时层,不影响构建大小

docker build如何工作的

真正的镜像构建工作是由服务器端“Docker daemon”来完成的,“docker”客户端会将“构建上下文”目录打包上传,服务器端的“Docker daemon”才能获取这些文件。

.dockerignore

排除一些不需要的文件

# docker ignore
*.swp
*.sh

表示不打包上传后缀是“swp”“sh”的文件

-f 参数

加上-f,显示指定Dockerfile文件

如果省略这个参数,docker build会在当前目录下找名字为Dockerfile的文件。

-t 参数

加上-t,指定镜像的标签(tag),用:分隔名字和标签,如果不提供标签默认为“latest”

总结

容器镜像是由多个只读的 Layer 构成的,同一个 Layer 可以被不同的镜像共享,减少了存储和传输的成本

编写Dockerfile:

  1. 创建镜像需要编写 Dockerfile,写清楚创建镜像的步骤,每个指令都会生成一个 Layer。
  2. Dockerfile 里,第一个指令必须是 FROM,用来选择基础镜像,常用的有 Alpine、Ubuntu 等。其他常用的指令有:COPY、RUN、EXPOSE,分别是拷贝文件,运行 Shell 命令,声明服务端口号。
  3. docker build 需要用 -f 来指定 Dockerfile,如果不指定就使用当前目录下名字是“Dockerfile”的文件。
  4. docker build 需要指定“构建上下文”,其中的文件会打包上传到 Docker daemon,所以尽量不要在“构建上下文”中存放多余的文件。
  5. 创建镜像的时候应当尽量使用 -t 参数,为镜像起一个有意义的名字,方便管理。

关于创建镜像还有很多高级技巧,比如使用缓存、多阶段构建等等,可以再参考 Docker 官方文档(docs.docker.com/engine/refe…),或者一些知名应用的镜像(如 Nginx、Redis、Node.js 等)进一步学习。

《极客时间-Kubernetes入门实战课》学习笔记 Day5 774a0d4e91146e72b83719c671f4b6d.jpg