当前位置: 首页 > news >正文

一键上云:Vultr × Caddy 可直接部署模板(Terraform + Docker Compose + Caddyfile)

目标:一条命令把 Caddy 网关和你的应用部署到 Vultr,自动 HTTPS、反向代理、静态托管、日志可观测开箱即用。本文包含完整模板与操作步骤,直接复制即可落地。


🧭 方案概览

  • 基础设施即代码:用 Terraform 创建 Vultr 实例、防火墙组、SSH 公钥注入等。

  • 云端初始化:cloud-init 在首启时安装 Docker & Compose。

  • 应用编排:Docker Compose 起 Caddy(入口)+ 示例 app(Flask/Gunicorn)。

  • 自动 HTTPS:Caddy 通过 Let’s Encrypt 自动申请/续签证书(需域名 A 记录指向实例 IP)。

  • 可观测性:结构化访问日志(JSON)落盘,便于后续接入 Loki/ELK。

你可以把示例 app 替换成你的服务,Caddy 仍旧自动代理与签证书。


🗂 目录结构

vultr-caddy-starter/
├─ terraform/
│  ├─ main.tf
│  ├─ variables.tf
│  ├─ outputs.tf
│  └─ cloud-init.yaml
├─ compose/
│  ├─ docker-compose.yml
│  ├─ Caddyfile
│  ├─ app/            # 示例后端(可删换为你的)
│  │  ├─ Dockerfile
│  │  └─ wsgi.py
├─ .env.example
└─ Makefile

🔑 准备工作

  1. Vultr API Token:在 Vultr 控制台创建(只读不可用,需读写)。

  2. SSH 公钥ssh-keygen -t ed25519 生成,公钥内容填到 variables.tf.tfvars

  3. 域名解析:将你的域名(如 example.com)的 A 记录 指向后文输出的实例公网 IP(Terraform 输出中会显示)。

  4. 本地安装:terraformdockerdocker compose(仅用于本地构建镜像时需要)。


🧱 Terraform:创建 Vultr 资源

terraform/main.tf

terraform {required_version = ">= 1.6.0"required_providers {vultr = {source  = "vultr/vultr"version = "~> 2.18"}}
}provider "vultr" {api_key = var.vultr_api_key
}# 可选:上传你的 SSH 公钥到 Vultr 账户,供实例注入
resource "vultr_ssh_key" "main" {name    = "caddy-starter-key"ssh_key = var.ssh_public_key
}# 防火墙组 & 规则(仅放行 22/80/443)
resource "vultr_firewall_group" "web" {description = "caddy web ingress"
}resource "vultr_firewall_rule" "allow_ssh" {firewall_group_id = vultr_firewall_group.web.idprotocol          = "tcp"port              = "22"subnet            = "0.0.0.0"subnet_size       = 0
}resource "vultr_firewall_rule" "allow_http" {firewall_group_id = vultr_firewall_group.web.idprotocol          = "tcp"port              = "80"subnet            = "0.0.0.0"subnet_size       = 0
}resource "vultr_firewall_rule" "allow_https" {firewall_group_id = vultr_firewall_group.web.idprotocol          = "tcp"port              = "443"subnet            = "0.0.0.0"subnet_size       = 0
}# 创建实例(Ubuntu 22.04 LTS 示例)
resource "vultr_instance" "caddy_host" {region             = var.region          # 例: "sea"、"sjo"、"fra"plan               = var.plan            # 例: "vc2-1c-1gb"os_id              = 1743                # Ubuntu 22.04 x64(如变更可在 Vultr 文档查最新 ID)label              = "caddy-starter"hostname           = var.hostnamefirewall_group_id  = vultr_firewall_group.web.idenable_ipv6        = truebackups            = "disabled"ssh_key_ids        = [vultr_ssh_key.main.id]user_data          = file("${path.module}/cloud-init.yaml")tags               = ["caddy", "starter"]# 更换为你的私有网络等(可选)# vpc_ids          = [ vultr_vpc.main.id ]
}

terraform/variables.tf

variable "vultr_api_key" {type        = stringdescription = "Vultr API token"sensitive   = true
}variable "ssh_public_key" {type        = stringdescription = "Your SSH public key content"
}variable "region" {type        = stringdefault     = "sea"   # 例:西雅图description = "Vultr region code"
}variable "plan" {type        = stringdefault     = "vc2-1c-1gb"description = "Vultr compute plan"
}variable "hostname" {type        = stringdefault     = "caddy-gw"description = "Instance hostname"
}

terraform/outputs.tf

output "instance_ip" {value       = vultr_instance.caddy_host.main_ipdescription = "Public IPv4 address of the instance"
}output "ssh_connect" {value       = "ssh root@${vultr_instance.caddy_host.main_ip}"description = "SSH command (root for first login; cloud-init will create a non-root user if you modify it)"
}

