ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Docker 容器实战 1—— docker 基础与镜像构建

2026/8/31 5:52:54 拓冰建站 浏览量
Docker 容器实战 1—— docker 基础与镜像构建 1 Docker 简介Docker 是一套开源的容器化虚拟化引擎基于 Linux 内核能力实现应用隔离与打包。传统虚拟机 KVM 是完整虚拟化 CPU、内存、磁盘拥有独立操作系统内核Docker 容器不虚拟化硬件直接复用宿主机 Linux 内核仅对应用进程做资源隔离。底层依赖三大 Linux 内核技术Namespace做隔离隔离 PID 进程、网络、挂载、用户容器内部看见一套独立的进程、网卡、文件系统视图。Cgroups做资源限制限制容器最多占用多少 CPU、内存、磁盘 IO防止单个容器耗尽宿主机资源。UnionFS 联合文件系统实现镜像分层存储镜像可以多层叠加修改容器只新增读写层不改动底层只读镜像节省存储空间。四大核心概念镜像 Image镜像属于只读模板存放应用程序、运行依赖、配置文件、环境变量。例如 mysql:5.7 镜像相当于预制好的软件包镜像本身不能运行。容器 Container容器是镜像运行后的实例。在镜像只读层之上新增一层可读写层。同一个镜像可以启动无数个独立容器。容器销毁时读写层数据默认丢失需要数据卷持久化保存数据。Dockerfile文本构建脚本一系列指令定义如何一步步构建镜像。写好 Dockerfile 后执行docker build生成自定义镜像。理解基于已有镜像模板通过 Dockerfilebuild 制作出新镜像再 run 这个新镜像跑容器。仓库 Registry存放镜像的服务器公共仓库 Docker Hub企业内部搭建私有镜像仓库用来上传、下载镜像。核心优势轻量共享宿主机内核不需要整套操作系统占用磁盘、内存远小于 KVM 虚拟机。启动快进程级启动秒级拉起业务虚拟机需要启动完整操作系统耗时分钟级别。环境一致性一次打包镜像开发、测试、生产环境完全一致解决 “本地能跑线上跑不起来” 环境问题。资源利用率高一台物理机可以部署成百上千容器虚拟机数量受硬件资源限制。版本管理镜像分层支持版本回滚分发拷贝便捷。局限性内核共享容器直接使用宿主机内核Windows 不能直接跑 Linux 容器Linux 容器不能运行不同版本内核。隔离弱于虚拟机进程级隔离不是硬件级隔离对安全隔离等级要求极高的业务优先选用 KVM 虚拟机。不适合长时间运行传统大型数据库MySQL 等建议做好数据卷持久化配置。2 Docker 架构Docker EnginedockerdDocker 守护进程。接收docker run / build / pull等客户端命令处理镜像管理、网络、存储对外提供 API。不再直接操作容器把容器生命周期任务交给下层containerd。containerd容器管理守护进程工业级容器管理器K8s 也默认使用 containerd。接收 dockerd 下发指令负责容器生命周期管理、镜像解压、存储管理。每启动一个容器就生成一个containerd‑shim。containerd‑shim垫片每个容器对应独立的 shim 进程。充当 containerd 和 runC 中间垫片保证即使 containerd 进程挂掉容器还可以继续运行接管容器标准输入输出、日志转发给上层。runCOCI 容器运行时规范的参考实现是真正直接调用 Linux 内核接口完成容器创建的工具。OCIOpen Container Initiative开放容器倡议是一套行业统一标准。主要定义两套规范镜像规范容器镜像该长什么样、如何分层存储和运行时规范如何调用内核 API 去启动一个容器。containerd‑shim 调用 runCrunC 去调用 Linux 系统调用创建 Namespace、Cgroups生成容器进程。容器创建完成后 runC 就退出容器业务进程由 shim 托管。Linux KernelLinux 内核层真正实现隔离、资源限制、安全管控图中三个组件namespacesPID、NET、MNT 等做隔离制造独立视图。cgroupsCPU、内存、IO 资源限制防止容器资源滥用。SELinux/AppArmor内核安全模块强制访问控制加固容器安全。完整调用流程执行docker run用户执行docker run→ 发送请求到Docker Engine(dockerd)dockerd 处理镜像、网络把 “创建容器” 任务下发给containerdcontainerd 为该容器启动containerd‑shim垫片进程shim 调用runCrunC 调用 Linux 系统调用创建 Namespace、Cgroups拉起容器 APP 进程容器运行shim 持续监控容器状态、转发日志runC 任务完成退出架构特点就算上层dockerd、containerd重启只要containerd‑shim还在容器业务进程不会停止。3 Docker 部署配置软件源、安装软件并启动服务[rootdocker1 ~]# cd /etc/yum.repos.d/[rootdocker1 yum.repos.d]# wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo[rootdocker1 yum.repos.d]# yum install -y docker-ce[rootdocker1 yum.repos.d]# systemctl enable --now docker查看 Docker 守护进程 (dockerd) 全局信息[rootdocker1 yum.repos.d]# docker info如果此处有警告需激活以下内核选项[rootdocker1 ~]# sysctl -a|grep iptable[rootdocker1 ~]# sysctl -a|grep ip6table[rootdocker1 ~]# sysctl -a|grep ip_forward把内核参数追加写入 docker 专属 sysctl 配置文件此文件本身没有是手动创建的。执行sysctl --system会按顺序扫描下面 4 个目录加载目录下全部*.conf文件/usr/lib/sysctl.d/*.conf/run/sysctl.d/*.conf/etc/sysctl.d/*.conf/etc/sysctl.conf[rootdocker1 ~]# echo net.bridge.bridge-nf-call-iptables 1 /etc/sysctl.d/docker.conf[rootdocker1 ~]# echo net.bridge.bridge-nf-call-ip6tables 1 /etc/sysctl.d/docker.conf[rootdocker1 ~]# echo net.ipv4.ip_forward 1 /etc/sysctl.d/docker.conf[rootdocker1 ~]# sysctl --system# 从系统全部配置文件加载生效内核参数一次性读取所有 sysctl 配置文件并应用查看IPdocker0是 Docker默认网桥bridge虚拟网卡这个 IP 是dockerd 守护进程自动生成分配的安装网桥工具查看 Linux 网桥信息[rootdocker1 ~]# yum install -y bridge-utils[rootdocker1 ~]# brctl show4 Docker 基本操作查看正在运行的容器为空[rootdocker1 ~]# docker ps搜索仓库镜像[rootdocker1 ~]# docker search docker.1ms.run/yakexi007从仓库拉取镜像[rootdocker1 ~]# docker pull docker.1ms.run/yakexi007/mario[rootdocker1 ~]# docker images# 查看本地镜像列表[rootdocker1 ~]# docker tag docker.1ms.run/yakexi007/mario:latest mario:latest# 给本地镜像打新标签相当于镜像别名[rootdocker1 ~]# docker rmi docker.1ms.run/yakexi007/mario:latest# # 删除原有长路径标签查看镜像的构建历史逐层展示镜像每一层的创建信息镜像内部 Dockerfile 写的EXPOSE 8080告诉使用者这个容器服务默认监听 8080 端口[rootdocker1 ~]# docker history mario:latest后台启动容器指定容器名 demo端口映射宿主机80:容器8080[rootdocker1 ~]# docker run -d --name demo -p 80:8080 mario[rootdocker1 ~]# docker ps访问IP5 部署应用堆栈堆栈Stack一组关联在一起协同工作的多个服务 / 容器的集合。举例子一个网站业务Nginx(前端) SpringBoot(后端) MySQL(数据库) Redis(缓存)这 4 个容器配合才能跑完整业务这一整套就叫应用堆栈。部署由httpd mysql组成的简易业务堆栈访问业务接口获取数据库用户列表[rootdocker1 ~]# docker stop demo# 停止运行的 demo 容器[rootdocker1 ~]# lsanaconda-ks.cfg httpd.tar mysql.tar os.sql# 从本地上传镜像包[rootdocker1 ~]# docker load -i httpd.tar[rootdocker1 ~]# docker load -i mysql.tar# 导入上述两个镜像查看镜像[rootdocker1 ~]# docker images启动mysql容器作为堆栈的数据库服务[rootdocker1 ~]# docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORDredhat --name dbserver mysql[rootdocker1 ~]# docker ps查看dbserver容器日志[rootdocker1 ~]# docker logs dbserver宿主机安装mariadb提供mysql客户端命令登录验证[rootdocker1 ~]# yum install -y mariadb[rootdocker1 ~]# mysql -h 127.0.0.1 -predhat将本地os.sql脚本导入到容器内的mysql数据库登录验证导入成功[rootdocker1 ~]# mysql -h 127.0.0.1 -predhat os.sql[rootdocker1 ~]# mysql -h 127.0.0.1 -predhat启动httpd容器使用--link关联数据库容器dbserver[rootdocker1 ~]# docker run -d -p 80:80 --link dbserver:db --name webserver httpd:webapp[rootdocker1 ~]# docker ps进入webserver容器内部查看action脚本源码action脚本就是浏览器访问cgi‑bin/action调用的业务程序该脚本会去连接数据库读取用户列表并输出网页[rootdocker1 ~]# docker exec -it webserver bash[root2e55ee5bf3ab /]# cd /var/www/cgi-bin/[root2e55ee5bf3ab cgi-bin]# lsactionhostname[root2e55ee5bf3ab cgi-bin]# cat action访问前端可以看到数据库里的内容6 镜像构建6.1 镜像层Docker 镜像由多层只读文件系统堆叠而成。每一层只记录这一层发生的文件变更新增、修改、删除标记不是完整操作系统。镜像层只读构建完成后不能修改要改内容只能往上叠加新层。容器启动时会在所有只读镜像层之上自动生成一层容器读写层。容器内所有增删改操作全部发生在读写层不会改动底层镜像层容器删除读写层连同内部改动全部销毁镜像层不受影响。写时复制 CoWCopy‑on‑Write修改下层镜像层已存在的文件会把文件复制到上层读写层再修改。镜像常用子命令命令简写完整命令作用docker imagesdocker image ls列出本地所有镜像docker build -t 名字:标签 .docker image build构建镜像Dockerfile 生成镜像docker rmi 镜像id/镜像名:tagdocker image rmrmi remove image只删除标签只有这个镜像没有其他标签时才真正删除镜像层数据docker tag 旧镜像:旧tag 新镜像:新tagdocker image tag给镜像打新标签别名docker inspect 镜像iddocker image inspect查看镜像详细元数据层、CMD、ENV 等docker pull 镜像名docker image pull从镜像仓库拉取镜像到本地docker push 镜像名docker image push推送镜像到镜像仓库docker save -o xxx.tar 镜像名:tagdocker image save把镜像导出为 tar 压缩包离线迁移docker load -i xxx.tardocker image load从 tar 包导入镜像查看镜像层历史的命令每一行代表一层镜像层[rootdocker1 docker]# docker history mysql:latestIMAGE镜像层 IDmissing代表本地没有缓存这一层多阶段构建 / 导入镜像经常出现CREATED BY生成这一层对应的 Dockerfile 指令截图能看到EXPOSE 8080/tcp、WORKDIR、COPY、RUN apt‑get install、CMDSIZE该镜像层占用大小默认docker history mario:latest会把长命令截断如上图中的省略号。加参数--no-truncno truncate不截断输出完整展示每一层 CREATED BY 的原始命令6.2 两种构建方式镜像构建有两种方式docker commit容器提交构建手工构建流程run 启动容器 → 在容器里面手动改环境、创建文件、装软件 → commit 把当前容器磁盘状态打包成新镜像原理抓取容器读写层全部变更整体打包生成1 个全新的只读镜像层追加到底层基础镜像之上缺点所有修改全部压进同一层无法拆分步骤docker history看不到具体做了哪些操作docker build DockerfileDockerfile 文件构建自动化构建写 Dockerfile把每一步操作写成指令RUN / COPY / ADD / EXPOSE …原理Dockerfile每一条可执行指令RUN、COPY、ADD都会生成独立的只读镜像层一层层向上堆叠。缓存机制构建会复用旧镜像层缓存某一条指令改动该指令及后续全部层重新构建前面不变层直接复用。查看分层历史docker history demo:v1可以看到每一层对应的 Dockerfile 指令Dockerfile 常用核心指令指令作用说明FROM xxx:tag [as 别名]指定基础镜像必须第一行所有镜像都要基于一个父镜像as build给阶段起别名供COPY --from引用RUN 命令构建阶段执行命令构建镜像的时候运行生成镜像层不要用来启动容器程序COPY src dest复制文件到镜像把宿主机上下文的文件拷贝进镜像内部ADD src dest复制 解压比 COPY 多自动解压 tar 压缩包普通文件优先用 COPYEXPOSE 80声明暴露端口仅文档说明不会自动做端口映射run 的时候才 - p 映射CMD [“程序”,“参数”]容器启动默认命令容器 run 起来之后执行只会最后一条生效ENTRYPOINT容器入口程序和 CMD 配合固定启动命令追加参数WORKDIR /path设置工作目录后续 RUN/COPY 等指令默认工作目录不存在自动创建CMD [“程序”,“参数”]容器启动默认命令容器 run 起来之后执行仅最后一条生效可被docker run后面命令覆盖容器 PID1 进程由此产生解释CMD容器 run 起来之后执行仅最后一条生效可被docker run后面命令覆盖容器 PID1 进程由此产生例在Dockerfile 里写CMD [/usr/local/nginx/sbin/nginx,-g,daemon off;]正常情况下启动容器docker run -d --name web3 webserver:v3容器就执行nginx -g daemon off若后面追加指令如bashdocker run -it webserver:v3 bash此时不再执行 nginx转而执行bash把 Dockerfile 的 CMD 直接作废正常情况不覆盖 CMDPID1 /usr/local/nginx/sbin/nginxnginx 作为一号进程nginx 前台运行容器存活docker logs可以捕获 nginx 的 stdout 标准输出日志nginx 停止 → PID1 消失 →容器直接退出停止。run 后面写 bash覆盖 CMDPID1 bashnginx 要手动在容器内部敲命令启动只是普通后台子进程docker logs 拿不到 nginx 日志退出 bashPID1 结束容器立刻停止。6.2.1 docker commit把容器可写层新增 file1/file2/file3、os.sql做成一个新镜像层叠加原来 busybox 底层只读镜像[rootdocker1 ~]# docker run -it --name demo docker.1ms.run/busybox# 启动交互式容器/# touch file1 file2 file3# 容器内创建3个空文件/# [rootdocker1 ~]# docker psctrlpq 退出容器后台继续运行[rootdocker1 ~]# docker cp os.sql demo:/# 宿主机文件拷贝到容器demo的根目录attach 接入容器终端查看已经有file1‑3、os.sql[rootdocker1 ~]# docker attach democtrld 退出容器把停止的 demo 容器提交保存为新镜像 demo:v1[rootdocker1 ~]# docker commit -m add os.sql demo demo:v1# docker commit把容器当前可写层的改动打包生成全新镜像# -m add os.sql 添加注释说明查看生成的镜像及镜像分层历史[rootdocker1 ~]# docker images demo:v1[rootdocker1 ~]# docker history demo:v1第一层最新sh大小 1.58MB注释add os.sql这就是 commit 新增的镜像层包含我们新建的 3 个文件 拷贝进去的 os.sql。底层原始 busybox 镜像层 4.45MB。总镜像大小4.45 1.58 6.03MB6.2.2 Dockerfile单独建一个目录[rootdocker1 ~]# mkdir docker作用控制构建上下文避免上传无关文件执行docker build -t xxx ..代表把当前目录全部文件打包传给 Docker 引擎。如果在/root家目录直接 buildroot 下所有日志、脚本、缓存全部打包上传速度慢、体积巨大还会泄露文件。新建空目录里面只放 Dockerfile、nginx.conf、tar 源码、repo 文件上下文就只有需要的文件。文件管理干净不污染 root 家目录所有实验文件Dockerfile、配置、源码统一放在~/docker找文件、删除实验内容很方便不和系统文件混在一起。COPY 指令找文件不会乱COPY只能拿上下文目录里面的文件不能拿外面上级目录的文件。把所有素材全部放进这个文件夹COPY 直接写文件名即可。方便区分多个项目还可以继续建子目录mkdir nginx_build、mkdir python_build每个镜像构建单独一套目录。构建上下文目录把宿主机CentOS‑Base.repo源文件复制到 ~/docker 目录[rootdocker1 ~]# cd /etc/yum.repos.d/[rootdocker1 yum.repos.d]# cp CentOS-Base.repo ~/docker/确保资源文件都在当前目录[rootdocker1 yum.repos.d]# cd[rootdocker1 ~]# cd docker/[rootdocker1 docker]# lscentos7.tar CentOS-Base.repo nginx-1.30.3.tar.gz导入本地centos7镜像包[rootdocker1 docker]# docker load -i centos7.tar[rootdocker1 docker]# rm -fr centos7.tar# 删除本地tar包释放磁盘编写 Dockerfile[rootdocker1 docker]# vim DockerfileFROM centos:7 RUN rm -f /etc/yum.repos.d/*.repo COPY CentOS‑Base.repo /etc/yum.repos.d/ RUN yum install -y gcc make pcre‑devel openssl‑devel ADD nginx‑1.30.3.tar.gz /mnt/ WORKDIR /mnt/nginx‑1.30.3 RUN sed -i s/CFLAGS$CFLAGS -g/#CFLAGS$CFLAGS -g/g auto/cc/gcc RUN ./configure --with‑http_ssl_module --with‑http_stub_status_module RUN make RUN make install EXPOSE 80 CMD [/usr/local/nginx/sbin/nginx,‑g,daemon off;] daemon off; 让 Nginx以前台方式运行不脱离终端。 Nginx 就成为容器的 PID 1 主进程进程在容器就存活。执行镜像构建读取 Dockerfile 逐层生成镜像-t webserver:v1‑t打标签tag镜像名webserver版本标签v1[rootdocker1 docker]# docker build -t webserver:v1 .过滤显示仓库名为webserver的所有镜像[rootdocker1 docker]# docker images webserver基于镜像webserver:v1创建一个名叫web1的容器后台启动[rootdocker1 docker]# docker run -d --name web1 webserver:v1[rootdocker1 docker]# docker ps查看容器web1的完整底层 JSON 格式元数据。包含容器 ID、状态、网络、端口映射、挂载、环境变量、镜像 ID、进程、文件路径等全部底层信息[rootdocker1 docker]# docker inspect web1172.17.0.2容器web1在 docker 默认 bridge 网桥里的容器内网 IP测试[rootdocker1 docker]# curl 172.17.0.2查看日志[rootdocker1 docker]# docker logs web1# docker logs 读取的是容器进程的 stdout、stderr标准输出、标准错误编译安装的 Nginx 默认把日志写入磁盘文件不打印到标准输出[rootdocker1 docker]# docker exec -it web1 bash# 交互式进入容器内部[rootc3540f688ac3 nginx-1.30.3]# cd /usr/local/nginx/logs# 在此目录下可看到日志7 镜像优化7.1 缩减镜像层在 v1 版本中每一个RUN生成一个独立镜像层这里yum、sed、configure、make、make install都是单独 RUN 镜像层数非常多yum 安装完没有清理 yum 缓存缓存文件留在镜像层镜像体积大nginx 源码解压包/mnt/nginx‑1.30.3编译完成后还留在镜像里占磁盘空间[rootdocker1 docker]# docker rm -f web1[rootdocker1 docker]# vim Dockerfile在 v2 版本中可以做以下优化FROM centos:7 RUNrm-f/etc/yum.repos.d/*.repo COPY CentOS‑Base.repo /etc/yum.repos.d/ ADD nginx‑1.30.3.tar.gz /mnt/# 全部编译流程合并为 1 条RUNRUN yuminstall-ygccmakepcre‑devel openssl‑devel\cd/mnt/nginx‑1.30.3\sed-is/CFLAGS$CFLAGS -g/#CFLAGS$CFLAGS -g/gauto/cc/gcc\./configure --with‑http_ssl_module --with‑http_stub_status_module\make\makeinstall\cd..\rm-frnginx‑1.30.3\# 删除nginx源码包删掉编译残留文件yum clean all# 清理yum缓存减小镜像大小EXPOSE80CMD[/usr/local/nginx/sbin/nginx,‑g,daemon off;]构建 v2 版本镜像[rootdocker1 docker]# docker build -t webserver:v2 .镜像体积明显变小[rootdocker1 docker]# docker images webserver运行容器没问题[rootdocker1 docker]# docker run -d --name web1 webserver:v2[rootdocker1 docker]# docker ps测试7.2 多阶段构建多阶段构建原理一份 Dockerfile 可以写多个FROM每一个FROM代表一个独立阶段。构建阶段builder用完整基础镜像安装编译工具下载源码编译出可执行程序。这个阶段只负责 “生产成品”。运行阶段最终镜像重新开启一个全新干净的基础镜像只把编译好的成品文件从 builder 复制过来。builder 阶段的所有编译器、源码、临时文件全部直接丢弃不会进入最终镜像最终镜像只保留运行业务程序必须的文件重新进入容器将nginx.conf文件拷贝到容器[rootdocker1 docker]# docker rm -f web1web1[rootdocker1 docker]# docker run -it --name web1 webserver:v2 bash若 web1 容器还在运行中可执行docker exec -it web1 bash直接进入[roota98b205e6240 /]# cd /usr/local/nginx/conf/ctrlpq 退出容器但不停止容器回到宿主机 shell容器继续后台运行[roota98b205e6240 conf]# [rootdocker1 docker]# docker cp web1:/usr/local/nginx/conf/nginx.conf ./nginx.conf[rootdocker1 docker]# docker rm -f web1[rootdocker1 docker]# vim nginx.conferror_log /dev/stderr notice; # nginx 错误日志输出到**标准错误**日志级别notice及以上输出 log_format main ...; # 定义main格式日志模板 access_log /dev/stdout main; # 访问日志输出到标准输出使用main格式 在 Docker 容器中写入这两个文件的内容就会被 Docker 引擎捕获通过 docker logs 容器名 查看日志**不用写磁盘文件**[rootdocker1 docker]# vim Dockerfile#第一阶段build编译阶段编译nginx FROM centos:7 as build RUN rm -f /etc/yum.repos.d/*.repo COPY CentOS-Base.repo /etc/yum.repos.d/ ADD nginx-1.30.3.tar.gz /mnt/ RUN yum install -y gcc make pcre-devel openssl-devel cd /mnt/nginx-1.30.3 sed -i s/CFLAGS$CFLAGS -g/#CFLAGS$CFLAGS -g/g auto/cc/gcc ./configure --with-http_ssl_module --with-http_stub_status_module make make install cd .. rm -fr nginx-1.30.3 yum clean all COPY nginx.conf /usr/local/nginx/conf/ #第二阶段最终镜像重新拉取一个干净纯净的 centos:7 基础镜像复制编译产物舍弃编译工具 FROM centos:7 COPY --frombuild /usr/local/nginx /usr/local/nginx # 引用上面叫 build 的那个阶段把编译好的文件拷贝过来 EXPOSE 80 CMD [/usr/local/nginx/sbin/nginx,-g,daemon off;]根据当前目录的 Dockerfile 编译生成名为 webserver:v3 的Nginx镜像[rootdocker1 docker]# docker build -t webserver:v3 .查看各个版本的 webserver 镜像v3 体积又被压缩[rootdocker1 docker]# docker images webserver运行容器[rootdocker1 docker]# docker run -d --name web1 webserver:v3[rootdocker1 docker]# docker ps[rootdocker1 docker]# docker logs web1# 日志可直接查看7.3 使用最精简的基础镜像精简镜像也常叫 Distroless无发行版镜像只保留业务程序运行必需的二进制文件、依赖库剔除操作系统 shell、包管理器、调试工具、文档等无关组件。优点体积小去掉大量系统工具镜像大小大幅下降传输、部署速度更快。安全性高减少系统组件攻击面变小即使攻击者入侵容器没有 shell、curl 等工具很难执行恶意命令。环境干净只保留业务运行最低限度依赖避免系统自带工具带来版本冲突。缺点无法使用 docker exec 调试镜像内部没有sh/bash也没有curl、ss、netstat 等排错工具docker exec执行命令直接报executable file not found。排错困难不能进入容器内部修改配置、查看文件配置错误只能在宿主机修改源码重新 build 镜像。故障排查手段有限只能依靠容器日志、nsenter切入命名空间、宿主机抓包等外部方式排错。导入基础镜像[rootdocker1 ~]# docker load -i base-debian12.tar[rootdocker1 ~]# mkdir new[rootdocker1 ~]# cd new/[rootdocker1 new]# docker tag docker.1ms.run/nginx nginx:latest[rootdocker1 new]# docker rmi docker.1ms.run/nginx:latest[rootdocker1 new]# vim Dockerfile[rootdocker1 new]# docker build -t webserver:v4 .[rootdocker1 new]# docker run -d --name web1 webserver:v4日志