Docker高级管理 --Dockerfile镜像制作

Docker高级管理 --Dockerfile镜像制作

一、Dockerfile 基础概念

1. 定义与作用
  • 定义:Dockerfile 是一个包含创建 Docker 镜像所需指令的文本文件。
  • 作用:
    • 自动化镜像构建流程,避免手动配置的繁琐和不一致性。
    • 版本控制:Dockerfile 可纳入代码仓库,便于团队协作和追踪变更。
    • 可重复性:相同的 Dockerfile 构建出的镜像内容完全一致。
2. 核心组件
  • 指令(Instructions):如 FROMRUNCOPY 等,用于定义镜像构建步骤。
  • 构建上下文(Build Context):执行 docker build 时指定的路径(通常是 .),包含构建所需的文件。

二、Dockerfile 指令详解

1. 基础环境设置
FROM [:tag] [AS ]  # 指定基础镜像(必须是第一条指令)
ARG [=]    # 定义构建时变量(可在 build 命令中通过 --build-arg 覆盖)
2. 文件操作
COPY [--chown=:] ...   # 复制文件/目录到镜像
ADD [--chown=:] ...    # 类似 COPY,但支持远程 URL 和自动解压
WORKDIR   # 设置工作目录(后续指令的相对路径基准)
  • 区别ADD 支持解压 tar 文件和远程 URL,而 COPY 更简洁透明,推荐优先使用 COPY
3. 命令执行
RUN   # 执行 shell 命令(如安装软件)
RUN ["executable", "param1", "param2"]  # exec 格式(避免 shell 解析)
  • 分层原则:每条 RUN 命令创建一个新的镜像层,建议合并相关命令以减少层数(如 RUN apt-get update && apt-get install -y ...)。
4. 环境配置
ENV = ...  # 设置环境变量(在容器运行时保持)
ENV PATH="/app/bin:$PATH"  # 示例:添加路径到环境变量
5. 容器启动配置
CMD ["executable", "param1", "param2"]  # 容器启动时执行的默认命令(可被 docker run 覆盖)
ENTRYPOINT ["executable", "param1", "param2"]  # 容器启动时固定执行的命令(CMD 会作为参数附加)
  • 组合使用

    ENTRYPOINT ["python"]
    CMD ["app.py"]  # 最终命令:python app.py
    
6. 端口声明与卷挂载
EXPOSE  [/...]  # 声明容器运行时监听的端口(仅文档作用)
VOLUME ["/data"]  # 声明挂载点(用于持久化数据)
7. 用户与权限
USER [:]  # 设置后续命令的执行用户
RUN mkdir /app && chown user:group /app  # 创建目录并修改权限

三、镜像构建原理

1. 分层构建(Layered Architecture)
  • 每层的生成:每个 Dockerfile 指令(如 RUNCOPY)创建一个新的镜像层。

  • 层的缓存:若某层的指令未变化,构建时会复用缓存,加速构建过程。

  • 查看层:

    docker history <image>
    
2. 构建上下文(Build Context)
  • 构建时,客户端会将指定路径(如 .)下的所有文件发送给 Docker 引擎。
  • 排除文件:通过 .dockerignore 文件排除不需要的文件(如 .gitnode_modules)。
3. 多阶段构建(Multi-stage Build)
  • 原理:使用多个 FROM 指令,每个指令定义一个构建阶段,可从前一阶段复制所需文件。

  • 优势:大幅减小最终镜像体积(仅保留运行时必要文件)。

  • 示例:

    # 构建阶段
    FROM maven:3.8.4 AS builder
    COPY . /app
    RUN mvn package
    
    # 运行阶段
    FROM openjdk:17-slim
    COPY --from=builder /app/target/app.jar /app.jar
    CMD ["java", "-jar", "/app.jar"]
    

四、最佳实践

1. 减小镜像体积
  • 使用轻量级基础镜像(如 alpineslim 版本)。
  • 合并 RUN 命令,避免冗余文件(如清理包管理器缓存)。
  • 优先使用多阶段构建。
2. 提高构建效率
  • 合理安排指令顺序,将变化频繁的部分放在后面(如代码)。
  • 先复制依赖文件(如 package.jsonrequirements.txt),再安装依赖,利用缓存。
3. 安全增强
  • 避免使用 root 用户运行容器。
  • 定期更新基础镜像和依赖,修复安全漏洞。
  • 不存储敏感信息(如密钥、密码)在镜像中。
4. 可维护性
  • 添加注释说明每个部分的用途。
  • 使用语义化的标签(如 :v1.0.0 而非 :latest)。
  • 遵循单一职责原则,每个容器专注一个功能。

五、常见误区与问题

