【Docker基础-镜像】--查阅笔记2

目录

Docker镜像概述

  • 镜像是Docker容器的基石,容器是镜像的运行实例,有了镜像才能启动容器。
  • 镜像是一种轻量级、可执行的独立软件包,包含运行某个软件所需的所有内容,一个可交付的运行环境(包括代码、运行时所需的库、环境变量和配置文件等)

base镜像

  • 不依赖其他镜像,从scratch构建(指完全从零开始创建镜像,不依赖任何现有操作系统层或工具链);比如各种Linux发行版本的Docker镜像,CentOS、Ubuntu等
  • 其他镜像可以已之为基础进行扩展

从 scratch 构建镜像是对 Docker 镜像分层的极致利用,通过剥离冗余内容实现"仅需即所得"。但需权衡开发调试的便利性,适合对安全、体积敏感且技术栈可控的场景。

与常规基础镜像的区别

对比项 scratch Alpine/Centos
文件系统 完全空白 包含最小化的 Linux 发行版 rootfs
镜像体积 仅包含用户添加的文件(可低至几 KB) 通常 5MB(Alpine)至 200MB(CentOS)
适用场景 静态编译的单一程序 需要系统工具或动态库支持的复杂应用

镜像的分层结构

Docker支持通过扩展现有镜像,创建新的镜像。实际上,Docker Hub中99%的镜像都是通过在base镜像中安装和配置需要的软件构建出来的。新镜像是从base镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。

  • Docker镜像实际上由一层层的文件系统组成,这种层级的文件系统就是 UnionFS
  • bootfs(boot file system) 主要包括 bootloader 和 kernel,bootloader 主要是引导加载 kernel,Docker镜像的最底层就是引导文件 bootfs。
  • rootfs(root file system),在bootfs之上,就是 Linux 系统的 /dev、/proc、/etc 等标准目录和文件。rootfs 就是各种不同操作系统发行版,如:ubuntu、centos 等。

为什么使用这种分层结构?

最大好处就是共享资源,方便复制迁移

镜像的理解

  • 当容器启动时,一个新的可写层被加载到镜像的顶部,这一层通常被称为容器层,容器层之下的都叫镜像层。
  • 容器的Copy-on-Write特性,使得对文件的修改会被限制在单个容器内。
  • 所有对容器的改动,无论添加、删除,还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享。

镜像的构建

docker commit 制作镜像

docker commit命令是创建新镜像最直观的方法,其过程包含三个步骤:

  • 运行容器
  • 修改容器
  • 将容器保存为新的镜像
powershell 复制代码
docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的目标镜像名:TAG

Docker并不建议用户通过这种方式构建镜像。原因如下:

  • 手动创建,容易出错,效率低且可重复性弱
  • 使用者并不知道镜像是如何创建出来的,里面是否有恶意程序,无法对镜像进行审计,存在安全隐患。

Dockerfile

Dockerfile是一个文本文件,记录了镜像构建的所有步骤,类似于 Linux 中的 Shell 脚本文件。推荐使用这种方式构建镜像,其实它的底层也是docker commit一层一层构建新镜像的。

powershell 复制代码
docker build --no-cache --force-rm -t 镜像名称:TAG .
# --no-cache : 表示构建的时候不使用之前的缓存
# --force-rm:删除构建过程中的中间容器层
# .:表示构建环境的上下文,通常而言是 . ,表示以 Dockerfile 所在的目录作为构建的起点
  • 每条保留字指令都必须为大写字母,且后面跟至少一个参数
  • 指令从上到下依次执行
  • #表示注释
  • 每条指令都会创建一个新的镜像层并对镜像进行提交

Docker 执行 Dockerfile 的大致流程:

  • 从基础镜像上运行一个容器
  • 执行一条指令并对容器进行修改
  • 执行类似 docker commit 的操作提交一个新的镜像层
  • Docker 再基于刚才提交的镜像运行一个新的容器
  • 依次类推,直到 Dockerfile 文件中的指令全部执行完成

Dockerfile 指令

FROM

指定基础镜像。eg:如果是 JS 模块化应用,可以选择 nodejs 基础镜像;FROM nodejs:18.20.1

LABEL

用来标注镜像的一些说明信息,常常用来指定维护者的信息。eg: LABEL maintainer=xxx

