Docker高阶实战:从镜像构建优化策略实践到MySQL主从集群详解+一主二从容器化实现,一文打通生产级部署!

文章目录

本篇摘要

本文详解Docker核心技术:从C++镜像构建、CMD/ENTRYPOINT用法对比、多阶段构建优化,到MySQL主从集群实战。通过Dockerfile优化、缓存利用及主从同步原理剖析,提供高效容器化部署方案,助力掌握生产级Docker应用技巧。

一.基于centos:7使用dockerfile的C++镜像编写

对应dockerfile:

cpp 复制代码
FROM centos:7

# 设置版本
ENV VERSION 1.0

# 替换国内源
RUN sed -i.bak \
  -e 's|^mirrorlist=|#mirrorlist=|g' \
  -e 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.ustc.edu.cn/centos-vault/centos|g' \
  /etc/yum.repos.d/CentOS-Base.repo

# 设置工作目录
WORKDIR /src

# 拷贝源文件
COPY demo.c .

# 安装gcc编译器
RUN yum makecache && yum install -y gcc

# 编译并清理
RUN gcc demo.c -o demo && \
    rm -f demo.c && \
    yum remove -y gcc

# 运行可执行文件 默认启动就执行
CMD ["/src/demo"]

下面进行build:

  • 等待镜像制作。
  • 成功运行最后一次cmd。

二.基于CMD与ENTRYPOINT的正确使用

CMD与ENTRYPOINT知识:

  1. 基本功能

    • ENTRYPOINT和CMD均用于定义容器启动时执行的命令。
    • 若镜像未设置这两个指令,运行时会报错。
  2. 覆盖行为

    • Dockerfile中后写的指令会覆盖同类型的前置指令。
    • 用户运行容器时可通过命令行参数覆盖Dockerfile中的CMD,但ENTRYPOINT需用--entrypoint与-c强制覆盖。
  3. 模式区别

    • Shell模式 (如CMD echo "hello"):
      命令通过/bin/sh -c启动,导致主进程PID非1,无法正常接收信号(如优雅终止容器)。
    • Exec模式 (如CMD ["echo", "hello"]):
      直接运行命令,PID为1,支持信号转发,强烈推荐使用
  4. 组合使用

    • ENTRYPOINT定义固定执行命令,CMD提供默认参数。
    • 最终命令为拼接形式:<ENTRYPOINT> <CMD>,运行时可覆盖CMD参数。
  5. 应用建议

    • 若希望容器始终执行固定程序,使用ENTRYPOINT;
    • 若需保留参数灵活性,可组合使用ENTRYPOINT(Exec模式) + CMD(提供默认参数)。

演示下:

1·如果dockerfile出现两次cmd会咋样?


  • 发现已经有提示说明了。
  • 被最后一个cmd内容覆盖。
  • 手动输入参数也会覆盖cmd。

2·如果dockerfile出现两次entrypoint会咋样?


  • 同样也是覆盖。

演示下强制覆盖entrypoint:

  • 一个exec执行一个就是shell。

3·entrypoint搭配run输入的cmd参数使用:


  • 可以看出直接喂给它,作为参数了。

4·exec与shell模式对比:


  • shell模式发现启动后容器主进程不是对应指令而是bin/sh----->shell。

如果是exec模式呢(默认不会加上bin/sh)?



  • 发现对应的主进程就是1,对应的命令。

5.cmd配合entrypoint使用:

  • 默认ping主机也是成功的。

还可以根据ping的对象随时更改:

  • cmd做参数,entrypoint做命令,实时修改cmd;实现解耦性;两者相互配合,使得操作更加灵活。

三.使用dockerignore测试

简单介绍下:

  • Docker 是 C/S 架构。

  • 构建镜像时,客户端(Client)会把当前目录(即命令最后的 .)所有文件打包发送给服务端(Server),这个包叫做 build context

  • .dockerignore 文件可以忽略不需要的文件(如日志、缓存),让镜像更小更安全。

构建命令:

