> 技术文档 > Docker--Docker 镜像制作_制作镜像工具

Docker--Docker 镜像制作_制作镜像工具


镜像制作的原因

镜像制作是因为官方镜像无法满足自身需求,从而需要自己制作,我们需要通过条件来进行满足需求

在软件开发过程中,开发环境和生产环境的差异可能导致“在我的机器上可以运行”的问题。Docker镜像将应用程序及其依赖打包在一起,确保在任何环境中都能以相同的方式运行

不同操作系统、不同版本的软件库可能会导致应用程序运行异常。通过Docker镜像,可以将应用程序运行在完全一致的环境中,减少因环境差异带来的问题。

Docker镜像包含了运行应用程序所需的一切,只需将镜像拉取到目标机器并运行容器即可。这大大简化了部署过程,尤其是对于微服务架构的应用程序,每个服务都可以打包为一个镜像,快速启动和扩展。

无论是开发人员、测试人员还是运维人员,都可以使用相同的镜像进行操作,减少了沟通成本和部署错误。

Docker容器通过命名空间和资源限制等技术,为应用程序提供隔离的运行环境。每个容器都有自己独立的文件系统、网络接口和进程空间,互不干扰。

Docker镜像制作的方式

  • 制作快照镜像:在基础镜像上,先登录容器,然后安装所需要的软件,最后整体制作快照;
  • Dockerfiles 制作镜像: 将软件安装的流程写成Dockerfile,使用docker build进行构建容器镜像。

快照方式制作镜像

利用 Docker commit 指令在镜像中创建一个新的镜像

下面演示下 C++ HelloWorld 镜像制作

C++ HelloWorld 镜像制作

先创建一个临时目录

mkdir commitimagecd commitimage

写一个demo的c文件

#include int main(){ printf(\"Test commit image\\n\"); return 0;}

启动一个centos7 的容器

docker run -it --name mycppcommit centos:7 bash

修改 yum 为国内源

vi /etc/yum.repos.d/CentOS-Base.repo

将里面内容替换为

[base]name=CentOS-$releasever - Base - mirrors.aliyun.combaseurl=http://mirrors.aliyun.com/centos/$releasever/os/$basearch/gpgcheck=1gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7[updates]name=CentOS-$releasever - Updates - mirrors.aliyun.combaseurl=http://mirrors.aliyun.com/centos/$releasever/updates/$basearch/gpgcheck=1gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7[extras]name=CentOS-$releasever - Extras - mirrors.aliyun.combaseurl=http://mirrors.aliyun.com/centos/$releasever/extras/$basearch/gpgcheck=1gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7[centosplus]name=CentOS-$releasever - Plus - mirrors.aliyun.combaseurl=http://mirrors.aliyun.com/centos/$releasever/centosplus/$basearch/gpgcheck=1enabled=0gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7

清理缓存并更新

sudo yum clean allsudo yum makecachesudo yum update

安装编译软件,并创建源代码目录

yum install -y gccmkdir /src/

将源文件复制过去

docker cp ./demo.c mycppcommit:/src

编译可执行程序

cd /src/gcc -o demo demo.c

Docker--Docker 镜像制作_制作镜像工具

提交一个镜像

docker commit mycppcommit mycppimg:1.0

Docker--Docker 镜像制作_制作镜像工具
测试镜像是否正常运行

docker run -it mycppimg:1.0 ./src/demo

Docker--Docker 镜像制作_制作镜像工具

Dockerfiles制作镜像

Dockerfile是什么?

Dockerfile 是 Docker 镜像生成的核心配置文件。它包含了用户的所有容器配置信息,通过这些指令,用户可以基于基础镜像自定义生成新的镜像。Docker 根据构建镜像的上下文(包括 Dockerfile 和所引用的文件)来构建镜像。

Dockerfile的格式

Dockerfile 是一个文本文件,用于定义构建 Docker 镜像的指令。以下是 Dockerfile 的格式和一些关键指令:

# 注释FROM 基础镜像MAINTAINER 维护者信息RUN 安装命令COPY 文件复制命令WORKDIR 工作目录EXPOSE 端口CMD 容器启动命令

对于指令来说,不区分大小写,当然默认为大写,以便与参数进行区分;