☁️ cloud-init:首启自动安装 Docker & Compose

terraform/cloud-init.yaml

#cloud-config
package_update: true
package_upgrade: trueusers:- name: devopsgroups: [sudo, docker]shell: /bin/bashsudo: ['ALL=(ALL) NOPASSWD:ALL']ssh_authorized_keys:- ${SSH_PUBLIC_KEY}   # 可用模板工具替换;或直接省略用 root 登录write_files:- path: /etc/motdpermissions: '0644'content: |Welcome to Vultr Caddy Host 🚀- path: /opt/compose/.envpermissions: '0644'content: |DOMAIN=example.comEMAIL=admin@example.com- path: /opt/compose/docker-compose.ymlpermissions: '0644'content: |services:caddy:image: caddy:2.8restart: unless-stoppedports:- "80:80"- "443:443"volumes:- caddy_data:/data- caddy_config:/config- /opt/compose/Caddyfile:/etc/caddy/Caddyfile:ro- /var/log/caddy:/var/log/caddy- /opt/site:/srv/www:rodepends_on:- appapp:build: ./apprestart: unless-stoppedexpose:- "5000"environment:- APP_ENV=prodvolumes:caddy_data:caddy_config:- path: /opt/compose/Caddyfilepermissions: '0644'content: |{# 全局设置:出现问题时便于定位email {env.EMAIL}}{env.DOMAIN} {encode gzip zstd@api path /api/*handle @api {reverse_proxy app:5000 {header_up X-Request-ID {http.request.id}}}handle {root * /srv/wwwfile_server}# 健康检查与日志respond /health 200log {output file /var/log/caddy/access.logformat json}}- path: /opt/compose/app/Dockerfilepermissions: '0644'content: |FROM python:3.11-slimWORKDIR /appRUN pip install --no-cache-dir flask gunicornCOPY wsgi.py .CMD ["gunicorn", "-b", "0.0.0.0:5000", "wsgi:app", "--workers=2", "--threads=4"]- path: /opt/compose/app/wsgi.pypermissions: '0644'content: |from flask import Flask, jsonifyapp = Flask(__name__)@app.route("/api/hello")def hello():return jsonify(ok=True, msg="Hello from Vultr + Caddy + Docker!")if __name__ == "__main__":app.run(host="0.0.0.0", port=5000)- path: /opt/site/index.htmlpermissions: '0644'content: |<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Vultr × Caddy Starter</title></head><body><h1>Vultr × Caddy Starter</h1><p>Static site served by Caddy.</p><p>Try API: <code>/api/hello</code></p></body></html>runcmd:# 安装 Docker & Compose(Ubuntu/Debian)- curl -fsSL https://get.docker.com | sh- usermod -aG docker devops || true- mkdir -p /var/log/caddy- cd /opt/compose && docker compose up -d

提示

  • 若你要用 通配符证书DNS-01 验证,可改为带 DNS 插件的 Caddy 镜像并在全局块配置 acme_dns

  • 默认 EMAIL 会用于 Let’s Encrypt 注册,修改为你自己的邮箱。

  • 第一次启动后,确保你的域名 A 记录已指向实例 IP,Caddy 才能自动签证书。


🧩 Docker Compose(本地调试可用)

如果你想先在本地跑通(非必须),compose/docker-compose.yml 与云端一致。只需:

cd compose
cp ../.env.example ./.env   # 设置 DOMAIN/EMAIL
docker compose up -d --build

浏览器访问:

  • 静态站点:http://localhost(本地无证书,线上自动 https)

  • API:http://localhost/api/hello


🧪 一键部署步骤

  1. 填变量

    • 复制 .env.example.env,改 DOMAINEMAIL(线上 cloud-init 也会写入)。

    • 准备 Terraform 变量:
      创建 terraform/terraform.tfvars(或以环境变量方式传入):

      vultr_api_key = "YOUR_VULTR_API_TOKEN"
      ssh_public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI.... yourname"
      region = "sea"
      plan   = "vc2-1c-1gb"
      hostname = "caddy-gw"
      
  2. 创建资源

    cd terraform
    terraform init
    terraform apply -auto-approve
    

    输出会显示 instance_ipssh_connect

  3. 设置域名解析
    在你的域名服务商处,将 A 记录(如 example.com)指向 instance_ip。等解析生效(通常几分钟)。

  4. 验证服务

    • http://example.com/health 返回 200

    • https://example.com/ 能看到静态页

    • https://example.com/api/hello 返回 JSON

  5. 更新应用
    SSH 到实例:

    ssh root@INSTANCE_IP
    cd /opt/compose
    # 替换为你的 app 镜像,或修改 app 目录并重建
    docker compose up -d --build
    

