背景
の続きです。
今回はnginx経由でRailsにアクセスするよう、構成を変更します。
なお、私の場合はhttps化の一環でnginxを挟む必要があったので行いました。
手順
nginxのDockerイメージ
今回用意するnginxは、以下の処理を行うよう設定ファイルを書きます。
- 80番に来たhttpアクセスを処理
-
$http_x_forwarded_proto
が"http"なら、"https"の同ページへリダイレクト - (お好みで)"/public/..."に来たらそのまま返す
- それ以外は
unix:///myapp/tmp/sockets/puma.sock
を通してRailsへ渡す
後述の設定と合っていれば、ソケットファイルの位置は自由です。
サンプルは↓です。
お好きな設定を追記してください。
upstream myapp {
server unix:///myapp/tmp/sockets/puma.sock;
}
server {
listen 80;
server_name your.domain;
if ($http_x_forwarded_proto = "http") {
return 301 https://$host$request_uri;
}
root /myapp/public;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
if (!-f $request_filename) {
proxy_pass http://myapp;
break;
}
}
}
このファイルをnginx.confとして、nginx用Dockerfileと一緒にnginxフォルダの中に置きます。
- nginx
- Dockerfile
- nginx.conf
Dockerfileでは、設定ファイルをコピーして使うだけのイメージを作ります。
FROM nginx:1.11.7
RUN rm -f /etc/nginx/conf.d/*
ADD nginx.conf /etc/nginx/conf.d/app.conf
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
Railsの調整
nginxのイメージを使ってコンテナを立てるよう、deployment.ymlに追記をします。
ただし、Rails側のコンテナとsocketを共有するため(publicもついでに)、ボリュームをマウントする必要があります。
空のボリュームをマウントするため、元々publicに入っていたものが消えてしまうので、RailsのDockerfileを工夫して回避します。
今回は、事前に中身を/tmpに移し、マウントされてからコピーする苦肉の策を取りました。
回避策はプロジェクトに依存するので、お好みの方法を適用してください。
忘れず起動にsocketを使うようにします。
...
# 逃がす
RUN mkdir -p /tmp/public && \
cp -rf /myapp/public/* /tmp/public
...
# 戻す
CMD \
bundle exec rails db:migrate \
&& cp -rf /tmp/public/* /myapp/public \
&& bundle exec puma -C config/puma.rb
config/puma.rb
に以下を追記します。
app_root = File.expand_path('../..', __FILE__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"
serviceの設定
nginxを挟んでいるので、80番でアクセスしないといけません。
service.yml
の3000を80に書き換えるだけです。
apiVersion: v1
kind: Service
metadata:
name: testapp
labels:
app: testapp
spec:
type: LoadBalancer
ports:
- port: 80
selector:
app: testapp
deploymentの設定
deployment.ymlには、ボリュームマウントの設定と、nginxコンテナの設定を追記します。
シンプルですね。
...
spec:
containers:
- image <Railsのimage>
...
########### 追記 #############
volumeMounts:
- mountPath: /myapp/public
name: web-assets
- mountPath: /myapp/tmp/sockets
name: web-sock
########### おわり ############
- image: <Cloud SQL Proxyのimage>
...
########### 追記 #############
# [START nginx]
- name: nginx
image: asia.gcr.io/<Your Project ID>/nginx:latest
ports:
- name: http-server
containerPort: 80
- name: https-server
containerPort: 443
volumeMounts:
- mountPath: /myapp/public
name: web-assets
readOnly: true
- mountPath: /myapp/tmp/sockets
name: web-sock
# [END nginx]
# [START volumes]
volumes:
- name: web-assets
emptyDir: {}
- name: web-sock
emptyDir: {}
########### おわり ############
- name: cloudsql-oauth-credentials
secret:
secretName: cloudsql-oauth-credentials
- name: ssl-certs
hostPath:
path: /etc/ssl/certs
# [END volumes]
デプロイ
いよいよ大詰めです。
RailsのDockerイメージ、nginxのDockerイメージをビルドしてpushします。
手順は前回と同じです。
IMAGE_ID=$(docker build --no-cache -q .)
url_base="asia.gcr.io/<Your Project ID>/app"
docker tag ${IMAGE_ID} ${url_base}:latest
gcloud docker -- push ${url_base}:latest
IMAGE_ID=$(docker build --no-cache -q ./nginx)
url_base="asia.gcr.io/<Your Project ID>/nginx"
docker tag ${IMAGE_ID} ${url_base}:latest
gcloud docker -- push ${url_base}:latest
両方のpushが完了したら、serviceとdeploymentを更新します。
kubectl apply -f deployment.yml
kubectl delete svc testapp
kubectl apply -f service.yml
なお、誰も使っていないサービスなのでdeleteをしていますが、ダウンタイムが発生するので普通のサービスではちゃんとRolling Updateなどをする必要があります。
一向にExternal IPが振られない場合、↓のコマンドで詳細を確認してみてください。
Static IPの制限に引っかかっているとExternal IPが振られない、という事態になるため、古いIPは削除するなりしてください。
kubectl describe svc
さて、podやsvcが正常に立ち上がったら、80番にアクセスしてみましょう。
以前と変わらない画面が出てくれば成功です