envsubstを使用してnginx Dockerイメージに環境変数を埋め込む
2024-08-02

この記事では、Webサーバーの1つであるNginxでダイナミックな環境変数をサポートするようにDockerイメージを構成する方法に焦点を当てます。これにより、より柔軟なフロントエンドのデプロイを実現することができます。
しかし、その前に、Nginx自体についての簡単な紹介と、いつ使用すべきか、いつ使用すべきでないか、フロントエンドの配信でNginxを使用する理由について説明します。
2025/07/15:Nginxの公式Dockerイメージは日々更新されており、より手軽に環境変数を埋め込むことができるようになっているため、記事を更新しました。
Nginxとは?
Nginxは人気のあるオープンソースの Web サーバーソフトウェアで、リバースプロキシ、ロードバランサー、メールプロキシ、および HTTP キャッシュとしても機能します。
いつ使用すべきか?
Nginxは、いくつかのシナリオで優れた性能を発揮する多用途なWebサーバーです:
- 高トラフィックのウェブサイト 多数の同時接続を効率的に処理し、忙しいサイトに最適です。
- 静的コンテンツの配信 画像、CSS、JavaScriptなどの静的ファイルを迅速に配信します。
- ロードバランシング トラフィックを複数のサーバーに効果的に分散し、信頼性とパフォーマンスを向上させます。
- リバースプロキシ アプリケーションサーバーの前に配置し、キャッシングやSSL終端などの機能を追加します。
- API ゲートウェイ 分散アーキテクチャ内の異なるマイクロサービスへのリクエストルーティングに適しています。
- パフォーマンス最適化 キャッシング機能により、Nginxはウェブサイトの速度を大幅に向上させます。
- 高度なセキュリティ レート制限などの機能により、特定の種類の攻撃から保護します。
いつ使用を避けるべきか?
Nginxは強力で多用途なWebサーバーですが、適さないシナリオもあります:
- シンプルで低トラフィックのウェブサイト 基本的なウェブサイトにはNginxの高度な機能は過剰かもしれません。Apache HTTP Serverのようなシンプルなサーバーの方が設定と管理が簡単かもしれません。
- Windowsベースの環境 NginxはWindows でも動作しますが、主にUnix系システム向けに設計されています。Windows 中心の環境では、IIS (Internet Information Services) の方が自然な選択かもしれません。
- .htaccess サポートが必要な場合
Apache HTTP Serverとは異なり、Nginxは
.htaccess
ファイルをサポートしていません。アプリケーションが.htaccess
に大きく依存している場合、Nginxに移行するには大幅な変更が必要です。 - Webサーバーとの深い統合が必要なアプリケーション 特定のWebサーバーと緊密に連携するように構築されたアプリケーションがあります。別の Web サーバーのアーキテクチャに密接に結び付いているアプリケーションをNginxに移行するのは困難です。
- リアルタイム通信が主な要件の場合 NginxはWebSockets を扱えますが、リアルタイムの双方向通信に主に焦点を当てたアプリケーションには、Node.jsとSocket.IOのような専門的なソリューションがより適しているかもしれません。
- 社内の専門知識が限られている場合 他のWeb サーバーに慣れているチームの場合、Nginxの学習曲線はシンプルなプロジェクトにはあまり価値がないかもしれません。
- GUI ベースの管理が広範に必要な場合 Nginxには管理用の組み込みGUIがありません。非技術スタッフが Web サーバーを管理する必要がある環境では、包括的なグラフィカルインターフェースを備えたソリューションが好まれるかもしれません。
なぜNginxをフロントエンド配信で使うか?
今までRe:Earthでは、フロントエンドをデプロイする方法として、ソースコードをGCS(Google Cloud Storage)のバケット上に配置し、CDN経由でそれらの配信を行っていました。
ところがこの方法では、次の問題が発生するようになりました。
- フロントエンドをgsutil rsyncでデプロイしているため、何らかの理由で途中でデプロイが中断されると、バケット上のファイルが中途半端な状態になる可能性がある。また、デプロイ中にユーザーのアクセスがあると、すべてのファイルが揃っていない状態でファイルが配信される可能性がある。
- 新バージョンのデプロイ後にフロントエンドにバグが発見されたときに、迅速にロールバックすることが難しい。
- GCS上で簡単にロールバックできる仕組みを構築しようとすると、CI/CDのワークフローやスクリプトが煩雑になる。
そのため、Nginxでフロントエンドのファイルを配信するようにし、そのDockerイメージをCloud Runにデプロイする方法へ移行しました。
この方法により、以下のメリットを得られます。
- CI/CDのワークフローがシンプルになる。
- Cloud Runの機能で簡単にフロントエンドのロールバックやトラフィックの変更が可能になる。中途半端な状態はなくなり、ダウンタイム無しでリビジョンの切り替えが可能になる。
- Cloud Deployを導入し、フロントエンド含めてカナリアリリースの実現が可能になる。
この方法は、フロントエンドの配信においてGCSのスケーラビリティの恩恵を受けられなくなるというデメリットはありますが、それ以上にメリットが大きいと考えました。
ただし、これを実現するためには、nginxイメージが環境変数を通していくつかの設定をカスタマイズできる機能を持つ必要があります。これがないと、1つでも設定を変更する度にDockerイメージの再ビルドが必要になってしまいます。
シンプルなNginx Dockerイメージを作成する
基本的なNginx Docker イメージを作成するために、公式のNginxイメージを出発点として使用します。設定方法は以下の通りです。
はじめに、プロジェクト用の新しいディレクトリを作成します:
mkdir nginx-docker
cd nginx-docker
Dockerfileを作成します:
touch Dockerfile
好きなテキストエディタで Dockerfile を開き、以下の内容を追加します:
FROM nginx:alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY html /usr/share/nginx/html
Dockerfileの内容は以下の通りです:
- 公式のNginx Alpineイメージをベースとして使用
- カスタムnginx.confファイルをコンテナにコピー
- HTMLファイルをデフォルトのNginxウェブルートにコピー
次に、基本的なnginx.confファイルを作成します:
touch nginx.conf
以下のシンプルな設定を追加します:
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
/etc/nginx/nginx.conf
ではなく/etc/nginx/conf.d/*.conf
に設定ファイルを置く場合、設定ファイルにhttp
ブロックは不要です。設定ファイルがデフォルトのnginx.conf
のhttp
ブロック内に埋め込まれるためです。
HTML ディレクトリを作成し、index.html ファイルを追加します:
mkdir html
echo "<h1>Hello from Nginx Docker!</h1>" > html/index.html
Docker イメージをビルドします:
docker build -t my-nginx-image .
コンテナを実行します:
docker run -d -p 8080:80 my-nginx-image
http://localhost:8080 にアクセスすると Hello from Nginx Docker! が表示されるかと思います。
イメージの環境変数を使用する
Nginxイメージでは、コンテナ起動時に /etc/nginx/templates/*.templates
内のファイルを自動的に処理し、 /etc/nginx/conf.d/*
に配置してくれる機能を持っています。これを使って、コンテナ起動時に、動的にNginxの設定ファイルに環境変数を埋め込むことができます。
まず、Nginx設定のテンプレートを作成します。nginx.conf
を nginx.conf.template
にリネームし、環境変数を使用するように変更します:
server {
listen ${PORT};
server_name ${SERVER_NAME};
location / {
root /usr/share/nginx/html;
index index.html;
}
}
次に、Dockerfileを更新して、テンプレートを使用するようにします:
FROM nginx:alpine
COPY nginx.conf.template /etc/nginx/templates/default.conf.template
COPY html /usr/share/nginx/html
更新されたDockerイメージをビルドします:
docker build -t my-nginx-env-image
環境変数を指定してコンテナを実行します:
docker run -d -p 8080:80 -e PORT=80 -e SERVER_NAME=example.com my-nginx-env-image
これで、Nginx設定は提供された環境変数の値を使用します。この設定により、イメージを再ビルドすることなくnginxの設定を変更することができます。
この方法を使用して他のNginx設定も変更できます。テンプレートに変数を追加し、コンテナ実行時に渡すだけです。更に、nginx.conf以外にも任意のファイルを生成することもできます。
このアプローチは、イメージをできるだけ汎用的で再利用可能に保ち、実行時に特定の構成を提供することが求められるコンテナ化環境で特に有用です。
なぜ自動的に環境変数が設定ファイルに埋め込まれるのか
なぜこれだけで、環境変数が設定ファイルに埋め込まれるのか?と不思議に思った人もいるかも知れません。
実は、Nginxイメージの ENTRYPOINT
には、 /docker-entrypoint.sh
が設定されています。つまり、コンテナ起動時にこのスクリプトが実行されるようになっているのです。
このスクリプト内には、 /docker-entrypoint.d/*.sh
を順番に実行し、それからNginxを起動するという処理が実装されています(参考)。
更に、コンテナ内にはデフォルトで、以下のスクリプトが /docker-entrypoint.d
に含まれています。
10-listen-on-ipv6-by-default.sh
: IPv6サポートを有効にするスクリプト(参考)15-local-resolvers.envsh
: ローカルDNSリゾルバーを環境変数として設定するスクリプト(参考)20-envsubst-on-templates.sh
: テンプレートファイルの環境変数置換を行うスクリプト(参考)30-tune-worker-processes.sh
: Nginxのワーカープロセス数を自動調整するスクリプト(参考)
このうち 20-envsubst-on-templates.sh
が実行される際に、内部で envsubst
ユーティリティが呼ばれ、 /etc/nginx/templates/*.template
が /etc/nginx/conf.d/*
に環境変数を埋め込みながら配置してくれるというわけです。
よって、環境変数をただNginx設定ファイルに埋め込みたい場合は、シェルスクリプトを自分で作成してイメージ内に配置する必要はありません。ただ、/etc/nginx/templates/*.template
を配置するだけです。
ただし、 /etc/nginx/conf.d/nginx.conf
はすでに使われているので、意図せぬ上書きを防ぐためにも、 /etc/nginx/templates/nginx.conf.template
は作成しないほうが安全です。
起動前にカスタムスクリプトを動かす
Nginx起動時に、環境変数を設定ファイルに埋め込むだけでなく、ほかにも様々な処理を実行したいことがあるかもしれません。その場合も簡単です。
例えば、予め用意したJSONファイルテンプレートに環境変数を埋め込み、 /usr/share/nginx/html
内に配置することを考えます。
まずは config.json.template
ファイルを作成し、以下のようにします。
{ "apiUrl": "${API_URL}" }
次に、 04-envsubst-config.sh
シェルスクリプトを作成します(実行権限をつけることをお忘れなく)。シェルスクリプトの内容は以下の通り。
#!/bin/sh
envsubst < /opt/config.json.template > /usr/share/nginx/html/config.json
Nginxイメージで環境変数を使用するには、envsubst
ユーティリティを使用します。このツールはNginxのイメージに既に含まれています。
次に、このシェルスクリプトをDockerfile内で /docker-entrypoint.d
ディレクトリにコピーします。
FROM nginx:alpine
COPY nginx.conf.template /etc/nginx/templates/default.conf.template
COPY config.json.template /opt/config.json.template
COPY 04-envsubst-config.sh /docker-entrypoint.d/04-envsubst-config.sh
COPY html /usr/share/nginx/html
これで設定完了です。Dockerイメージをビルドし、 -p API_URL=https://example.com
オプションを付けてコンテナを起動し、 http://localhost:8080/config.json
にアクセスすれば、以下のようなJSONが閲覧できるはずです。
{ "api_url": "https://example.com" }
これは、フロントエンドが設定を環境変数経由で詠み込む場合に役に立ちます。
なぜ 04-
という数字をシェルスクリプトのファイル名の先頭に使用したかは、もうお気づきかもしれません。/docker-entrypoint.sh
は /docker-entrypoint.d/*.sh
を辞書順で順番に実行するため、デフォルトのスクリプトのあとに実行されるようにするためです。
この例では、API_URL環境変数が設定されていない場合は、エラーは発生せずにJSONに
${API_URL}
がそのまま出力されてしまいます。本番環境で使用する際は、バリデーションやエラーハンドリングをシェルスクリプト内で実装したほうが良いかもしれません。
まとめ
この投稿では、動的な環境変数をサポートするようにNginxのDockerイメージを構成する方法を探りました。以下はカバーした内容の概要です:
- 最初にNginxの概要を説明し、その強みと潜在的な使用例を議論しました。
- 静的コンテンツを提供するための基本的な Nginx Docker イメージを構築しました。
- その後、環境変数を使用した動的な構成をサポートするようにDockerイメージを拡張しました。
- 最後に、起動前にカスタムスクリプトを動かす方法を学びました。
この設定を実装することで、開発環境から本番システムまで、さまざまなシナリオでnginxを簡単かつ効率的に展開できるようになります。
この方法は強力ですが、特に本番環境では環境変数を安全に管理することが重要です。Dockerシークレットやその他の安全な方法を使用して機密情報を扱うことを検討してください。
NginxのパフォーマンスとDockerの柔軟性の組み合わせにより、多くのWebサーバーのニーズに対応する堅牢な基盤が構築されます。これらの技術を自分の特定の使用例に最適に適応させ続けてください。
Docker化を楽しんでください!
参考文献
Eukaryaでは様々な職種で積極的にエンジニア採用を行っています!OSSにコントリビュートしていただける皆様からの応募をお待ちしております!
Eukarya is hiring for various positions! We are looking forward to your application from everyone who can contribute to OSS!
Eukaryaは、Re:Earthと呼ばれるWebGISのSaaSの開発運営・研究開発を行っています。Web上で3Dを含むGIS(地図アプリの公開、データ管理、データ変換等)に関するあらゆる業務を完結できることを目指しています。ソースコードはほとんどOSSとしてGitHubで公開されています。
➔ Eukarya Webサイト / ➔ note / ➔ GitHub
Eukarya is developing and operating a WebGIS SaaS called Re:Earth. We aim to complete all GIS-related tasks including 3D (such as publishing map applications, data management, and data conversion) on the web. Most of the source code is published on GitHub as OSS.
➔ Eukarya Official Page / ➔ Medium / ➔ GitHub