bash 复制代码
docker build -t 镜像名 .

一句话总结:build context 是客户端发给服务端的"材料包",用 .dockerignore过滤掉多余文件

下面来测试下:

  • 这里把对应宿主机此目录下的所有文件都cp进镜像里。


  • 然后把忽略的txt结尾文件加进去。
  • 启动后发现只有这俩,无txt结尾的文件;说明ignore成功。

四.基于dockefile的多级构建

  • 镜像程序构建步骤。

Docker 镜像构建的三种方式:

  1. 全部塞一起(一个镜像干所有事)

    • 问题:Dockerfile 又长又难维护,镜像超大,部署慢。
  2. 分多个步骤(用多个Dockerfile+脚本)

    • 镜像变小了,但要写多个文件+脚本,太麻烦。
  3. 多阶段构建(一个Dockerfile搞定)

    • 只需一个文件,自动把编译和运行分开,最终镜像只留需要的东西,又小又干净!

结论:直接用第三种(多阶段构建),高效又省事!

下面演示下:

一个Dockerfile,先编译C程序,再把生成的可执行文件拷贝到小镜像里运行:

之前是默认一个centos的镜像同时编译运行等操作,下载了很多相关包文件没有清理干净:

  • 可以看到一个centos的镜像外加对应包等很大(但是实际只需要一个exe即可)。

下面采取多级构建:


  • 下面采取的是一个非常小的镜像源作为最终镜像(真正build的最后镜像以最后一个为主,其他中间镜像都会被清除);因此对应只需要对应程序的镜像就可以采取多级构建,把对应的exe只需要放在一个能跑程序的小镜像里即可(如busybox)。

总结:

多阶段构建可以很好的解决镜像的层次多,体积大,部署时间长、维护性低等问题;我们编译使用的软件,都没有打到我们的运行态的软件里面,所以可以变得更小。

五.合理使用缓存

  1. 顺序执行与缓存复用

    Docker会按Dockerfile中的指令顺序构建,每步都会优先查找并使用缓存中已有的镜像层,避免重复工作。

  2. 修改即失效原则

    Dockerfile中每条指令都会生成一个镜像层。一旦某一层的内容被修改,它之后的所有层缓存都会失效,需要重新构建。

  3. 可手动禁用缓存

    可以通过 docker build --no-cache=true 命令选项来完全禁用缓存,但合理利用缓存能极大提升构建效率。

举个例子:

  • 这里的镜像只在COPY开始到以上有改变,因此再次构建镜像的时候下面的镜像就可以复用。

比如在频繁更改代码来编译程序:

  • 这样更改就可以减少对应安装软件的频繁安装,直接复用上面的层。

演示下:

还是以之前的c++镜像制作为例:

  • 这里发现很多都复用了之前构建的镜像的内容。

下面因为代码频繁更改,可以把对应的安装过程搞到上面,复用下对应缓存:


  • 这里发现改了顺序时间更长了;因为之前的那一次完全复用缓存。
  • 然后这一次从跑到yum这里发现上面的层就不同了;因此地下的复用(看到的就是过程的cached效果);然后下面重新构建。

下面测试下更改源代码效果:

  • 可以发现现在只有6秒比第一次还快(第一次和第三次才有对比意义);可以看出先跑到更改层;然后发现之前的层都没动,直接复用之前的;然后更改层上面的重新构建(这里其实对应的重新安装软件也是比较耗时间的)。

总结下:

因此对应镜像构建的过程中;尽量让它不变化的放一起,变化的放一起都在上层;让它尽量能复用之前的缓存层;加快构建镜像的速度。

六.mysql主从同步

docker compose build使用

docker-compose.yml 里写 build,就能直接编译镜像,不用手动敲命令;就像给构建脚本找了个管家!

  • 用途:一键把代码打包成镜像
  • 用法:指定 Dockerfile 所在目录
  • 好处:省事、自动化

两种方式进行docker-compose.yml使用:



  • 成功位于不同位置的dockerfile进行构建镜像,但是不启动。

