如何在 Laravel Vapor 中添加无限自定义域名

发布于 作者

How to add unlimited custom domains to Laravel Vapor image

我们刚刚发布了自定义域名 V2,我将与大家分享所有技术细节。高潮和低谷,我所学到的东西以及如何自己动手。最终结果是高度可用且全球快速的基础设施。我们的客户喜欢它,我们也喜欢它。但让我们回到这个故事的开始。

我经营着一个简单、合乎道德的 Google Analytics 的替代方案。人们最讨厌分析的地方之一,除了它们过于复杂难以理解之外,就是它们的脚本会被广告拦截器阻止。可以理解的是,Google Analytics 被地球上每一个广告拦截器都阻止了。谷歌有太多隐私丑闻,很多人害怕向他们发送访客数据。但 Fathom 专注于隐私,不存储任何客户数据,因此不应该以相同的方式对待。所以我们不得不引入一个名为“自定义域名”的功能,我们的用户可以将自己的子域名(例如 wealdstoneraider.ronnie-pickering.com)指向我们的 Laravel Vapor 应用程序。如果您不熟悉 Vapor,它是 Laravel 团队构建的一款产品,可以让您将应用程序部署到 AWS 上的无服务器基础设施(我实际上有一个 关于它的课程,这就是我如此喜爱 Vapor 的原因)。

AWS 的服务并没有让这个自定义域名任务变得容易。如果您使用的是应用程序负载均衡器 (ALB),您最多只能使用 25 个不同的域名。如果您使用的是 AWS API Gateway,您可以获得几百个,但 AWS 不会让您超过这个限制。所以我们处于一个需要跳出框框思考的位置。

尝试 1:Vapor 与 Forge 的支持

我尝试实现自定义域名的第一件事是为每个自定义域名在 Vapor 中创建一个环境。Vapor 中的环境通常类似于“生产”或“登台”,但我决定另辟蹊径。相反,我们将“miguel-piedrafita-com”和“pjrvs-com”作为我们的环境。回想起来这听起来很愚蠢,但这在当时奏效了,我认为它非常前沿。

我通过查看 GitHub 上的 Vapor 源代码并使用 API 来构建了整个过程。我不会分享我的代码,因为现在已经毫无意义了,但它包含以下步骤

  1. 用户在前端输入他们的自定义域名
  2. 我们运行一个后台任务,通过 Vapor 将他们的域名添加到 Route53
  3. 我们通过 Vapor 启动 SSL 证书请求
  4. 我们通过电子邮件发送 SSL 证书所需的 CNAME 更改
  5. 用户进行更改
  6. 我们每隔 30 分钟检查一次以查看 AWS 是否已颁发 SSL 证书
  7. 当证书有效时,我们向 Forge(另一个用于部署的 Laravel 服务)发送 API 请求,该服务将打包一个单独的 Laravel 应用程序并将其部署到 Vapor,作为新环境。
  8. Forge 然后 ping 我们的 Vapor 设置并说“我们在这里都完成了”。
  9. Vapor 然后检查子域名。太棒了,它可以工作,所以我们通过电子邮件通知用户,告诉他们可以开始使用。

这从未投入生产,我羞愧地删除了所有代码。

尝试 2 – Forge 代理服务器

在我意识到第一次尝试根本无法扩展之后,我又回到了起点。我接受了必须使用代理层的事实,并且为了让 Chris Fidao 开心,我在我们的基础设施设置中添加了 EC2 服务器。

我通过 Forge 配置了一个负载均衡器,并在其中放置了 2 台服务器。计划是,我将通过 Forge 的 API 在每台服务器上创建 SSL 证书和添加站点,这样如果一台服务器脱机,另一台服务器就会接管。整个过程将在 NGINX 上运行,我们唯一的“限制”将是 Forge 的 API 速率限制。小菜一碟。

