用 Terraform 在 VPS 上搭建 WordPress 开发环境(Part 1):从零到跑起来

前言

我一直想认真练一下 Terraform 和 Ansible,但不想为了练习去开一堆云服务器烧钱。后来发现,其实手上那台跑着各种服务的 Ubuntu VPS,就是最好的练习场。

这篇文章记录我第一次用 Terraform 在 VPS 上搭建 WordPress 开发环境的完整过程。目标很简单:用代码创建 MySQL 和 WordPress 两个 Docker 容器,让站点跑起来。 过程中踩的坑也一并写下来,因为那些坑才是最值钱的部分。

为什么用 Terraform 管 Docker

Terraform 是 HashiCorp 出的基础设施即代码(IaC)工具,最常用来管理 AWS、Azure 这类云资源。但它有一个很大的优点:通过 Provider 插件,几乎什么都能管,包括本机的 Docker。

这意味着我可以用同一套声明式语法,描述“我要一个 MySQL 容器和一个 WordPress 容器”,然后让 Terraform 去跟 Docker 引擎沟通。不用手写 docker run 一长串命令,也不用维护 docker-compose.yml

更重要的是,这套流程和真实工作中管理云资源的流程几乎一样:init → plan → apply。练熟了,换到 AWS 也是同一套思路。

环境准备

我的 VPS 是 Ubuntu(jammy),上面已经跑了不少 Docker 服务,所以 Docker 本身不用装。需要补的是 Terraform。

安装 Terraform 时踩了一个小坑:VPS 上之前配置过 Jenkins 的软件源,但缺少 GPG 公钥,导致 apt-get update 直接报错,连带后面的安装也中断了。解决办法很简单——暂时把 Jenkins 的源文件移走:

bash

sudo mv /etc/apt/sources.list.d/jenkins.list /etc/apt/sources.list.d/jenkins.list.bak
sudo apt-get update

然后添加 HashiCorp 官方源并安装:

bash

sudo apt-get install -y gnupg software-properties-common
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt-get update && sudo apt-get install -y terraform

验证:

bash

terraform -version

编写 main.tf

新建一个练习目录,创建 main.tf

hcl

terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 3.0"
    }
  }
}

provider "docker" {}

resource "docker_network" "wp_net" {
  name = "wp-practice-net"
}

resource "docker_image" "mysql" {
  name = "mysql:8.0"
}

resource "docker_image" "wordpress" {
  name = "wordpress:latest"
}

resource "docker_container" "mysql" {
  name  = "wp-db-practice"
  image = docker_image.mysql.image_id
  env = [
    "MYSQL_ROOT_PASSWORD=rootpass",
    "MYSQL_DATABASE=wordpress",
    "MYSQL_USER=wpuser",
    "MYSQL_PASSWORD=wppass",
  ]
  restart = "unless-stopped"
  networks_advanced {
    name = docker_network.wp_net.name
  }
}

resource "docker_container" "wordpress" {
  name  = "wp-app-practice"
  image = docker_image.wordpress.image_id
  ports {
    ip       = "100.98.125.115"  # 改成你 VPS 的 Tailscale IP
    internal = 80
    external = 8081
  }
  env = [
    "WORDPRESS_DB_HOST=wp-db-practice:3306",
    "WORDPRESS_DB_USER=wpuser",
    "WORDPRESS_DB_PASSWORD=wppass",
    "WORDPRESS_DB_NAME=wordpress",
  ]
  restart    = "unless-stopped"
  depends_on = [docker_container.mysql]
  networks_advanced {
    name = docker_network.wp_net.name
  }
}

几个要点:

  • docker_network:这是关键。Docker 默认的 bridge 网络不支持容器名解析,必须用自定义网络,容器之间才能通过名字互相访问。
  • depends_on:显式声明 WordPress 依赖 MySQL,Terraform 会先创建数据库。
  • 端口绑定到 Tailscale IP:我的 VPS 在 Tailscale 内网里,把 8081 只绑定到 100.x.x.x,公网访问不到,更安全。

执行流程

bash

terraform init
terraform plan
terraform apply

init 下载 Docker Provider 插件,plan 预览将要创建的资源,apply 实际执行。确认时输入 yes

Apply 成功后输出:

text

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

踩坑记录:500 错误

容器都起来了,但访问 http://<Tailscale_IP>:8081 返回 500 错误

排查过程:

  1. 看 MySQL 日志:ready for connections ... port: 3306,数据库和用户都建好了,没问题。
  2. 看 WordPress 日志:Apache 正常,但页面返回 500。
  3. 关键一步——在 WordPress 容器里测试解析 MySQL 容器名:

bash

docker exec wp-app-practice bash -c "getent hosts wp-db-practice"

返回空。 问题找到了:两个容器不在同一个支持名字解析的网络里。

修复:自定义网络

其实上面的 main.tf 已经包含了修复——加了 docker_network 资源,并让两个容器都通过 networks_advanced 加入它。

重新 terraform apply 后,容器被重建到新网络里。再测一次:

bash

docker exec wp-app-practice bash -c "getent hosts wp-db-practice"

这次返回:

text

172.20.0.2      wp-db-practice

刷新浏览器,WordPress 安装向导出来了。

这个坑教会我的事

Terraform 负责“创建资源”,但资源之间的“连通性”需要你显式设计。

默认 bridge 网络不支持容器名 DNS,这个坑几乎每个用 Docker 的人都会踩一次。理解了这点,也就理解了为什么很多 Docker Compose 项目会显式声明 networks

下一步

现在环境跑起来了,但 WordPress 本身还没配置。Part 2 我会用 Ansible 来做配置管理——检查容器状态、操作 WordPress、备份数据,完成从“置备”到“配置”的完整自动化流程。

如果你也在用 VPS 练手,欢迎一起交流踩坑经验。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注