MySQL主从同步

主从同步概览

  1. 核心机制

    数据从一个 MySQL 主节点自动复制到一个或多个从节点,默认采用异步方式

  2. 主要目的(三大好处)

    • 读写分离:主库处理写操作,从库处理读操作,提升整体性能和可用性。
    • 数据备份:从库作为主库的实时数据备份,防止主库故障导致数据丢失。
    • 高可用(HA):主库出现故障时,可以快速切换到从库继续提供服务。
  3. 灵活配置

    从库可以复制主库的全部数据 ,也可以只复制特定的数据库或表

主从同步大致流程

  1. 主库记录日志

    主库(Master)收到写操作(增、删、改)后,先将数据更新,同时将变更操作记录到二进制日志(binlog) 中。

  2. 主库发送日志

    主库为每个连接的从库(Slave)创建一个 binlog dump 线程 。该线程负责读取主库的 binlog,并将其发送给从库的 I/O 线程

  3. 从库接收日志

    从库的 I/O 线程 接收到 binlog 内容后,将其写入到本地的中继日志(relay log) 中。

  4. 从库应用日志

    从库的 SQL 线程 读取本地的 relay log,解析并执行其中的 SQL 事件,从而在从库上重做数据变更,最终实现与主库的数据同步。

一句话总结:主库将写操作记入binlog,从库的IO线程取、SQL线程执行,最终实现数据同步。

那什么是binlog?

MySQL的binlog是记录数据库写操作(增删改)的日志,主要用于数据同步和恢复。它有三种记录模式:

  1. Statement :记SQL语句(日志小,但可能出错,比如使用了now()主从同步就可能会出问题)
  2. Row:记每行数据变化(绝对准,但日志大)
  3. Mixed混合模式(默认推荐,智能选择前两种,兼顾效率和准确性)

主从同步五大方式

全同步方式
  1. 做法:主库干完活,必须等所有从库也干完,才能告诉你成功了。
  2. 好处 :主从数据100%一致
  3. 坏处超级慢 (被最慢的从库拖垮(多从的时候)),且一个从库挂了,主库也卡住

一句话:用速度和可用性换绝对的数据一致。

异步方式
  1. 默认模式 :MySQL主从复制默认采用的就是异步方式。
  2. 主库优先 :主库在自己成功执行完事务后 ,会立即向客户端返回成功响应,而不会等待从库的同步结果。
  3. 弱一致性 :主库不保证从库是否收到日志、是否已完成数据处理。这会导致主从数据存在短暂延迟,无法保证强一致性。

一句话:主库追求自身处理速度和响应能力,牺牲了数据的强一致性(比如主在同步过程,自己写了但是正在写从的时候挂了)。

半同步复制
  1. 核心机制

    主库(Master)在自己完成事务后 ,必须等待至少一个从库(Slave)确认收到数据日志(binlog),才能向客户端返回成功响应。

  2. 最大优点(与异步复制比)
    数据一致性更强。因为确保了至少一个从库有最新数据,降低了主库宕机导致数据完全丢失的风险。

  3. 主要缺点(与异步复制比)

    • 性能较低:因为主库需要等待从库的确认,增加了事务处理的延迟。
    • 存在幻读风险:如果主库在等待确认后、但在返回响应前宕机,客户端可能认为事务已成功,但实际上从库可能还未完全应用该数据。

一句话:半同步是异步和全同步的折衷方案,用部分性能换取比异步更好的一致性,但不如全同步可靠。

增强半同步复制
  1. 主库干活:收到请求,先记日志(binlog),发给从库(相当于半同步只是对应的主库存储在从库完成后进行的,数据一致性增强)。
  2. 主库等待必须等至少一个从库回复"收到",才能写入主库;告诉用户"成功了"。
  3. 从库备份:从库收日志、存日志(relaylog)、执行日志,然后通知主库。

好处:数据更安全(主从一致),解决了"幻读"问题。
代价:比纯异步慢一点(因为要等从库回信)。

