ぷらすのブログ

WebサーバをNginxから証明書自動更新に対応したCaddy 2に移行した

#開発#Caddy#Nginx

こんにちは、@p1assです。

最近、運用していた Web サーバを Nginx から Caddy に移行しました。この記事では Caddy の特徴や移行してみた感想などを書きたいと思います。

What is Caddy?

Caddy はデフォルトで HTTPS に対応している OSS の Web サーバです。

Caddy

つい先日(2020/05/04)、v2 にメジャーバージョンアップされアーキテクチャが刷新されました。

Caddy は様々な特徴があるのですがいくつかピックアップして紹介します。(v2 からの新機能というわけではないです。)

デフォルトかつ自動で HTTPS に対応

Caddy は Let's Encrypt の証明書を自動で取得し、適用してくれます。そのため、ユーザ側が証明書のことを気にする必要がありません。 また、80 から 443 へのリダイレクトも自動で行われるのでユーザが設定する必要がありません。

Nginx の場合は、これらを実装するために多くのコンフィグを書いていたことを考えると、かなりの作業を省略できます。

Nginx にあった基本的な機能をカバー

一般的なユースケースで必要となる機能は大体カバーしています。

他にも面白い機能として、

などもあります。

シンプルに設定を記述できる Caddyfile

Caddy の設定の反映方法は 2 通り用意されており、ユーザが用途に合わせて選べるようになってます。

1 つ目は Caddyfile というファイルで設定する方法で、Nginx のコンフィグに似た書き方で記述できます。

# https://hoge.p1ass.comにきたリクエストをlocalhost:8080にプロキシ
hoge.p1ass.com {
  reverse_proxy /* localhost:8080
}

# バーチャルホストで複数のドメインを設定できる
fuga.p1ass.com {
  reverse_proxy /* localhost:3000
}

後は起動時にファイルを指定すれば完了です。

$ caddy run --config ./Caddyfile --adapter caddyfile

公式で、

Caddyfiles to be ~15% the size of a less capable nginx.conf!

と謳われているように、Nginx と似た文法ながらもシンプルに記述することが可能です。

JSON API 経由での設定変更

2 つ目は API 経由で設定をアタッチする方法です。 Caddy は 80 番と 443 番以外に 2019 番も Listen していて、管理用の API が提供されています。

例えば、現在の設定は GET /config で取得できます。

$ curl -s localhost:2019/config | jq
{
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "listen": [
            ":443"
          ],
          "routes": [
            {
              "handle": [
                {
                  "handler": "subroute",
                  "routes": [
                    {
                      "handle": [
                        {
                          "handler": "reverse_proxy",
                          "upstreams": [
                            {
                              "dial": "localhost:8080"
                            }
                          ]
                        }
                      ],
                      "match": [
                        {
                          "path": [
                            "/*"
                          ]
                        }
                      ]
                    }
                  ]
                }
              ],
              "match": [
                {
                  "host": [
                    "hoge.p1ass.com"
                  ]
                }
              ],
              "terminal": true
            },
              {
              "handle": [
                {
                  "handler": "subroute",
                  "routes": [
                    {
                      "handle": [
                        {
                          "handler": "reverse_proxy",
                          "upstreams": [
                            {
                              "dial": "localhost:3000"
                            }
                          ]
                        }
                      ],
                      "match": [
                        {
                          "path": [
                            "/*"
                          ]
                        }
                      ]
                    }
                  ]
                }
              ],
              "match": [
                {
                  "host": [
                    "fuga.p1ass.com"
                  ]
                }
              ],
              "terminal": true
            }
          ]
        }
      }
    }
  }
}

これと同じ形式で POST /load に JSON を投げれば動的に設定を変更できます。なお、デフォルトでは JSON ですが、Content-Type: text/caddyfile とすればそのまま Caddyfile を投げることもできます。

また、この設定の変更は ACID を保証しています。

Atomic: Multiple changes in a single request are treated as a single unit; any failed change aborts all the other changes.
Consistent: No invalid configurations can be loaded; your server will never break if a problem is detected at config load.
Isolated: No config changes rely on another. (It helps that HTTP is a stateless protocol!)
Durable: Caddy automatically persists the current, valid configuration to disk and can safely resume it after a power cycle if the --resume flag is used.

無効な設定が POST されたとしてもサーバが止まる心配はありません。

余談: 設定方法の使い分け

CI・CD パイプラインに組み込みたいなら API 経由での設定がオススメです。 Caddyfileを使うか、JSON を使うかは悩みどころですが、

かなぁと個人的に思っています。

Nginx から Caddy への移行のモチベーション

このように様々な機能があるのですが、Caddy への移行の最も大きなモチベーションになったのは「HTTPS の自動対応」でした。

今までは Nginx+certbot という組み合わせで運用していたのですが、証明書のためだけにバッチ処理を走らせるのも面倒で、Nginx 単体で完結しないかなぁと思っていました。

その Nginx も HTTPS に対応するには、実際の動作とはあまり関係のない非本質的な記述を大量に書く必要があります。HTTPS が当たり前になってきたこのご時世に合ってないと感じていました。

その点、Caddy は証明書の更新は内部でやってくれて、設定の記述もシンプルです。メンテナンス性を考慮すると Caddy は非常に魅力的でした。

移行してみた感想

Caddy には Nginx のコンフィグをそのまま読み込むアダプターが存在するのですが、今回は1からで Caddyfile を書きました。

動作に関しては概ね問題ないです。セットアップが完了してから一度もプロセスは死んでないし、メモリリークなどもなさそうです。

そして当初の目論見通り、メンテナンスのしやすさは非常に向上しました。バーチャルホストを新しく切るのに 3 行追加するだけ いうのは非常に楽でした。

細かいところで言うと、 caddy fmtCaddyfile のフォーマットができるのも良いポイントでした。

おわりに

← レイヤードアーキテクチャを採用した際のWebSocket実装例[Go] スライスのfor rangeループ内で新しいスライスにappendしたらバグらせた →
Topへ戻る