Dockerfile 基础与 C++ 镜像编写
对应 Dockerfile:
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 .
RUN yum makecache && yum install -y gcc
RUN gcc demo.c -o demo && \
rm -f demo.c && \
yum remove -y gcc
CMD ["/src/demo"]
执行构建命令:
docker build -t my-cpp-image .
等待镜像制作完成并运行。
CMD 与 ENTRYPOINT 的正确使用
基本功能
ENTRYPOINT 和 CMD 均用于定义容器启动时执行的命令。若镜像未设置这两个指令,运行时会报错。
覆盖行为
Dockerfile 中后写的指令会覆盖同类型的前置指令。用户运行容器时可通过命令行参数覆盖 Dockerfile 中的 CMD,但 ENTRYPOINT 需用 --entrypoint 强制覆盖。
模式区别
- Shell 模式(如
CMD echo "hello"):命令通过/bin/sh -c启动,导致主进程 PID 非 1,无法正常接收信号(如优雅终止容器)。 - Exec 模式(如
CMD ["echo", "hello"]):直接运行命令,PID 为 1,支持信号转发,强烈推荐使用。
组合使用
ENTRYPOINT 定义固定执行命令,CMD 提供默认参数。最终命令为拼接形式:<ENTRYPOINT> <CMD>,运行时可覆盖 CMD 参数。
应用建议
- 若希望容器始终执行固定程序,使用 ENTRYPOINT;
- 若需保留参数灵活性,可组合使用 ENTRYPOINT(Exec 模式)+ CMD(提供默认参数)。
多次指令覆盖测试
如果 Dockerfile 出现两次 CMD 或 ENTRYPOINT,后面的指令会覆盖前面的指令。手动输入参数也会覆盖 CMD。
Exec 与 Shell 模式对比
Shell 模式下启动后容器主进程是 /bin/sh。Exec 模式下主进程是对应命令,PID 为 1。
CMD 配合 ENTRYPOINT 使用
CMD 做参数,ENTRYPOINT 做命令,实时修改 CMD,实现解耦性;两者相互配合,使得操作更加灵活。
使用 .dockerignore 测试
Docker 是 C/S 架构。构建镜像时,客户端会把当前目录所有文件打包发送给服务端,这个包叫做 build context。用 .dockerignore 文件可以忽略不需要的文件(如日志、缓存),让镜像更小更安全。
构建命令:
docker build -t image-name .
一句话总结:build context 是客户端发给服务端的'材料包',用 .dockerignore 过滤掉多余文件。
测试显示,忽略 txt 结尾的文件后,启动后发现只有指定文件,无 txt 结尾的文件,说明 ignore 成功。
基于 Dockerfile 的多级构建
Docker 镜像构建的三种方式:
- 全部塞一起(一个镜像干所有事)
- 问题:Dockerfile 又长又难维护,镜像超大,部署慢。
- 分多个步骤(用多个 Dockerfile+ 脚本)
- 镜像变小了,但要写多个文件 + 脚本,太麻烦。
- 多阶段构建(一个 Dockerfile 搞定)
- 只需一个文件,自动把编译和运行分开,最终镜像只留需要的东西,又小又干净!
结论:直接用第三种(多阶段构建),高效又省事!
演示:
一个 Dockerfile,先编译 C 程序,再把生成的可执行文件拷贝到小镜像里运行。
之前是默认一个 centos 的镜像同时编译运行等操作,下载了很多相关包文件没有清理干净。
下面采取多级构建:
最后采取的是一个非常小的镜像源作为最终镜像(真正 build 的最后镜像以最后一个为主,其他中间镜像都会被清除);因此对应只需要对应程序的镜像就可以采取多级构建,把对应的 exe 只需要放在一个能跑程序的小镜像里即可(如 busybox)。
总结:
多阶段构建可以很好的解决镜像的层次多,体积大,部署时间长、维护性低等问题;我们编译使用的软件,都没有打到我们的运行态的软件里面,所以可以变得更小。
合理使用缓存
- 顺序执行与缓存复用
Docker 会按 Dockerfile 中的指令顺序构建,每步都会优先查找并使用缓存中已有的镜像层,避免重复工作。 - 修改即失效原则
Dockerfile 中每条指令都会生成一个镜像层。一旦某一层的内容被修改,它之后的所有层缓存都会失效,需要重新构建。 - 可手动禁用缓存
可以通过docker build --no-cache=true命令选项来完全禁用缓存,但合理利用缓存能极大提升构建效率。
例子:
这里的镜像只在 COPY 开始到以上有改变,因此再次构建镜像的时候下面的镜像就可以复用。
比如在频繁更改代码来编译程序:
这样更改就可以减少对应安装软件的频繁安装,直接复用上面的层。
演示:
还是以之前的 c++ 镜像制作为例:
这里发现很多都复用了之前构建的镜像的内容。
下面因为代码频繁更改,可以把对应的安装过程搞到上面,复用下对应缓存:
这里发现改了顺序时间更长了;因为之前的那一次完全复用缓存。
然后这一次从跑到 yum 这里发现上面的层就不同了;因此地下的复用(看到的就是过程的 cached 效果);然后下面重新构建。
下面测试下更改源代码效果:
可以发现现在比第一次快;可以看出先跑到更改层;然后发现之前的层都没动,直接复用之前的;然后更改层上面的重新构建(这里其实对应的重新安装软件也是比较耗时间的)。
总结:
因此对应镜像构建的过程中;尽量让它不变化的放一起,变化的放一起都在上层;让它尽量能复用之前的缓存层;加快构建镜像的速度。
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 个从节点 Dockerfile-初始化脚本
- 编写 docker-compose.yml
- 构建镜像
- 启动服务,检查服务和同步状态
- 创建库表写入数据,检查是否同步
首先看下对应目录:
构建主库的 dockerfile(默认初始化,故无需 copy 对应 sql 脚本):

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

对应的从的初始化 sql:

对应的总的 docker-compose 文件:
services:
mysql-master:
build:
context: ./
dockerfile: ./master/Dockerfile-master
image: mysqlmaster:v1.0
restart: always
container_name: mysql-master
volumes:
- ./master/var/lib/mysql:/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:
- ./slave/var/lib/mysql:/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:
- ./slave2/var/lib/mysql:/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 中。
通过下面的指令进行主从查看:
SHOW MASTER STATUS\G
SHOW SLAVE STATUS\G
下面进行插入数据看是否同步:
主库插入:
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 过滤等技巧,实现容器化部署的高性能与高可用。

