Docker数据管理与Dockerfile镜像创建

前言

在容器化环境中,如何有效地管理和持久化数据成为了开发人员和运维团队面临的挑战之一;另一方面,镜像的创建是构建容器化应用的基础。优化的镜像设计可以提高部署效率和应用性能,减少资源消耗和运行成本。本文将介绍 Docker 中的数据管理策略,包括数据卷、数据卷容器、绑定挂载、持久化存储等技术;以及 Docker 镜像的构建过程,包括 Dockerfile 的编写、镜像层的管理和多阶段构建等技术。

目录

一、Docker 的数据管理

1. 数据卷

1.1 概述

1.2 创建数据卷

2. 数据卷容器

2.1 概述

2.2 创建数据卷容器

3. 容器互联(使用centos镜像)

二、Docker 镜像的创建

1. 基于现有镜像创建 

2. 基于本地模板创建

3. 基于 Dockerfile 创建

3.1 联合文件系统(UnionFS)

3.2 镜像的分层

3.3 镜像加载原理 

3.4 Dockerfile 概述

3.5 Dockerfile 操作常用的指令

3.5.1 FROM 镜像

3.5.2 MAINTAINER 名字

3.5.3 RUN 命令

3.5.4 ENTRYPOINT ["要运行的程序", "参数 1", "参数 2"]

3.5.5 CMD ["要运行的程序", "参数1", "参数2"] 

3.5.6 EXPOSE 端口号

3.5.7 ENV 环境变量变量值

3.5.8 ADD  源文件/目录  目标文件/目录

3.5.9 COPY  源文件/目录  目标文件/目录

3.5.10 VOLUME [“目录”]

3.5.11 USER 用户名/UID 

3.5.12 WORKDIR 路径

3.5.13 ONBUILD 命令

三、Dockerfile 案例

1. 建立 Dockerfile 遵循格式

2. 制作 apache 镜像

3. 制作 SSH 镜像

4. 制作 Systemctl 镜像

5. 制作 nginx 镜像

6. 制作 tomcat 镜像 

7. 制作 mysql 镜像

四、总结


一、Docker 的数据管理

管理 Docker 容器中数据主要有两种方式:数据卷(Data Volumes)和数据卷容器(DataVolumes Containers)。

1. 数据卷

1.1 概述

数据卷是 Docker 中用于持久化存储数据的一种机制,它可以在容器之间共享和重用数据。数据卷独立于容器的生命周期,即使容器被删除,数据卷中的数据仍然保留。数据卷可以用于存储应用程序数据、配置文件、日志等信息。

数据卷是一个供容器使用的特殊目录,数据卷实际上并不直接存储在容器内部,而是存储在宿主机的文件系统上。可将宿主机的目录挂载到数据卷上,对数据卷的修改操作立刻可见,并且更新数据不会影响镜像,从而实现数据在宿主机与容器之间的迁移。数据卷的使用类似于 Linux 下对目录进行的 mount 操作。

1.2 创建数据卷

[root@localhost ~]# docker run -v /var/www:/data1 --name a1 -it centos:7 /bin/bash
# 将宿主目录/var/www挂载到容器中的/data1,如果/var/www不存在可以自动创建
# -v 代表在容器内创建数据卷
[root@3fdee923a6c6 /]# echo web1 > /data1/1.txt
[root@3fdee923a6c6 /]# exit      # 返回宿主机进行查看
exit
[root@localhost ~]# cat /var/www/1.txt 
web1当容器a1退出后,根据路径/var/www新建容器,查看数据持久性:
[root@localhost ~]# docker run -v /var/www:/data2 --name b1 -it centos:7 /bin/bash
[root@6c974fc26dcd /]# cat data2/1.txt 
web1
# 数据卷是通过路径/var/www与容器关联的,因此,即使容器名称不同,只要挂载了宿主机同一个路径,那么数据卷就是相同的,数据也是共享的

2. 数据卷容器

2.1 概述

如果需要在容器之间共享一些数据,最简单的方法就是使用数据卷容器。数据卷容器是一个普通的容器,用于存储和管理数据卷,专门提供数据卷给其他容器挂载使用。通过创建一个数据卷容器,可以方便地将数据卷与其他容器共享,实现数据的集中管理和共享。

2.2 创建数据卷容器

[root@localhost ~]# docker run --name a2 -v /data1 -v /data2 -it centos:7 /bin/bash
# 启动一个名为a2的CentOS 7容器,并将主机上的/data1和/data2目录分别挂载到容器中
[root@2e82ed78cf61 /]# echo 111 > /data1/1.txt
[root@2e82ed78cf61 /]# echo 222 > /data2/2.txt
[root@2e82ed78cf61 /]# exit
exit
[root@localhost ~]# docker run -it --volumes-from a2 --name a3 centos:7 /bin/bash
# 使用--volumes-from来挂载a2容器中的数据卷到新的容器
[root@ecbac00e3384 /]# cat /data1/1.txt 
111
[root@ecbac00e3384 /]# cat /data2/2.txt 
222

3. 容器互联(使用centos镜像)

容器互联是一种在 Docker 中连接多个容器并实现它们之间通信的方法。通过容器互联,您可以轻松地创建一个网络,使得不同容器之间可以相互通信,共享数据和服务。即在源容器和接收容器之间建立一条隧道,接收容器可以看到源容器指定的信息。

[root@localhost ~]# docker run -itd -P --name web1 centos:7 /bin/bash
04dcedb3a41a52924363c094d1538210ea4fb516ca0b45fb0b553f74ec35b6ef
# 创建并运行源容器取名 web1
[root@localhost ~]# docker run -itd -P --name web2 --link web1:web111 centos:7 /bin/bash
d6883a2a4b8d30d11bd15c7486a96d0bb84f7460f3e3af773a7bd5e9492e4559
# 创建并运行接收容器取名web2,使用--link选项指定连接容器以实现容器互联
# --link容器名:连接的别名
[root@localhost ~]# docker exec -it web2 /bin/bash
[root@d6883a2a4b8d /]# ping web1
PING web111 (172.17.0.2) 56(84) bytes of data.
64 bytes from web111 (172.17.0.2): icmp_seq=1 ttl=64 time=0.490 ms
64 bytes from web111 (172.17.0.2): icmp_seq=2 ttl=64 time=0.150 ms
64 bytes from web111 (172.17.0.2): icmp_seq=3 ttl=64 time=0.191 ms
[root@d6883a2a4b8d /]# ping a1  
ping: a1: Name or service not known
[root@d6883a2a4b8d /]# ping a2
ping: a2: Name or service not known

即使其他容器处于 up 状态,只有建立了安全通道容器间才可以进行通讯。

二、Docker 镜像的创建

创建镜像有三种方法,分别为基于已有镜像创建、基于本地模板创建以及基于 Dockerfile 创建。

1. 基于现有镜像创建 

① 首先创建一个镜像,在容器里做修改

[root@localhost ~]# docker create -it centos:7 /bin/bash
3904598fe8087a267d8cb33a6dde41ea84fb6198b35c1483fc89ed939859ab24
[root@localhost ~]# docker ps -a
CONTAINER ID   IMAGE      COMMAND       CREATED          STATUS    PORTS     NAMES
3904598fe808   centos:7   "/bin/bash"   10 seconds ago   Created             heuristic_leavitt