1. 混淆 CMDENTRYPOINT
  • CMD:提供默认参数,可被 docker run 覆盖。
  • ENTRYPOINT:设置固定命令,CMD 作为其参数。
2. 过度使用 ADD
  • ADD 的远程 URL 和自动解压功能可能导致意外行为,优先使用 COPY
3. 忽略 .dockerignore
  • 未排除无关文件会导致构建上下文过大,延长构建时间。
4. 镜像层过多
  • 每层都增加镜像体积和复杂度,建议合并相关命令。

六、高级技巧

1. 条件构建
ARG ENV=production
RUN if [ "$ENV" = "development" ]; then apt-get install -y debug-tools; fi
2. 跨平台构建
docker buildx build --platform linux/amd64,linux/arm64 -t my-image .
3. 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost/ || exit 1

七、Dockerfile 与 CI/CD 集成

  • GitLab CI/CD

    build:
      image: docker:latest
      script:
        - docker build -t my-image .
        - docker push my-image
    
  • Jenkins

    pipeline {
      agent any
      stages {
        stage('Build') {
          steps {
            sh 'docker build -t my-image .'
          }
        }
      }
    }
    

示例

Docker内搭建LNMP

一.nginx

创建一个目录将我们所需要的软件都方进入该目录中

nginx/
├── dockerfile          # 构建 Nginx 镜像的脚本
├── nginx-1.19.5.tar.gz # Nginx 源码包
├── nginx.conf          # Nginx 配置文件按需更改最后一行要添加daemon off
└── run.sh              # 容器启动脚本

dockerfile内容

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

