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过滤等技巧,实现容器化部署的高性能与高可用。

相关推荐
网络小白不怕黑15 小时前
Docker容器网络:四大模式解析与自定义网络
运维·docker·容器
梁萌18 小时前
MySQL数据库分库分表介绍
数据库·mysql·shardingsphere·分库分表
gwd20019 小时前
如何快速设置 Docker 代理设置
运维·人工智能·docker·容器
Cat God 00721 小时前
SQL使用及注意事项
数据库·sql·mysql
一苓二肆21 小时前
代码常用工具使用
git·vscode·docker·github·vim
Lynnxiaowen21 小时前
今天我们继续学习kubernetes内容Helm
linux·学习·容器·kubernetes·云计算
华仔啊21 小时前
如何避免MySQL死锁?资深DBA的9条黄金法则
后端·mysql
weixin_5214311221 小时前
Docker容器技术
运维·docker·容器
@老蝴21 小时前
MySQL数据库 - 约束和联合查询
android·数据库·mysql