Learning
VOL. XIII · NO. 13 · Elixir · 01 JAN 1970

Vultr 速通 — API + Snapshot + 按小时起 VM

Elixir 编程 · 01 JAN 1970 · 9 min read · 1,342 words
· · ·

不是 Vultr 介绍,是 对 agquant.com / ex/elixir 项目真正有用 的 8 个能力。

这课不讲”什么是 VM”——你已经知道。讲的是 Vultr 表面下真正改变游戏规则的 8 个能力:

  1. API 起 VM — 30 行代码从 0 起一个 production 节点
  2. 按小时计费 — halt 不计费,destroy 不计费
  3. Snapshot + cloud-init — cold start 15-30s 的关键
  4. Object Storage — S3 兼容,$5/TB/月
  5. Firewall Group — default-deny + CF IP 白名单
  6. Startup Scripts — 模板化 cloud-init
  7. Block Storage — 100GB + $10/月挂载
  8. Marketplace Apps — 1-click deploy OpenLiteSpeed / GitLab 等

**预计时间:**60 分钟。

视角
所有例子都基于"agquant.com 站长 / ex/elixir 项目方",要把这套 VM 编排成"按需起、按秒计费、CF Tunnel 接入"的现代架构。

一、为什么选 Vultr(不选 AWS / DigitalOcean / Hetzner)

维度VultrAWS EC2DigitalOceanHetzner
按小时计费✅ 原生❌ 月费✅ 部分
$5/月起✅ 1GB✅ t3.nano✅ 1GB€4.35 CX22
Snapshot 价$0.05/GB/月$0.05/GB/月$1/GB/月€0.01/GB/月
API 质量v2 设计干净300+ 服务,复杂简单简单
CF 兼容
中国节点宁夏 (CN)
Document quality⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Vultr 真·优势
  • 按小时计费 + Snapshot 廉价 = 按需起 VM 经济模型可行(详见 lesson 0006)
  • API 干净 = 30 行代码自动化
  • Snapshot 价 $0.05/GB = 4GB VM 快照 $0.20/月(DO 同样 4GB = $4/月,20x 贵)

详见 [concepts-vultr-glossary.md §Vultr vs 其他云](../concepts-vultr-glossary)。

二、API 起 VM(30 行代码)

2.1 API 鉴权

CF Dashboard → Account → API → 创建 Personal Access Token。

export VULTR_API_KEY="VTLxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# 列出现有 VM
curl -H "Authorization: Bearer $VULTR_API_KEY" https://api.vultr.com/v2/instances

2.2 Elixir wrapper(你 ex/elixir 项目可加)

defmodule Vultr do
  @base "https://api.vultr.com/v2"

  defp req(method, path, body \\ nil) do
    headers = [
      {"authorization", "Bearer #{System.fetch_env!("VULTR_API_KEY")}"},
      {"content-type", "application/json"}
    ]
    body_json = if body, do: Jason.encode!(body), else: ""

    case HTTPoison.request(method, "#{@base}#{path}", body_json, headers, recv_timeout: 30_000) do
      {:ok, %{status: code, body: resp}} when code in 200..299 ->
        if resp == "", do: {:ok, nil}, else: {:ok, Jason.decode!(resp)}
      {:ok, %{status: code, body: resp}} ->
        {:error, {code, Jason.decode!(resp)}}
      {:error, reason} -> {:error, reason}
    end
  end

  def list_instances, do: req(:get, "/instances")
  def get_instance(id), do: req(:get, "/instances/#{id}")

  def create_instance(attrs) do
    req(:post, "/instances", Map.merge(%{
      region: "sgp",
      plan: "vc2-2c-4gb",
      label: "pipeline-#{:os.system_time(:second)}"
    }, attrs))
  end

  def delete_instance(id), do: req(:delete, "/instances/#{id}")
  def halt_instance(id), do: req(:post, "/instances/#{id}/halt")
  def start_instance(id), do: req(:post, "/instances/#{id}/start")