所以我把这一切都构建出来了。这太容易了。 Forge 的 API 非常棒。所以我做了任何狂热的 Twitter 用户都会做的事情,我分享了我的进度

我对自己非常自豪。然后 Alex Bouma 走过,扔了一颗手榴弹,然后走了。

然后 Mattias Geniar 加入进来(毕竟这是他的文章),然后是 Owen Conti(他显然已经告诉我这是最好的方法,早在 2019 年 11 月),然后是 Matt Holt(Caddy 的创建者)加入进来,然后,砰,我的成品就消失了。谢谢你们。

现在我知道你在想什么……这里有很多“未发货”的东西。我通常不会陷入完美主义的泥潭,但我想在我的 无服务器 Laravel 课程 中包含最佳的解决方案作为视频。因此,如果有一个更好的解决方案可用,我有义务向我的课程成员和 Fathom 客户确保我知道它。

而这就是 Caddy 进入我生命的地方。

尝试 3:高可用 Caddy 代理层

我希望我可以回到 2019 年,告诉自己这个解决方案。也许是时机问题?毕竟,当我看到 Alex 的推文时,Caddy 2 刚刚发布。

Caddy 2 是一款具有自动 HTTPS 功能的开源 Web 服务器。因此,您不必担心管理虚拟主机或 SSL 证书,它会自动为您完成。它比我们习惯的要简单得多。

所以您已经听过所有失败的尝试,让我们来看看我们是如何解决问题的。

步骤 1. SSL 证书存储

我们需要做的第一件事是创建一个 DynamoDB 表。这将是我们的集中式存储。为什么我使用 DynamoDB?因为我不想将证书存储在服务器文件系统上。我相信我们可以管理某种 NAS,但我不知道这将如何在跨区域工作。最终,我熟悉 DynamoDB,Matt Holt 非常乐意将 Caddy DynamoDB 模块升级到 V2(感谢老兄!)。

  1. 在 AWS 中打开 DynamoDB
  2. 创建一个名为 caddy_ssl_certificates 的新表,其中包含名为 PrimaryKey 的主键
  3. 取消勾选默认设置,使用按需(没有免费层,但可以自动扩展)
  4. 创建表后,单击“备份”选项卡并启用“时间点恢复”。
  5. 就这样完成了

步骤 2. 创建 IAM 用户

现在我们需要在 AWS 中创建一个具有有限访问权限的用户。理论上,我们可以为 Caddy 提供一个具有 root 权限的访问密钥,但它并不需要它。

  1. 创建一个名为 **caddy_ssl_user** 的用户
  2. 勾选启用程序访问的框
  3. 点击直接附加现有策略
  4. 点击创建策略
  5. 选择 DynamoDB 作为您的服务,并添加基本权限,以及限制对您表的访问权限(如果您不确定这一部分,可以在我的课程中找到一个 视频演练
  6. 好了。确保您保存访问密钥 ID 和秘密访问密钥

步骤 3. 在您的应用程序中创建一个路由来验证域名

我们不想让任何人都可以将他们的域名指向我们的基础设施并为他们生成 SSL 证书。不,我们只希望为我们的客户生成 SSL 证书。

为了这篇说明,我将保持一切超级简单并对其进行硬编码。如果您要将其投入生产,显然您将进行数据库是否存在检查;)

routes/web.php

Route::get('caddy-check-8q5efb6e59', 'CaddyController@check');

app\Http\ControllersCaddyController.php

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
 
class CaddyController extends Controller
{
protected static $authorizedDomains = [
'portal.my-customers-website.com' => true
];
 
public function check(Request $request)
{
if (isset(self::$authorizedDomains[$request->query('domain')])) {
return response('Domain Authorized');
}
 
// Abort if there's no 200 response returned above
abort(503);
}
}

您很快就会发现为什么这个端点如此重要。

步骤 4. 准备我们的 Forge 食谱