# 配置阿里云镜像源以加速软件下载
RUN rm -rf /etc/yum.repos.d/*
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
RUN yum clean all
RUN yum makecache

# 安装编译 Nginx 所需的工具和依赖库
RUN yum -y install gcc make pcre-devel openssl-devel zlib-devel

# 将本地的 Nginx 源码包添加到镜像中并解压到 /opt 目录
ADD nginx-1.19.5.tar.gz /opt

# 设置工作目录,后续命令将在此目录执行
WORKDIR /opt/nginx-1.19.5

# 配置并编译安装 Nginx 到 /usr/local/nginx 目录
RUN ./configure --prefix=/usr/local/nginx && make && make install 

# 添加自定义的 Nginx 配置文件,覆盖默认配置
ADD nginx.conf /usr/local/nginx/conf/nginx.conf

# 声明容器运行时监听的端口(仅文档作用,实际映射需通过 docker run -p 参数)
EXPOSE 80
EXPOSE 443

# 添加启动脚本并赋予执行权限
ADD run.sh /run.sh
RUN chmod 755 /run.sh

# 设置容器启动时执行的命令,运行自定义启动脚本
CMD ["/run.sh"]

run.sh内容

#!/bin/bash
/usr/local/nginx/sbin/nginx
构建镜像
# 使用当前目录(.)的Dockerfile构建一个名为mynginx的Docker镜像
# -t 参数:指定镜像的标签(tag),格式为 [仓库名]:[标签名]
# 若省略标签名,默认为 latest
# . 表示构建上下文(build context),即Docker引擎获取构建文件的路径
docker build -t mynginx .

二.php

创建一个目录将我们所需要的软件都方进入该目录中

php/
├── dockerfile ###构建php的脚本

dockerfile内容

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

# 设置维护者信息
MAINTAINER dufu

# 删除默认的yum源配置
RUN rm -rf /etc/yum.repos.d/*

# 配置阿里云的CentOS 7基础源
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo

# 配置阿里云的EPEL扩展源
RUN curl -o /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo

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

# 安装常用工具和PHP相关组件
RUN yum -y install wget net-tools php php-fpm php-common php-devel php-mysql

# 修改php-fpm配置,监听所有IP地址
RUN sed -i 's/127.0.0.1/0.0.0.0/g' /etc/php-fpm.d/www.conf

# 注释掉允许访问的客户端限制
RUN sed -i 's/listen.allowed_clients/;listen.allowed_clients/g' /etc/php-fpm.d/www.conf

# 启用PHP默认字符集配置
RUN sed -i 's/;default_charset/default_charset/g' /etc/php.ini

# 声明容器运行时监听的端口
EXPOSE 9000

# 容器启动时执行的命令,启动php-fpm服务
CMD ["php-fpm"]

构建镜像

docker build -t myphp . 

三.mysql

跟上面一样创建目录

mysql/                 # MySQL 相关配置与脚本的存放目录
├── dockerfile         # Docker 镜像构建文件(可选)
│                       - 用于自定义 MySQL 镜像(如预装工具、修改配置等)
│                       - 若未使用 `docker build` 构建镜像,此文件可能未生效
│                       - 示例场景:添加额外依赖、修改默认配置路径等
│
├── init.sh            # 初始化脚本(通常用于容器启动时执行预处理操作)
│                       - 常见用途:
│                         1. 启动前创建数据库、用户或导入初始数据
│                         2. 修改 MySQL 配置参数(如调整 max_connections)
│                         3. 修复目录权限(如给 /var/lib/mysql 赋予正确权限)
│                       - 使用方式:需在启动容器时通过 `--entrypoint` 或镜像构建时指定为入口脚本
│
└── my.cnf             # MySQL 核心配置文件
                        - 用于覆盖容器内默认的 MySQL 配置(如端口、数据目录、字符集等)
                        - 需注意与 MySQL 版本兼容(避免使用 MariaDB 特有配置,如 [mysqld_safe])
                        - 启动容器时通过 `-v 路径 挂载生效

dockerfile内容

# 基于CentOS 7基础镜像构建(与其他服务保持一致的操作系统版本,确保兼容性)
FROM centos:7

# 删除系统默认的yum源配置文件(清除自带的官方源,避免下载速度慢)
RUN rm -rf /etc/yum.repos.d/*

# 配置阿里云的CentOS 7基础yum源(使用国内镜像源,加速依赖包下载)
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo

# 清理yum缓存(删除旧的缓存数据,避免干扰新源的使用)
RUN yum clean all

# 生成yum缓存(将新源的包信息提前缓存,加速后续安装步骤)
RUN yum makecache

# 安装MariaDB数据库及服务端(MariaDB是MySQL的开源分支,CentOS 7默认推荐版本)
RUN yum -y install mariadb mariadb-server

# 修改MySQL数据目录权限(将/var/lib/mysql目录及其子文件的所有者改为mysql用户和组,确保数据库进程有权限读写数据)
RUN chown -R mysql:mysql /var/lib/mysql

# 将当前目录的init.sh脚本添加到容器的根目录(init.sh是数据库初始化脚本,用于设置密码、授权等)
ADD init.sh /init.sh

# 给init.sh脚本添加可执行权限(755表示所有者有读写执行权限,组和其他用户有读和执行权限)
RUN chmod 755 /init.sh

# 执行初始化脚本(在镜像构建阶段运行init.sh,完成数据库初始化、密码设置、权限授权等操作)
RUN /init.sh

# 声明容器对外暴露的端口(MySQL默认端口为3306,用于外部或其他容器访问数据库)
EXPOSE 3306

# 容器启动时执行的命令(启动mysqld服务,以安全模式运行确保服务稳定)
# 注意:原命令可能存在笔误,正确应为"mysqld_safe"(少了一个d)
CMD ["mysqld_safe"]

init.sh 内容

#!/bin/bash 
# 声明脚本解释器为 bash,确保脚本按 bash 语法执行

mysql_install_db --user=mysql
# 初始化 MySQL 数据库文件(适用于较旧的 MySQL 版本,如 5.6/5.7)
# --user=mysql:指定以 mysql 用户身份创建数据库文件(避免权限问题)
# 注意:MySQL 8.0+ 已弃用此命令,改用 mysqld --initialize 初始化

sleep 3
# 暂停 3 秒,等待数据库文件初始化完成(避免后续命令因初始化未完成而失败)

mysqld_safe &
# 以安全模式启动 MySQL 服务(后台运行,& 表示将进程放入后台)
# mysqld_safe 是 MySQL 的守护进程启动脚本,会自动重启崩溃的 mysqld 进程
# 注意:MySQL 8.0+ 推荐直接使用 mysqld 启动,此命令可能不兼容

sleep 3
# 暂停 3 秒,等待 MySQL 服务完全启动(确保后续命令能连接到数据库)

mysqladmin -u "root" password "123456"
# 为 root 用户设置初始密码(123456)
# mysqladmin 是 MySQL 管理工具,用于执行管理操作(如修改密码、关闭服务等)
# 注意:MySQL 8.0+ 初始密码存储在日志中,此命令可能失效,需改用 ALTER USER 语句

mysql -uroot -p123456 -e "grant all privileges on *.* to 'root'@'%' identified by '123456';"
# 允许 root 用户从任意主机(% 表示所有 IP)访问数据库,并授予所有权限
# -e:直接执行引号内的 SQL 命令(无需进入交互模式)
# 注意:MySQL 8.0+ 授权语法需分开设置权限和密码(先创建用户再授权)

mysql -uroot -p123456 -e "grant all privileges on *.* to 'root'@'localhost' identified by '123456';"
# 允许 root 用户从本地主机(localhost)访问数据库,并授予所有权限
# 补充本地访问权限,避免仅配置远程权限后本地无法登录

mysql -uroot -p123456 -e "flush privileges;"
# 刷新权限表,使上述授权配置立即生效(无需重启 MySQL 服务)

构建镜像

docker build -t mysql .

四.启动容器

# 1. 创建自定义桥接网络(命名为 my_net)
# 作用:让多个容器在同一网络内可通过容器名互相访问(无需依赖 IP)
docker network create my_net
# 输出:d751e714da17f23d09bc168de7f388ee07449db9278b400f987233d269166855
# 说明:
# - 自定义网络解决了容器间通信问题(默认 bridge 网络需用 IP 访问,自定义网络支持容器名解析)
# - 后续启动的 php01、nginx01、mysql01 都加入此网络,可直接通过容器名(如 php01)互相访问


# 2. 启动 PHP 容器(命名为 php01),并加入 my_net 网络
docker run -id \
  --net=my_net \          # 将容器加入 my_net 网络
  -v /web:/web \          # 挂载宿主机的 /web 目录到容器内的 /web(共享网站文件,如 PHP 代码)
  --name php01 \          # 给容器命名为 php01(方便网络访问和管理)
  myphp                   # 使用的镜像名为 myphp(自定义的 PHP 镜像,需确保已构建)
# 输出:a8c424e31437ce35023d9da5c5c745794abad3514fd7c130d939a6148fab9776
# 说明:
# - -i:交互式模式(保持标准输入打开),-d:后台运行( detach 模式),合并为 -id
# - 容器内的 PHP-FPM 通常监听 9000 端口,供 Nginx 反向代理(对应 Nginx 配置中的 fastcgi_pass php01:9000)


# 3. 启动 Nginx 容器(命名为 nginx01),加入 my_net 网络并映射 80 端口
docker run -id \
  --net=my_net \                  # 加入 my_net 网络,可访问 php01 容器
  -v /web:/web \                  # 挂载宿主机 /web 目录(与 PHP 容器共享网站文件,确保 Nginx 和 PHP 访问的文件一致)
  -v /root/nginx/nginx.conf:/usr/local/nginx/conf/nginx.conf \  # 挂载自定义 Nginx 配置文件(覆盖容器默认配置)
  -p 80:80 \                      # 端口映射:宿主机 80 端口 -> 容器内 80 端口(对外提供 HTTP 服务)
  --name nginx01 \                # 容器命名为 nginx01
  mynginx                         # 使用的镜像名为 mynginx(自定义的 Nginx 镜像,需确保已构建)
# 输出:22add0879a485aa3bf87efb2c3cd2392504a1c50622204c41f47ee60b74821b2
# 说明:
# - Nginx 容器通过挂载的配置文件,会将 .php 请求转发到 php01:9000(PHP-FPM)处理
# - 共享 /web 目录确保 Nginx 能读取 PHP 文件,且 PHP 解析时访问的文件路径一致


# 4. 启动 MySQL 容器(命名为 mysql01),加入 my_net 网络并映射 3306 端口
docker run \
  -p 3306:3306 \                  # 端口映射:宿主机 3306 端口 -> 容器内 3306 端口(对外提供 MySQL 服务)
  --name mysql01 \                # 容器命名为 mysql01
  -v /root/mysql/my.cnf:/etc/my.cnf \  # 挂载自定义 MySQL 配置文件(覆盖容器默认配置)
  --net=my_net \                  # 加入 my_net 网络,允许 PHP 容器通过 mysql01:3306 连接数据库
  -d \                            # 后台运行
  mysql                           # 使用官方 MySQL 镜像(未指定版本时默认 latest,建议显式指定如 mysql:8.0)
# 输出:eb6a96f2f03c31fdcffaa11c3738ecc62a47e8be9df8fc78f14eddddad12d516
# 说明:
# - 需注意挂载的 my.cnf 配置需与 MySQL 镜像版本兼容(避免 MariaDB 特有配置,否则可能启动失败)
# - 首次启动建议添加 -e MYSQL_ROOT_PASSWORD=xxx 环境变量设置 root 密码(否则可能无法登录)
# - PHP 容器中的应用可通过主机名 mysql01 和端口 3306 连接数据库(如 PHP 代码中数据库主机填写 mysql01)

五.验证

###在web目录有一个html的文件和两个php的文件我们分别访问这三个文件
[root@localhost web]# ls
test1.php  test2.php  index.html
###test1测试php  test2 连接数据库  index.html 自己处理nginx本身处理
[root@localhost ~]# curl 192.168.10.101:80
adasd
[root@localhost ~]# curl -I 192.168.10.101:80/test1.php
HTTP/1.1 200 OK
Server: nginx/1.19.5
Date: Thu, 10 Jul 2025 18:11:35 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/5.4.16
[root@localhost ~]# curl  192.168.10.101:80/test2.php
test OK

你可能感兴趣的:(docker,容器,LNMP,dockerfile,镜像制作)