end

2.3 完整 spin-up 流程

defmodule VultrAutoscaler do
  def spin_up_worker do
    case Vultr.create_instance(%{
      os_id: 1743,  # from snapshot
      snapshot_id: System.fetch_env!("VULTR_SNAPSHOT_ID"),
      hostname: "worker-#{:os.system_time(:second)}",
      user_data: cloud_init_script()
    }) do
      {:ok, %{"instance" => %{"id" => id, "main_ip" => ip, "status" => status}}} ->
        Mix.shell().info("✓ VM #{id} created at #{ip}, status=#{status}")
        # 等 ~30s VM 起来,cloud-init 跑完
        wait_until_ready(id, timeout: 90_000)
        {:ok, %{id: id, ip: ip}}

      {:error, reason} ->
        Mix.raise("✗ Failed: #{inspect(reason)}")
    end
  end

  defp wait_until_ready(id, timeout) do
    deadline = System.monotonic_time(:millisecond) + timeout
    poll_until(deadline, fn ->
      case Vultr.get_instance(id) do
        {:ok, %{"instance" => %{"status" => "running", "server_status" => "ok"}}} ->
          :ready
        _ -> nil
      end
    end)
  end

  defp poll_until(deadline, fun) do
    if System.monotonic_time(:millisecond) > deadline do
      :timeout
    else
      case fun.() do
        :ready -> :ready
        _ ->
          Process.sleep(2_000)
          poll_until(deadline, fun)
      end
    end
  end
end

三、Snapshot + cloud-init(按需起 VM 的关键技术)

3.1 Snapshot 是什么

Snapshot = 整个 VM 磁盘状态(OS + 你的代码 + 依赖 + 配置)。从 snapshot 起新 VM 只需 15-30s(vs 从 ISO 装 OS 要 2-3 分钟)。

3.2 怎么用

# 1. 创建基础 VM(手动或 API)
vultr-cli instance create --region sgp --plan vc2-2c-4gb --os-id 1743 --label "base"

# 2. SSH 进去装好所有依赖
ssh root@<base-vm-ip> << 'EOF'
  apt update
  apt install -y curl git libpq-dev
  mix local.hex --force
  mix local.rebar --force
  # ... 你的所有安装
EOF

# 3. 拍 snapshot
vultr-cli snapshot create --instance-id <base-vm-id> --description "pipeline-prod-v0.1.0"

# 4. 从 snapshot 起新 VM
vultr-cli instance create --region sgp --plan vc2-2c-4gb --os-id 1743 \
  --snapshot-id <snapshot-id> --label "worker-01"

3.3 cloud-init 注入(user_data)

#cloud-config
write_files:
  - path: /opt/pipeline.env
    permissions: '0600'
    content: |
      DATABASE_URL=postgres://user:pass@db.example.com/pipeline_prod
      LLM_URL=https://api.openai.com/v1
      LLM_KEY=sk-xxx
      SECRET_KEY_BASE=$(openssl rand -hex 64)
      LIBCLUSTER_SEEDS=sgp-pipeline-01
runcmd:
  - curl -L https://github.com/your/repo/releases/download/v0.1.0/pipeline.tgz -o /tmp/pipeline.tgz
  - tar xzf /tmp/pipeline.tgz -C /opt
  - useradd -r -s /bin/false pipeline
  - chown -R pipeline:pipeline /opt/pipeline
  - systemctl enable /opt/pipeline/_build/prod/rel/pipeline/bin/pipeline
  - systemctl start pipeline
  - curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared -o /usr/local/bin/cloudflared
  - chmod +x /usr/local/bin/cloudflared
  - cloudflared service install --token=$CLOUDFLARE_TUNNEL_TOKEN
  - systemctl enable cloudflared
  - systemctl start cloudflared
final_message: "Pipeline worker $HOSTNAME ready"

