前置工作

创建实验专用目录

此次实验依旧分为三个章节,使用root用户进行操作避免多次输入密码,然后我们执行以下命令创建6个目录:

mkdir -p /root/docker_experiment/{lesson13,lesson14,lesson15,lesson16,lesson17,lesson18}

在之前的章节KVM虚拟化与Docker基础实践-第三章中我们使用https://docker.m.daocloud.io拉取了几个镜像,本次实验延续使用拉取的这几个镜像。我们先执行

docker images | grep -E "debian|nginx|mysql|tomcat"

验证已有的这几个镜像。确认无误后我们再开始后续的实验。

一、Dockerfile构建自定义镜像

掌握Dockerfile核心指令(FROM、RUN、EXPOSE、CMD、COPY、ADD)的作用,镜像构建流程与优化技巧

这里只介绍了部分命令。

FROM

作用:指定基础镜像        说明:必须作为首条非注释指令(即所有dockerfile文件必须以from开头)

RUN

作用:构建镜像时执行的命令        说明:每条RUN新建一层镜像层(用于安装依赖或执行其他操作)

EXPOSE

作用:声明容器监听端口        说明:仅为文档性声明,不实际发布端口(声明容器运行时暴露的端口)

CMD

作用:指定容器启动时的默认命令        说明:提供默认命令(参数),可被覆盖(CMD指令可以被docker run命令中的参数覆盖)

COPY

作用:复制文件或目录到镜像        说明:纯粹复制(将本地文件复制到容器中)

ADD

作用:复制文件或目录到镜像        说明:支持自动解压和URL下载(将本地文件复制到容器中)

以下是Dockerfile指令的执行流程(较为完整的流程,具体操作视情况而看)

FROM:指定基础镜像。

ARG:定义构建时的变量。

RUN:安装依赖或执行命令。

COPY/ADD:复制文件到容器中。

WORKDIR:设置工作目录。

ENV:设置环境变量。

EXPOSE:声明端口。

ENTRYPOINT/CMD:定义启动命令。

Dockerfile编写:基于debian:12镜像,编写Dockerfile,构建包含nginx服务的自定义镜像(my-nginx:v1)

我们先执行

cd ~/docker_experiment/lesson13

进入我们事先创建好的专属目录进行这一步的操作。

编写未优化的Dockerfile.v1

cat > Dockerfile.v1 <<'EOF'
FROM debian:12
RUN apt-get update
RUN apt-get install -y nginx
RUN apt-get clean
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
EOF

FROM debian:12指定基础镜像为Debian 12。

RUN apt-get update更新Debian的软件包索引,如果不执行可能找不到最新的nginx包或提示404。RUN apt-get install -y nginx安装nginx包,-y表示自动确认所有提示,避免交互式输入。

RUN apt-get clean清理安装包,减小镜像体积。

EXPOSE 80声明容器运行时监听的端口是80,这只是说明文档,不会真正打开端口,但配合-p映射时能提供提示。

CMD ["nginx", "-g", "daemon off;"]容器启动时默认执行的命令,前台运行nginx而daemon off;能让nginx不进入后台,如果Dockerfile中有多个CMD只有最后一个会生效。

镜像构建与验证:执行docker build命令构建镜像,查看镜像是否创建成功,启动容器验证nginx服务正常

构建并验证

docker build -f Dockerfile.v1 -t my-nginx:v1 .
docker images | grep my-nginx
docker run -d --name nginx-test -p 8080:80 my-nginx:v1
curl http://localhost:8080
docker stop nginx-test && docker rm nginx-test

docker build -f Dockerfile.v1 -t my-nginx:v1 .使用Dockerfile.v1构建镜像,标签为my-nginx:v1,构建上下文为当前目录(.)。

docker images | grep my-nginx列出所有镜像,过滤出包含my-nginx的行,可查看镜像大小和标签。

docker run -d --name nginx-test -p 8080:80 my-nginx:v1其中-d以后台模式运行my-nginx容器,-p将宿主机的8080端口映射到容器的80端口。

curl http://localhost:8080访问宿主机的8080端口,应该看到nginx默认欢迎页,证明服务正常。

docker stop nginx-test && docker rm nginx-test停止容器并删除容器(避免冲突)。

镜像优化:合并RUN指令、添加yum clean all清理缓存,构建优化后的镜像(my-nginx:v2),对比两个镜像的大小差异

编写优化后的Dockerfile.v2

cat > Dockerfile.v2 <<'EOF'
FROM debian:12
RUN apt-get update && \
    apt-get install -y nginx && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
EOF