为什么需要Dockerfile?

  • 环境一致性:Docker镜像的核心价值在于其封装性和可移植性,而Dockerfile则是确保这种一致性的关键。通过Dockerfile,我们可以确保不同环境中构建出的镜像是完全一致的。例如,无论是在开发者的本地机器上,还是在测试环境或生产环境中,只要基于同一个Dockerfile构建镜像,就能保证这个镜像包含的应用程序及其依赖关系是一样的。
  • 方便的自动化构建,可重复执行:Dockerfile 是一个文本文档,包含一系列指令和参数,用于定义Docker镜像的构建步骤。 它是一个脚本,告诉 Docker如何构建镜像。具体来说,它是构建镜像的唯一蓝图。
  • 可复用性 :通过Dockerfile构建的镜像具有高度的可复用性。一旦构建了一个镜像,它可以在任何支持Docker的环境中运行,不需要重新安装和配置环境。例如,一个镜像可以被部署到开发环境、测试环境、生产环境,甚至可以被推送到Docker Hub等镜像仓库,供其他人下载和使用。

Dockerfile 指令

Dockerfile官方地址

指令 说明 示例 FROM 指定基础镜像 FROM ubuntu:
FROM node:12-alpine MAINTAINER 维护者信息 MAINTAINER JohnDoe johndoe@example.com RUN 执行命令 RUN apt-get update && apt-get install -y curl COPY 将文件从宿主机复制到镜像 COPY index.html /var/www/html/ ADD 类似于 COPY,支持从 URL 下载文件 ADD https://example.com/file.zip /path/ WORKDIR 设置工作目录 WORKDIR /app ENV 设置环境变量 ENV PORT 3000 EXPOSE 声明容器运行时监听的端口 EXPOSE 80 443 CMD 容器启动时执行的默认命令 CMD [“node”, “app.js”] ENTRYPOINT 定义容器启动时执行的命令或脚本 ENTRYPOINT [“sh”, “entrypoint.sh”] VOLUME 创建挂载点 VOLUME [“/data”] ARG 定义构建时变量 ARG buildno=1 LABEL 添加元数据 LABEL version=“1.0” description=“My Docker Image”

实战演示各个Dockerfile的指令

FROM

指定源镜像来源
首先我们要在指定的文件在创建一个名为Dockerfile的文件,然后进行编辑:

FROM ubuntu:22.04 AS buildbase

Docker--Docker 镜像制作_制作镜像工具
执行构建,打造镜像0.1版本

docker buildx build -t web1:v0.1

运行制作的镜像,可以看到操作系统版本

