近年来,随着 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
参数:
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 来管理,不需要我们关心具体的存储位置:
docker run --name some-mysql -v mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
如果要实现 MySQL 的配置文件外挂,也可以通过数据卷来实现:
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:
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,确保数据的持久性和可恢复性。同时,也要根据实际需求,合理配置服务的高可用和负载均衡。
或许,比较理想的方式是在掌握传统运维的基础上,多了解和学习云原生的新技术,根据自己的实际情况分析利弊,然后再决定是否要上容器的"车"。
与其教条地说"必须用"或"绝不用",不如开放一些,拥抱变化。