3.4 cold start 时间线

阶段耗时
Vultr API 接收 + 排队5-10s
启动 snapshot VM10-20s
cloud-init 拉 release + 起服务15-30s
总计30-60s

vs Spring Boot 2-3 分钟,4x 快——这是 Elixir + Vultr “按需起 VM” 能 work 的技术基础

四、按小时计费(核心经济模型)

Vultr 的核心差异化——halt 不计费、destroy 不计费、只按实际开机时间按小时计

defmodule VultrBilling do
  def current_month_cost do
    {:ok, %{"billing" => %{"balance" => balance}}} = req(:get, "/billing")
    balance
  end

  def vm_hourly_cost(plan_id) do
    {:ok, %{"plans" => plans}} = req(:get, "/plans")
    case Enum.find(plans, &(&1["id"] == plan_id)) do
      nil -> 0
      plan -> plan["monthly_cost"] / 730
    end
  end
end

# 例:vc2-2c-4gb = $20/月 = 730 小时 = $0.027/h
# 每月跑 50 小时 = $1.37(vs 24/7 跑 = $20)

五、Object Storage(S3 兼容,$5/TB)

存 release 产物、用户上传的 xlsx/PDF、课程视频——S3 兼容但便宜 4x:

服务存储EgressAPI
AWS S3$0.023/GB$0.09/GBS3
GCP Cloud Storage$0.020/GB$0.12/GBS3
Vultr Object Storage$0.020/GB$0.01/GBS3
Cloudflare R2$0.015/GB$0S3

结论:Vultr Object Storage 适合 Vultr VM 内网访问(免费 egress),跨云访问用 R2。

# 创建 bucket
aws s3 --endpoint-url https://sgp1.vultrobjects.com mb s3://pipeline-releases

# 上传 release 产物
aws s3 --endpoint-url https://sgp1.vultrobjects.com \
  cp pipeline.tgz s3://pipeline-releases/pipeline-v0.1.0.tgz

# 读(在 Vultr VM 内网,免费)
aws s3 --endpoint-url https://sgp1.vultrobjects.com \
  cp s3://pipeline-releases/pipeline-v0.1.0.tgz /tmp/

# 公开读(给 CDN 用)
https://sgp1.vultrobjects.com/pipeline-releases/pipeline-v0.1.0.tgz

六、Firewall Group(CF IP 白名单)

关键:Vultr Firewall 是 default-deny——新 VM 所有端口都封。必须显式开端口。

# 1. 创建 group
curl -X POST -H "Authorization: Bearer $VULTR_API_KEY" \
  -d '{"description":"agquant CF whitelist"}' \
  https://api.vultr.com/v2/firewall-groups

# 2. 加 CF IP 段(必须限制来源 IP,不能 0.0.0.0/0)
# CF IP 列表:https://www.cloudflare.com/ips/

# Cloudflare IPv4
curl -X POST -H "Authorization: Bearer $VULTR_API_KEY" \
  -d '{"ip_type":"v4","protocol":"tcp","port":"8002","subnet":"173.245.48.0","subnet_size":20}' \
  https://api.vultr.com/v2/firewall-groups/<group-id>/rules

# Cloudflare IPv6
curl -X POST ... -d '{"ip_type":"v6","protocol":"tcp","port":"8002","subnet":"2400:cb00::","subnet_size":32}' ...

# 3. SSH 限制(仅你的 IP)
curl -X POST ... -d '{"ip_type":"v4","protocol":"tcp","port":"22","subnet":"YOUR.IP","subnet_size":32}' ...

# 4. attach 到 VM
curl -X POST -H "Authorization: Bearer $VULTR_API_KEY" \
  -d '{"firewall_group_id":"<group-id>","instance_id":"<vm-id>"}' \
  https://api.vultr.com/v2/firewall-groups/<group-id>/attachments