我说了 Forge 吗?Laravel Forge?你说的没错。我们使用 Forge 来托管我们的代理服务器(感谢 Taylor)。我这里有一个食谱模板,但您需要填写空白(AWS 密钥、您的网站等)。同样,如果您对这方面没有信心,我的 视频演练 会详细介绍所有这些内容

# Stop and disable NGINX
sudo systemctl stop nginx
sudo systemctl disable nginx
 
# Grab the bin file I’ve hosted on the Fathom Analytics CDN
sudo wget “https://cdn.usefathom.com/caddy”
 
# Move the binary to $PATH
sudo mv caddy /usr/bin/
 
# Make it executable
sudo chmod +x /usr/bin/caddy
 
# Create a group named caddy
sudo groupadd —system caddy
 
# Create a user named caddy, with a writeable home folder
sudo useradd —system \
—gid caddy \
—create-home \
—home-dir /var/lib/caddy \
—shell /usr/sbin/nologin \
—comment “Caddy web server” \
caddy
 
# Create the environment file
sudo echo
AWS_ACCESS_KEY=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=| sudo tee /etc/environment
 
# Create the caddy directory & Caddyfile
sudo mkdir /etc/caddy
sudo touch /etc/caddy/Caddyfile
 
# Write the config file
sudo echo ‘{
on_demand_tls {
ask https://your-website.com/caddy-endpoint
}
 
storage dynamodb caddy_ssl_certificates
}
 
:80 {
respond /health “Im healthy!” 200
}
 
:443 {
on_demand
}
 
reverse_proxy https://your-website.com {
header_up Host your-website.com
header_up User-Custom-Domain {host}
header_up X-Forwarded-Port {server_port}
 
health_timeout 5s
}
}’ | sudo tee /etc/caddy/Caddyfile
 
sudo touch /etc/systemd/system/caddy.service
 
# Write the caddy service file
sudo echo ‘# caddy.service
#
# WARNING: This service does not use the —resume flag, so if you
# use the API to make changes, they will be overwritten by the
# Caddyfile next time the service is restarted. If you intend to
# use Caddys API to configure it, add the —resume flag to the
# `caddy run` command or use the caddy-api.service file instead.
 
[Unit]
Description=Caddy
Documentation=https://caddyserver.com.cn/docs/
After=network.target
 
[Service]
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run —environ —config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload —config /etc/caddy/Caddyfile
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
EnvironmentFile=/etc/environment
 
[Install]
WantedBy=multi-user.target’ | sudo tee /etc/systemd/system/caddy.service
 
# Start the service
sudo systemctl daemon-reload
sudo systemctl enable caddy
sudo systemctl start caddy
 
# Remember, when making changes to the config file, you need to run
#sudo systemctl reload caddy

然后我们将这个食谱添加到 Forge。哦,还要选择“Root”作为运行它的用户。

快速提醒一下,您可以看到 Caddy 从 Fathom CDN 下载。这个 Caddy bin 文件由 Caddy 的创建者编译,包括 DynamoDB 附加组件。如果您愿意,也可以 自己编译

步骤 5. 创建我们的代理服务器

是服务器时间了!所以让我们在这里明确一点:这些服务器将运行零个 PHP 代码。它们将是最小的代理服务器。我再说一遍,我们不会在这些服务器上运行任何 PHP 代码,它们是代理服务器。

  1. 在创建服务器(在 Forge 中)下点击 AWS
  2. 服务器配置
  3. 任意名称,
  4. t3.small 或更高
  5. VPC 不重要
  6. 勾选“作为负载均衡器配置”框(保持最小化)
  7. 为创建的每个服务器选择不同的区域
  8. 当您的服务器完成设置后,复制 IP,然后加载以下 URL:http://[your-server-ip]/health。如果 Caddy 按预期运行,这将返回一条消息,这将是您的健康检查端点。

注意:Caddy 通过端口 80(HTTP)提供的唯一路径是这个健康检查,其余部分通过 443(HTTPS)提供。