② 然后将修改后的容器提交为新的镜像,需要使用该容器的 ID 号创建新镜像

[root@localhost ~]# docker commit -m "newimage" -a "user" 3904598fe808 centos:new7
sha256:a1aae83766085d70ee4b2f2e5007fbf142426bc805a5dc80b0100c163828bf4c
# -m 说明信息
# -a 作者信息
# -p 生成过程中停止容器的运行
[root@localhost ~]# docker images | grep new7
centos               new7      a1aae8376608   32 seconds ago   204MB

③ 也可以使用 export 导出(迁移)、导入

导出:
docker export 容器id/名称 > 文件名
示例:
docker export id > centos7.tar导入:
cat 文件名 | docker import - 镜像名称:标签
示例:
cat centos7.tar | docker import - centos:new7
注意:导入会生成镜像,不会创建容器

2. 基于本地模板创建

① 通过导入操作系统模板文件可以生成镜像,模板可以从 OPENVZ 开源项目下载,下载地址为http://openvz.org/Download/template/precreated

[root@localhost opt]# wget http://download.openvz.org/template/precreated/debian-7.0-x86-minimal.tar.gz
[root@localhost opt]# ls
debian-7.0-x86-minimal.tar.gz

② 导入为镜像

[root@localhost opt]# cat debian-7.0-x86-minimal.tar.gz | docker import - debian:test
sha256:deaaddd9c659118872a8d29438258a680bf1f5907169960cf3fb3d0d52e30e03
[root@localhost opt]# docker images
REPOSITORY           TAG       IMAGE ID       CREATED          SIZE
debian               test      deaaddd9c659   33 seconds ago   215MB

3. 基于 Dockerfile 创建

3.1 联合文件系统(UnionFS)

联合文件系统(UnionFS)是一种文件系统技术,允许将多个不同的文件系统(类型)合并挂载为一个单一的文件系统。在容器技术中,UnionFS 用于构建容器镜像和实现容器的分层文件系统。

在 Docker 中,镜像是通过一层一层的文件系统(UnionFS)构建而成的。当下载 Docker 镜像时,实际上是在下载这些文件系统的不同层。每一层都包含了文件和目录的变化,使得镜像可以被构建和管理。实际上是在下载这些不同层的文件系统。如果之前已经下载过相同的基础镜像,那么只需要下载新的层,而不需要重新下载整个镜像。这种分层的设计使得镜像的构建、共享和更新变得高效和灵活。

具体来说,每个 Docker 镜像都由多个只读层(read-only layers)组成,其中最底层是基础镜像(Base Image),而其他层则包含了对基础镜像的修改。这些层按照顺序叠加在一起,形成一个联合的文件系统,最终呈现给用户的是一个完整的镜像。

3.2 镜像的分层

镜像不是一个单一的文件,而是有多层构成。容器其实是在镜像的最上面加了一层读写层,在运行容器里做的任何文件改动,都会写到这个读写层。如果删除了容器,也就删除了其最上面的读写层,文件改动也就丢失了。Docker 使用存储驱动管理镜像每层内容及可读写层的容器层。

  • Dockerfile 中的每个指令都会创建一个新的镜像层
  • 镜像层将被缓存和复用
  • 当Dockerfile 的指令修改了,复制的文件变化了,或者构建镜像时指定的变量不同了,对应的镜像层缓存就会失效
  • 某一层的镜像缓存失效,它之后的镜像层缓存都会失效
  • 镜像层是不可变的,如果在某一层中添加一个文件,然后在下一层中删除它,则镜像中依然会包含该文件,只是这个文件在 Docker 容器中不可见了

① Kernel(内核层)

是操作系统的核心组件,负责管理硬件资源、提供系统服务,并为应用程序提供运行环境。在Docker场景下,提及 Kernel 通常是指 Docker 宿主机的 Kernel,而不是镜像内部的 Kernel;

② Base Image(基础镜像层)

是构建其他 Docker 镜像的基础。它通常是最底层的镜像,包含一个精简的操作系统环境(如Alpine Linux、Ubuntu、CentOS等)以及必要的系统工具;

③ Image(只读镜像层)

是 Docker 中用于创建容器的模板,它是由一系列分层组成的。每个镜像层代表了对前一层的增量更改,如安装一个软件包、添加文件或修改配置。镜像层是只读的,且每一层都有一个唯一的标识符;

④ Container(容器层)

是基于镜像实例化的、轻量级的、可执行的软件单元。容器包含了运行特定应用所需的所有依赖(代码、运行时、库、环境变量等),并利用Linux内核的隔离机制与其他容器及宿主机隔离。每个容器都从其对应的镜像顶部的读写层(也称为“容器层”或“Overlay层”)开始,在此之上进行运行时的写入操作;

⑤ Worke(工作节点)

在需要修改文件时创建文件的副本,而不是直接修改原始文件。在容器中,这意味着当容器试图修改镜像中的文件时,文件系统并不直接修改原始文件,而是在需要时将原始文件复制到新的位置,然后对副本进行修改。确保原始镜像层的完整性,同时允许容器在自己的文件系统视图中进行修改,而不会影响其他容器或原始镜像。

⑥ Merge(合并视图层)

对 Docker 镜像信息进行抽象、组织并展示给用户的逻辑层面;实际上是软件为用户提供的一种逻辑抽象和交互界面,用于展示和操作 Docker 镜像的相关信息。由于它是一个逻辑概念,且通过用户接口隐藏了底层细节,所以被称为“看不见的”。

3.3 镜像加载原理 

其实就是当容器启动时,镜像的各个层通过联合文件系统技术被叠加到一起,形成一个单独的文件系统视图。

① 分层结构:Docker镜像采用分层结构,每一层包含了文件系统的一部分和相关设置。这些层相互叠加,形成完整的镜像。

② 联合文件系统:Docker使用联合文件系统(UnionFS)技术将这些分层的只读文件系统叠加在一起,形成一个虚拟的文件系统视图。

③ 镜像加载:当容器被创建时,Docker会根据镜像的定义,将这些层加载到内存中,并创建一个可写的容器层,以便容器内的应用程序可以对其进行修改而不影响原始镜像。

④ 写时复制:对于容器内的文件修改,Docker使用写时复制(Copy-on-Write)技术,即只有在需要修改文件时才会复制底层数据,确保原始镜像层的完整性。

⑤ 一旦镜像的各个层被加载并合并到一起,容器运行时负责启动容器进程,并提供隔离的运行环境,使得应用程序能够在其中独立运行。

可以理解成一开始内核里什么都没有,操作一个命令下载 centos7,这时就会在内核上面加了一层基础镜像;再安装一个 nginx,会在基础镜像上叠加一层 image;接着再安装一个 tomcat,又会在 image 上面再叠加一层 image。最后它们看起来就像一个文件系统即容器的 rootfs(是容器启动时所使用的根文件系统。容器的文件系统是从镜像的 rootfs 开始构建的,然后通过联合文件系统等技术叠加其他层来形成完整的容器文件系统视图。)。在 Docker的体系里把这些 rootfs 叫做 Docker 的镜像。但是,此时的每一层 rootfs 都是 read-only 的,此时还不能对其进行操作。当创建一个容器,也就是将 Docker 镜像进行实例化(指根据某个模板或类来创建一个具体的实例或对象的过程),系统会在一层或是多层 read-only的 rootfs 之上分配一层空的 read-write 的 rootfs。 

