Ansible 是一款能够大规模管理基础设施的软件工具。Ansible 是无代理的——它只需要能够 SSH 到目标系统并在目标系统上安装 Python 即可运行。
Ansible 与 Terraform 配合使用可简化 Cloudflare Tunnel 的设置过程。在本指南中,您将使用 Terraform 在 Google Cloud 上部署 SSH 服务器并创建本地管理的隧道,从而使该服务器可通过互联网访问。Terraform 将自动运行 Ansible playbook 并在服务器上安装配置 cloudflared。
要完成本指南中的步骤,您需要:
- 一个 Google Cloud 项目 ↗,以及已安装并身份验证的 GCP CLI ↗。
- Terraform 的基础知识以及已安装的 Terraform。
- 一个 Cloudflare 上的区域。
- 一个具有
Cloudflare Tunnel和DNS权限的 Cloudflare API 令牌。
请参阅 Ansible 安装说明 ↗。
Terraform 和 Ansible 需要未加密的 SSH 密钥来连接到 GCP 服务器。如果您还没有密钥,可以按如下方式生成:
-
打开终端并输入以下命令:
ssh-keygen -t rsa -f ~/.ssh/gcp_ssh -C <username in GCP> -
当提示输入密码短语(passphrase)时,按两次
Enter键将其留空。Terraform 无法解码加密的私钥。
将生成两个文件:包含私钥的 gcp_ssh 和包含公钥的 gcp_ssh.pub。
-
为您的 Terraform 和 Ansible 配置文件创建一个文件夹:
mkdir ansible-tunnel -
切换到新目录:
cd ansible-tunnel
以下变量将被传递到您的 GCP 和 Cloudflare 配置中。
-
在您的配置目录中,创建一个
.tf文件:touch variables.tf -
在文本编辑器中打开该文件并复制并粘贴以下内容:
# GCP 变量 variable "gcp_project_id" { description = "Google Cloud Platform (GCP) 项目 ID" type = string } variable "zone" { description = "GCP VM 实例的地理区域" type = string } variable "machine_type" { description = "GCP VM 实例的机器类型" type = string } # Cloudflare 变量 variable "cloudflare_zone" { description = "用于向互联网公开 GCP VM 实例的域名" type = string } variable "cloudflare_zone_id" { description = "您域的区域 (Zone) ID" type = string } variable "cloudflare_account_id" { description = "您 Cloudflare 账户(account)的账户 ID" type = string sensitive = true } variable "cloudflare_email" { description = "您 Cloudflare 账户(account)的电子邮件地址" type = string sensitive = true } variable "cloudflare_token" { description = "Cloudflare API 令牌" type = string sensitive = true }
-
在您的配置目录中,创建一个
.tfvars文件:touch terraform.tfvars如果该文件命名为
terraform.tfvars,Terraform 将自动使用这些变量,否则需要手动传入该变量文件。 -
将以下变量添加到
terraform.tfvars中。请确保使用您自己的值来修改示例。cloudflare_zone = "example.com" cloudflare_zone_id = "023e105f4ecef8ad9ca31a8372d0c353" cloudflare_account_id = "372e67954025e0ba6aaa6d586b9e0b59" cloudflare_email = "[email protected]" cloudflare_token = "y3AalHS_E7Vabk3c3lX950F90_Xl7YtjSlzyFn_X" gcp_project_id = "testvm-123" zone = "us-central1-a" machine_type = "e2-medium"
您需要声明用于配置基础设施的提供程序(providers) ↗。
-
在您的配置目录中,创建一个
.tffile:touch providers.tf -
将以下提供程序添加到
providers.tf。random提供程序用于生成隧道密钥(secret)。terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare" version = ">= 5.8.2" } google = { source = "hashicorp/google" } } required_version = ">= 1.2" } # Providers provider "cloudflare" { api_token = var.cloudflare_token } provider "google" { project = var.gcp_project_id } provider "random" { }
以下配置将修改您的 Cloudflare 账户中的设置。
-
In your configuration directory, create a
.tffile:touch Cloudflare-config.tf -
将以下资源添加到
Cloudflare-config.tf:# 为 GCP VM 创建一个新的远程托管隧道。 resource "cloudflare_zero_trust_tunnel_cloudflared" "gcp_tunnel" { account_id = var.cloudflare_account_id name = "Ansible GCP tunnel" config_src = "cloudflare" } # 读取用于在服务器上运行隧道的令牌。 data "cloudflare_zero_trust_tunnel_cloudflared_token" "gcp_tunnel_token" { account_id = var.cloudflare_account_id tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id } # 创建将 http_app.${var.cloudflare_zone} 路由到隧道的 CNAME 记录。 resource "cloudflare_dns_record" "http_app" { zone_id = var.cloudflare_zone_id name = "http_app" content = "${cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id}.cfargotunnel.com" type = "CNAME" ttl = 1 proxied = true } # 为隧道配置已发布应用程序,以进行无客户端访问。 resource "cloudflare_zero_trust_tunnel_cloudflared_config" "gcp_tunnel_config" { tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id account_id = var.cloudflare_account_id config = { ingress = [ { hostname = "http_app.${var.cloudflare_zone}" service = "http://localhost:80" }, { service = "http_status:404" } ] } }
-
在您的配置目录中,创建一个
.tffile:touch GCP-config.tf -
在文本编辑器中打开该文件,然后复制并粘贴以下示例。请务必插入您自己的 GCP 用户名和 SSH 密钥对。
# 为 GCP VM 选择操作系统。 data "google_compute_image" "image" { family = "ubuntu-2204-lts" project = "ubuntu-os-cloud" } # 设置 GCP VM 实例。 resource "google_compute_instance" "http_server" { name = "ansible-inst" machine_type = var.machine_type zone = var.zone tags = [] boot_disk { initialize_params { image = data.google_compute_image.image.self_link } } network_interface { network = "default" access_config { // 临时 IP } } scheduling { preemptible = true automatic_restart = false } // 在 VM 上安装 Python3。 provisioner "remote-exec" { inline = [ "sudo apt update", "sudo apt install python3 -y", "echo Done!" ] connection { host = self.network_interface.0.access_config.0.nat_ip user = "<username in GCP>" type = "ssh" private_key= file("<path to private key>") } } provisioner "local-exec" { // 如果指定了 SSH 密钥和用户,添加 `--private-key <path to private key> -u var.name` command = "ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -u <username in GCP> --private-key <path to private key> -i ${self.network_interface.0.access_config.0.nat_ip}, playbook.yml" } metadata = { cf-email = var.cloudflare_email cf-zone = var.cloudflare_zone ssh-keys = "<username in GCP>:${file("<path to public key>")}" } depends_on = [ local_file.tf_ansible_vars_file ] }
tf_ansible_vars_file.yml。Ansible 将使用该隧道令牌在服务器上配置并运行 cloudflared。
-
在您的配置目录中,创建一个新的
tf文件:touch export.tf -
将以下内容复制并粘贴到
export.tf中:resource "local_file" "tf_ansible_vars_file" { content = <<-DOC # 包含来自 Terraform 变量值的 Ansible vars_file。 tunnel_id: ${cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id} tunnel_name: ${cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.name} tunnel_token: ${data.cloudflare_zero_trust_tunnel_cloudflared_token.gcp_tunnel_token.token} DOC filename = "./tf_ansible_vars_file.yml" }
Ansible playbook 是声明 Ansible 将要部署的配置的 YAML 文件。
-
创建一个新的
.yml文件:touch playbook.yml -
在文本编辑器中打开该文件,然后复制并粘贴以下内容:
---
- hosts: all
become: yes
# 将隧道变量导入到 VM。
vars_files:
- ./tf_ansible_vars_file.yml
# 在 VM 上执行以下命令。
tasks:
- name: 下载 cloudflared Linux 包。
shell: wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
- name: 解包 cloudflared。
shell: sudo dpkg -i cloudflared-linux-amd64.deb
- name: 将隧道安装为 systemd 服务。
shell: "cloudflared service install {{ tunnel_token }}"
- name: 启动隧道。
systemd:
name: cloudflared
state: started
enabled: true
masked: no
- name: 在端口 80 上部署一个示例 Apache Web 服务器。
shell: apt update && apt -y install apache2
- name: 编辑默认的 Apache 索引文件。
copy:
dest: /var/www/html/index.html
content: |
<!DOCTYPE html>
<html>
<body>
<h1>Hello Cloudflare!</h1>
<p>This page was created for a Cloudflare demo.</p>
</body>
</html>关键字(Keywords) ↗定义了 Ansible 将如何执行配置。在上述示例中,vars_files 关键字指定了存储变量定义的位置,而 tasks 关键字指定了 Ansible 将执行的操作。
模块(Modules) ↗指定了要完成的任务。在此示例中,copy 模块会创建一个文件并用内容填充它。
创建配置文件后,您可以通过 Terraform 部署它们。在运行 ansible-playbook 命令时,Ansible 部署会在 Terraform 部署内发生。
-
初始化您的配置目录:
terraform init -
(可选)预览将要创建的所有内容:
terraform plan -
部署配置:
terraform apply
要进行测试,请打开浏览器并访问 http://http_app.<CLOUDFLARE_ZONE>.com(例如 http_app.example.com)。您应该会看到 Hello Cloudflare! 测试页面。