RUN
  • 在当前镜像层顶部的新层执行任何命令,并提交结果,生成新的镜像层(多个 RUN 指令都是在同一个目录操作的)
  • 生成的提交镜像将用于 Dockerfile 中的下一步,分层运行 RUN 指令并生成提交符合 Docker 的核心概念
  • 由于 [] 不是 shell 形式,所以不能输出变量信息,而是输出 $msg 。其他任何 /bin/sh -c 的形式都可以输出变量信息

构建期是指使用 Dockerfile 构建镜像的整个过程时期,如:docker build 等。

运行期是指使用之前构建的镜像启动容器的过程,如:docker start 、docker run 等。

powershell 复制代码
# shell 形式,/bin/bash -c 的方式运行,可以破坏 shell 字符串 eg:RUN /bin/sh -c 'echo $msg'
RUN <command>
# exec 的形式 eg:RUN ["/bin/sh","-c","echo $msg"]
RUN ["executable", "param1", "param2"]
# 在 RUN 中可以使用 \ 将一条 RUN 指令继续到下一行。
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'
# 等同于
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
ARG
  • 定义一个变量,可在构建时使用 --build-arg name=value 传递,docker build 命令会将其传递给构建器
  • --build-arg 指定参数会覆盖 Dockerfile 中指定的同名参数
  • 如果用户指定了 未在 Dockerfile 中定义的构建参数 ,则构建会输出 警告
  • ARG 只在构建时期有效,运行时期无效
  • 不建议使用构建时变量来传递注入 github 密码、用户凭据等机密,因为构建时变量的值可以通过 docker history 来观察到
  • ARG 变量定义从 Dockerfile 定义的行开始生效
  • 使用 ENV 指定定义的环境变量始终会覆盖同名的 ARG 指令
powershell 复制代码
# 语法
ARG name=defaultValue

ARG param="Hi Docker"
RUN echo ${param}
ENV
  • 跟 ARG 类似,但是 ENV 在构建期和运行期都有效,并且使用 ENV 指定定义的环境变量始终会覆盖同名的 ARG 指令
  • 可以使用 docker run -e name=value 修改 ENV 定义的环境变量
  • ENV 在构建期就会被解析并持久化,可以通过 docker inspect image 查看
powershell 复制代码
# 语法
ENV name=value

ENV app=taobao
ADD
  • 将上下文指定的内容添加(复制)到镜像中,如果是压缩包,会自动解压;如果是远程 URL,会自动下载;但是 ADD 并没有自动下载远程压缩文件并解压的功能

src 路径必须在构建的上下文,不能使用 .../.../xxx 这种方式,因为 Docker 构建的第一步是将上下文目录(包括子目录)发送给 Docker 的守护进程

如果 src 是 URL ,并且 dest 不以 / 结尾,那么就会从 URL 下载文件并将其复制为 dest(名称)

如果 src 是 URL ,并且 dest 以 / 结尾,会自动推断出文件的名称(URL 的最后一部分)并保存到 dest(目录)中

如果 src 是目录,则将复制目录的整个内容,包括文件系统元数据

powershell 复制代码
# 语法
ADD src dest

ADD https://download.redis.io/releases/redis-6.2.6.tar.gz /dest
ADD redis-6.2.6.tar.gz /dest/
COPY
  • COPY 和 ADD 类似,但是,没有像 ADD 的自动下载和解压压缩文件的功能
  • --chown 功能仅在用于构建 Linux 容器的 Dockerfile 上受支持,而在 Windows 容器上不起作用
powershell 复制代码
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
WORKDIR
  • 为 Dockerfile 中跟随它后面的 RUN、CMD、ENTRYPOINT、COPY、ADD 指令设置工作目录
  • 可在 Dockerfile 中多次使用。如果提供了相对路径,则它将相对于上一个 WORKDIR 指令的路径
powershell 复制代码
WORKDIR /a/b/c
# 路径 /a/b/c
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
# 路径 /path/$DIRNAME
ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd
USER
  • USER 指令和 WORKDIR 指令类似,都是改变环境状态并影响以后的层,WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN 、CMD 、以及 ENTRYPOINT 这类命令的身份
  • USER 只是帮助我们切换到指定的用户而已,这个用户必须事先建立好的,否则无法切换
