0x1 紹介#
jeff の投稿需求:给网站绑一万个域名,自动生成 HTTPS 证书を見て、自分のいくつかの思い出を思い出しましたので、私の提案を共有します。
この提案は 2019 年に本番環境に導入され、安定して稼働しています。ただし、残念ながら、このプロジェクトは現在、いくつかの理由で停止することに決めましたが、興味のある方は個別に連絡してください。外食プラットフォームを開発しましたが、複数のユーザーをサポートし、各ユーザーが独自のバックエンドを持つことができます。たとえば、私が AABBCC という名前の店舗を持っている場合、プラットフォームは私にサブドメインを割り当てます、例えばAABBCC.example.com
。もちろん、この店舗は独自のドメインを持つこともできます、例えばAABBCC.com
、そしてこれらの 2 つのドメインは実際には同じサイトを指しています。
0x2 簡単な考察#
上記の提案を実現するためには、まず SaaS のマルチユーザーを実現する方法を考える必要があります。各ユーザーは独立したエンティティであり、データも分けて保存する必要があります。どのデータベースを選択するかはどうでしょうか?例えば、MySQL では各ユーザーごとにテーブルを作成することができますか?それとも、mongodb では各ユーザーごとにデータベースを作成することができますか?私は後者を選びましたが、理由は次のとおりです。SaaS システムであるにもかかわらず、各ユーザーは独自のデータベースを持つことができ、後のメンテナンスが容易です。例えば、ユーザーに特別な要件がある場合、特定の機能をカスタマイズすることができます。これらの機能は、彼ら独自のバージョン内で特定の特別な要件を追加することができます。
ドメインへのアクセスを実現する方法は 2 つあります。
- リバースプロキシ
- ルーティング
私はリバースプロキシをより好んでいます。なぜなら、各ユーザーは独自のプログラムを持っており、互いに影響を受けず、1 つのシステムがダウンしても他の商人が連鎖的に影響を受けることはありません。もちろん、100 人のユーザーに対しては全く問題ありません。バックエンドには Node.js を使用しているため、メモリ使用量はほとんど無視できます。
リバースプロキシについては、NGINX にはかなり詳しいですが、ここでは OpenResty を使用します。OpenResty は基本的に NGINX と同じですが、lua モジュールをサポートしているため、ここでは lua モジュールを使用して簡単なトラフィック分散を行うことができます。例えば、ドメインAABBCC.example.com
またはAABBCC.com
にアクセスする場合、対応するバックエンドアドレスをどこかで見つけてリクエストを転送するだけです。キーと値の感覚がありますよね?はい、それが Redis です。Redis は私たちが多くのことを行うのに役立ちます。
ドメインの問題が解決されたので、SSL 証明書も重要ですよね?もちろん、このようなサービスにはウェブサイトに緑のロックを付ける必要があります。ここでは有名なLet's Encrypt
を選択します。無料の 3 ヶ月間の SSL 証明書を提供してくれてありがとう。もちろん、更新の問題も考慮する必要があります...
0x3 問題の解決#
最初の問題は、OpenResty のインストールです。私は Baota パネルを選択しましたが、かなり古いバージョンです。重要なのは、lua モジュールが適切かどうかを確認することです。以前に作成したワンクリックスクリプトを見つけることができませんでした... 皆さんの手腕で解決できるはずです。
次に、ドメインのバインド方法について説明します。まず、ワイルドカードドメインを取得する必要があります。例えば、*.example.com
というドメインは、AABBCC.example.com
をホストするために使用されます。このドメインにアクセスすると、自動的に対応するバックエンドにリバースプロキシされます。先ほど述べたキーと値の原則に基づいて、キーと値のクエリを実行し、OK が返された場合は対応するポートを返し、下流のプロキシに割り当てます。存在しない場合はエラーを返します。
set $subdomain default;
access_by_lua '
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.say("failed to connect: ", err)
return
end
local host = ngx.var.host;
local res, err = red:get(host)
if type(err)=="nil" then
if res ~= ngx.null then
ngx.log(ngx.INFO, "[web.xxx.fr]取出域名对应端口 => ", res)
ngx.var.subdomain = "http://127.0.0.1:"..res
else
ngx.log(ngx.INFO, "[web.xxx.fr]端口未找到 => ", res)
ngx.var.subdomain = "http://127.0.0.1:9998"
end
else
ngx.log(ngx.INFO, "[web.xxx.fr]错误信息 => ", err)
ngx.var.subdomain = "http://127.0.0.1:9998"
end
';
location / {
proxy_pass $subdomain;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Origin "";
add_header X-Cache $upstream_cache_status;
}
これで簡単なトラフィック分散ができました。
緑のロックを自動的に開始する方法はどうでしょうか?プロジェクトは Node.js で書かれていますが、GreenLock というライブラリがありますが、私のプロジェクトには適していませんでした... 小規模なプロジェクトの場合、問題はないはずですが、少なくとも 4 年前にはありませんでした...
ここでは、https://github.com/auto-ssl/lua-resty-auto-ssl というライブラリを選択します。インストールプロセスは省略します。ここでは redis アダプタを選択し、すべての証明書を redis に入れておくことができます。自動更新もありますが、私にとっては自動更新は望ましいものではありません。スマートな自動更新を実現するために、Node.js で定期的なタスクを作成しました。
lua-resty-auto-ssl を使用すると、自動更新するためには、彼らのソースコードを見ると、更新時に secret というものを取得する必要があります。したがって、次のようなコードを作成し、常に最新の secret を取得してサーバーに送信して更新操作を行います。
const getToken = () => {
return new Promise((resolve, reject) => {
httpRequest.get(`http://127.0.0.1:${HOOK_SERVER_PORT}/getSecret`)
.then((response) => {
resolve(response.data.trim());
}).catch((e) => {
reject(e);
});
});
}
更新コマンドは非常に重要です!HOOK_SERVER_PORT を設定して、8999 ポートを開放する必要があります。これもスクリプトを呼び出して実現されます。目的は、'/getSecret' という名前のエンドポイントを開くことです。これは、次の更新コマンドを実行するためです。
export HOOK_SERVER_PORT=8999;
export HOOK_SECRET=前面获取到的;
/resty-auto-ssl路径/dehydrated -c --accept-terms --no-lock -d 域名 --config xxxx -x --challenge http-01 --hook xxxx
毎晩、誰もいない時間に定期的なタスクを実行し、更新が必要な場合はサーバーをリロードする必要があります。とにかく、スクリプトの定期的なタスクが完了します。
また、8999 ポートで更新することもできますが、設定に応じてポートを変更できます。
以上で要件は満たされました。
証明書は Redis にあり、証明書は独自の証明書に置き換えることができます。顧客がドメインをバンドルするのは非常に簡単です。単一のデプロイメントは快適です。
0x4 結論#
この提案を選択する前に、Caddy も考慮しましたが、当時の Caddy は安定しておらず、更新が頻繁に遅れることがありましたので、諦めました。ソースコードを読む道に進みましたが、このようなフレンドリーなオープンソースプロジェクトを見たことで、自分自身を向上させることができました。この更新の方法については、オンラインで多くの人々が言及していますが、自動的に secret をフックしてアクティブに更新するスクリプトは誰も言及していませんでした。少し共有します。時間があればデモを上げますが、途中で断念しました...