当前位置: 首页 > news >正文

Docker-镜像构建原因

在现代软件开发和运维中 Docker 已成为一种非常流行的工具 它通过容器化应用程序来简化部署过程

然而 默认的官方镜像往往只能满足基础需求 无法涵盖所有特定项目的具体要求 因此 构建自己的Docker 镜像是非常必要的 

你负责维护一个线上服务,使用的是基于 nginx 的镜像,运行着一个前端静态站点,并配置了复杂的 Nginx 路由、HTTPS 证书和访问控制策略。

现在公司要进行一次 灾备演练,要求你在不依赖线上环境的情况下,在测试集群中快速还原出一个“与生产几乎一致”的服务实例,用于模拟故障切换。

在某些场景下,确实不需要构建镜像

尤其是满足以下条件时:

  • 所有配置文件都通过 volume 挂载

  • 应用数据也通过 volume 存储

  • 容器本身只是运行时环境,没有额外的修改

镜像存在的意义,是在什么情况下不可替代?

你负责一个部署内部服务,该服务基于 nginx:1.21-alpine 运行,但为了满足公司安全合规要求,你需要:

  • 在容器中安装审计工具(如 auditd, syslog, osquery

  • 修改默认 root 用户为非 root 用户

  • 设置 SELinux 或 AppArmor 策略

  • 删除一些不必要的系统命令(如 telnet, nc, tcpdump

  • 强化日志输出格式并集成到统一日志平台

这些操作不能通过 volume 实现,因为它们涉及到:

  • 容器内系统组件的变更

  • 用户权限、安全策略等底层设置

  • 系统二进制文件的删除或替换

完成这些操作后,希望把当前容器的状态保存下来,用于后续所有节点统一部署。

原因说明
系统级改动无法通过 volume 实现修改用户、删除命令、安装系统组件等只能在镜像层完成
安全加固要求可复制、可验证你不能让每个节点手动执行一遍加固脚本,必须统一标准
合规性要求可追溯

构建镜像可以打标签、记录作者和时间,方便审计和版本管理

镜像 vs Volume 的适用场景对比表

场景是否需要构建镜像使用 Volume 是否足够
修改系统组件(如安装包、删命令)必须构建镜像不够
用户权限、安全策略调整必须构建镜像不够
日志配置、访问控制等应用级配置可以用 volume足够
数据持久化、网站静态资源可以用 volume足够
安全合规、标准化部署必须构建镜像不够

Volume 是用来存放“可变的业务数据”,而镜像则是用来固化“不变的运行环境”。

构建 Docker 镜像的方式

编号构建方式说明
1docker commit(容器提交)基于运行中的容器状态生成镜像
2Dockerfile + docker build通过编写 Dockerfile 文件定义镜像内容,并使用 docker build 构建

docker commit

docker commit 是一个用于将容器的当前状态保存为新镜像的命令

这种方法非常适合快速测试或对现有镜像进行小范围的修改

然而 它不推荐用于复杂的或需要长期维护的应用场景 因为它的透明度较低 不可重复性高 并且难以版本控制和团队协作

自定义 HTTP 镜像

在 CentOS 7 容器中安装 httpd,并提交为新镜像 my-httpd-image

启动容器并进入交互式 shell

docker run -it --name my-httpd-container centos:7 /bin/bash

这会创建一个名为 my-httpd-container 的容器,并进入它的 bash 环境。

在容器内安装 httpd

curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

yum clean all
yum makecache

yum install -y httpd

配置启动服务

做一些简单配置,比如添加一个测试页面:

echo "Hello from Apache in Docker!" > /var/www/html/index.html

注意:容器中通常不推荐使用 systemctl 来启动服务。

PID 1 问题:Docker 容器的设计初衷是运行单个进程。当你尝试在容器内运行 Systemd 或其他初始化系统作为 PID 1(即主进程),可能会遇到一些问题,包括无法正确处理信号和僵尸进程的管理。

权限问题:某些容器镜像可能并未包含完整的 Systemd 支持或未以 root 用户身份运行容器,这会导致 systemctl 命令不可用或者行为异常。

复杂性增加:引入 Systemd 会增加容器的复杂性和资源消耗,违背了容器轻量化的原则。此外,这也可能导致维护困难和潜在的安全隐患。

对于大多数服务,可以直接启动它们,并以前台模式运行,而不是依赖于 Systemd。

例如,对于 Nginx 可以使用 nginx -g 'daemon off;',Apache 可以使用 /usr/sbin/httpd -DFOREGROUND

这种方式更符合 Docker 的设计理念,同时也简化了服务管理。

退出容器:

exit

提交容器为新镜像

docker commit my-httpd-container my-httpd-image:latest

查看镜像信息

docker images | grep my-httpd

你应该看到类似输出:

my-httpd-image   latest   xxxxxxxxxxxx   ...

运行该镜像并启动 Apache

为了运行这个镜像并启动 Apache,可以这样运行:

docker run -d -p 8080:80 --name my-running-httpd my-httpd-image /usr/sbin/httpd -DFOREGROUND

解释:

  • -d: 后台运行

  • -p 8080:80: 映射宿主机端口 8080 到容器的 80 端口

  • /usr/sbin/httpd -DFOREGROUND: 强制 Apache 前台运行(避免容器立即退出)

自定义 Nginx 镜像

在 CentOS 7 容器中安装 nginx,并提交为新镜像 my-nginx-image

启动容器并进入交互式 shell

docker run -it --name my-nginx-container my-httpd-image /bin/bash

添加 EPEL 仓库并安装 nginx

CentOS 7 默认仓库中没有 nginx,需要先添加 EPEL 仓库:

yum install -y epel-release
yum install -y nginx

修改默认页面

同样地,可以修改默认首页内容:

echo "Hello from Nginx in Docker" > /usr/share/nginx/html/index.html

退出容器:

exit

提交容器为新镜像

docker commit my-nginx-container my-nginx-image:latest

查看镜像信息

docker images | grep my-nginx

运行该镜像并启动 Nginx

docker run -d -p 8080:80 --name my-running-nginx my-nginx-image nginx -g 'daemon off;'

解释:

  • nginx -g 'daemon off;' 表示以非守护模式运行 Nginx,确保容器不会退出

Dockerfile

Dockerfile 参考 | Docker 文档

Dockerfile 是一个文本文件 包含了一系列用于构建Docker 镜像的指令(Instructions )

它类似于shell 脚本 但不是直接运行命令 而是告诉Docker 如何一步步构建出一个定制化的镜像

一个完整的 Dockerfile 通常包含以下几个部分:

  1. 基础镜像

  2. 元数据信息

  3. 安装与配置

  4. 工作目录设置

  5. 文件复制

  6. 环境变量设置

  7. 端口暴露

  8. 启动命令

  9. 入口点

  10. 其他高级指令

指令详解

FROM —— 指定基础镜像

作用:指定当前镜像构建所依赖的基础镜像。

格式

FROM <image>[:<tag>]

说明

必须是 Dockerfile 中的第一个有效指令(除非是多阶段构建)。

可以选择官方镜像(如 nginx, python, alpine)或你自己定义的镜像。

示例

FROM ubuntu:20.04
  FROM centos:7

LABEL —— 添加元数据标签

作用:为镜像添加描述性信息,比如作者、版本等。

格式

 LABEL <key>=<value> ...

说明

替代已废弃的 MAINTAINER 指令。

可以多次使用,也可以一次写多个键值对。

示例

LABEL maintainer="john@example.com" \
        version="1.0" \
        description="This is a custom Nginx image"

RUN —— 构建时执行命令

作用:在构建过程中执行命令,通常用于安装软件包等操作。

格式

Shell 形式(隐式调用 /bin/sh -c):

RUN command arg1 arg2

Exec 形式(推荐):

RUN ["command", "arg1", "arg2"]

说明

每个 RUN 指令都会生成一个新的镜像层(layer)。

建议将多个命令合并到一行中,减少层数。

示例

 RUN yum update && \
      yum install -y nginx

WORKDIR —— 设置工作目录

作用:设置后续指令(如 COPY, ADD, CMD, ENTRYPOINT)的工作目录。

格式

  WORKDIR /path/to/workdir

说明

如果目录不存在,会自动创建。

推荐使用绝对路径。

示例

  WORKDIR /app

COPYADD —— 复制文件到镜像中

COPY

作用:从本地主机复制文件/目录到镜像中。

优点:简单、安全、推荐使用。

格式

COPY <src>... <dest>

示例

 COPY ./index.html /usr/share/nginx/html/index.html

ADD

作用:功能类似 COPY,但还能自动解压 tar 文件、支持远程 URL 下载。

示例

ADD app.tar.gz /opt/app/
  ADD https://example.com/file.txt /tmp/

推荐:优先使用 COPY,只有必要时才用 ADD

ENV —— 设置环境变量

作用:设置容器内使用的环境变量。

格式

 ENV <key>=<value> ...

说明

在后续指令中可以通过 $VAR_NAME 引用。

容器启动后也会保留这些变量。

示例

  ENV APP_HOME=/var/www/app \
      NODE_ENV=production

EXPOSE —— 声明容器监听端口

作用:声明容器在运行时会监听哪些端口。

格式

 EXPOSE <port>[/<protocol>]...

说明

不会自动映射宿主机端口,只是文档性质的说明。

实际映射需在 docker run 时通过 -p 参数指定。

示例

EXPOSE 80/tcp
  EXPOSE 8000

示例运行时映射:

docker run -d -p 8080:80 my-nginx-image

CMD —— 容器启动时默认执行的命令

作用:定义容器启动时默认执行的命令。

格式

Exec 形式(推荐):

CMD ["command", "arg1", "arg2"]

Shell 形式(隐式调用 /bin/sh -c):

CMD command arg1 arg2

说明

可以被 docker run 命令覆盖。

一个 Dockerfile 中只能有一个有效的 CMD(如果有多个,最后一个生效)。

示例

 CMD ["nginx", "-g", "daemon off;"]

ENTRYPOINT —— 容器入口点(固定命令)

作用:设置容器启动时必须运行的命令,比 CMD 更“固定”。 格式

ENTRYPOINT ["command", "param1", "param2"]

说明

CMD 配合使用时,CMD 的内容作为参数传递给 ENTRYPOINT

不能轻易被 docker run 覆盖(除非加 --entrypoint)。

示例

ENTRYPOINT ["nginx"]
  CMD ["-g", "daemon off;"]

含义:nginx -g daemon off; 会被执行。

指令是否可省略是否影响运行是否可覆盖典型用途
FROM指定基础镜像
LABEL添加元数据
RUN安装软件
CMD设置默认启动命令
ENTRYPOINT❌(需特殊参数)固定入口命令
EXPOSE声明监听端口
ENV设置环境变量
COPY复制文件
ADD类似 COPY,支持 tar 和 URL
WORKDIR设置工作目录

构建 httpd 镜像

# 使用官方 CentOS 7 镜像作为基础镜像
FROM centos:7

# 添加元数据信息
LABEL maintainer="admin" \
      description="CentOS 7"

# 备份原始的 CentOS-Base.repo
RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak

# 使用 curl 下载阿里云的 CentOS Base.repo 文件
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo

# 清理并重建缓存
RUN yum clean all && yum makecache

# 安装一个httpd
RUN yum install -y httpd

EXPOSE 80

# 设置容器启动命令(示例)
CMD ["httpd", "-D", "FOREGROUND"]

构建 Docker 镜像

打开终端或命令行工具,导航到包含 Dockerfile 的目录,然后运行以下命令来构建 Docker 镜像。

请将 <your-image-name> 替换为你想要给这个镜像起的名字:

docker build -t <your-image-name> .

`-t` 参数用于指定镜像的名称。

`.` 表示当前目录是 Dockerfile 所在的位置。

构建 nginx 镜像

# 使用官方 CentOS 7 镜像作为基础镜像
FROM centos:7

# 添加元数据信息
LABEL maintainer="admin" \
      description="CentOS 7"

# 备份原始的 CentOS-Base.repo
RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak

# 使用 curl 下载阿里云的 CentOS Base.repo 文件
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo

# 清理并重建缓存
RUN yum clean all && yum makecache

# 安装 EPEL 源(Nginx 在 EPEL 中)
RUN yum install -y epel-release && \
    yum clean all && \
    yum makecache

# 安装 Nginx
RUN yum install -y nginx && \
    yum clean all

# 创建存放网页内容的目录
RUN mkdir -p /var/www/html

# 将HTML压缩包复制到容器中
COPY html.zip /tmp/html.zip

# 安装 unzip 工具
RUN yum install -y unzip && \
    yum clean all

# 解压HTML内容到指定目录
RUN unzip /tmp/html.zip -d /var/www/html/ && \
    rm /tmp/html.zip

# 修改 Nginx 默认配置文件,将根目录改为 /var/www/html
RUN sed -i 's|/usr/share/nginx/html|/var/www/html|g' /etc/nginx/nginx.conf

# 暴露80端口
EXPOSE 80

# 启动 Nginx 服务(前台模式)
CMD ["nginx", "-g", "daemon off;"]

构建服务镜像

FROM centos:7

LABEL maintainer="xyz" \
      description="CentOS 7 with Java application"

WORKDIR /usr/local/java

ADD jre-8u221-linux-x64.tar.gz ./
COPY msg.jar ./app.jar

ENV JAVA_HOME=/usr/local/java/jre1.8.0_221
ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH=$JAVA_HOME/bin:$PATH

EXPOSE 8080

CMD ["java", "-jar", "app.jar"]

使用云仓库

在实际应用中,大型企业和组织更倾向于使用云服务商提供的 Docker 仓库,因为它们能够提供高效、可靠的解决方案,并且减少了自行维护带来的挑战。

而对于一些中小企业或者对成本较为敏感的项目,如果团队有足够的技术能力并且对数据控制有较高要求的话,可能会选择自建私有仓库。

值得注意的是,很多情况下,企业会采用混合模式,即同时利用云服务提供商的仓库和自建的私有仓库,以便充分利用两者的优势。

例如,对外公开的服务可以托管在公有云仓库中,而内部敏感的应用则放在自建的私有仓库里。这种策略可以在保证效率的同时也兼顾了安全性和成本控制。

阿里云提供了容器镜像服务(Container Registry),允许用户方便地管理和分发 Docker 镜像。下面将详细介绍如何在阿里云容器镜像服务中进行镜像的上传(推送)与下载(拉取)。

创建命名空间和镜像仓库

登录 阿里云控制台,进入“容器镜像服务”页面。

在左侧菜单中选择“命名空间”,点击“创建命名空间”。为您的命名空间指定一个名称,例如 mycompany

配置 Docker 客户端认证

为了能够推送或拉取镜像,您需要登录到阿里云的 Docker Registry:

docker login --username=<your-aliyun-account> registry.cn-hangzhou.aliyuncs.com

这里的 <your-aliyun-account> 是您的阿里云账号用户名,registry.cn-hangzhou.aliyuncs.com 是默认的地域域名(您可以根据实际情况选择其他地域)。执行命令后,系统会提示输入密码,这里输入的是您的阿里云账号的密码或者是访问密钥中的 AccessKey Secret(如果启用了 RAM 用户的话)。

镜像标签与推送

假设你已经有了一个名为 myapp 的镜像,首先给它打上正确的标签以便于上传到阿里云容器镜像服务:

docker tag myapp:latest registry.cn-hangzhou.aliyuncs.com/<your-namespace>/<your-repo>:<tag>

例如:

docker tag myapp:latest registry.cn-hangzhou.aliyuncs.com/mycompany/myapp:v1

接着,使用 docker push 命令上传镜像:

docker push registry.cn-hangzhou.aliyuncs.com/mycompany/myapp:v1

从阿里云拉取镜像

当需要使用已上传的镜像时,可以通过以下命令拉取:

docker pull registry.cn-hangzhou.aliyuncs.com/mycompany/myapp:v1

这将会把指定版本的镜像下载到本地 Docker 环境中。

http://www.dtcms.com/a/276106.html

相关文章:

  • C语言基础教程--从入门到精通
  • Spring Boot整合MyBatis+MySQL+Redis单表CRUD教程
  • STM32中的RTC(实时时钟)详解
  • R 语言绘制 10 种精美火山图:转录组差异基因可视化
  • JavaScript 常见10种设计模式
  • 码头智能哨兵:AI入侵检测系统如何终结废钢盗窃困局
  • Redis专题总结
  • MyBatis实现一对多,多对一,多对多查询
  • Golang操作MySQL json字段优雅写法
  • CPU缓存一致性协议:深入解析MESI协议与多核并发设计
  • HTML/JOSN复习总结
  • 7. JVM类加载器与双亲委派模型
  • PyQt5 — QTimeEdit 学习笔记
  • Java中的wait和notify、Condition接口的使用
  • 分类问题与多层感知机
  • pip国内镜像源一览
  • [es自动化更新] Updatecli编排配置.yaml | dockerfilePath值文件.yml
  • springboot+swagger2文档从swagger-bootstrap-ui更换为knife4j及文档接口参数不显示问题
  • 【高等数学】第三章 微分中值定理与导数的应用——第七节 曲率
  • DirectX Repair修复工具下载,.NET修复,DirectX修复
  • python 中 ‘5‘ 和 5 有什么本质区别?
  • 【深度学习】 1 Deep Learning
  • 12. JVM的垃圾回收器
  • LangChain 代理(Agents)学习
  • 网页五子棋-对战
  • python学习打卡:DAY 37 早停策略和模型权重的保存
  • web网站无法抓包排查;burp无法抓包情况
  • comfyUI-controlNet-线稿软边缘
  • c++中的STL
  • Day59