docker run --name web1 --rm -it web1:v0.1 cat /etc/*release*

Docker--Docker 镜像制作_制作镜像工具

MAINTAINR

提供制造镜像者的详细信息(已弃用)

在Dokerfile文件中增加指令

MAINTAINER \"ahri ahri@123.com\"

Docker--Docker 镜像制作_制作镜像工具
编辑创建web0.2版本:

docker build -t web1:0.2 .

查看镜像信息,可以看到作者信息已经添加完成

docker image inspect web1:0.2

Docker--Docker 镜像制作_制作镜像工具

LABEL

为镜像添加元数据

LABEL test=\"dockerfile edit\" app=\"nginx\"

创建web1 的 0.3版本:

docker build -t web1:0.3 .

查看镜像元数据

Docker--Docker 镜像制作_制作镜像工具

COPY

== 从docker主机复制文件或目录到新创建的镜像中==

创建一个index.html文件,并编辑文件内容:

touch ./html/index.html
<html><h1>TEST:Dockerfile : CPoY </h1></html>

在我们的dockerfile文件中加入COPY:

COPY ./html/index.html /data/web/www/

再次编译 v0.4 版本镜像

docker build -t web1:0.4 .

运行容器并查看对应目录下的内容:

docker run -it --rm --name web1 web1:0.4 ls /data/web/www/

Docker--Docker 镜像制作_制作镜像工具

ENV

== 设置镜像的环境变量==

ENV <key>=<value> [<key>=<value>...]

实战:继续编辑dockerfile
Docker--Docker 镜像制作_制作镜像工具

创建web镜像0.5版本

docker build -t web1:0.5 .

运行容器并查看指定目录下的内容

docker run -it --name web1 --rm web1:0.5 ls /data/web/www

Docker--Docker 镜像制作_制作镜像工具
也可以查看镜像的详细内容

docker image inspect web1:0.5

Docker--Docker 镜像制作_制作镜像工具

WORKDIR

设置工作目录

WORKDIR /path/to/workdir

实战:后面我们要下载 nginx,我们指定一个工作目录,通过 WORKDIR 来指定

WORKDIR /usr/local

创建web的0.6版本

docker build -t web1:0.6 .

执行 pwd 命令查看当前目录

docker run -it --rm --name web1 web1:0.6 pwd /usr/local

ADD

== 添加本地的文件或目录到新创建的镜像中==

ADD [OPTIONS] …
ADD [OPTIONS] [“”, … “”]

实战
我们先访问iginx的官方下载网址
nginx官方链接地址

找到最新最稳定的版本
nginx下载地址
Docker--Docker 镜像制作_制作镜像工具
复制对应的下载链接
https://nginx.org/download/nginx-1.26.2.tar.gz

编辑dockerfile文件
Docker--Docker 镜像制作_制作镜像工具

ADD https://nginx.org/download/${NGINX_VERSION}.tar.gz ./src

由于未来版本会随之变化,所以这里将版本号设置为环境变量

创建web镜像0.7版本

docker build -t web1:0.7 .

运行容器并查看对应目录下的内容

docker run -it --name web1 --rm web1:0.7 ls -l /usr/local/src/

Docker--Docker 镜像制作_制作镜像工具
可以看到此时并没有被解压
我们将压缩包下载到主机目录中

wget https://nginx.org/download/nginx-1.26.2.tar.gz

Docker--Docker 镜像制作_制作镜像工具
再次编辑dockerfile,将主机目录的压缩包添加到创建镜像中
Docker--Docker 镜像制作_制作镜像工具
创建web镜像0.8版本

docker build -t web1:0.8 .

运行容器并查看对应目录下的内容

docker run -it --name web1 --rm web1:0.8 ls -l /usr/local/src2/

可以看到压缩包已经被解压
Docker--Docker 镜像制作_制作镜像工具

RUN

== 用于指定docker build 过程中的运行程序,可以是任何命令==

# Shell form:RUN [OPTIONS] <command> ...# Exec form:RUN [OPTIONS] [ \"\", ... ]

实战
因为我们 nginx 是源码所以我们需要先解压 src 文件,通过 RUN 命令可以完成 nginx 的解压;

编辑dockerfile文件

RUN cd ./src && tar zxvf ${NGINX_VERSION}.tar.gz

创建web镜像0.9版本

docker build -t web1:0.9 .

运行容器并查看对应目录下的内容

docker run -it --name web1 --rm web1:0.9 ls -l /usr/local/src/

Docker--Docker 镜像制作_制作镜像工具
因为是源码安装所以我们要编译安装 nginx,需要下载编译工具,已经依赖库信息,
并通过 make 来完成编译

#1.安装 build-essential 构建工具#2.安装依赖包 libpcre3 libpcre3-dev zlib1g-dev 依赖库RUN apt-get update -y && apt install -y build-essential libpcre3 libpcre3-dev zlib1g-dev #3.进入 nginx 目录#4.执行编译和构建RUN cd ./src/${NGINX_VERSION} \\ && ./configure --prefix=/usr/local/nginx \\ && make && make install

创建web1镜像1.0版本并运行容器进行查看

docker build -t web1:1.0 .docker run -it --name web1 --rm web1:1.0 /usr/local/nginx/sbin/nginx -V

Docker--Docker 镜像制作_制作镜像工具
nginx 的默认配置文件为/usr/local/nginx/conf/nginx.conf,我们修改 server 部分,配置一个我们自己的配置文件,然后覆盖它

#user nobody;worker_processes 1;#error_log logs/error.log;#error_log logs/error.log notice;#error_log logs/error.log info;#pid logs/nginx.pid;events { worker_connections 1024;}http { include mime.types; default_type application/octet-stream; #log_format main \'$remote_addr - $remote_user [$time_local] \"$request\" \' # \'$status $body_bytes_sent \"$http_referer\" \' # \'\"$http_user_agent\" \"$http_x_forwarded_for\"\'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root /data/web/www/; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \\.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \\.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache\'s document root # concurs with nginx\'s one # #location ~ /\\.ht { # deny all; #} }}

Docker--Docker 镜像制作_制作镜像工具

修改下dockerfile文件内容顺序,并将配置文件复制进去

ARG UBUNTU_VERSION=22.04FROM ubuntu:${UBUNTU_VERSION} AS buildbaseRUN mkdir -p /dataVOLUME [\"/data\"]RUN echo \"TEST Dockerfile volume\" > /data/myvolume.txtMAINTAINER \"ahri ahri@123.com\"LABEL test=\"dockerfile edit\" app=\"nginx\"ENV WEB_ROOT=/data/web/www/ENV NGINX_VERSION=\"nginx-1.26.2\"#1.安装 build-essential 构建工具#2.安装依赖包 libpcre3 libpcre3-dev zlib1g-dev 依赖库RUN apt-get update -y && apt install -y build-essential libpcre3 libpcre3-dev zlib1g-dev COPY ./html/index.html ${WEB_ROOT}WORKDIR /usr/localADD https://nginx.org/download/${NGINX_VERSION}.tar.gz ./srcADD ${NGINX_VERSION}.tar.gz ./src2RUN cd ./src && tar zxvf ${NGINX_VERSION}.tar.gz#3.进入 nginx 目录#4.执行编译和构建RUN cd ./src/${NGINX_VERSION} \\ && ./configure --prefix=/usr/local/nginx \\ && make && make installCOPY nginx.conf ./nginx/conf

创建web1镜像1.1版本并运行容器进行查看

docker build -t web1:1.1 .
docker run -it --name web1 --rm web1:1.1 /usr/local/nginx/sbin/nginx -Vnginx version: nginx/1.26.2built by gcc 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04) configure arguments: --prefix=/usr/local/nginx

CMD

== 设置在运行容器时要执行的程序命令==

RUN 指令运行于映像文件构建过程中,而 CMD 指令运行于基于 Dockerfile构建出的新映像文件启动一个容器时

编辑dockerfile文件:让nginx进入前台运行

CMD [\"/usr/local/nginx/sbin/nginx\",\"-g daemon off;\"]

在对应目录文件中,修改配置参数 -g用于设置全局指令;
daemon off能让程序在前台运行

创建web1镜像1.2版本并运行容器进行查看

docker build -t web1:1.2 .
docker run --name web1 --rm -d web1:1.2docker ps

Docker--Docker 镜像制作_制作镜像工具

EXPOSE

== 描述你要监听的端口==

Docker--Docker 镜像制作_制作镜像工具
创建web1镜像1.3版本并运行容器进行查看

docker build -t web1:1.3 .

我们尝试用浏览器来进行访问,但是行不通,因为expose只是来声明一个端口,不起作用

docker run -p 8080:80 --name web1 -d web1:1.3

需要通过端口映射
Docker--Docker 镜像制作_制作镜像工具

ENTRYPOINT

== 指定默认可执行文件==

将dockerfile中将CMD改为ENTRYPOINT
Docker--Docker 镜像制作_制作镜像工具
创建web1镜像1.4版本并运行容器进行查看

docker stop web1docker run -d -p 8080:80 --name web1 web1:1.4

通过浏览器访问可以正常访问

ARG

== 使用时构建变量==

我们通过命令build时,可以指定变量
Docker--Docker 镜像制作_制作镜像工具

创建web1镜像1.5版本,并通过变量指定对应版本

docker build --build-arg UBUNTU_VERSION=22.10 -t web1:1.5 .
docker stop web1docker run --name web1 --rm -p 80:80 -d web1:v1.5

Docker--Docker 镜像制作_制作镜像工具

VOLUME

== 挂载一个存储卷==

通过 VOLUME 指令创建的挂载点,无法指定主机上对应的目录,是自动生成的。

Docker--Docker 镜像制作_制作镜像工具
创建myvolume镜像0.1版本

docker build -t myvolume:0.1 .

查看容器的详细情况

docker container inspect myvolume

Docker--Docker 镜像制作_制作镜像工具
发现卷会存储在docker设置的存储卷的位置,再通过随机名字来生成
进入到对应存储卷的目录,查看index.html的内容

cd /data/var/lib/docker/volumes/44615e3aa82baab9d2e499ff2a135cfab2516fc960849b332bc7b1ab1d128918/_data

查看目录内容
Docker--Docker 镜像制作_制作镜像工具

cat myvolume.txtTEST Dockerfile volume

停止容器运行并删除

docker stop myvolumedocker rm myvolume

卷的内容依然存在
Docker--Docker 镜像制作_制作镜像工具

SHELL

设置镜像的默认shell

创建一个新的目录和新的Dockerfile
编辑Dockerfile

FROM ubuntu:22.04RUN ls -l / > /test1.txtSHELL [\"/bin/bash\",\"-cvx\"]RUN ls -l / > /test2.txt

创建一个镜像shell

docker build -t shell:0.1 --no-cache --progress=plain .

–no-cache:这个参数告诉 Docker 在构建镜像时不要使用缓存。
–progress=plain:这个参数设置了构建过程的输出格式。plain 格式提供了更简单的输出,没有额外的进度条或颜色编码,这有助于在某些不支持这些特性的终端或日志记录系统中更好地阅读输出。

#5 [2/3] RUN ls -l / > /test1.txt#5 DONE 0.3s#6 [3/3] RUN ls -l / > /test2.txt#6 0.294 ls -l / > /test2.txt#6 0.294 + ls -l /#6 DONE 0.3s

可以看到执行运行语句时多了两条语句

USER

设置用户和组ID

创建新目录和dockerfile
Docker--Docker 镜像制作_制作镜像工具
编辑Dockerfile

FROM ubuntu:22.04 AS buildbaseRUN groupadd nginxRUN useradd nginx -g nginxUSER nginx:nginxRUN whoami > /tmp/user1.txtUSER root:rootRUN groupadd mysqlRUN useradd mysql -g mysqlUSER mysql:mysqlRUN whoami > /tmp/user2.txt

创建user镜像

docker build -t user:0.1 .

运行容器

docker run --name user1 --rm -it user:0.1 cat /tmp/user1.txt /tmp/user2.txt

Docker--Docker 镜像制作_制作镜像工具

HEALTHCHECK

== 在启动时检查容器的运行状况==

创建目录

mkdir -p /data/myworkdir/dockerfile/healthcheck

编辑dockerfile

FROM nginx:1.23.4#更换国内镜像源RUN sed -i \'s/deb.debian.org/mirrors.ustc.edu.cn/g\' /etc/apt/sources.listRUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*HEALTHCHECK --interval=5s --timeout=3s \\ CMD curl -fs http://localhost/ || exit 1

创建镜像,并运行容器

docker build -t healthcheck:0.1 .docker run --name hc1 --rm -d healthcheck:0.1

通过命令docker ps 查到nginx容器默认端口是80
查看容器的详情

docker inspect hc1

Docker--Docker 镜像制作_制作镜像工具
在dockerfile中调整

HEALTHCHECK --interval=5s --timeout=3s \\ CMD curl -fs http://localhost:10080/ || exit 1

再次构造镜像

docker build -t healthcheck:0.2 .docker run --name hc2 -d --rm healthcheck:0.2

通过docker ps 查看得到状态
Docker--Docker 镜像制作_制作镜像工具
查看容器详情

docker inspect hc2

Docker--Docker 镜像制作_制作镜像工具

ONBUILD

== 定义一个触发器==
创建目录和dockerfile文件
Docker--Docker 镜像制作_制作镜像工具
编辑Dockerfile1文件内容

FROM ubuntu:22.04LABEL version=\"0.1\"ONBUILD RUN echo \"in build\" >> /tmp/build.txt 

使用 build:v0.1 作为基础镜像,新建一个 Dockerfile2,

# Dockerfile2内容FROM build:0.1LABEL version=\"0.2\"
docker build -t build:0.2 -f Dockerfile2 .[2/1] RUN echo \"in build\" >> /tmp/build.txt

可以看见创建镜像版本0.2时,触发了build:0.1设置的触发器

STOPSIGNAL

指定退出容器的系统调用信号
创建目录和dockerfile文件
Docker--Docker 镜像制作_制作镜像工具
编辑Dockerfile文件内容

FROM nginx:1.23.4STOPSIGNAL 9ENTRYPOINT [\"nginx\",\"-g daemon off;\"]

执行镜像构建

docker build -t stopsignal:0.1 .

运行镜像

docker run --name stopsingal1 --rm -d stopsignal:0.1

通过 docker ps 查看
Docker--Docker 镜像制作_制作镜像工具
打开另外一个 shell 窗口 ,执行 docker logs -f 查看日志

docker logs -f stopsingal1

在原来的 shell 窗口中执行 docker stop,然后查看日志是突然退出

再来创建一个 Dockerfile2,这个里面我们不配置停止信号

FROM nginx:1.23.4ENTRYPOINT [\"nginx\",\"-g daemon off;\"]

构建然后运行我们的容器

docker build -t stopsignal:0.2 -f Dockerfile2 .docker run --name stopsingal2 --rm -d stopsignal:0.2

通过日志查看,然后再进行终止

docker logs -f stopsingal2# 另一个窗口执行docker stop stopsingal2

Docker--Docker 镜像制作_制作镜像工具
可以看到容器是有序的退出的,而不是强制性一步到位的退出