3.4 Dockerfile 概述

Dockerfile 是一个文本文件,包含了一系列用于构建 Docker 镜像的指令和配置。通过编写 Dockerfile,可以将应用程序、依赖项和配置打包到一个镜像中,实现应用程序的快速部署和移植。可以使用 docker build 命令根据该文件构建出一个镜像,然后使用该镜像创建和运行容器。

  • 镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
  • Dockerfile是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了Dockerfile,当我们需要定制自己额外的需求时,只需在Dockerfile上添加或者修改指令,重新生成 image 即可, 省去了敲命令的麻烦。
  • 除了手动生成Docker镜像之外,可以使用Dockerfile自动生成镜像。Dockerfile是由多条的指令组成的文件,其中每条指令对应 Linux 中的一条命令,Docker 程序将读取Dockerfile 中的指令生成指定镜像。
  • Dockerfile结构大致分为四个部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。Dockerfile每行支持一条指令,每条指令可携带多个参数,支持使用以“#“号开头的注释。

3.5 Dockerfile 操作常用的指令

Dockerfile 中的指令可以定义基础镜像、安装软件、复制文件、设置工作目录、配置容器启动命令、声明端口、设置环境变量、挂载点和元数据等。

3.5.1 FROM 镜像

第一条指令必须为 FROM 指令。并且,如果在同一个 Dockerfile 中创建多个镜像时,可以使用多个 FROM 指令(每个镜像一次)。

格式:
FROM  <image>  或  FROM  <image>:<tag>
示例:
FROM  centos:7
3.5.2 MAINTAINER 名字

说明新镜像的维护人信息。

格式:
MAINTAINER	<name>	
示例:
MAINTAINER  this  is  newimage  <fql>
3.5.3 RUN 命令

在所基于的镜像上执行命令,并提交到新的镜像中。用于在 Docker 镜像构建过程中执行命令。

格式:
RUN	<command>
# 将在shell终端中运行命令,即 /usr/bin/bash -c
示例:
RUN yum install -y epel-release	
3.5.4 ENTRYPOINT ["要运行的程序", "参数 1", "参数 2"]

ENTRYPOINT 指令用于设置容器启动时要运行的命令。

设定容器启动时第一个运行的命令及其参数。可以通过使用命令docker run --entrypoint 来覆盖镜像中的ENTRYPOINT指令的内容。

格式:
ENTRYPOINT  ["executable", "param1", "param2"]
示例:
ENTRYPOINT  ["rm", "-rf", "/*"]
3.5.5 CMD ["要运行的程序", "参数1", "参数2"] 

CMD 指令用于指定容器启动时要运行的默认命令。是 exec 形式,shell形式:CMD 命令 参数1 参数2;启动容器时默认执行的命令或者脚本,Dockerfile 只能有一条CMD命令。如果指定多条命令,只执行最后一条命令。

支持三种格式:
格式1:CMD	["executable","param1","param2"] # 使用exec执行,推荐方式;
示例:CMD ["ls", "-l","/data"]
格式2:CMD	command	param1	param2		     # 在/bin/bash中执行,提供给需要交互的应用;
示例:CMD ls -l /data
格式3:CMD	["param1","param2"]		         # 提供给ENTRYPOINT的默认参数;
示例:ENTRYPOINT [mkdir]CMD	["-P","/a/b/c"]	

如果在 docker run 时指定了命令或者镜像中有 ENTRYPOINT,那么CMD就会被覆盖。
CMD 可以为 ENTRYPOINT 指令提供默认参数。

ENTRYPOINT ["rm"]
CMD ["cp" ,"-rf","*"]
# 最终的命令为: rm -rf *
3.5.6 EXPOSE 端口号

告诉 Docker 服务端容器暴露的端口号,供互联系统使用。在启动容器时需要通过 -P,Docker 宿主机会自动分配一个端口转发到指定的端口。

格式:
EXPOSE  <port>  [<port>...]	
示例:
EXPOSE 80
EXPOSE 80 443 8080           # 同时声明多个端口
3.5.7 ENV  环境变量  变量值

指定一个环境变量,会被后续 RUN 指令使用,并在容器运行时保持。

格式:
ENV <variable_name> <variable_value>变量名          变量值
示例:
ENV PATH $PATH:/opt   即:PATH=$PATH:/opt
3.5.8 ADD  源文件/目录  目标文件/目录

用于将本地源文件或目录复制到正在构建的镜像内部的目标位置。源文件要与 Dockerfile 位于相同目录中,或者是一个 URL。

格式:
ADD <src>... <dest>
# <src>:源文件或目录的路径。可以是相对于 Dockerfile 所在目录的相对路径,也可以是绝对路径。对于多个 <src>,它们会被依次复制到 <dest> 目录下
# <dest>:目标文件或目录的路径。必须是容器内部的绝对路径
示例:
ADD ./irectory /app/data/
# 将当前目录下的directory整个目录及其内部的所有文件和子目录,按原结构复制到镜像内部的/app/data/目录下
ADD https://downloads/package.tar.gz /opt/
# 从指定的URL下载package.tar.gz文件,并将其解压后的内容复制到镜像内部的/opt/目录下

有如下注意事项:

① 如果源路径是文件,目标路径是以 / 结尾, 则 docker 会把目标路径当作目录,会把源文件拷贝到该目录下。如果目标路径不存在,则会自动创建目标路径。

ADD  /home/1.txt  /file/

② 如果源路径是个文件,目标路径是不以 / 结尾,则 docker 会把目标路径当作一个文件。

  • 如果目标路径不存在,会以目标路径为名创建一个文件,内容同源文件
  • 如果目标文件是个存在的文件,会用源文件覆盖它,只是内容覆盖,文件名还是目标文件名
  • 如果目标文件实际是个存在的目录,则会源文件拷贝到该目录下。 注意,这种情况下,最好显示的以 / 结尾,以避免混淆
ADD  /home/data  /home/data

③ 如果源路径是个目录,且目标路径不存在,则 docker 会自动以目标路径创建一个目录,把源路径目录下的文件拷贝进来;如果目标路径是个已经存在的目录,则docker会把源路径目录下的文件拷贝到该目录下。

④ 如果源文件是个归档文件(压缩文件),则 docker 会自动帮解压;  URL 下载和解压特性不能一起使用。任何压缩文件通过 URL 拷贝,都不会自动解压。 

3.5.9 COPY  源文件/目录  目标文件/目录

用于将本地源文件或目录复制到正在构建的镜像内部的目标位置。相比 ADD 指令,COPY 更为简单和直观,不具备自动解压缩和从 URL 下载文件的功能。只复制本地主机上的文件/目录复制到目标地点,源文件/目录要与 Dockerfile 在相同的目录中。

格式:
COPY <src>... <dest>
# <src>:源文件或目录的路径。可以是相对于 Dockerfile 所在目录的相对路径,也可以是绝对路径。对于多个 <src>,它们会被依次复制到 <dest> 目录下
# <dest>:目标文件或目录的路径。必须是容器内部的绝对路径
示例:
COPY ./file1.txt ./file2.txt /app/
3.5.10 VOLUME [“目录”]

