文系プログラマによるTIPSブログ

文系プログラマ脳の私が開発現場で学んだ事やプログラミングのTIPSをまとめています。

dockerとnginxで静的リソースをgit環境無しで閲覧できる環境を構築しよう!

デザインhtmlをgit管理すると、どうしても営業やテスターが困っちゃうんですよね〜
f:id:treeapps:20170501215041p:plain

最近会社で「未だにsvnを使ってる悪い子はいねがぁ〜〜!?」と、社内をなまはげが闊歩しているような、していないような感じなので、デザインhtml関連をgitに移行してやりました。

しかしここで問題が発生しました。

デザインhtml

唐突に「デザインhtml」という言葉を出してしまったので説明します。WEBサイトの開発を行う際は、以下のフローで開発が行われます。

  1. デザイナがjpgやpng形式でサイト全体を画像化してお客様に見せる。
  2. お客様からデザインOKが出たら、画像だったものをhtmlファイル化する。これをデザインhtmlと呼ぶ
  3. エンジニアはデザインhtmlを元に、動的なサイトを実装する。

エンジニア以外の方とgitについて

営業「うむむ・・・gitに移行したのはいいんだけど、これ、最新ファイルを取得するツールとか入れないと駄目だよね?svnより簡単?」

テスター「私、あまりgitに詳しく無いので、ツールの使い方教えて貰えますか?」

デザイナ「私はgit使えますが、ローカルでhtmlコーディングしているので、今pushされている状態を見せるのにいちいちstashしたりするの面倒なんですよね…」

はい。エンジニア以外から「ツール(SourceTree)の使い方教えろや」「pushされている状態のhtmlを今すぐ見せろや」等といった要望が挙がりました。

GHEやGitbucketを使っている場合、画像やcssの中身は確認できますね。しかし、htmlファイルを開こうとしても、表示されるのソースコード(html)です。chromeのアドオン等でhtmlをプレビューできるものもあるようですが、全員にそれを強要するもの残念です。

かといって全員にSourceTreeの使い方と、gitについてレクチャーするもの大変です。新規メンバーが参入したらまた一からレクチャーしないといけません。

それらを考慮し、何とかしてみる事にしました。

ミッション

  • git環境無しに、デザイナが作成したデザインhtml(静的リソース)をブラウザで確認できる事。
  • 既存のサーバを可能な限り汚さない事。
  • 他サーバへの引っ越しがし易い事。
  • 他のエンジニアが比較的容易にカスタマイズしやすい事。
  • 下手に頑張り過ぎず、例えばapacheのDirectory Index(nginxだとautoindex)で済ませてしまう事。
  • その仕組を捨て去る事になっても泣かない事。

これらを考慮し、以下の構成にしました。

  • docker + docker-compose
  • Alpine Linuxベースのnginx
  • nginxのautoindexを使ってしまう。

最初はnode.js系のDirectory Indexの仕組みを使ってみたりもしましたが、どうしてもexpress等のjsのコードを書かないといけなかったり、面倒だったのです。使用するnode_modulesが用意しているcallbackを頑張ってチマチマ修正しても、モジュールが提供していない機能が欲しくなったりして面倒かも?と思ったので、ならいっそ皆扱える(筈)nginxにしちゃえ!と考え、そうする事にしました。

何故Dockerを使うか

既存のサーバを汚したくない

まあnginxだったらインストールしてもいいかな?とも思いますが、nginx自体のバージョンアップとか考えると面倒だし、そのサーバに既に別のwebサーバがインストールされていたりするとちょっと困ってしまいますね。既存のソフトウェアとのバッティングを避ける際はdockerを使うと楽です。

他のサーバへの引っ越しが楽

他のサーバに仕組みをゴッソリ引っ越したくなった場合、yum等でインストールしてしまうとアンインストールする必要があるし、アンインストールした事で既存環境に影響を及ぼしたり、ゴミが残ってしまったりすると面倒です。dockerであれば、Dockerfileやdocker-compose.yml等の少量のファイルさえあれば、大抵のサーバですぐ動きます。

モチベーション

地味に重要だと思うのですが、私は未だにオンプレ案件を多く担当していますが、オンプレ系プロジェクトはとにかく技術要素が古い場合が多く、モチベーションが上がりにくかったりします。それを少しでも解消するためにdockerを使ってみる、というものアリなんじゃないでしょうか。

軽量