一句话:主库等一个从库确认后才算成功,保证了数据不丢不错。

组复制
  1. 统一共识 :所有写操作必须先发送到中央的 "Consensus" (共识模块) 进行协调和冲突检测,确保所有节点认可该操作的有效性和顺序。这是实现多主复制的核心(分发给所有主节点;这样防止多主挂了;只要有一个主从在就保证数据一致)。

  2. 并行处理

    • 发起节点 (Master 1):执行事务 -> 提交共识 -> 记录binlog -> 提交。
    • 同步节点 (Master 2/3):从共识层获取操作 -> 写入relay log -> 应用操作 -> 记录自身binlog -> 提交。
  3. 最终一致 :通过此流程,所有节点最终都会以相同的顺序应用所有写操作,从而保证整个集群的数据最终一致性

一句话:所有写操作必须经过共识层审批,确保所有节点按相同顺序执行,最终实现多主数据同步。

MySQL主从形式

  1. 一主一从

    一个主库配一个从库,最基本、最常用的备份与读写分离架构。

  2. 一主多从

一个主库配多个从库,显著提高系统的读性能,是扩展读能力的标准做法。

  1. 多主一从

多个主库的数据同步到一个从库,用于将多个数据库备份到一台高性能存储服务器

  1. 双主复制

两个主库互为主从,任何一方的修改都会同步到另一方,实现双向同步。

  1. 级联复制

从库再作为其他从库的主库,减轻主库压力,避免主库连接过多而性能下降(比如对应分析数据库,时长进行读操作,不追求性能,此时就可以把它作为以从为主的从库)。

所有形式都是为了实现数据备份、读写分离、提升性能和可用性

一主二从MySQL集群搭建

下面基于一下流程进行搭建:

  1. 主节点Dockerfile-初始化脚本 注:这里主默认初始化故无需脚本了 2. 2个从节点Dockerfile-初始化脚本 3. 编写docker-compose.yml 4. 构建镜像 5. 启动服务, 检查服务和同步状态 6. 创建库表写入数据, 检查是否同步

首先看下对应目录:

构建主库的dockerfile(默认初始化,故无需copy对应sql脚本):

俩从库的dockerfile(需要手动同步):

对应的从的初始化sql:

对应的总的docker-compose文件:

cpp 复制代码
services:
  mysql-master:
    build:
      context: ./
      dockerfile: ./master/Dockerfile-master
    image: mysqlmaster:v1.0
    restart: always
    container_name: mysql-master
    volumes:
      - ./mastervarlib:/var/lib/mysql
    ports:
      - 9306:3306
    environment:
      MYSQL_ROOT_PASSWORD: root
    privileged: true
    command: [
      '--server-id=1',
      '--log-bin=mysql-bin',
      '--binlog-ignore-db=mysql',
      '--binlog_cache_size=256M',
      '--binlog_format=mixed',
      '--lower_case_table_names=1',
      '--character-set-server=utf8',
      '--collation-server=utf8_general_ci'
    ]

  mysql-slave:
    build:
      context: ./
      dockerfile: ./slave/Dockerfile-slave
    image: mysqlslave:v1.0
    restart: always
    container_name: mysql-slave
    volumes:
      - ./slavevarlib:/var/lib/mysql
    ports:
      - 9307:3306
    environment:
      MYSQL_ROOT_PASSWORD: root
    privileged: true
    command: [
      '--server-id=2',
      '--relay-log=slave-relay-bin',
      '--lower_case_table_names=1',
      '--character-set-server=utf8',
      '--collation-server=utf8_general_ci'
    ]
    depends_on:
      - mysql-master

  mysql-slave2:
    build:
      context: ./
      dockerfile: ./slave2/Dockerfile-slave2
    image: mysqlslave2:v1.0
    restart: always
    container_name: mysql-slave2
    volumes:
      - ./slave2varlib:/var/lib/mysql
    ports:
      - 9308:3306
    environment:
      MYSQL_ROOT_PASSWORD: root
    privileged: true
    command: [
      '--server-id=3',
      '--relay-log=slave2-relay-bin',
      '--lower_case_table_names=1',
      '--character-set-server=utf8',
      '--collation-server=utf8_general_ci'
    ]
    depends_on:
      - mysql-master