powershell 复制代码
USER <user>[:<group>]
USER <UID>[:<GID>]

USER test:test
VOLUME
  • 挂载 容器指定的文件夹,如果不存在,会自动创建
  • 指定了 VOLUME 指令后,即使启动容器的时候没有指定 -v 参数,也会自动进行匿名卷挂载
  • 用 VOLUME 声明了卷,那么以后对于卷内容的修改会被丢弃,所以,一定要在 volume 声明之前修改内容
powershell 复制代码
# JSON数组形式
VOLUME ["/var/log/"]
# 直接写
VOLUME /var/log
# 空格分割多个
VOLUME /var/log /var/db
EXPOSE
  • 通知 Docker 容器在运行的时候在指定的网络端口上进行侦听,可以指定端口是侦听 TCP 还是 UDP ,如果没有指定,默认就是 TCP
  • 实际上不会发布端口,它充当了构建镜像人员和运行容器人员之间的一种文档,即打算发布那些端口的信息,要在运行容器时映射端口,需要使用 docker run -p xxx:xxx 或 docker run -P 的命令。
powershell 复制代码
EXPOSE <port> [<port>/<protocol>...]

EXPOSE [80,443]
EXPOSE 80/tcp
CMD 和 ENTRYPOINT
  • Dockerfile 文件中,使用多个 CMD 或 ENTRYPOINT 作为唯一的入口,即写多个 CMD 或 ENTRYPOINT ,则会产生覆盖现象,只有最后一个生效
  • shell 方式是可以读取环境变量的值的(如:${xxx}),默认情况下,exec 的方式是读取不了环境变量值的,但是 exec 方式的 ["/bin/sh","-c","xxx"] 等同于 shell 方式,也是可以读取环境变量值
  • 推荐使用 RUN 、CMD 以及 ENTRYPOINT 使用 exec 的方式
  • 既有 CMD 的 exec 方式,又有 ENTRYPOINT 的 exec 方式,那么 CMD 是作为 ENTRYPOINT 的参数的
  • 使用 docker run -d xxx CMD 命令是可以覆盖 Dockerfile 中的 CMD 指令的,不是覆盖 exec 方式数组中的一个,而是全部

如果docker run指定了其他命令,CMD指定的默认命令将被忽略

ENTRYPOINT看上去与CMD很像,它们都可以指定要执行的命令及其参数。不同的地方在于ENTRYPOINT不会被忽略,一定会被执行,即使运行docker run时指定了其他命令

powershell 复制代码
# exec 方式, 首选方式
CMD ["executable","param1","param2"]
# shell 形式
CMD command param1 param2
# exec 方式, 首选方式
ENTRYPOINT ["executable", "param1", "param2"]
# shell 形式
ENTRYPOINT command param1 param2

# docker run -it [image] 启动时 输出: Hello world
# docker run -it [image] JS 启动时 输出: Hello JS
ENTRYPOINT ["/bin/echo", "Hello"] CMD ["world"]
相关推荐
alden_ygq1 小时前
k8s node inode被耗尽如何处理?
云原生·容器·kubernetes
爱知菜2 小时前
Windows安装Docker Desktop(WSL2模式)和Docker Pull网络问题解决
运维·docker·容器
月下雨(Moonlit Rain)3 小时前
Docker
运维·docker·容器
技术小甜甜4 小时前
[Dify] 使用 Docker 本地部署 Dify 并集成 Ollama 模型的详细指南
运维·docker·容器·dify
小白也有IT梦5 小时前
解决 Ubuntu 上 Docker 安装与网络问题:从禁用 IPv6 到配置代理
ubuntu·docker·网络配置
cen难取名6 小时前
记一次ubantu搭建vulhub靶场。从docker安装到拉取vulhub靶场一条龙服务
运维·docker·容器
黎明鱼儿8 小时前
高可用架构:Keepalived、Nginx与Docker深度解析
nginx·docker·架构
识途老码11 小时前
k8s通过service标签实现蓝绿发布
云原生·容器·kubernetes
cxr82812 小时前
主机IP动态变化时如何通过固定host.docker.internal访问本机服务
网络协议·tcp/ip·docker
烛.照10315 小时前
Docker 安装MySQL
linux·mysql·docker·容器