⚠️ 不要犯这个错
不要开 0.0.0.0/0:8002!Vultr VM 会被全世界扫描、挖矿、感染。


正确做法:只开 CF IP 段(200+ 城市节点),VM 在公网上 8002 端口只能从 CF 进。

七、Startup Scripts(模板化 cloud-init)

把”起 VM 流程”保存为模板,所有 VM 复用:

# 1. 创建 template
curl -X POST -H "Authorization: Bearer $VULTR_API_KEY" \
  -d '{"name":"agquant-pipeline","type":"boot","script":"#cloud-config\nwrite_files:\n  - path: /opt/pipeline.env\n    content: |\n      DATABASE_URL=postgres://..."}' \
  https://api.vultr.com/v2/startup-scripts

# 2. 创建 VM 时引用
curl -X POST ... -d '{
  "region":"sgp",
  "plan":"vc2-2c-4gb",
  "os_id":1743,
  "snapshot_id":"...",
  "script_id":"<template-id>"
}' https://api.vultr.com/v2/instances

模板可以分版本(v0.1.0, v0.1.1, v0.2.0)——CI 流水线自动更新。

八、Block Storage(额外磁盘)

当 OS 盘不够(一般 25-100GB),挂块存储:

规格月费
100 GB$10
250 GB$25
500 GB$50
1 TB$100
# 创建 + attach
curl -X POST -d '{"region":"sgp","size_gb":100,"label":"data"}' https://api.vultr.com/v2/block-storage

# 在 VM 内 mount
mkfs.ext4 /dev/vdb
mount /dev/vdb /mnt/data
echo "/dev/vdb /mnt/data ext4 defaults 0 0" >> /etc/fstab

agquant.com 场景:ETS 大表(GB 级)或 mnesia 数据放这里。

九、Marketplace Apps(1-click 部署)

不想自己装?直接装预制应用:

App用途
OpenLiteSpeed高性能 HTTP 服务器(比 nginx 快 3x)
Docker容器 runtime
GitLab CE自托管 Git
Mastodon自托管社交
LEMP / LAMPWordPress 之类
Nextcloud自托管网盘

CLI:vultr-cli instance create --image-id &lt;marketplace-image-id> ...

十、其他 6 个高级功能

功能用途价格
Load BalancerL4/L7 LB(支持 SSL 终止)$10/月
Reserved IP弹性公网 IP(可 detach/attach)免费(只付 VM 费)
Bare Metal独享硬件(不是虚拟化)$120/月起
High Frequency (vhf-)3.8GHz+ CPU(适合编译)$128/月起
VKE (Kubernetes)托管 K8s(比 EKS 便宜 50%)$0(按节点付)
Direct Connect到其他云的内网专线$0.05/GB

十一、agquant.com 实战:先做这 3 件事

  1. 10 分钟:开 Personal Access Token → 验证 API(curl /v2/instances
  2. 30 分钟:做一个 base VM → 装好所有依赖 → 拍 snapshot
  3. 30 分钟:写 cloud-init → 验证从 snapshot 起 VM 能在 60s 内 ready

这三步做完,你就有”按需起 VM”的物质基础了。剩下的就是代码:autoscaler + CF Tunnel + load balancer。

十二、测验(12 道)

1

Vultr 区别于 AWS EC2 / DigitalOcean 的最大优势是?

2

haltdestroy 的区别?

3

Vultr Firewall 默认是?

4

Snapshot 价(每 GB 每月)?

5

从 Snapshot 起一个新 VM 需多久?

6

cloud-init 配置在哪里传给 Vultr?

7

Vultr Object Storage 跟 AWS S3 的 API 兼容?

8

Vultr Firewall Group 必须配置的是?

9

Vultr Reserved IP 用途?

10

vc2-2c-4gb 套餐月费?

11

Vultr 月费上限机制?

12

1GB 套餐 vc2-1c-1gb 月费 + 时费?

**下一步:**看 Lesson 0009 · SEO 速通