Docker 镜像与容器核心概念详解

Docker 镜像与容器核心概念详解 1. Docker 镜像是什么Docker 镜像是一个轻量级、可执行的独立软件包包含运行某个软件所需的所有内容代码、运行时环境、系统工具、系统库和设置。镜像采用分层存储结构每一层都是只读的这种设计带来了两大核心优势节约存储空间多个镜像可以共享相同的基础层避免重复存储。高效分发与构建镜像层可以被缓存和复用加速构建和拉取过程。简单来说镜像 应用程序代码 微型操作系统环境 配置是一个静态的、分层的文件系统快照。2. Dockerfile 指令详解2.1 COPY 与 ADD 的区别COPY主要用于将宿主机构建上下文的文件或目录复制到镜像中。COPY app.py /app/ADD除了具备 COPY 的功能外还额外支持自动解压 tar、gzip、bzip2、xz 等压缩包支持从 URL 下载文件但不推荐因为无法保证下载文件的完整性ADD app.tar.gz /app/ # 会自动解压到 /app/ 目录最佳实践除非需要解压功能否则优先使用 COPY因为它更透明、更可预测。2.2 VOLUME 指令VOLUME 指令在镜像中声明一个挂载点用于持久化数据和共享数据VOLUME /var/lib/mysql当容器启动时Docker 会自动创建一个匿名卷随机名称挂载到容器的 /var/lib/mysql 目录即使容器被删除卷中的数据仍然保留也可以通过docker run -v指定具名卷或宿主机目录覆盖此声明2.3 CMD 与 ENTRYPOINT 的区别ENTRYPOINT定义容器启动时必须执行的核心命令通常是一个脚本或可执行文件。CMD定义容器启动时的默认参数。如果启动容器时指定了其他命令CMD 中的内容会被覆盖。组合使用示例ENTRYPOINT [/docker-entrypoint.sh] CMD [nginx, -g, daemon off;]等价于容器启动时执行/docker-entrypoint.sh nginx -g daemon off;关键点CMD 中的参数会作为位置变量传递给 ENTRYPOINT 对应的脚本daemon off强制 Nginx 以前台进程模式运行容器中运行的程序必须前台运行容器生命周期 容器中运行的主进程生命周期2.4 Shell 调试模式在 Dockerfile 或启动脚本中常用的 Shell 选项set -eux # -e (errexit)上条命令执行失败时立即退出脚本 # -u (nounset)引用未定义变量时报错退出 # -x (xtrace)打开调试模式显示执行的每一条命令 set x # 关闭调试模式3. 如何制作更小的镜像镜像大小直接影响构建速度、存储成本和网络传输效率。优化策略减少镜像层数每个 RUN、COPY、ADD、WORKDIR、ENV 等指令都会创建新层合并多个 RUN 命令RUN apt-get update apt-get install -y package rm -rf /var/lib/apt/lists/*减少不必要的文件复制使用最小的基础镜像Alpine Linux仅 5MB 左右Distroless只包含应用程序及其运行时依赖没有 Shell 和包管理器Scratch空镜像适合静态编译的程序合理使用多阶段构建在构建阶段安装编译工具最终镜像只包含运行所需文件使用 .dockerignore 文件排除不必要的构建上下文文件数据与镜像分离使用卷Volume挂载配置文件、日志、数据库文件等可变数据4. 实战案例Python Flask 微服务镜像制作4.1 项目结构/myapp/python2/ ├── app.py # Flask 应用程序 ├── requirements.txt # Python 依赖 └── Dockerfile # 镜像构建文件4.2 核心应用程序 (app.py)from flask import Flask from redis import Redis, RedisError import os import socket 连接 Redis redis Redis(hostredis, db0, socket_connect_timeout2, socket_timeout2) app Flask(name) app.route(/) def hello(): try: visits redis.incr(counter) except RedisError: visits icannot connect to Redis, counter disabled/i html lt;h3gt;Hello {name}!lt;/h3gt; \ lt;bgt;Hostname:lt;/bgt; {hostname}lt;br/gt; \ lt;bgt;Visits:lt;/bgt; {visits} return html.format(nameos.getenv(NAME, world), hostnamesocket.gethostname(), visitsvisits) if name main: app.run(host0.0.0.0, port80)4.3 依赖文件 (requirements.txt)flask redis4.4 Dockerfile 配置# 使用 Python 2.7 精简版作为基础镜像 FROM python:2.7-slim 设置工作目录 WORKDIR /app 复制当前目录所有文件到容器的 /app 目录 ADD . /app 安装 Python 依赖使用国内镜像加速 RUN pip install --trusted-host https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt 暴露端口 EXPOSE 80 设置环境变量 ENV NAME World ENV AUTHOR cali 容器启动命令 CMD [python, app.py]4.5 构建与运行# 构建镜像 docker build -t sweb:1.0 . 查看镜像 docker images 运行容器映射宿主机 8050 端口到容器 80 端口 docker run -d --name sweb-1 -p 8050:80 sweb:1.0 查看运行状态 docker ps 访问应用 浏览器打开http://宿主机IP:80504.6 网络配置与容器互联微服务架构中多个容器需要通信# 创建自定义网络指定子网和网关 docker network create --driver bridge \ --subnet 172.20.0.0/16 \ --gateway 172.20.0.1 \ my-custom-network 启动容器并指定固定 IP docker run -d --name nginx-fixed-ip -p 8051:80 --network my-custom-network --ip 172.20.0.10 nginx 启动 Redis 容器供 Flask 应用连接 docker run -d --name redis --network my-custom-network redis:alpine 修改 app.py 中的 Redis 连接主机名为 redis容器名