🔐 安全与生产化建议

  • 只暴露 80/443:后端服务仅 expose,不 ports;对外流量统一走 Caddy。

  • 非 root 运行:业务镜像使用非特权用户(USER 指令);Caddy 官方镜像默认非 root。

  • 证书持久化/data 持久化到卷(模板已提供),避免重建丢证书导致频繁签发。

  • 安全响应头

    header {X-Frame-Options DENYX-Content-Type-Options nosniffReferrer-Policy no-referrerContent-Security-Policy "default-src 'self'"
    }
    
  • 健康检查/熔断(多副本时):

    reverse_proxy {to app:5000lb_policy least_connhealth_uri /healthtry_duration 30s
    }
    
  • HTTP/3:开放 UDP/443 以启用 QUIC(Vultr 网络端口需允许)。

  • 日志采集/var/log/caddy/access.log 用 Fluent Bit/Vector 发往 Loki/ES。


🧰 Makefile(可选,简化操作)

Makefile

TF=cd terraform
apply:$(TF) && terraform apply -auto-approvedestroy:$(TF) && terraform destroy -auto-approvessh:@$(TF) && terraform output -raw ssh_connectip:@$(TF) && terraform output -raw instance_ip

🧷 常见问题速查

  • 证书失败 / 仍是 http:确认域名 A 记录已生效、80/443 未被其他进程占用、防火墙放行。首次签发需几秒到数十秒。

  • 502 / 回源失败:SSH 进实例 curl localhost:5000/health 测试后端是否存活;确认 Compose 服务名、端口与 reverse_proxy 一致。

  • 日志无输出:检查 /var/log/caddy 权限;确认 log { output file ... } 已配置。

  • 地域/系统/套餐os_idplanregion 可按需调整(保持与 Vultr 可用清单一致)。


🧩 换成你的应用

  • 后端替换:把 compose/app/ 换成你的代码或直接拉取你的镜像,更新 docker-compose.ymlapp 服务。

  • 多服务路由:新增一个服务 app2,在 Caddyfile 添加:

    @admin path /admin/*
    handle @admin {reverse_proxy app2:7000
    }
    
  • 子域拆分:复制 server 块:

    api.example.com {reverse_proxy app:5000
    }
    static.example.com {root * /srv/wwwfile_server
    }
    

✅ 总结

  • Terraform 把 Vultr 资源声明化,可重复、可版本化。

  • cloud-init 在首启时完成 Docker/Compose & 模板落盘与拉起。

  • Caddy 做入口层,自动 HTTPS、反向代理、静态托管与日志一把梭。

  • 全文模板可直接复制并按需小改域名与镜像,即可投入实战。

http://www.dtcms.com/a/536376.html

相关文章:

  • Android Studio 代码注释模板设置指南
  • 推荐做ppt照片的网站郑州营销型网站制作教程
  • 手机网站建设哪家专业网站备案通讯地址
  • 数字图像相关(DIC)技术:汽车板料成形极限(FLC)的精确测量与分析
  • 基于vlc的Player的构建编译
  • AR巡检轨道交通、地铁运维场景的应用技术方案|阿法龙XR云平台
  • 使用cursor/vscode开发服务器远程桌面应用(X11转发)
  • 织梦大气绿色大气农业能源化工机械产品企业网站源码模版做动态logo网站
  • 在建项目人员查询网站个人网站可以做论坛吗
  • 第53题 最大子数组和
  • 网站源码超市中国建设银行大沥网站
  • leetcode 242. 有效的字母异位词 python
  • 免费网站建设模板好看的商城网站
  • 0382. 链表随机节点
  • Vue3 路由完全指南:从基础配置到权限控制
  • 宁波seo推广咨询长沙 建站优化
  • 邯郸做网站熊掌号什么自己做网站
  • Spring Boot3零基础教程,监听 Kafka 消息,笔记78
  • 【Swift】LeetCode 41. 缺失的第一个正数
  • cuda编程笔记(34)-- 内存访问控制与缓存提示
  • webserver类续
  • 条款22:使用Pimpl惯用法时,将特种成员函数的定义放到实现文件中
  • 基于python大数据的省级城市政企客户业务分析系统
  • 合肥微网站制作初中做语文综合题的网站
  • dedecms做论坛网站广州网页设计师学校
  • Blender + MCP 全流程详细图文教程
  • 自定义classload实现热加载案例
  • 上海网站建设seo站霸网络网站建设推销拜访客户怎么开头
  • Spring Bean的生命周期 第二次思考
  • HttpServletResponse下载文件