步骤 6. 我们的“全局负载均衡器”

当我苦苦挣扎于负载均衡器,探索网络负载均衡器以及其他所有东西时,我偶然发现了 AWS 全球加速器。我从未听说过它,但我是在需要它的时候才发现它的。简而言之,这项服务为我们提供了单个端点(静态 IP 和 DNS 记录),但会将流量路由到多个位于不同区域的服务器。不同的区域部分让我大开眼界。这意味着如果美国的用户访问了我们的端点,他们将被路由到 us-east-1,而英国的用户(上帝保佑女王)将被路由到 eu-west-1。令人难以置信。

要做到这一点

  1. 打开 AWS -> AWS 全球加速器
  2. 创建一个加速器
  3. 选择一个名称并点击下一步
  4. 对于侦听器
  5. 端口:443
  6. 协议:TCP
  7. 客户端亲和性:无
  8. 然后点击下一步
  9. 添加端点组
  10. 选择您在 Forge 中创建的第 1 台服务器的区域
  11. 点击配置健康检查
    1. 健康检查端口:80
    2. 健康检查协议:HTTP
    3. 健康检查路径:/health
    4. 健康检查间隔:10
    5. 阈值计数:2
  12. 对您要添加的第 2 台服务器重复此操作
  13. 点击下一步
  14. 添加端点
  15. 对于每个组
    1. 端点类型:EC2 实例
    2. 端点:点击下拉菜单并找到您的服务器
  16. 点击创建加速器

现在去给自己泡杯茶或咖啡。我个人会选择茶,因为看到这个全球分布式的野兽端点运行时,你已经充满了肾上腺素,不想过载刺激。哦,还有,当您首次创建加速器时,它会告诉您您的服务器处于离线状态。不用担心,这只是它正在经历的一个阶段,一旦它完全配置好,就会恢复正常(通常不到 5 分钟)。

在我们等待的同时,另外说一句,您可以让您的基础设施变得更复杂。当我将我的视频发布到 无服务器 Laravel Slack 团队中的每个人时,一位朋友说他会为每个区域内的负载均衡器配置自动扩展 EC2 实例。我绝对不反对这种方法,您做您自己的。

无论如何,一旦加速器部署,您将拥有静态 IP 和 DNS 名称来使用。对于根级条目,您必须使用静态 IP。而对于 CNAME 条目,您可以使用 DNS 条目。在 Fathom,我们实际上创建了“starman.fathomdns.com”,它指向 AWS 在这里给我们的 DNS 名称,因此我们可以为我们的用户提供一个漂亮的 CNAME,让他们添加。

步骤 7. 您的第一个自动 SSL 证书

因此,选择一个子域(例如 hey.yourwebsite.com)并添加 DNS 条目。您还应该在 CaddyController 中添加 hey.yourwebsite.com 作为授权网站(参见步骤 3)。完成此操作后,加载您的网站,您应该会看到自动 SSL 代理到 Vapor。我记得在 Chrome 中,第一次运行可能会遇到问题,因为它似乎不喜欢 SSL 是动态生成的,因此如果需要,请使用多个浏览器(这仅适用于第一次加载)。

步骤 8. 与您的客户一起使用

您应该始终在您这边运行检查。当客户添加子域/域名时,您应该在模型上具有 3 种状态

  1. STATUS_AWAITING_DNS_CHANGES
  2. STATUS_AWAITING_STATUS_CHECK
  3. STATUS_ACTIVE

然后您需要有两个命令来处理所有这些

  1. CheckDNSChanges – DNS 查找以确保客户已进行更改
  2. CheckIfEnvironmentDeployed – 通过 https 访问网站以确保 Caddy 能够为网站生成 SSL 证书。在这里,您可以向用户发送电子邮件,让他们知道他们的域名已准备就绪

