别让 Docker 毁了你的 MySQL!

近年来,随着 Docker 容器技术的火热,越来越多的应用开始被"装箱"。我们这些开发者享受着 Docker 带来的便捷和灵活,几乎恨不得把所有东西都塞进容器里。于是,连 MySQL 这样的有状态服务,也开始频频出现在 Docker 的舞台上。

但是,把 MySQL 装进 Docker,真的是个明智的选择吗?是否会给系统稳定性和数据安全带来隐患?这个问题在社区里引发了激烈的讨论。

"Docker 与有状态服务无缘"派

持反对意见的一派认为,所有有状态的服务,都不应该放在 Docker 里跑。他们列举了 MySQL、Redis、Elasticsearch、ActiveMQ 等服务作为例子,理由是这些服务都需要保证高可用和稳定性,而这恰恰是 Docker 难以做到的。

即便是在 Kubernetes 这样的容器编排平台中,他们也不建议把有状态服务放进去。这也是为什么云厂商会专门提供 MySQL、Redis、MQ 等服务的原因。

那么,究竟是什么原因让"Docker 与有状态服务无缘"呢?

首先,传统的高可用方案在容器中难以实现。以 MySQL 为例,传统的 HA 方案通常是两台机器+共享存储+Fence 设备。

但在 Kubernetes 中,这种方案如何配置?Fence 设备又该如何在容器中实现?这些都是没有现成答案的问题。

其次,一些 MySQL 的高可用和集群方案与 Docker 的理念不太兼容。

比如,MySQL 8.0 的 InnoDB Cluster 通常采用 1 主 2 从的模式,这在 Kubernetes 中如何实现?虽然有 MySQL Operator 这样的尝试,但成熟度和可行性还有待验证。

最后,对于 DBA 来说,在虚拟机上部署 MySQL 才是最熟悉和放心的方式。

毕竟数据库最需要的是稳定,调整 CPU、内存、磁盘等参数,再加上定期备份,就可以做到大多数时候"甩手掌柜"。

如果是在公有云上,那就更应该直接用云厂商提供的 MySQL 服务。

在他们看来,Docker 适合运行无状态服务。因为无状态服务挂了就拉起来,不需要做额外的检查,而有状态服务则可能由于数据不一致而带来新的问题。

"MySQL 照样可以容器化"派

但支持在 Docker 中运行 MySQL 的声音也不小。毕竟,所有主流数据库都已经提供了官方镜像,多数也有容器化的集群方案。他们认为,只要在部署时遵循一些最佳实践,就可以充分发挥 Docker 的优势,而不必担心稳定性和性能问题。

首先,在部署 MySQL 容器时,一定要记得挂载数据卷(Data Volume) 。通过数据卷,可以将容器内的数据目录映射到宿主机上,这样不仅可以避免容器删除时数据丢失,还能减少额外的 I/O 开销。例如,如果我们希望将容器内的 /var/lib/mysql 目录(MySQL 默认的数据目录)映射到宿主机的 /my/own/datadir 目录,可以在启动容器时使用 -v 参数:

bash 复制代码
docker run --name some-mysql -v /my/own/datadir:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag

除了手动指定目录,我们也可以使用 Docker 的命名卷(Named Volume)功能。命名卷可以由 Docker 来管理,不需要我们关心具体的存储位置:

bash 复制代码
docker run --name some-mysql -v mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag

如果要实现 MySQL 的配置文件外挂,也可以通过数据卷来实现:

bash 复制代码
docker run --name some-mysql -v /my/own/datadir:/var/lib/mysql -v /my/custom/mysql.cnf:/etc/mysql/conf.d/mysql.cnf -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag

其次,使用容器部署 MySQL 有诸多优点。容器可以封装不同版本的 MySQL,而不用担心与宿主机的兼容性问题 。同时,由于容器之间是隔离的,可以保证 MySQL 不会与其他服务产生干扰。此外,通过容器还能在单机上部署多个 MySQL 实例,提高资源利用率

至于性能问题,支持者认为在正确使用数据卷的情况下,MySQL 容器的性能损失微乎其微。毕竟容器只是一层轻量级的封装,并不会对 I/O、网络等产生实质影响。

在 Kubernetes 中部署有状态服务,通常需要用到 StatefulSet 和 PersistentVolumeClaim(PVC) 。StatefulSet 用于管理有状态的应用,它为每个 Pod 提供一个唯一且固定的标识符。PVC 则用于申请持久化存储。下面是一个简单的 StatefulSet 示例,用于部署一个单节点的 MySQL:

yaml 复制代码
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:5.7
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: my-secret-pw
          volumeMounts:
            - name: data
              mountPath: /var/lib/mysql
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 10Gi

在这个例子中,StatefulSet 会自动为 MySQL 的 Pod 创建一个专属的 PVC,并将其挂载到容器的 /var/lib/mysql 目录。这样,即使 Pod 重建,它的数据也不会丢失。

当然,这只是一个最简单的示例。在实际使用中,我们可能还需要配置 MySQL 的主从复制、读写分离等,这就需要更复杂的 StatefulSet 和 Service 配置了。

求同存异,因地制宜

对于我这个又做开发又做 DevOps 的过来人觉得,MySQL 是否适合容器化,还是要具体问题具体分析。正如 Stackoverflow 上一位答主所说,

技术只是达成目标的工具,适合自己的才是最好的

对于开发和测试环境,或者对性能和稳定性要求不高的场景,Docker 化的 MySQL 确实能带来不少便利。开发者可以快速搭建数据库,方便地进行各种实验和测试。

对于生产环境尤其是核心系统的数据库,稳定性和数据安全性无疑是第一位的,这时谨慎一些,采用成熟的方案或许更有保障。

同时也要认识到,容器技术正在飞速发展,很多以前难以想象的场景,现在都已逐渐成为可能。比如有了 Kubernetes、Operator 这样的云原生技术,有状态服务在容器中的管理已经比以前简单很多。未来会怎样,现在下定论还为时尚早。

在 Docker 和 Kubernetes 中运行有状态服务,关键是要充分利用数据卷和 PVC,确保数据的持久性和可恢复性。同时,也要根据实际需求,合理配置服务的高可用和负载均衡。

或许,比较理想的方式是在掌握传统运维的基础上,多了解和学习云原生的新技术,根据自己的实际情况分析利弊,然后再决定是否要上容器的"车"。

与其教条地说"必须用"或"绝不用",不如开放一些,拥抱变化

相关推荐
冰红茶兑滴水16 分钟前
MySQL 表的约束
数据库·mysql
小五Z33 分钟前
MySql--增删改查表设计总结
数据结构·数据库·mysql
锐策1 小时前
〔 MySQL 〕数据类型
数据库·mysql
Firechou1 小时前
SpringBoot+MyBatis+MySQL的Point实现范围查找
spring boot·mysql·mybatis·point·范围查找·附近查找
basic_code1 小时前
Docker部署kafka集群
docker·容器·kafka
门牙咬脆骨2 小时前
【MYSQL】数据库日志 (了解即可)
数据库·mysql
ModelBulider2 小时前
十三、注解配置SpringMVC
java·开发语言·数据库·sql·mysql
豆 腐2 小时前
MySQL【四】
android·数据库·笔记·mysql
苹果酱05672 小时前
C语言 char 字符串 - C语言零基础入门教程
java·开发语言·spring boot·mysql·中间件
水宝的滚动歌词2 小时前
K8S单节点部署及集群部署
云原生·容器·kubernetes