https://hub.docker.com/_/nginx/

公式のAlpine linuxベースのnginxが用意されているので、これを使います。

イメージのサイズも15MByteと非常に小さく、イメージのダウンロードもすぐ終わります。

$ docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
nginx                                1.12.0-alpine       61055e9116d4        11 days ago         15.5 MB

nginxで作る静的リソースビューア

github.com

例によって完成形は作っておきました。git cloneして、docker-compose upすればすぐ起動します。

こちらをcloneし、必要な部分をカスタマイズして下さい。

dockerのインストール

手順はオフィシャルサイトに丸投げします。

docs.docker.com

使用するイメージ

https://hub.docker.com/_/nginx/

こちらです。公式のAlpineベースのnginxを使っています。

docker-compose.yml

version: '3'
services:
  webserver:
    build: .
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ./default.conf:/etc/nginx/conf.d/default.conf:ro
      - ./htdocs:/var/www/html:ro
      - ./assets/:/etc/nginx/assets/:ro
    image: treetips/simple-static-file-viewer
    container_name: simple-static-file-viewer
    tty: true
    restart: always
    environment:
      - LANG=ja_JP.UTF-8
      - TZ=Asia/Tokyo
    ports:
      - "8081:80"
    command: "nginx -g 'daemon off;'"

これは最新バージョンのインタックスなので、v3に非対応の場合はversionを2に書き換えて下さい。

では、少しづつ解説していきます。

/etc/localtime:/etc/localtime:ro

このマウントは、タイムゾーンを設定するために行います。

一般的なlinuxのタイムゾーン変更であれば以下のようにしますね。

ln -sf  /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

しかしAlpine Linuxにはそもそも /usr/share/zoneinfo自体が無いので、dockerホストのlocaleをコンテナにマウントしてしまいます。こうする事で、dockerホストとコンテナが同一のlocaleとなります。

/default.conf:/etc/nginx/conf.d/default.conf:ro