请记住,我在整个事情上都有一个大型视频演练,以及我 无服务器 Laravel 课程 中的另外 48 个视频,其中我涵盖了从初学者到高级的 Laravel Vapor 内容。

就是这样了

我希望您喜欢我在这里整理的内容。我要感谢以下人员为该过程做出的贡献:Matt HoltAlex BoumaMattias GeniarOwen ContiChris FidaoFrancis Lavoie 以及其他许多人。

Jack Ellis photo

Fathom Analytics 联合创始人 无服务器 Laravel 作者

归档于
Cube

Laravel 新闻

加入 40,000 多位其他开发者,永不错过新的技巧、教程等等。

Laravel Forge logo

Laravel Forge

轻松创建和管理您的服务器,并在几秒钟内部署您的 Laravel 应用程序。

Laravel Forge
Tinkerwell logo

Tinkerwell

Laravel 开发人员必备的代码运行器。使用 AI、自动完成和对本地和生产环境的即时反馈进行调试。

Tinkerwell
No Compromises logo

绝不妥协

Joel 和 Aaron,来自“绝不妥协”播客的两位经验丰富的开发者,现在可为您的 Laravel 项目提供雇佣服务。⬧ 固定价格 7500 美元/月。⬧ 无需冗长的销售流程。⬧ 无需合同。⬧ 100% 退款保证。

绝不妥协
Kirschbaum logo

Kirschbaum

提供创新和稳定性,确保您的 Web 应用程序取得成功。

Kirschbaum
Shift logo

Shift

正在运行旧版本的 Laravel?即时、自动化的 Laravel 升级和代码现代化,让您的应用程序保持新鲜。

Shift
Bacancy logo

Bacancy

每月只需 2500 美元,即可为您的项目配备一名经验丰富的 Laravel 开发人员,拥有 4-6 年的经验。获得 160 小时的专业知识和 15 天的无风险试用。立即安排电话会议!

Bacancy
Lucky Media logo

Lucky Media

现在就获得幸运 - 拥有十年以上经验的 Laravel 开发的理想之选!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar:Laravel 电子商务

Laravel 的电子商务。一个开源包,将现代无头电子商务功能的力量带到 Laravel。

Lunar:Laravel 电子商务
LaraJobs logo

LaraJobs

官方 Laravel 职位招聘平台

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit:Laravel SaaS 启动工具包

SaaSykit 是一个 Laravel SaaS 启动工具包,包含运行现代 SaaS 所需的所有功能。支付、漂亮的结账、管理面板、用户仪表板、身份验证、现成的组件、统计信息、博客、文档等等。

SaaSykit:Laravel SaaS 启动工具包
Rector logo

Rector

您无缝 Laravel 升级的合作伙伴,降低成本,加速创新,让企业取得成功

Rector
MongoDB logo

MongoDB

通过 MongoDB 和 Laravel 的强大集成增强您的 PHP 应用程序,使开发人员能够轻松高效地构建应用程序。支持事务、搜索、分析和移动用例,同时使用熟悉的 Eloquent API。了解 MongoDB 的灵活的现代数据库如何改变您的 Laravel 应用程序。

MongoDB
Maska is a Simple Zero-dependency Input Mask Library image

Maska 是一个简单的零依赖输入掩码库

阅读文章
Add Swagger UI to Your Laravel Application image

在您的 Laravel 应用程序中添加 Swagger UI

阅读文章
Assert the Exact JSON Structure of a Response in Laravel 11.19 image

在 Laravel 11.19 中断言响应的精确 JSON 结构

阅读文章
Build SSH Apps with PHP and Laravel Prompts image

使用 PHP 和 Laravel 提示构建 SSH 应用程序

阅读文章
Building fast, fuzzy site search with Laravel and Typesense image

使用 Laravel 和 Typesense 构建快速、模糊的网站搜索

阅读文章
Add Comments to your Laravel Application with the Commenter Package image

使用 Commenter 包在您的 Laravel 应用程序中添加评论

阅读文章