这里对应的master的密码用户都是root;然后在两个从的初始化sql中都设置了用户名和密码都是root。

下面熟悉了之前的docker compose 配置参数后;这里就值介绍对应command了:

主节点 (mysql-master) 参数

  1. --server-id=1

    • 设置 MySQL 实例的唯一 ID(主从集群中每个节点必须不同)。
  2. --log-bin=mysql-bin

    • 启用二进制日志(binlog),记录所有数据更改,用于主从同步。
  3. --binlog-ignore-db=mysql

    • 忽略系统库 mysql 的变更,避免不必要的同步。
  4. --binlog_format=mixed

    • 设置 binlog 格式为混合模式(兼顾效率和可靠性)。

从节点 (mysql-slave, mysql-slave2) 参数

  1. --server-id=2 / --server-id=3

    • 设置从库的唯一 ID(不能与主库或其他从库重复)。
  2. --relay-log=slave-relay-bin

    • 启用中继日志(relay log),暂存从主库接收的 binlog 数据。
  3. --lower_case_table_names=1

    • 表名不区分大小写(避免主从同步因大小写问题失败)。
  4. --character-set-server=utf8

    • 设置默认字符集为 UTF-8,支持中文等字符。
  5. --collation-server=utf8_general_ci

    • 设置默认排序规则,确保字符串比较一致。

下面同规格docker compose进行镜像生成:

  • 等待很长时间。
  • 成功构建。
  • 成功构建好容器。


  • 进入主从mysql中。

通过下面的指令进行主从查看:

bash 复制代码
SHOW MASTER STATUS\G
SHOW SLAVE STATUS\G



下面进行插入数据看是否同步:

主库插入:

bash 复制代码
mysql> create database db1
    -> ;
Query OK, 1 row affected (0.00 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| db1                |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

mysql> use db1;
Database changed
mysql> insert into t1 values("zs",1),("lm",2);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from t1
    -> ;
+------+------+
| name | age  |
+------+------+
| zs   |    1 |
| lm   |    2 |
+------+------+
2 rows in set (0.00 sec)

mysql> 

从库查看:


  • 发现同步成功。

至此可以看到我们搭建的 mysql 集群已经能够正常进行工作。

七.本篇小结

本篇通过Dockerfile多阶段构建大幅精简镜像体积,利用缓存机制提升构建效率;深入解析MySQL主从复制五大模式,并完成一主二从集群实战。掌握ENTRYPOINT+CMD组合、.dockerignore过滤等技巧,实现容器化部署的高性能与高可用。

相关推荐
万物得其道者成37 分钟前
用 Python + MySQL + Web 打造我的私有 Apple 设备监控面板
前端·python·mysql
0__O1 小时前
Docker 浅谈
docker
kaico20181 小时前
MYSQL的日志文件
数据库·mysql
wok1571 小时前
Windows 上使用 Docker Desktop 教程
windows·docker·容器
MUTA️2 小时前
x86 架构下运行 ARM-ROS2 Docker 镜像操作指南
arm开发·docker·架构
hqzing2 小时前
低成本玩转鸿蒙容器的丐版方案
docker·harmonyos
我的golang之路果然有问题2 小时前
mysql 个人笔记导出之-数据库时间戳问题以及增删改查
数据库·笔记·学习·mysql·分享·个人笔记
JH30732 小时前
我的笔记:怎么用 MySQL 的 EXPLAIN 来分析 SQL
笔记·sql·mysql
ybb_ymm2 小时前
如何通过跳板机链接mysql数据库
数据库·mysql
7ioik2 小时前
RC和RR隔离级别下MVCC的差异?
数据库·sql·mysql