dockerでnginxを試してみる
nginxをよく見かけます。
1度くらい触ってみようと思って数年。ようやく重い腰を上げたいと思います。
とりあえず試して削除したいときに便利なのがdockerです。
dockerを利用してローカルPCで基本機能を試してみました。
コンテナ実行
docker run -it --name nginx -p 8080:80 nginx /bin/bash
ローカルにnginxのイメージがなければDockerhubから自動で取得して実行してくれます。
本来のnginxコンテナの使い方としては末尾の「/bin/bash」は不要ですが、今回は中に入って色々いじってみたいのでこのようにしました。
実行するとコンテナの中に自動で入ります。
タグを指定していないのでnginx:latestが使われます。nginx:latestのベースはdebian:buster-slimですのでコンテナの中ではdebian系のコマンドを使う必要があります。
nginx実行
docker runでコマンドを指定(/bin/bash)したので、デフォルトの「CMD ["nginx", "-g", "daemon off;"]」が上書きされしまい、nginxが起動していません。
手動で起動します。
/etc/init.d/nginx start /etc/init.d/nginx status
確認
ブラウザでhttp://localhost:8080にアクセスしてWelcomeページが表示されることを確認します。
設定確認
基本的な設定を確認します。
more /etc/nginx/nginx.conf
nginx.confのhttpディレクティブに以下の行があります。具体的なWebサイト関連の設定については/etc/nginx/conf.d/配下のconfファイルを参照する設定です。
include /etc/nginx/conf.d/*.conf;
/etc/nginx/conf.d/を見に行くとdefault.confがあります。
default.confを確認すると以下の行があります。/usr/share/nginx/htmlにあるファイルをクライアントに返す設定です。
location / { root /usr/share/nginx/html; index index.html index.htm; }
Welcomeページの変更
/usr/share/nginx/html/index.htmlを編集してWelcomeページを変更しましょう。
buster-slimは軽量なのでエディタがありません。viをインストールします。
apt-get update apt-get install vim
index.htmlを適当に編集します。
vi /usr/share/nginx/html/index.html
ブラウザを再読み込みしてWelcomeページが変更されたことを確認します。
リバースプロキシ
nginxといえばリバースプロキシとして利用されることが多いです。リバースプロキシを試します。
default.confを変更します。localhost:8080/へのアクセスをGoogleに転送する設定です。非現実的な構成ですが手っ取り早いです。
location / { proxy_pass http://www.google.com; }
設定ファイルのValidationをしてからnginxを再起動します。
/etc/init.d/nginx configtest /etc/init.d/nginx restart
ブラウザを再読み込みしてGoogleが表示されることを確認します。
静的ファイル配信
APIコールはWebアプリケーションに転送して、静的ファイルはnginxが配信するというのもよく見る構成です。
TCP 5000番で待っている適当なWebアプリケーションをローカルPCで立ち上げます。
default.confを以下のように変更してnginxを再起動します。
location / { root /usr/share/nginx/html; index index.html index.htm; } location /api { proxy_pass http://host.docker.internal:5000; }
localhost:8080にアクセスするとnginxが静的ファイルを返します。html、js、cssファイルやfavconなどを返す想定です。
localhost:8080/api配下へのAPIコールはTCP 5000番で待っているアプリケーションに転送されます。
「host.docker.internal」はdockerを実行しているローカルPCを表しています。Docker for Mac固有のようです。これでローカルPCの5000番で待機しているアプリケーションに転送されます。誤って「localhost」と記載するとコンテナ自身を表すのでnginxコンテナの5000番に転送してエラーになります。
「proxy_pass http://host.docker.internal:5000」の後ろに/をつけると厄介なことになります。このリンクに説明があります。
https://www.xmisao.com/2014/05/09/nginx-proxy-pass.html
アプリケーションもコンテナの場合
本番環境ではアプリケーションもコンテナ化してDockerで起動することでしょう。その場合はコンテナ同士はコンテナ名やnetwork-aliasで通信できます。
よって本番ではproxy_passディレクティブは以下のようにアプリケーションコンテナの名前(myapp等)にします。アプリケーションは5000番を外部にEXPOSEする必要はありません。
location /api { proxy_pass http://myapp:5000; }
コンテナの起動順序
nginxは起動時にupstream(上の例のmyapp)の名前解決を行っているようです。名前解決できないと「host not found in upstream 'myapp'」のようなエラーを出して起動に失敗します。
これはdocker-composeを利用するときに注意が必要です。myappコンテナの起動がmydbコンテナの起動待ちで遅れるとnginxが起動できなくなります。depends_onなどで起動順序を制御しましょう。
try_filesディレクティブ
try_filesディレクティブを使う場合はこうなります。
location / { root /usr/share/nginx/html; try_files $uri $uri/ @webapp; } location @webapp { proxy_pass http://host.docker.internal:5000; }
私が用意したアプリケーションは静的ファイルを持っていません。try_filesだとアプリケーションに不要な静的ファイル要求が多く飛ぶ気がするのでやめました。
プロキシヘッダの付与
アプリケーションのアクセスログを見ると要求を送信したクライアントではなくnginxのIPアドレスからアクセスがあったように見えます。nginxがパケットの送信元アドレスを書き換えてしまうからです。
本当に見たいのは要求を送信したクライアントのアドレスです。default.confを以下のように変更してnginxを再起動します。
location /api { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://host.docker.internal:5000; }
nginxはパケットを書き換えるときにX-Forwarded-ForヘッダにクライアントPCのアドレスを追記します。多くのアプリケーションはX-Forwarded-Forヘッダがある場合はアクセスログにX-Forwarded-Forヘッダの値を出力します。これでアクセスログにクライアントのアドレスが出力されるようになります。
Macではまった
上記の設定でDocker for Mac(Docker version 20.10.0, build 7287ab3)ではうまく動いてくれずdocker bridgeのアドレスが出力されました。悩んだ末、Centosで試したら期待通りに動きました。バージョン20.10.0の不具合かDocker for Mac固有の問題かはわかりません。
bridgeがだめなら、と試したhost networkが動かなかったり、Mac上でのDockerは落とし穴が多いです。
その他のヘッダ情報の転送
アドレスだけでなくホスト名やスキームなど諸々の情報もアプリケーションへ転送したい場合は下記のようにします。
location /api { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://host.docker.internal:5000; }
Basic認証
認証をかけてみましょう。
さくっと試せるBasic認証を使います。
htpasswdコマンドを利用するためにパッケージを追加します。
apt-get install apache2-utils
ログインIDとパスワードを登録します。
htpasswd -c /etc/nginx/.htpasswd hoge New password: hoge Re-type new password:hoge Adding password for user hoge
default.confのlocation /とlocation /apiに下記の2行を追加してngnxを再起動します。
auth_basic "Login Required."; auth_basic_user_file /etc/nginx/.htpasswd;
ブラウザでlocalhost:8080/にアクセスするとログイン画面が表示されるようになります。ログインするとコンテンツが表示されます。
Postmanを使って認証なしでlocalhost:8080/apiをGETすると「401 Authorization Required」が返ってきます。Basic認証付きでGETすると値を取得できました。
HTTPS
長くなるので別の記事として投稿します。
ログ
default.conf内の下記行のコメントアウトを削除してnginxを再起動します。
access_log /var/log/nginx/host.access.log main;
/var/log/nginx/host.access.logにアクセス情報が記録されます。
終了
exitするとnginxコンテナも終了します。
exit
掃除
終了したnginxコンテナを削除します。
docker rm nginx
nginxのDockerイメージを削除します。後でまた試したいなら残しておいてもいいです。
docker rmi nginx:latest