Docker 容器中创建一个挂载点(mount point)以存储持久化数据。

格式:
VOLUME  ["目录"]    # 指定要挂载为卷的目录路径
示例:
VOLUME  ["/data"]
# 当容器启动时,可以将数据写入到/data目录,这些数据将会保存在宿主机上,即使容器被删除也不会丢失
3.5.11 USER 用户名/UID 

指定运行容器时的用户。

格式:
USER 用户名/UID
示例:
USER 1001
# USER 1001指令告诉Docker在容器启动时切换到用户ID为1001的用户
3.5.12 WORKDIR 路径

为后续的 RUN、CMD、ENTRYPOINT 指定工作目录进行操作,比如复制文件、运行命令等。

格式:
WORKDIR  路径
示例:
WORKDIR /apps
3.5.13 ONBUILD 命令

ONBUILD 指令是用于延迟执行操作的 Dockerfile 指令之一。在 Dockerfile 中,ONBUILD 指令定义了在当前镜像作为其他镜像的基础镜像时执行的操作。

当在一个 Dockerfile 文件中加上 ONBUILD 指令,该指令对利用该 Dockerfile 构建镜像(比如为A镜像)不会产生实质性影响。但是当编写一个新的 Dockerfile 文件来基于A镜像构建一个镜像(比如为B镜像)时,这时构造A镜像的 Dockerfile 文件中的 ONBUILD 指令就生效了,在构建B镜像的过程中,首先会执行 ONBUILD 指令指定的指令,然后才会执行其它指令。

格式:
OBuild 命令 参数

三、Dockerfile 案例

1. 建立 Dockerfile 遵循格式

  • 第一行必须使用 FROM 指令指明所基于的镜像名称
  • 之后使用 MAINTAINER 指令说明维护该镜像的用户信息
  • 然后是镜像操作相关指令,如 RUN 指令。每运行一条指令,都会给基础镜像添加新的一层
  • 最后使用 CMD 指令指定启动容器时要运行的命令操作

2. 制作 apache 镜像

① 建立工作目录

[root@localhost ~]# mkdir /opt/apache
[root@localhost ~]# cd /opt/apache/

② 创建 apache Dockerfile 文件

[root@localhost apache]# vim Dockerfile
FROM centos:7                           # 基于的基础镜像centos:7
MAINTAINER apache image <fql>           # 维护镜像的用户信息
RUN yum install -y httpd                # 基于基础镜像安装apache
EXPOSE 80                               # Docker服务器开启80端口
ADD index.html /var/www/html/index.html # 复制本地源文件至正在构建的镜像内部
ADD apache.sh /apache.sh               
RUN chmod 755 /apache.sh                # 给镜像中的apache.sh脚本添加权限
CMD ["/apache.sh"]                      # 启动容器时执行脚本
或者:
ENTRYPOINT ["/usr/sbin/apachect1"]      # 启动容器时运行apache服务
CMD ["-D","FOREGROUND"]                
# "-D" 参数用于指定一个调试标志或定义服务器的特定行为,FOREGROUND以前台模式运行,进程结束时终止
# CMD优先级低于ENTRYPOINT,为ENTRYPOINT提供命令参数,且
或者:
CMD [ "/usr/sbin/apachectl","-D", "FOREGROUND"]
或者运行容器时覆盖默认参数:
docker run my_apache_container -k start

③ 创建启动容器时执行的脚本

