Terraform创建云服务器时,关键在于声明式资源编排而非编写bash脚本;MySQL部署应通过cloud-init或systemd实现,避免使用易失败的remote-exec provisioner,并需显式处理SELinux和firewalld等系统级配置。

用 Terraform 创建云服务器节点时,关键不是“写脚本”,而是定义资源生命周期
直接写 bash 脚本部署 MySQL 在 Terraform 里是反模式。Terraform 的核心是声明式资源编排,provisioner "remote-exec" 只能作为最后手段,且极易因 SSH 连接中断、命令超时或权限问题导致 apply 卡住或失败。
真正可靠的路径是:Terraform 负责拉起节点 + 配置基础环境(如用户、目录、防火墙),MySQL 安装和初始化交给更健壮的工具——比如 cloud-init(主流云平台原生支持)或 systemd 服务单元文件。
- 在 AWS/Azure/GCP 中,把 MySQL 安装逻辑写进
user_data,比用remote-exec稳定十倍 - 避免在
provisioner里调用mysql_secure_installation——它会交互式阻塞,必须用expect或重写为非交互命令,但后者依赖 MySQL 版本行为,8.0.4+ 和 8.0.28+ 的初始化流程已有差异 - 如果必须用
remote-exec,务必加timeout = "600s"并用connection块显式指定host、user、private_key,别依赖默认解析
MySQL 8.0 初始化必须绕开 interactive mode,否则 Terraform 会 hang 住
Terraform 执行 remote-exec 时没有伪终端(pty),而 mysql_secure_installation 默认检测到非交互终端就直接退出,报错类似:ERROR 1045 (28000): Access denied for user 'root'@'localhost',实际是因为它根本没运行完初始化流程。
正确做法是跳过向导,用纯命令行完成初始化:
- 先用
mysqld --initialize-insecure --datadir=/var/lib/mysql启动空实例(仅限测试环境) - 或更安全地:用
mysqld --initialize --datadir=/var/lib/mysql生成临时密码,再通过mysql -u root -p"$(cat /var/log/mysqld.log | grep 'temporary password' | awk '{print $NF}')" ...自动登录并改密 - 必须禁用 validate_password 插件(8.0 默认启用),否则
ALTER USER会因密码强度被拒;可在my.cnf加validate_password.policy=LOW,或初始化后立刻执行UNINSTALL COMPONENT "file://component_validate_password";
provider 配置和变量设计决定能否跨云复用
不同云厂商的 MySQL 部署差异极大:AWS RDS 直接用 aws_db_instance,GCP 用 google_sql_database_instance,而自建节点则要用 aws_instance + google_compute_instance。硬编码会导致模块无法复用。
推荐结构:
- 用
locals抽出共性配置:比如mysql_version = "8.0.35"、root_password = random_password.mysql_root.result - 用
count或for_each控制节点数量,别写死三台;主从场景下,用tags = { role = "mysql-master" }区分角色 - 敏感值如密码,必须走
random_password或aws_secretsmanager_secret,绝不能写进variables.tf的default - 若用
cloud-init,把整个安装逻辑写成 YAML 放进user_data = data.template_cloudinit_config.main.rendered,而不是拼接字符串
最易被忽略的点:SELinux 和 firewalld 不是“配好就行”,而是必须显式处理
CentOS/RHEL 系统上,即使 mysqld 启动成功,netstat -tlnp | grep :3306 也看不到监听——因为 SELinux 默认禁止 mysqld 绑定非标准端口,且 firewalld 没开 3306。Terraform 不会自动关它们,靠 remote-exec 手动关又违背幂等性原则。
解决方式只有两个:
- 在
user_data里加sudo setsebool -P mysqld_connect_any on && sudo firewall-cmd --permanent --add-port=3306/tcp && sudo firewall-cmd --reload - 或更彻底:用
cloud-init的bootcmd阶段提前禁用 SELinux(sed -i 's/SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config),重启后再装 MySQL
不处理这个,Terraform 显示 Apply complete,但连不上数据库是常态。


