公式のnginxイメージは、/etc/nginx/nginx.confの中に、include /etc/nginx/conf.d/*.conf; という形でファイルが読み込む記述があります。つまり、nginx.confを直接カスタマイズするのではなく、conf.d配下にファイルを配置してね、という事ですね。

何故「default.conf」という名前にしているのかというと、公式イメージに既に/etc/nginx/conf.d/default.confが存在するためです。そのdefault.confは大部分がコメントアウトされており、コメントされていない部分のみを抽出すると、以下のようになります。

/etc/nginx/conf.d # cat default.conf
server {
    listen       80;
    server_name  localhost;
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

これだったら、もういっそこのファイル自体を上書きして、自分の自由にカスタマイズしたいので、default.confをマウントして上書いています。

./htdocs:/var/www/html:ro

nginxのautoindexを使うにあたり、htdocsが重要になります。このhtdocsにファイルが配置される事で、autoindexにファイル・フォルダが反映されます。rw(read only)と書いていますが、ファイル・フォルダをコピーしたり移動したりする事ができるので、ホスト側のhtdocs配下にファイルを配置すれば、ブラウザで即確認可能になります。

./assets/:/etc/nginx/assets/:ro

これはちょっと特殊です。nginx標準のautoindexは、以下のような表示になります。

f:id:treeapps:20170501210528p:plain

何だかガタガタして縦に綺麗に並ばないし、見難いのですよね。見難いのであれば、スタイルをいじってしまえばいいのです。

nginxでスタイルをいじるには、ngx_http_addition_moduleモジュールを使います。このモジュールはalpineベースのnginxイメージに含まれているので、コンパイル等をせず使う事ができます。

Module ngx_http_addition_module

使い方は以下のような感じで、add_before_bodyとadd_after_bodyを設定します。引数はファイルパス等ではなく、URIなのでご注意下さい。

    location / {
        add_before_body       /assets/nginx-before.txt;
        add_after_body        /assets/nginx-after.txt;
        autoindex             on;
        autoindex_localtime   on;
    }

/assets/というURIは、以下のようにhtdocsとは別に、aliasで作り出しています。/etc/nginx/assets自体は、docker-compose.ymlでマウントして用意したディレクトリです。

    location /assets {
        alias                 /etc/nginx/assets;
    }

ngx_http_addition_moduleですが、通常のautoindexは、以下のようなhtmlを出力します。

<html>
<head><title>Index of /</title></head>
<body bgcolor="white">
<h1>Index of /</h1><hr><pre><a href="../">../</a>
<a href="%E4%BB%8A%E6%97%A5%E3%81%AF%E3%81%84%E3%81%84%E3%81%8A%E5%A4%A9%E6%B0%97%E3%81%A6%E3%82%99%E3%81%99/">今日はいいお天気です/</a>                                       30-Apr-2017 18:57                   -
<a href="Dockerfile">Dockerfile</a>                                         29-Apr-2017 14:57                  65
<a href="README.md">README.md</a>                                          29-Apr-2017 15:36                2624
<a href="default.conf">default.conf</a>                                       29-Apr-2017 13:55                1236
<a href="docker-compose.yml">docker-compose.yml</a>                                 29-Apr-2017 14:07                 451
</pre><hr></body>
</html>

ngx_http_addition_moduleを使うと、以下のように、<pre>タグより前と後のhtmlを自由にカスタマイズする事ができるようになります。簡単に言えば、ヘッダーとフッターを自由に書く事ができるようになり、テンプレートエンジンのように共通のヘッダー・フッターを持つ事ができます。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" type="text/css" />
</head>
<body>
<div class="container-fluid">
  <div class="row-fluid">
    <div class="col-md-12"><html>
<head><title>Index of /</title></head>
<body bgcolor="white">
<h1>Index of /</h1><hr><pre><a href="../">../</a>
<a href="%E4%BB%8A%E6%97%A5%E3%81%AF%E3%81%84%E3%81%84%E3%81%8A%E5%A4%A9%E6%B0%97%E3%81%A6%E3%82%99%E3%81%99/">今日はいいお天気です/</a>                                       30-Apr-2017 18:57                   -
<a href="Dockerfile">Dockerfile</a>                                         29-Apr-2017 14:57                  65
<a href="README.md">README.md</a>                                          29-Apr-2017 15:36                2624
<a href="default.conf">default.conf</a>                                       29-Apr-2017 13:55                1236
<a href="docker-compose.yml">docker-compose.yml</a>                                 29-Apr-2017 14:07                 451
</pre><hr></body>
</html>
    </div>
  </div>
</div>
</body>
</html>

例えばこのように、bootstrapを適用するだけで、以下のような見た目になります。

f:id:treeapps:20170501211538p:plain

たったこれだけでレスポンシブ化して縦も揃って見やすくなりました(日本語の列はズレますが・・)。

<pre>タグが厄介ではありますが、<head>タグが解放されたという事は、cssやjsを適用し放題な訳ですね。

定期的にhtdocs配下にファイルを配置する

私の場合はデザインhtmlのファイル群をjenkinsでpullし、dockerホストのhtdocsを更新しています。デザインファイルがpushされるとホスト側のhtdocs配下に上書かれ、それがnginxのコンテナに伝搬してブラウザでそのファイル・フォルダを確認する事ができるようになります。

autoindexの注意点

autoindexには注意点があり、ngx_http_index_moduleに注意する必要があります。

Module ngx_http_index_module

「index index.html」等と書いてしまうと、http://localhost:8081/ 等とアクセスした際にindex.htmlが自動的に参照されてしまい、autoindexの一覧表示がされず、index.htmlを表示してしまいます。特にデザインhtmlであれば大抵index.htmlが存在するので、autoindexが全然開けない事態になります。

これを防ぐため、通常有用であるindexモジュールを敢えて効かないような設定にする必要があります。

何故Fancy Indexモジュールを使わないの?

Fancy Indexは残念ながら標準モジュールではなく追加モジュールです。従って、有効にするにはコンパイルオプションを指定してコンパイルしないといけません。そこまで頑張りたくないので、今回は見送りました。

h5aiは使わないの?

larsjung.de

まあ使ってもいいとは思うのですが、ちょっと、仕組みが重すぎるというか、重厚過ぎます。そこまでリッチなものは欲していないというか、カスタマイズが凄く難しそうというか。phpが必要な点も嫌ですね。

雑感

Directory Indexの仕組みを色々調べてみると、結構ヘビーな実装のものが多く、ちょっと敬遠してしまいます。

特にh5aiは面倒ですね。超シンプルでいい、もしくは自分で全部いじりたい、といった場合、今回のようにngx_http_addition_moduleでいじるのが丁度いいと思います。

今回私が用意しものはかなりシンプルなものですが、ここからリッチにしたい等があれば、是非いじってみて下さい!
github.com