[root@localhost apache]# vim apache.sh
#!/bin/bash
rm -rf /run/httpd/*                # 清理httpd的缓存
/usr/sbin/apachectl -D FOREGROUND  # 指定为前台运行

④ 准备网站页面

[root@localhost apache]# echo this is apache web > index.html
[root@localhost apache]# ls
apache.sh  Dockerfile  index.html

⑤ 生成镜像

[root@localhost apache]# docker build -t httpd:centos .
# -t 参数用于指定构建镜像的名称及标签
# . 就是指示Docker在当前目录中查找名为Dockerfile的文件,并以此文件作为构建镜像的基础

⑥ 新镜像运行容器

[root@localhost apache]# docker run -d -p 10000:80 httpd:centos
# 以后台运行基于httpd:centos的docker进程,并将宿主机10000端口映射到容器的80端口
32fcdbb50bb3afaf291f3f1d7abf1b90169155b626c0e5497d7ad0892b4d8167
[root@localhost apache]# docker ps
CONTAINER ID   IMAGE          COMMAND        CREATED         STATUS         PORTS                                     NAMES
32fcdbb50bb3   httpd:centos   "/apache.sh"   5 seconds ago   Up 3 seconds   0.0.0.0:10000->80/tcp, :::10000->80/tcp   keen_joliot

⑦ 测试页面

3. 制作 SSH 镜像

构建SSH镜像的作用在于为用户提供一个具备SSH(Secure Shell)功能的容器环境。SSH是一种加密的网络协议,常用于安全地远程登录到计算机系统,并在远程系统上执行命令。 

① 建立工作目录

[root@localhost apache]# mkdir /opt/sshd
[root@localhost apache]# cd ../sshd/

② 创建 sshd Dockerfile 文件

[root@localhost sshd]# vim Dockerfile
FROM centos:7                   # 基于的基础镜像centos:7       
MAINTAINER sshd image <fql>     # 维护镜像的用户信息
RUN yum install -y openssh* net-tools lsof telnet passwd
# 安装OpenSSH服务器和客户端工具、net-tools(用于网络配置和诊断)、lsof(用于列出打开文件的工具)、telnet(用于远程登录测试)和passwd(用于设置密码)
RUN echo '123456' | passwd --stdin root
# 设置了root用户的密码为123456
RUN sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config
# -i选项表示直接在文件中进行修改
# 将其中的UsePAM yes配置改为UsePAM no。这个修改通常用于禁用PAM认证
RUN sed -ri '/^session\s+required\s+pam_loginuid.so/ s/^/#/' /etc/pam.d/sshd
# -r选项用于启用扩展正则表达式
# 使用sed命令注释了/etc/pam.d/sshd文件中与pam_loginuid.so相关的配置行
# \s+: 这是一个转义序列,表示匹配一个或多个空白字符(例如空格、制表符等)
RUN ssh-keygen -t rsa -A        # 生成了SSH服务器的RSA密钥对
RUN mkdir -p /root/.ssh && chown root.root /root && chmod 700 /root/.ssh
# 创建了root用户的.ssh目录,并设置权限。这个目录通常用于存放SSH用户的公钥,以实现密钥认证登录
EXPOSE 22                       # Docker容器监听SSH服务的22端口           
CMD ["/usr/sbin/sshd" , "-D"]
# 对于OpenSSH服务器,由于其默认行为已经是前台模式,因此不需要额外指定参数FOREGROUND
# 为了确保将输出发送到控制台并以调试模式持续运行,仍需要在启动命令中添加-D参数

③ 生成镜像

[root@localhost sshd]# docker build -t sshd:centos .

④ 启动容器并修改 root 密码

[root@localhost sshd]# docker run -d -P sshd:centos
# 在Docker中以后台模式运行一个基于sshd:centos的SSH服务器容器,并将容器内部的SSH服务端口映射到主机上的一个随机端口上,以便可以通过主机的端口访问SSH服务
[root@localhost sshd]# docker ps
CONTAINER ID   IMAGE          COMMAND               CREATED          STATUS          PORTS                                     NAMES
52be8693ca2e   sshd:centos    "/usr/sbin/sshd -D"   13 seconds ago   Up 11 seconds   0.0.0.0:32768->22/tcp, :::32768->22/tcp   tender_kepler
32fcdbb50bb3   httpd:centos   "/apache.sh"          34 minutes ago   Up 34 minutes   0.0.0.0:10000->80/tcp, :::10000->80/tcp   keen_joliot[root@localhost ~]# ssh 192.168.190.107 -p 32768
The authenticity of host '[192.168.190.107]:32768 ([192.168.190.107]:32768)' can't be established.
ECDSA key fingerprint is SHA256:Z8w4BCFeZGbJ6NMUgNo8RZ2MsRwOQUAk58/Z67JJYTA.
ECDSA key fingerprint is MD5:5e:12:e1:5b:07:4c:a0:7c:a4:e1:11:4d:51:22:ce:1f.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[192.168.190.107]:32768' (ECDSA) to the list of known hosts.
root@192.168.190.107's password: 
[root@52be8693ca2e ~]# passwd
Changing password for user root.
New password: 
BAD PASSWORD: The password is shorter than 8 characters
Retype new password: 
passwd: all authentication tokens updated successfully.
[root@52be8693ca2e ~]# exit
logout
Connection to 192.168.190.107 closed.

4. 制作 Systemctl 镜像

① 建立工作目录

[root@localhost sshd]# mkdir /opt/systemctl
[root@localhost sshd]# cd /opt/systemctl

② 创建 Systemctl Dockerfile 文件

[root@localhost systemctl]# vim Dockerfile
FROM sshd:centos
MAINTAINER systemctl image <fql>
ENV container docker          # 设置一个环境变量container,其值为docker
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \ 
# 删除除了systemd-tmpfiles-setup.service的其它所有文件       
rm -f /lib/systemd/system/multi-user.target.wants/*; \
rm -f /etc/systemd/system/*.wants/*; \
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;
VOLUME [ "/sys/fs/cgroup" ]
#CMD ["/usr/sbin/init"]
# 基础镜像中的CMD指令会被继承到新镜像中,除非在新的Dockerfile中显式地覆盖它
# 基础镜像(sshd:centos)已经定义了CMD指令(CMD ["/usr/sbin/sshd", "-D"])

③ 生成镜像

[root@localhost systemctl]# docker build -t systemd:centos .

④ 启动容器,并挂载宿主机目录挂载到容器中,和进行初始化

[root@localhost systemctl]# docker run --privileged -d -P -v /sys/fs/cgroup:/sys/fs/cgroup:ro systemd:centos /sbin/init
4d6d63ecb829cf8a9069eb21a340dbed8a4e311023ef06c048c9832570101f7d
# --privileged: 使用特权模式,这会赋予容器访问主机上所有设备的权限
# 使container内的root拥有真正的root权限。否则,container内的root只是外部的一个普通用户权限
# -P: 将容器内部使用的网络端口映射到主机上的随机端口
# -v /sys/fs/cgroup:/sys/fs/cgroup:ro: 将主机的/sys/fs/cgroup目录挂载到容器的相同路径,并设置为只读(ro表示read-only)
# 这是为了让容器能够访问主机的 cgroup 文件系统,通常用于容器内运行systemd
# /sbin/init: 指定容器启动时要运行的命令,这里是启动systemd的初始化进程
或者:
docker run --privileged -it -P -v /sys/fs/cgroup:/sys/fs/cgroup:ro systemd:centos /sbin/init &

⑤ 进入容器,使用 systemcd 管理服务

# docker exec -it a0d624d2bfa9 bash
[root@4d6d63ecb829 /]# systemctl start sshd                      
[root@4d6d63ecb829 /]# systemctl status sshd
● sshd.service - OpenSSH server daemonLoaded: loaded (/usr/lib/systemd/system/sshd.service; disabled; vendor preset: enabled)Active: active (running) since Sat 2024-04-27 10:19:08 UTC; 2s ago

5. 制作 nginx 镜像

① 建立工作目录

[root@localhost systemctl]# mkdir /opt/nginx
[root@localhost systemctl]# cd /opt/nginx/
[root@localhost nginx]# ls
nginx-1.24.0.tar.gz         # 上传nginx安装包

② 创建 nginx Dockerfile 文件

[root@localhost nginx]# vim Dockerfile
FROM centos:7
MAINTAINER nginx image <fql>
RUN yum -y install pcre-devel zlib-devel gcc gcc-c++ make
RUN useradd -M -s /sbin/nologin nginx
ADD nginx-1.24.0.tar.gz /opt/
WORKDIR /opt/nginx-1.24.0
WORKDIR /opt/nginx-1.12.0
RUN ./configure \
--prefix=/usr/local/nginx \     # 指定了NGINX安装目录
--user=nginx \
--group=nginx \
--with-http_stub_status_module && make -j 2 && make install # 启用了NGINX的stub status模块
ENV PATH /usr/local/nginx/sbin:$PATH
EXPOSE 80                       # 指定了NGINX监听的HTTP和HTTPS端口
EXPOSE 443
RUN echo "daemon off;" >> /usr/local/nginx/conf/nginx.conf
# daemon off;指示NGINX在前台运行,而不是作为守护进程
ADD nginx.sh /nginx.sh         
RUN chmod 755 /nginx.sh   
CMD ["/nginx.sh"]               # 容器启动时要执行的脚本
#CMD ["/usr/local/sbin/nginx", "-g", "daemon off;"]
# -g 参数是用来设置NGINX的全局配置项的

③ 创建启动容器时执行的脚本

[root@localhost nginx]# vim nginx.sh
#!/bin/bash
/usr/local/nginx/sbin/nginx

④ 生成镜像

[root@localhost nginx]# docker build -t nginx:centos .

⑤ 启动容器并开启宿主机端口映射

[root@localhost nginx]# docker run -d -P nginx:centos
7b0478a6dacdeae4456c406b5cbb391cb6f93068c8f7a32c9264691f3c295c99
[root@localhost nginx]# docker ps
CONTAINER ID   IMAGE            COMMAND               CREATED             STATUS             PORTS                                                                                NAMES
7b0478a6dacd   nginx:centos     "/nginx.sh"           7 seconds ago       Up 6 seconds       0.0.0.0:32771->80/tcp, :::32771->80/tcp, 0.0.0.0:32770->443/tcp, :::32770->443/tcp   amazing_moser
4d6d63ecb829   systemd:centos   "/sbin/init"          26 minutes ago      Up 26 minutes      0.0.0.0:32769->22/tcp, :::32769->22/tcp                                              festive_hellman
52be8693ca2e   sshd:centos      "/usr/sbin/sshd -D"   47 minutes ago      Up 47 minutes      0.0.0.0:32768->22/tcp, :::32768->22/tcp                                              tender_kepler
32fcdbb50bb3   httpd:centos     "/apache.sh"          About an hour ago   Up About an hour   0.0.0.0:10000->80/tcp, :::10000->80/tcp  

⑥ 访问页面

6. 制作 tomcat 镜像 

① 建立工作目录

[root@localhost nginx]# mkdir /opt/tomcat
[root@localhost nginx]# cd /opt/tomcat
[root@localhost tomcat]# ls
apache-tomcat-9.0.16.tar.gz  jdk-8u291-linux-x64.tar.gz
# 准备jdk、tomcat安装包

② 创建 tomcat Dockerfile 文件

[root@localhost tomcat]# vim Dockerfile
FROM centos:7
MAINTAINER tomcat image <fql>
ADD jdk-8u291-linux-x64.tar.gz /usr/local/  # 将jdk……tar.gz文件解压到/usr/local/目录下
WORKDIR /usr/local/                         # 为后续命令指定工作目录/usr/local/
RUN mv jdk1.8.0_291 /usr/local/java         # 解压后的JDK目录重命名为java并移动到/usr/local/目录下
ENV JAVA_HOME /usr/local/java               # 设置环境变量JAVA_HOME为/usr/local/java
ENV JRE_HOME ${JAVA_HOME}/jre               # 设置环境变量JRE_HOME为${JAVA_HOME}/jre
ENV CLASSPATH .:${JAVA_HOME}/lib:${JRE_HOME}/lib # 置环境变量CLASSPATH,包含当前目录、Java库和JRE库 
ENV PATH $JAVA_HOME/bin:$PATH               # 将JAVA_HOME/bin加入到PATH环境变量中
ADD apache-tomcat-9.0.16.tar.gz /usr/local/
WORKDIR /usr/local/                         # 设置工作目录为/usr/local/
RUN mv apache-tomcat-9.0.16 /usr/local/tomcat # 将解压后的Tomcat目录重命名为tomcat并移动到/usr/local/目录下
EXPOSE 8080                                 # 暴露容器的 8080 端口
#CMD ["/usr/local/tomcat/bin/catalina.sh","run"]
# 设置容器启动时执行的命令为 /usr/local/tomcat/bin/catalina.sh run,以启动 Tomcat 服务
ENTRYPOINT ["/usr/local/tomcat/bin/catalina.sh","run"]
CMD ["/usr/local/tomcat/bin/startup.sh","start"]
# 设置容器启动时默认执行的命令为 /usr/local/tomcat/bin/startup.sh start,以启动Tomcat服务

③ 生成镜像

[root@localhost tomcat]# docker build -t tomcat:centos .

④ 启动容器并指定宿主机端口映射 tomcat 8080 端口

[root@localhost tomcat]# docker run -d --name tomcat1 -p 10001:8080 tomcat:centos 
82e5b4e98b92cea750c17b0082169893e26786ab82ecc68022fc04ff0bd25ec8
[root@localhost tomcat]# docker ps
CONTAINER ID   IMAGE            COMMAND                   CREATED             STATUS             PORTS                                                                                NAMES
82e5b4e98b92   tomcat:centos    "/usr/local/tomcat/b…"   13 seconds ago      Up 12 seconds      0.0.0.0:10001->8080/tcp, :::10001->8080/tcp                                          tomcat1
7b0478a6dacd   nginx:centos     "/nginx.sh"               18 minutes ago      Up 18 minutes      0.0.0.0:32771->80/tcp, :::32771->80/tcp, 0.0.0.0:32770->443/tcp, :::32770->443/tcp   amazing_moser
4d6d63ecb829   systemd:centos   "/sbin/init"              45 minutes ago      Up 45 minutes      0.0.0.0:32769->22/tcp, :::32769->22/tcp                                              festive_hellman
52be8693ca2e   sshd:centos      "/usr/sbin/sshd -D"       About an hour ago   Up About an hour   0.0.0.0:32768->22/tcp, :::32768->22/tcp                                              tender_kepler
32fcdbb50bb3   httpd:centos     "/apache.sh"              2 hours ago         Up 2 hours         0.0.0.0:10000->80/tcp, :::10000->80/tcp                                              keen_joliot

⑤ 访问页面

7. 制作 mysql 镜像

① 建立工作目录

[root@localhost tomcat]# mkdir /opt/mysqld
[root@localhost tomcat]# cd /opt/mysqld
[root@localhost mysqld]# ls
mysql-boost-5.7.20.tar.gz

② 创建 mysql Dockerfile 文件

[root@localhost mysqld]# vim Dockerfile
FROM centos:7
MAINTAINER mysql image <fql>
RUN yum -y install ncurses ncurses-devel bison cmake pcre-devel zlib-devel gcc gcc-c++ make;useradd -M -s /sbin/nologin mysql
ADD mysql-boost-5.7.20.tar.gz /usr/local/src/   # 添加并解压MySQL源码文件至/usr/local/src/目录
WORKDIR /usr/local/src/mysql-5.7.20/
RUN cmake \
-DCMAKE_INSTALL_PREFIX=/usr/local/mysql \
-DMYSQL_UNIX_ADDR=/usr/local/mysql/mysql.sock \
-DSYSCONFDIR=/etc \
-DSYSTEMD_PID_DIR=/usr/local/mysql \
-DDEFAULT_CHARSET=utf8  \
-DDEFAULT_COLLATION=utf8_general_ci \
-DWITH_EXTRA_CHARSETS=all \
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
-DWITH_ARCHIVE_STORAGE_ENGINE=1 \
-DWITH_BLACKHOLE_STORAGE_ENGINE=1 \
-DWITH_PERFSCHEMA_STORAGE_ENGINE=1 \
-DMYSQL_DATADIR=/usr/local/mysql/data \
-DWITH_BOOST=boost \
-DWITH_SYSTEMD=1;make -j 2;make install
ADD my.cnf /etc/my.cnf           # 添加MySQL的配置文件my.cnf到镜像/etc/目录
EXPOSE 3306                      # 暴露MySQL默认端口3306
RUN chown -R mysql:mysql /usr/local/mysql/;chown mysql:mysql /etc/my.cnf
WORKDIR /usr/local/mysql/bin/    # 进入/usr/local/mysql/bin/目录并执行MySQL初始化命令
RUN ./mysqld \
--initialize-insecure \
--user=mysql \
--basedir=/usr/local/mysql \
--datadir=/usr/local/mysql/data;cp /usr/local/mysql/usr/lib/systemd/system/mysqld.service /usr/lib/systemd/system/;systemctl enable mysqld
ENV PATH=/usr/local/mysql/bin:/usr/local/mysql/lib:$PATH 
# 设置环境变量PATH,包括MySQL的bin和lib目录
VOLUME [ "/usr/local/mysql" ]    # 定义数据卷/usr/local/mysql用于持久化存储MySQL数据
CMD ["/usr/sbin/init"]
#ADD mysql.sh /mysql.sh
#RUN chmod 755 /mysql.sh
#CMD ["/mysql.sh"]

③ 编辑核心配置文件

[root@localhost mysqld]# vim my.cnf
[client]
port = 3306
default-character-set=utf8
socket = /usr/local/mysql/mysql.sock[mysql]
port = 3306
default-character-set=utf8
socket = /usr/local/mysql/mysql.sock[mysqld]
user = mysql
basedir = /usr/local/mysql
datadir = /usr/local/mysql/data
port = 3306
character_set_server=utf8
pid-file = /usr/local/mysql/mysqld.pid
socket = /usr/local/mysql/mysql.sock
server-id = 1sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_AUTO_VALUE_ON_ZERO,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,PIPES_AS_CONCAT,ANSI_QUOTES

④ 可选创建启动容器时执行的脚本

[root@localhost mysqld]# vim mysql.sh
#!/bin/bash
/usr/local/mysql/bin/mysqld	
systemctl enable mysqld

⑤ 生成镜像

[root@localhost mysqld]# docker build -t mysql:centos .

⑥ 启动容器,并进行初始化

[root@localhost mysqld]# docker run --name mysql_server -d -P --privileged mysql:centos /usr/sbin/init
f07f84192afd1c75c8eaaf478c8fea562be0a7428dc800fd0045f557d80acce9
# --privileged:使container内的root拥有真正的root权限。否则,container内的root只是外部的一个普通用户权限
[root@localhost mysqld]# docker ps
CONTAINER ID   IMAGE            COMMAND                   CREATED          STATUS         PORTS                                                                                NAMES
f07f84192afd   mysql:centos     "/usr/sbin/init"          25 seconds ago   Up 4 seconds   0.0.0.0:32772->3306/tcp, :::32772->3306/tcp                                          mysql_server
82e5b4e98b92   tomcat:centos    "/usr/local/tomcat/b…"   2 hours ago      Up 2 hours     0.0.0.0:10001->8080/tcp, :::10001->8080/tcp                                          tomcat1
7b0478a6dacd   nginx:centos     "/nginx.sh"               2 hours ago      Up 2 hours     0.0.0.0:32771->80/tcp, :::32771->80/tcp, 0.0.0.0:32770->443/tcp, :::32770->443/tcp   amazing_moser
4d6d63ecb829   systemd:centos   "/sbin/init"              3 hours ago      Up 3 hours     0.0.0.0:32769->22/tcp, :::32769->22/tcp                                              festive_hellman
52be8693ca2e   sshd:centos      "/usr/sbin/sshd -D"       3 hours ago      Up 3 hours     0.0.0.0:32768->22/tcp, :::32768->22/tcp                                              tender_kepler
32fcdbb50bb3   httpd:centos     "/apache.sh"              4 hours ago      Up 4 hours     0.0.0.0:10000->80/tcp, :::10000->80/tcp                                              keen_joliot

⑦ 进入容器,授权远程连接 mysql

[root@localhost mysqld]# docker exec -it f07f84192afd /bin/bash
# 可以选择修改密码:
# 给root账号设置密码
# mysqladmin -u root -p password "123456"  直接回车
# 登录 mysql -u root -p123456
[root@f07f84192afd bin]# mysql -u root -p  # 无初始密码直接回车
mysql> grant all privileges on *.* to 'root'@'%' identified by 'abc123';
# 授予了一个名为'root'的用户在任何主机上('%'表示所有主机)对所有数据库的所有表拥有全部权限,并设置了密码为'abc123'
mysql> grant all privileges on *.* to 'root'@'localhost' identified by 'abc123';
# 仅授权了在本地主机(即指定为 'localhost')上的'root'用户
mysql> flush privileges;
# 刷新MySQL的权限,使新授权的权限立即生效

⑧ 在客户端连接 mysql 容器

[root@master01 ~]# mysql -h 192.168.190.107 -uroot -P32772 -p'abc123'
mysql> 

四、总结

1. 数据管理

(1)数据卷创建

docker run -v 宿主机绝对路径目录(不存在直接创建):容器数据集目录 --name 容器名 -it 镜像:标签 /bin/bash

(2)数据卷容器创建

首先创建数据卷:

docker run --name 容器a -v  /容器挂载点1 -v /容器挂载点2 -it 镜像:标签 /bin/bash

挂载容器a中的数据卷:

docerk run -it --volume-from 容器a --name 容器b 镜像:标签 /bin/bash

(3)容器互联,文件共享,传输

容器a:

docker run -itd -P --name 容器a 镜像:标签 /bin/bash

容器b:

docker run -itd -P --name 容器b --link 容器a:容器a的别名 镜像:标签 /bin/bash

进入容器b:

docker exec -it 容器b的id/容器b(名字) /bin/bash

2. Dockerfile 构建

(1)基于现有镜像创建

docker run 创建并启动容器;再通过 docker exec/cp 等容器操作指令修改容器内容;然后 docker commit 提交成新的镜像。

(2)基于本地模版创建

从网上下载现有的镜像模版 ,或使用 docker export 导出本地容器的快照模版,然后 docker import - 将快照模版导入本地镜像。

(3)基于 dockerfile 创建镜像

dockerfile 构建镜像的步骤:

  • 先用 FROM 指令指定基础镜像
  • 再用 MAINTAINER 指定维护人信息
  • 然后再用 RUN、EXPOSE、ADD、ENV、USER、WORKDIR 等指令编写镜像的过程
  • 最后使用 CMD 或 ENTPYONT 指令指定启动容器时执行的命令

ENTRYPOINT 与 CMD 的区别:容器启动时执行命令的优先级

① docker run --entrypoint=命令 镜像 选项 参数

  • 基于指定镜像运行的容器中,覆盖默认的 ENTRYPOINT,使用指定的命令来启动容器

② ENTRYPOINT ["要运行的程序", "参数 1", "参数 2"]

③ docker run 镜像 命令 选项 参数 

  • 基于指定镜像运行的容器中,使用指定的命令来启动容器,并且可以附加各种选项和参数

④ CMD ["命令","选项","参数"]

  • 如果在同一个 Dockerfile 文件中同时存在 ENTRYPOINT 和 CMD 时,ENTRYPOINT 会覆盖 CMD 运行命令,CMD 为 ENTRYPOINT 提供选项和参数

ADD 和 COPY 区别:

  • 都可以复制本地文件/目录到镜像中
  • ADD 可以通过 URL 路径下的文件并复制到镜像,还可以把本地的 tar 压缩包进行解压后复制到镜像
  • COPY 支持配合 --from 选项实现多个阶段构建

如何缩小 Dockerfile 构建的镜像体积大小? 

  • 尽可能减少指令的数量,比如把RUN的linux命令进行合并
  • 尽可能得使用最简洁的基础镜像
  • 使用多阶段(多级)构建

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/314953.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java 解决pdfbox转图片显示中文乱码 No glyph for 165 (CID 5752) in font STSong-Light

问题描述&#xff1a; 在开发PDF转JPG图片后&#xff0c;图片显示中文乱码&#xff0c;以下为具体示例&#xff1a; pdf源文件&#xff1a; 转换成JPG后&#xff1a; 截图可以看到&#xffe5;符号无法转换为乱码 关键代码报错截图&#xff1a; 代码已经提示我们STSong-Light字…

IDEA 编码规约扫描 Code inspection did not find anything to report.

IDEA安装了Alibaba Java Coding Guidelines插件&#xff0c;却看不到规约检查结果。手动进行编码规约扫描&#xff0c;弹窗提示“Code inspection did not find anything to report.”&#xff1a; 这种情况是因为代码文件所在的目录被标记成了测试文件&#xff08;Test Source…

[leetcode] B树是不是A树的子结构

给定两棵二叉树 tree1 和 tree2&#xff0c;判断 tree2 是否以 tree1 的某个节点为根的子树具有 相同的结构和节点值 。 注意&#xff0c;空树 不会是以 tree1 的某个节点为根的子树具有 相同的结构和节点值 。 示例 1&#xff1a; 输入&#xff1a;tree1 [1,7,5], tree2 [6,…

ansible-copy用法

目录 概述实践不带目录拷贝带目录拷贝 概述 ansible copy 常用用法举例 不带目录拷贝&#xff0c;拷贝的地址要写全 带目录拷贝&#xff0c;拷贝路径不要写在 dest 路径中 实践 不带目录拷贝 # with_fileglob 是 Ansible 中的一个循环关键字&#xff0c;用于处理文件通配符匹…

docker 基本命令

目录 一、docker 镜像操作命令 1.1.查询软件镜像 1.2.docker pull&#xff1a;下载镜像 1.3.docker push&#xff1a;上传镜像 1.4.docker images&#xff1a;查看本地镜像 1.5.docker inspect &#xff1a;获取镜像详细信息 1.6.docker tag&#xff1a;添加镜像标签 …

【vscode】2024最新!vscode云端配置同步方案:code settings sync

小tian最近对电脑进行了系统重装&#xff0c;结果vscode相关配置和插件都没有保存记录&#xff0c;还好公司电脑里还有。痛定思痛&#xff0c;决定写一篇vscode云端同步配置方案&#xff0c;以作记录和分享~ 步骤一&#xff1a;安装vscode插件&#xff1a;code settings sync …

基于SpringBoot + Vue实现的家政服务管理系统设计与实现+毕业论文+答辩PPT+指导搭建视频(包运行成功)

目录 项目介绍 论文展示 资源获取 项目介绍 家政服务管理平台是一个管理信息系统&#xff0c;为了宣传的需要&#xff0c;为了给用户提供方便快捷的服务&#xff0c;从而设计了家政服务管理平台。管理员可以通过这个系统把家政服务信息发布出去&#xff0c;可以方便用户快…

Mac下使用homebrew管理多版本mysql同时启动

Mac下使用homebrew管理多版本mysql同时启动 思路 给每个版本分配不同的数据目录和配置文件即可 本文尝试了使用 brew 安装管理多个MySQL版本&#xff0c;同时运行、直接切换 安装 如果已有数据文件请自行备份以及使用 安装 mysql 5.7 brew install mysql5.7在 /opt/home…

微信小程序使用echarts实现条形统计图功能

微信小程序使用echarts组件实现条形统计图功能 使用echarts实现在微信小程序中统计图的功能&#xff0c;其实很简单&#xff0c;只需要简单的两步就可以实现啦&#xff0c;具体思路如下&#xff1a; 引入echarts组件调用相应的函数方法 由于需要引入echarts组件&#xff0c;代…

Vue2学习笔记(尚硅谷天禹老师)

目录 一、入门案例 二、模板语法 三、数据绑定 四、el和data的两种写法 五、MVVM模型 六、Object.defineproperty方法 七、Vue中响应式原理 八、数据代理 九、methods配置项 十、Vue中的事件处理 十一、Vue中的键盘事件 十二、计算属性 十三、监视属性watch 十四、绑定Class样式…

深入理解操作系统与计算机体系结构

文章目录 操作系统(Operator System)为什么要有操作系统操作系统是如何进行管理的为什么说操作系统是安全&#xff0c;稳定&#xff0c;高效的理解系统调用和库函数 操作系统(Operator System) 概念&#xff1a; 操作系统&#xff08;Operating System&#xff0c;简称OS&…

会跳舞的网站引导页HTML源码

源码介绍 这套引导页源码非常好看&#xff0c;网址也不会不停的动起来给人一种视觉感很强烈 简单修改一下里面的地址就行看&#xff0c;非常简单&#xff01; 效果预览 源码下载 会跳舞的网站引导页HTML源码

数据结构——插入排序

基本思想&#xff1a; 直接插入排序是一种简单的插入排序法&#xff0c;其基本思想是&#xff1a;把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为止&#xff0c;得到一个新的有序序列 。 实际中我们玩扑克牌时&…

视频通话实时换脸:支持训练面部模型 | 开源日报 No.235

iperov/DeepFaceLive Stars: 19.7k License: GPL-3.0 DeepFaceLive 是一个用于 PC 实时流媒体或视频通话的人脸换装工具。 可以使用训练好的人脸模型从网络摄像头或视频中交换面部。提供多个公共面部模型&#xff0c;包括 Keanu Reeves、Mr. Bean 等。支持自己训练面部模型以…

图像处理到神经网络:线性代数的跨领域应用探索

作者介绍&#xff1a;10年大厂数据\经营分析经验&#xff0c;现任大厂数据部门负责人。 会一些的技术&#xff1a;数据分析、算法、SQL、大数据相关、python 欢迎加入社区&#xff1a;码上找工作 作者专栏每日更新&#xff1a; LeetCode解锁1000题: 打怪升级之旅 python数据分析…

Maven解决找不到依赖项

报错如图 方案一&#xff1a;Maven的Setting文件中添加albaba的镜像文件 1.下载maven &#xff1a;Maven – Download Apache Maven 2. 配置镜像 更改成这个&#xff1a; <mirror> <id>alimaven</id> <name>aliyun maven</name> <url&g…

Rust腐蚀服务器搭建架设教程ubuntu系统

Rust腐蚀服务器搭建架设教程ubuntu系统 大家好我是艾西一个做服务器租用的网络架构师。Rust腐蚀游戏对于服务器的配置有一定的要求很多小伙伴就思考用linux系统搭建的话占用会不会小一点&#xff0c;有一定电脑基础的小伙伴都知道Linux系统和windows系统相比较linux因为是面板…

短视频生成背景文字工具(前端工具)

过年这两天有些无聊就刷刷抖音&#xff0c;刷着刷着自己也蠢蠢欲动&#xff0c;想发上几个&#xff0c;可是却找不到合适自己的模板。由于个人喜欢一些古诗文之类的&#xff0c;所以自己简单的编写了一个小工具&#xff0c;如下图&#xff1a; 当设置好了之后&#xff0c;将浏…

小程序线多点路图绘制

需求 当接口返回一连串地图坐标&#xff0c;需要根据这些坐标串联起来&#xff0c;形成一个线路图&#xff08;本次使用步行导航线路图&#xff09;。 思路 首先优先想到使用小程序Map组件的polyline属性去进行展示。但是我们发现直接使用该属性进行坐标绘制画出来的数据都是…

《架构风清扬-Java面试系列第25讲》聊聊ArrayBlockingQueue的特点及使用场景

ArrayBlockingQueue是BlockingQueue接口的一个实现类之一 这个属于基础性问题&#xff0c;老规矩&#xff0c;我们将从使用场景和代码示例来进行讲解 来&#xff0c;思考片刻&#xff0c;给出你的答案 1&#xff0c;使用场景 实现&#xff1a;基于数组实现的有界阻塞队列&…