DockerでGhost + Nginx + Varnish ブログサイトを構築する
ども、takiponeです。
このブログ(takipone.com
)の構成をリニューアルしたので、それを紹介してみたいと思います。
構成
このブログは、GCEのContainer-optimized Instance上で以下3つのソフトウェアをDockerコンテナで実行しています。
- Ghost : Node.js製ブログプラットフォーム
- Nginx : HTTPSと静的ファイル(画像)のホスト
- Varnish : HTTPキャッシュ
設定は、こちらのブログ記事を参考にしました。
それぞれの設定ファイルは以下のconfigs
ディレクトリにまとめ、開発環境は手元のMBAにFigで構築しています。
$ tree configs
configs
├── ghost
│ ├── config.js
│ ├── content
| | :(中略)
│ └── docker
│ ├── Dockerfile
│ ├── LICENSE
│ ├── README.md
│ └── start.bash
├── nginx
│ ├── conf
│ │ ├── cert.pem
│ │ └── nginx.conf
│ └── logs
└── varnish
├── conf
│ └── default.vcl
└── docker
├── Dockerfile
├── LICENSE
└── README.md
30 directories, 195 files
Ghostコンテナの設定
Ghostの設定は、メール通知用のAmazon SES以外は既定のままです。
config.js
var path = require('path'),
config;
config = {
production: {
url: 'http://takipone.com',
mail: {
transport: 'SES',
options: {
AWSAccessKeyID: "AKIAXXXXXXXXXXXX",
AWSSecretKey: "XXXXXXXXXXXXXXXXXXX"
}
},
database: {
client: 'sqlite3',
connection: {
filename: path.join(__dirname, '/content/data/ghost.db')
},
debug: false
},
server: {
host: '0.0.0.0',
port: '2368'
},
forceAdminSSL: true
}
};
// Export config
module.exports = config;
Dockerイメージは、dockerfile/ghostをフォークし、takipone/ghost
としてDocker HubのAutomated Buildを利用しています。Dockerfileは特に変更せず、最新バージョンを利用するためにビルドのタイミングを自分で制御したかったためです。
Nginxコンテナの設定
Nginxの設定としては、HTTPのポートを8080番にしてproxy_pass
をDockerホストのIP(172.17.42.1
)にしているところ、HTTPSのバーチャルホストで管理画面(/ghost
)を意識してキャッシュヘッダを無効にしているところがポイントです。
nginx.conf
worker_processes 1;
daemon off;
events {
worker_connections 1024;
}
http {
server_tokens off;
keepalive_timeout 65;
gzip on;
set_real_ip_from 172.17.0.0/16;
real_ip_header X-Forwarded-For;
server {
listen 8080;
server_name takipone.com;
location / {
proxy_pass http://172.17.42.1:2368/;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
expires 1d;
}
location /content/images {
root /ghost-override;
expires max;
}
}
server {
listen 443 ssl;
server_name takipone.com;
ssl_certificate /nginx/cert.pem;
ssl_certificate_key /nginx/cert.pem;
location / {
proxy_pass http://172.17.42.1:2368/;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
expires -1;
}
}
}
Dockerイメージは、dockerfile/nginxをそのまま使っています。
Varnishの設定
Varnishの設定は、こちらもリバースプロキシとしてDockerホストのNginxのポートを向けています。あと、Dockerホストからのキャッシュインバリデーションの許可設定を追加しました。
default.vcl
# Marker to tell the VCL compiler that this VCL has been adapted to the
# new 4.0 format.
vcl 4.0;
acl purge {
"172.17.0.0/16";
}
backend default {
.host = "172.17.42.1";
.port = "8080";
}
sub vcl_recv {
if (!(req.url ~ "ghost")) {
unset req.http.cookie;
}
if (req.url == "/") {
return (pass);
}
if (req.method == "PURGE") {
if (!client.ip ~ purge) {
return(synth(405,"Not allowed."));
}
return (purge);
}
}
sub vcl_backend_response {
if (!(bereq.url ~ "ghost")) {
unset beresp.http.set-cookie;
}
}
sub vcl_deliver {
# Happens when we have all the pieces we need, and are about to send the
# response to the client.
#
# You can do accounting or modifying the final object here.
}
Dockerイメージは良さそうなものがなかったので、Dockerfileを自作しました。Varnishのドキュメントを参考にし、takipone/varnishとしてDocker HubのAutomated Buildで設定しました。
Dockerfile
#
# Varnish Dockerfile
#
# https://github.com/otaki-ryuta/docker-varnish
#
# based on 14.04(Trusty Tahr)
FROM dockerfile/ubuntu
# Install Varnish.
RUN \
apt-get update && \
apt-get install -y apt-transport-https && \
curl https://repo.varnish-cache.org/ubuntu/GPG-key.txt | apt-key add - && \
echo "deb https://repo.varnish-cache.org/ubuntu/ trusty varnish-4.0" >> /etc/apt/sources.list.d/varnish-cache.list && \
apt-get update && \
apt-get install -y varnish && \
rm -rf /var/lib/apt/lists/*
# Define mountable directories.
VOLUME ["/etc/varnish"]
# Define working directory.
WORKDIR /etc/varnish
# Expose ports.
EXPOSE 80
# Define default command.
CMD /usr/sbin/varnishd -F -f /etc/varnish/default.vcl -a :80 -s malloc,128m
開発環境
手元のMBAでデバッグ用に利用している、Figの設定ファイルです。
fig.yml
ghost:
image: takipone/ghost:latest
volumes:
- ./configs/ghost:/ghost-override
ports:
- "2368:2368"
nginx:
image: dockerfile/nginx:latest
volumes:
- ./configs/nginx/conf:/nginx
- ./configs/nginx/logs:/nginx-log
volumes_from:
- ghost
ports:
- "8080:8080"
- "443:443"
command: nginx -c /nginx/nginx.conf
varnish:
image: takipone/varnish:latest
volumes:
- ./configs/varnish/conf:/etc/varnish
ports:
- "80:80"
本番環境
Container-optimized Instanceでは、実行するDockerコンテナの構成を、VMインスタンス作成時に読み込んで自動構成することができます。最初のversion
がコロコロ変わるので、実行するVMイメージと対応しているか確認しましょう。あと、command
がfig.yml
と異なり厳密な配列で記述しなければいけないところがわからず、苦戦しました。
kubelet.manifest
version: v1beta2
containers:
- name: ghost
image: takipone/ghost:latest
ports:
- name: ghost
containerPort: 2368
hostPort: 2368
volumeMounts:
- name: ghost-override
mountPath: /ghost-override
- name: nginx
image: dockerfile/nginx:latest
command: [ 'nginx', '-c', '/nginx/nginx.conf' ]
ports:
- name: http-proxy
containerPort: 8080
hostPort: 8080
- name: https
containerPort: 443
hostPort: 443
volumeMounts:
- name: nginx
mountPath: /nginx
- name: nginx-log
mountPath: /nginx-log
- name: ghost-override
mountPath: /ghost-override
- name: varnish
image: takipone/varnish:latest
ports:
- name: http
containerPort: 80
hostPort: 80
volumeMounts:
- name: varnish
mountPath: /etc/varnish
volumes:
- name: ghost-override
source:
hostDir:
path: /mnt/pd1/ghost
- name: nginx
source:
hostDir:
path: /mnt/pd1/nginx/conf
- name: nginx-log
source:
hostDir:
path: /mnt/pd1/nginx/logs
- name: varnish
source:
hostDir:
path: /mnt/pd1/varnish/conf
これに加えて、前述した構成ファイルをアーカイブファイルconfigs.tgz
にまとめてGoogle Cloud StorageにアップロードしVMインスタンス起動時にダウンロードするようにしました。VMインスタンス起動時のスタートアップスクリプトについてはこちらの記事が詳しいです。
startup-script.bash
#!/bin/bash
if [ ! -d /mnt/pd1 ]; then
mkdir /mnt/pd1
fi
gsutil cp gs://<Bucket名>/configs.tgz /tmp
tar zxf /tmp/configs.tgz -C /mnt/pd1
で、それぞれの設定をVMインスタンス作成時のコマンドラインで指定します。長くなるので、シェルスクリプトでまとめました。
create_containerinstance.bash
#!/bin/bash
if [ -s $1 ]; then
echo "Usage: $0 <VM_NAME>"
exit 0
fi
gcloud compute instances create $1 \
--image container-vm \
--zone asia-east1-a \
--machine-type f1-micro \
--scope storage-rw \
--tags http-server https-server \
--address test-takipone-com \
--metadata-from-file \
google-container-manifest=kubelet.manifest \
startup-script=startup-script.bash
スタートアップスクリプトの実行後、DockerエージェントであるKubeletがDockerの構成を読み取るのでうまく動作するのですが、
厳密な順序制御はされないようでスタートアップスクリプトの実行に時間がかかると、並行してDockerコンテナが実行されるケースもあると思います。この辺り、ちゃんと順番が制御できる仕組みがあると嬉しいですね。