webサーバーにNginxを組み入れる【Docker環境】

# はじめに

以前に、railsとvueのアプリ構成でdocker環境を構築した。

https://qiita.com/Shuhei_Nakada/items/148ce4c7c5bd4d11bcf0

しかし、Railsの開発用サーバーをpuma単独で動かしていたので、
役割分担して構成を変更した。

* webサーバー: nginx　　
* applicationサーバー: puma

作業内容を備忘録として残す。



# アプリ構成イメージ

![スクリーンショット 2022-04-05 5.58.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1380401/2fbdff58-d8e8-5900-0018-372fc4b56eb8.png)




# 使用技術

* Rails: 6.0.4.4
* nginx: 1.21.6



# 前提

* dockerでrailsの開発環境を構築済み



# nginxの初期設定を理解する

実装前にnginxのデフォルト設定がどう動作しているのか確認しておく。

DockerHubから`nginx:1.21.6-alpine`イメージを選択し、同イメージでコンテナを生成すると、
コンテナ内の/etc/nginx/conf.d/に`default.conf`が自動生成されていた。

```nginx:/etc/nginx/conf.d/default.conf
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}
```

そのまま起動されたコンテナに`http://localhost`でアクセスすると、welcomeページに表示されてnginxが正常起動していることが分かる。

![スクリーンショット 2022-04-04 13.49.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1380401/dc482cad-2817-965a-b159-32ff4b0785b9.png)


ひとまずデフォルトでは当ファイルをもとに動作していることが分かった。
(Nginxの初期設定の詳細は別の機会に学ぶことにする)



# 実装

今回は`nginxからRailsにアクセスし、正常に動作する`をゴールにする。

負荷分散やエラーログの整理などの詳細設定は、nginxの技術書を読んでブラッシュアップする。

では実装に移る。


## ディレクトリ構成

frontendと.envは今回の実装に直接は関係しないが、全体像の為に載せておく。

* frontend: Vueを構成
* .env: 環境変数を管理

```zsh:ターミナル

% tree root -a
root
├── docker-compose.yml
├── .env
├── backend
・　　　　　　　・
・　　　　　　　・
・　　　　　　　・
│   ├── Dockerfile
│   └── config
│       └── puma.rb
├── frontend
・　　　　　　　・
・　　　　　　　・
・　　　　　　　・
│   └── Dockerfile
└── web
    ├── Dockerfile
    └── nginx.cnf
```


## 作業

各ファイルにコメントで補足している。

なお、nginxとpumaを繋ぐソケットは
以下の順で使われていることを留意しておく。

1. Dockerfile(Rails)のCMDでpumaを起動する
2. puma.rbでbindを実行し、puma.sockというソケットを作成する
3. docker-compose.ymlのvolumesで同ソケットをnginxコンテナにも共有する
4. Dockerfile(Nginx)のCOPYでホスト側のnginx.confをnginxコンテナに複製する
5. そのnginx.confのupstreamで共有されたソケットで繋ぐ

```yml:docker-compose.yml

### 環境変数は.envファイルで管理　###
version: '3.8'

services:

  # DBコンテナ
  db:
    image: mysql:8.0.28
    command: --default-authentication-plugin=mysql_native_password
    volumes: 
      - "./db:/var/lib/mysql"
      - "./db/personal_config/my.cnf:/etc/mysql/conf.d/my.cnf"
    environment:
      MYSQL_ROOT_PASSWORD: $MYSQL_ROOT_PASSWORD
      TZ: Asia/Tokyo

  # Railsコンテナ
  backend:
    build:
      context: ./backend
      args:
        WORKDIR: $WORKDIR
    volumes:
      - "./backend:/$WORKDIR"
    environment:
      MYSQL_ROOT_PASSWORD: $MYSQL_ROOT_PASSWORD
    ports:
      - "8000:3000"
    depends_on:
      - db

  # Nginxコンテナ
  web:
    build:
      # Dockerfileのパスを指定する
      context: ./web
    volumes:
      # 独自のNginx設定ファイルをコンテナに同期させる
      - ./web:/app/public
      # Railsのpuma起動時に生成されるpuma.sockをNginxコンテナにも共有する
      - ./backend/tmp/sockets:/app/tmp/sockets
    ports:
      - 80:80
    # 先にRailsが動くように依存関係を作る
    depends_on:
      - backend

  # Vueコンテナ
  frontend:
    build:
      context: ./frontend
      args:
        WORKDIR: $WORKDIR
    volumes:
      - "./frontend:/$WORKDIR"
    ports:
      - "3000:3000"
    depends_on:
      - backend
```

```ruby:backend/Dockerfile
FROM ruby:2.7.5-alpine

ARG WORKDIR

ARG RUNTIME_PACKAGES="bash imagemagick nodejs yarn tzdata mysql-dev mysql-client git"
ARG DEV_PACKAGES="build-base curl-dev"

ENV HOME=/${WORKDIR} \
    TZ=Asia/Tokyo

WORKDIR ${HOME}

COPY Gemfile* ./

RUN apk update && \
    apk upgrade && \
    apk add --no-cache ${RUNTIME_PACKAGES} && \
    apk add --virtual build-dependencies --no-cache ${DEV_PACKAGES} && \
    bundle install -j4 && \
    apk del build-dependencies

COPY . ./

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

# pumaを起動させる
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
```

```ruby:backend/config/puma.rb
--省略--

#　puma起動時にソケットであるpuma.sockを作成する
bind "unix:///app/tmp/sockets/puma.sock"

```

```dockerfile:web/nginx/Dockerfile
# ベースイメージ
FROM nginx:1.21.6-alpine

# Nginxに関わる設定ファイルを全削除
RUN rm -f /etc/nginx/conf.d/*

# 独自の設定ファイルを機能する場所に配置する
COPY nginx.conf /etc/nginx/conf.d/custum.conf

```

```nginx:web/nginx/nginx.conf

# Railsのpumaが生成したソケットにnginxを繋ぐ
upstream app {
  server unix:///app/tmp/sockets/puma.sock;
}

server {
  # クライアントからのリクエストを80番ポートで待ち受ける
  listen 80;
  # サーバー名称をlocalhostに設定
  server_name localhost;

  # ドキュメントルートを/app/publicに設定
  root /app/public;
  # 静的ファイルが存在しなければ、@app（=Rails）にリクエストを渡す
  try_files  $uri/index.html $uri @app;

  location @app {
    # プロキシのヘッダー情報をクライアント情報に書き換えする
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    # プロキシの受け渡し先(upstreamで定義したappに合わせる)
    proxy_pass http://app;
  }
}
```



# 動作検証

ブラウザで`http://localhost`を入力すれば、Railsの起動画面が表示される。

![スクリーンショット 2022-04-04 18.14.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1380401/0a4cafb6-f636-c30b-23fb-e3ffd6fade95.png)

ちなみにVueからaxiosでHTTP通信を図るときは、Nginxの80番ポートに向けて送信すれば通信できる。



# 参考記事

https://qiita.com/kohki4115/items/c37ff8550b01bbc75df2#dockerfilenginx

https://qiita.com/mby/items/9ece46893b0967c2296d

https://qiita.com/leomaro7/items/98d6af85a4e155449aed#%E3%83%AA%E3%83%90%E3%83%BC%E3%82%B9%E3%83%97%E3%83%AD%E3%82%AD%E3%82%B7

https://hack-le.com/nginx/



# 終わりに

可用性やセキュリティ面を考えると今の設定は不十分だろうな…

時間に余裕があれば、nginxの技術書を完読した上でしっかりしたものを実装したかった。

最後までお読みいただき、ありがとうございました！