合并RUN指令将apt-get update、install、clean、rm -rf放在一个RUN中,用&&连接,这样只会产生一层镜像层,有效减小镜像体积,其中rm -rf /var/lib/apt/lists/*删除apt更新的软件包列表缓存,这些文件在运行时会产生一些不需要的缓存,删除能进一步缩小镜像。

对比镜像大小

docker build -f Dockerfile.v2 -t my-nginx:v2 .
docker images | grep my-nginx

先执行docker build进行构建,然后再输出两个镜像,输出内容中就会看到大小。

我们可以观察到v2明显比v1小这是因为它将删除工作放在了同一层中,中间文件不会出现遗留问题,而v1产生的中间文件不会被删除,他只在最后一层镜像层执行了删除操作,其他层的会被保留。

二、Docker数据卷配置与数据持久化

掌握Docker数据卷的作用、原理,数据卷与宿主机目录绑定的区别,数据持久化的核心意义。

什么是数据卷

数据卷是Docker中的核心组件,是Docker提供的一种数据持久化方式,使容器间可以共享数据并能够长期保存,即便容器被删除,卷中的数据仍然存在。本质上它就是一个虚拟目录,指向宿主机文件系统中的某个文件,它们的修改是双向同步的,多个容器可以绑定到相同的数据卷目录上。

作用

数据的持久化储存,多容器间的数据共享,容器重启或迁移时数据的快速恢复。

原理

在容器创建的过程中,docker会将宿主机上的指定目录挂载到容器中指定的目录上。这里使用的挂载方式为绑定挂载(bind mount),所以挂载完成后的宿主机目录和容器的目标目录表现一致

区别

数据卷挂载由Docker管理,方便数据持久化,解耦性好;数据位置较深,路径在宿主机/var/lib/docker/volumes不易寻找。目录挂载需要手动管理宿主机目录,操作简单,方便查找,容器与宿主机之间的耦合度较高,迁移性差。

核心意义

数据卷中的数据是独立且持久的,即使容器内的文件看似被删除了,或者整个容器被删除,数据卷中的原始数据也安然无恙,可以被新的容器重新挂载和使用。

数据卷基础操作:创建数据卷(nginx-volume),查看数据卷详情,绑定数据卷启动nginx容器,验证宿主机与容器数据共享

还是一样的先执行

cd ~/docker_experiment/lesson14

进入专属目录进行这一步的操作

然后我们依次执行

docker volume create nginx-volume
docker volume inspect nginx-volume
docker run -d --name nginx-v1 -p 8081:80 -v nginx-volume:/usr/share/nginx/html nginx:latest
echo "Data volume test" > /var/lib/docker/volumes/nginx-volume/_data/index.html
curl http://localhost:8081

docker volume create nginx-volume创建一个名为nginx-volume的Docker数据卷。创建后Docker会在宿主机/var/lib/docker/volumes/nginx-volume/_data目录下创建对应的存储位置。

docker volume inspect nginx-volume可以查看该数据卷的详细信息,包括:卷名称、驱动类型、挂载点(宿主机上的实际路径)、标签等。常用于确认卷是否创建成功以及获取路径。

命令docker run -d --name nginx-v1 -p 8081:80 -v nginx-volume:/usr/share/nginx/html nginx:latest中-d在后台运行容器,--name容器名称为nginx-v1,-p将宿主机的8081端口映射到容器的80端口(nginx默认端口)-v数据卷挂载,将前面创建的卷挂载到容器内的/usr/share/nginx/html目录。

echo "Data volume test" > /var/lib/docker/volumes/nginx-volume/_data/index.html直接在宿主机上向数据卷的存储目录写入index.html文件,这个操作模拟了宿主机对共享数据的修改。

curl http://localhost:8081访问宿主机的8081端口,实际进入nginx-v1容器,Nginx会返回index.html的内容,即"Data volume test",该操作验证了宿主机修改数据卷文件,容器内会同步实现。

容器间数据共享:启动第二个nginx容器,绑定同一个数据卷,验证两个容器的数据同步效果

容器间数据共享

docker run -d --name nginx-v2 -p 8082:80 -v nginx-volume:/usr/share/nginx/html nginx:latest
docker exec -it nginx-v1 bash -c "echo 'Shared data' > /usr/share/nginx/html/test.html"
curl http://localhost:8082/test.html

docker run 启动第二个Nginx容器nginx-v2,和nginx-v1挂载相同数据卷到相同路径,这样就实现了两个容器共享一份数据。

docker exec -it nginx-v1 bash进入nginx-v1容器并启动一个交互式bash终端,-c执行后面的命令并输出,具体是向test.html写入"Shared data"。

curl访问第二个容器nginx-v2的8082端口下的test.html路径,他会读取其中文件并返回内容。

该过程验证了一个容器修改数据卷内容,另一个挂载相同卷的容器能够立即看到变化,实现了容器间的数据共享。这里我们是使用nginx-v1写数据,用nginx-v2的端口进行的查看,不要搞混了。

宿主机目录绑定:启动nginx容器,绑定宿主机自定义目录,修改宿主机目录下的文件,验证容器内内容同步

宿主机目录绑定

mkdir -p /root/host_nginx_html
echo "Host binding test" > /root/host_nginx_html/index.html
docker run -d --name nginx-bind -p 8083:80 -v /root/host_nginx_html:/usr/share/nginx/html:ro nginx:latest
curl http://localhost:8083
echo "Modified" >> /root/host_nginx_html/index.html
curl http://localhost:8083

mkdir -p /root/host_nginx_html在宿主机上创建一个自定义目录。

echo "Host binding test" > /root/host_nginx_html/index.html在该目录下创建index.html文件,写入Host binding test。

docker run中-v /root/host_nginx_html:/usr/share/nginx/html:ro 是宿主机目录绑定,将宿主机上的绝对路径/root/host_nginx_html挂载到容器内的/usr/share/nginx/html。:ro表示容器内对该目录只有只读权限(read-only)。容器无法修改里面的文件,但宿主机可以修改。这一步与数据卷不同,数据卷由Docker管理,目录绑定直接使用宿主机任意路径。

curl访问容器并返回文件内容。

echo "Modified" >> /root/host_nginx_htnl/index/html在宿主机上追加内容"Modified”到文件内。

curl再次访问和返回。

该步骤验证了宿主机修改绑定目录下的文件,容器内实时同步(即使容器内是只读挂载,宿主机修改仍会影响到容器)。

清理(可选)

docker stop nginx-v1 nginx-v2 nginx-bind
docker rm nginx-v1 nginx-v2 nginx-bind
docker volume rm nginx-volume

这几步分别是停止运行中的容器,删除容器(只是删除容器不会自动删除数据卷),以及删除数据卷(这一步建议只有当没有任何容器使用该数据卷时才能成功删除,是彻底清理资源)。

更多推荐