文章目录
- 本篇摘要
- 一.基于centos:7使用dockerfile的C++镜像编写
- 二.基于CMD与ENTRYPOINT的正确使用
- 三.使用`dockerignore`测试
- 四.基于dockefile的多级构建
- 五.合理使用缓存
- 六.mysql主从同步
-
- [`docker compose build使用`](#
docker compose build使用) - `MySQL`主从同步
- [`docker compose build使用`](#
- 七.本篇小结

本篇摘要
本文详解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知识:
-
基本功能
- ENTRYPOINT和CMD均用于定义容器启动时执行的命令。
- 若镜像未设置这两个指令,运行时会报错。
-
覆盖行为
- Dockerfile中后写的指令会覆盖同类型的前置指令。
- 用户运行容器时可通过命令行参数覆盖Dockerfile中的CMD,但ENTRYPOINT需用
--entrypoint与-c强制覆盖。
-
模式区别
- Shell模式 (如
CMD echo "hello"):
命令通过/bin/sh -c启动,导致主进程PID非1,无法正常接收信号(如优雅终止容器)。 - Exec模式 (如
CMD ["echo", "hello"]):
直接运行命令,PID为1,支持信号转发,强烈推荐使用。
- Shell模式 (如
-
组合使用
- ENTRYPOINT定义固定执行命令,CMD提供默认参数。
- 最终命令为拼接形式:
<ENTRYPOINT> <CMD>,运行时可覆盖CMD参数。
-
应用建议
- 若希望容器始终执行固定程序,使用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 镜像构建的三种方式:

-
全部塞一起(一个镜像干所有事)
- 问题:Dockerfile 又长又难维护,镜像超大,部署慢。
-
分多个步骤(用多个Dockerfile+脚本)
- 镜像变小了,但要写多个文件+脚本,太麻烦。
-
多阶段构建(一个Dockerfile搞定)
- 只需一个文件,自动把编译和运行分开,最终镜像只留需要的东西,又小又干净!
结论:直接用第三种(多阶段构建),高效又省事!
下面演示下:
一个Dockerfile,先编译C程序,再把生成的可执行文件拷贝到小镜像里运行:
之前是默认一个centos的镜像同时编译运行等操作,下载了很多相关包文件没有清理干净:

- 可以看到一个centos的镜像外加对应包等很大(但是实际只需要一个exe即可)。
下面采取多级构建:


- 下面采取的是一个非常小的镜像源作为最终镜像(真正build的最后镜像以最后一个为主,其他中间镜像都会被清除);因此对应只需要对应程序的镜像就可以采取多级构建,把对应的exe只需要放在一个能跑程序的小镜像里即可(如busybox)。
总结:
多阶段构建可以很好的解决镜像的层次多,体积大,部署时间长、维护性低等问题;我们编译使用的软件,都没有打到我们的运行态的软件里面,所以可以变得更小。
五.合理使用缓存
-
顺序执行与缓存复用
Docker会按Dockerfile中的指令顺序构建,每步都会优先查找并使用缓存中已有的镜像层,避免重复工作。
-
修改即失效原则
Dockerfile中每条指令都会生成一个镜像层。一旦某一层的内容被修改,它之后的所有层缓存都会失效,需要重新构建。
-
可手动禁用缓存
可以通过
docker build --no-cache=true命令选项来完全禁用缓存,但合理利用缓存能极大提升构建效率。
举个例子:

- 这里的镜像只在COPY开始到以上有改变,因此再次构建镜像的时候下面的镜像就可以复用。
比如在频繁更改代码来编译程序:

- 这样更改就可以减少对应安装软件的频繁安装,直接复用上面的层。
演示下:
还是以之前的c++镜像制作为例:

- 这里发现很多都复用了之前构建的镜像的内容。
下面因为代码频繁更改,可以把对应的安装过程搞到上面,复用下对应缓存:


- 这里发现改了顺序时间更长了;因为之前的那一次完全复用缓存。

- 然后这一次从跑到yum这里发现上面的层就不同了;因此地下的复用(看到的就是过程的cached效果);然后下面重新构建。
下面测试下更改源代码效果:

- 可以发现现在只有6秒比第一次还快(第一次和第三次才有对比意义);可以看出先跑到更改层;然后发现之前的层都没动,直接复用之前的;然后更改层上面的重新构建(这里其实对应的重新安装软件也是比较耗时间的)。
总结下:
因此对应镜像构建的过程中;尽量让它不变化的放一起,变化的放一起都在上层;让它尽量能复用之前的缓存层;加快构建镜像的速度。
六.mysql主从同步
docker compose build使用
在 docker-compose.yml 里写 build,就能直接编译镜像,不用手动敲命令;就像给构建脚本找了个管家!
- 用途:一键把代码打包成镜像
- 用法:指定 Dockerfile 所在目录
- 好处:省事、自动化
两种方式进行docker-compose.yml使用:



- 成功位于不同位置的dockerfile进行构建镜像,但是不启动。
MySQL主从同步
主从同步概览

-
核心机制
数据从一个 MySQL 主节点自动复制到一个或多个从节点,默认采用异步方式。
-
主要目的(三大好处)
- 读写分离:主库处理写操作,从库处理读操作,提升整体性能和可用性。
- 数据备份:从库作为主库的实时数据备份,防止主库故障导致数据丢失。
- 高可用(HA):主库出现故障时,可以快速切换到从库继续提供服务。
-
灵活配置
从库可以复制主库的全部数据 ,也可以只复制特定的数据库或表。
主从同步大致流程

-
主库记录日志
主库(Master)收到写操作(增、删、改)后,先将数据更新,同时将变更操作记录到二进制日志(binlog) 中。
-
主库发送日志
主库为每个连接的从库(Slave)创建一个 binlog dump 线程 。该线程负责读取主库的 binlog,并将其发送给从库的 I/O 线程。
-
从库接收日志
从库的 I/O 线程 接收到 binlog 内容后,将其写入到本地的中继日志(relay log) 中。
-
从库应用日志
从库的 SQL 线程 读取本地的 relay log,解析并执行其中的 SQL 事件,从而在从库上重做数据变更,最终实现与主库的数据同步。
一句话总结:主库将写操作记入binlog,从库的IO线程取、SQL线程执行,最终实现数据同步。
那什么是binlog?
MySQL的binlog是记录数据库写操作(增删改)的日志,主要用于数据同步和恢复。它有三种记录模式:
- Statement :记SQL语句(日志小,但可能出错,比如使用了
now()主从同步就可能会出问题) - Row:记每行数据变化(绝对准,但日志大)
- Mixed :混合模式(默认推荐,智能选择前两种,兼顾效率和准确性)
主从同步五大方式
全同步方式

- 做法:主库干完活,必须等所有从库也干完,才能告诉你成功了。
- 好处 :主从数据100%一致。
- 坏处 :超级慢 (被最慢的从库拖垮(多从的时候)),且一个从库挂了,主库也卡住。
一句话:用速度和可用性换绝对的数据一致。
异步方式

- 默认模式 :MySQL主从复制默认采用的就是异步方式。
- 主库优先 :主库在自己成功执行完事务后 ,会立即向客户端返回成功响应,而不会等待从库的同步结果。
- 弱一致性 :主库不保证从库是否收到日志、是否已完成数据处理。这会导致主从数据存在短暂延迟,无法保证强一致性。
一句话:主库追求自身处理速度和响应能力,牺牲了数据的强一致性(比如主在同步过程,自己写了但是正在写从的时候挂了)。
半同步复制

-
核心机制
主库(Master)在自己完成事务后 ,必须等待至少一个从库(Slave)确认收到数据日志(binlog),才能向客户端返回成功响应。
-
最大优点(与异步复制比)
数据一致性更强。因为确保了至少一个从库有最新数据,降低了主库宕机导致数据完全丢失的风险。 -
主要缺点(与异步复制比)
- 性能较低:因为主库需要等待从库的确认,增加了事务处理的延迟。
- 存在幻读风险:如果主库在等待确认后、但在返回响应前宕机,客户端可能认为事务已成功,但实际上从库可能还未完全应用该数据。
一句话:半同步是异步和全同步的折衷方案,用部分性能换取比异步更好的一致性,但不如全同步可靠。
增强半同步复制

- 主库干活:收到请求,先记日志(binlog),发给从库(相当于半同步只是对应的主库存储在从库完成后进行的,数据一致性增强)。
- 主库等待 :必须等至少一个从库回复"收到",才能写入主库;告诉用户"成功了"。
- 从库备份:从库收日志、存日志(relaylog)、执行日志,然后通知主库。
好处:数据更安全(主从一致),解决了"幻读"问题。
代价:比纯异步慢一点(因为要等从库回信)。
一句话:主库等一个从库确认后才算成功,保证了数据不丢不错。
组复制

-
统一共识 :所有写操作必须先发送到中央的 "Consensus" (共识模块) 进行协调和冲突检测,确保所有节点认可该操作的有效性和顺序。这是实现多主复制的核心(分发给所有主节点;这样防止多主挂了;只要有一个主从在就保证数据一致)。
-
并行处理:
- 发起节点 (Master 1):执行事务 -> 提交共识 -> 记录binlog -> 提交。
- 同步节点 (Master 2/3):从共识层获取操作 -> 写入relay log -> 应用操作 -> 记录自身binlog -> 提交。
-
最终一致 :通过此流程,所有节点最终都会以相同的顺序应用所有写操作,从而保证整个集群的数据最终一致性。
一句话:所有写操作必须经过共识层审批,确保所有节点按相同顺序执行,最终实现多主数据同步。
MySQL主从形式
-
一主一从

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

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

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

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

从库再作为其他从库的主库,减轻主库压力,避免主库连接过多而性能下降(比如对应分析数据库,时长进行读操作,不追求性能,此时就可以把它作为以从为主的从库)。
所有形式都是为了实现数据备份、读写分离、提升性能和可用性。
一主二从MySQL集群搭建
下面基于一下流程进行搭建:
- 主节点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) 参数
-
--server-id=1- 设置 MySQL 实例的唯一 ID(主从集群中每个节点必须不同)。
-
--log-bin=mysql-bin- 启用二进制日志(binlog),记录所有数据更改,用于主从同步。
-
--binlog-ignore-db=mysql- 忽略系统库
mysql的变更,避免不必要的同步。
- 忽略系统库
-
--binlog_format=mixed- 设置 binlog 格式为混合模式(兼顾效率和可靠性)。
从节点 (mysql-slave, mysql-slave2) 参数
-
--server-id=2/--server-id=3- 设置从库的唯一 ID(不能与主库或其他从库重复)。
-
--relay-log=slave-relay-bin- 启用中继日志(relay log),暂存从主库接收的 binlog 数据。
-
--lower_case_table_names=1- 表名不区分大小写(避免主从同步因大小写问题失败)。
-
--character-set-server=utf8- 设置默认字符集为 UTF-8,支持中文等字符。
-
--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过滤等技巧,实现容器化部署的高性能与高可用。