初学者によるDocker理解まとめ③ 〜nginxを使ってAPIをproxyするまで〜


はじめに

ようやくDockerを学び始めたので自分の理解をまとめておく。
前回 => https://qiita.com/theFirstPenguin/items/58e6bc5acf90dc497cb0
次回 => https://qiita.com/theFirstPenguin/items/b5f6ed253bed95fbec5a

今日の学びをメモ

ローカルPC上でnode.jsでexpressを実行するコンテナとnginxを実行するコンテナを立ち上げる。そしてnginxを経由してexpressのAPIを叩く。たったこれだけをdocker-composeを使ってやってみた。

前回までと今回の違い

前回ではexpressのコンテナにアクセスするためにportを指定する必要があった。

curl http://localhost:3000
Hello

今回はnginxのproxy機能を用いて、portを指定せずexpressのコンテナにアクセスできるようにした。

curl http://:localhost
Hello

開発の全体像

cd ~
mkdir sample

sampleディレクトリの中で開発する。ディレクトリの中身はこんな感じになる。
appディレクトリとwebディレクトリがそれぞれexpressとnginxのコンテナとなる。

index.js
const express = require('express');
const app = express();
app.get('/', (req, res) => {
  res.send('Hello').status(200);
});

app.listen(3000, () => {
  console.log('Listening on port 3000');
});

expressバックエンドのDockerfileの準備

FROM node:alpine

# Create app directory
WORKDIR /usr/src/app

# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./

RUN npm install
# If you are building your code for production
# RUN npm ci --only=production

# Bundle app source
COPY . .
# package.jsonをコピー後にnpm installするという作業をビルドの度に毎回やりたくない。
# そのため、上記を終わらせてから COPY. .をやる。
# そうすればコードを変えた部分だけビルドが実行される。

EXPOSE 3000
CMD [ "node", "index.js" ]

nginx.confの準備

nginxコンテナの中の、localhostの80port(つまりhttpのデフォルトport)の全てのエンドポイント(/)に来たリクエストをhttp://app:3000にproxyする。
Dockerの管理下において、appコンテナのドメインがappになっていることに注目。
ちなみに、この設定ファイル名はnginx.confで固定とのこと。配置場所はwebディレクトリ以下であれば特に気にしない。

nginx.conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 1024;
}

http {
    # Weather Report Reverse Proxy
         server {
            listen 80;
            server_name localhost 127.0.0.1;

            location / {
                proxy_pass          http://app:3000;
                proxy_set_header    X-Forwarded-For $remote_addr;
            }

        }
}

docker-compose.ymlの準備

docker-compose.yml
version: '3'
# 何も考えず3を指定
services:
  app:
    build:
      context: ./app
      # docker-compose.ymlから見て./appにあるDockerfileにしたがってビルドする
    container_name: express-app
    # 一応コンテナ名をつける
    ports:
      - '3000:3000'
      # docker run -p 3000:3000 app と一緒
    volumes:
      - './app:/usr/src/app'
      # ローカルPCの./appフォルダと、コンテナ内の ~/usr/src/appフォルダを同期させる。
      # docker exec -it express-app sh で確認できる

  web:
    image: nginx:latest 
    #nginx:alpilneだとエラーが起きるので、一応最新版で対応。
    container_name: nginx-web
    ports:
      - '80:80'
    volumes:
      - './web/reverse_proxy/nginx.conf:/etc/nginx/nginx.conf'
      # ローカルPCの./web/reverse_proxy/nginx.confと、コンテナ内の ~/etc/nginx/nginx.confを同期させる。
      # ~/etc/nginx/nginx.confは場所指定。
    links:
      - app
    depends_on:
      - app
      # appコンテナが起動しないと機能しない、という依存関係を明記。

docker-compose up して終わり

docker-compose upすればコンテナが立ち上がります。
ローカルPCからcurlコマンドを打てばappコンテナから返事が帰ってきます。最初の図の通りです。

curl http://localhost
Hello

ちなみに直接appコンテナにもアクセスできます。当然ですが。

curl http://localhost:3000
Hello

最後に

初歩中の初歩をまとめました。
ちなみに、自分は何度やってもnginxがうまく起動せず絶望しかけましたが、docker psをするとk8s_controller_ingress-nginx-controllerという謎のコンテナ(というかkubernetesのingressだと思われる)がdocker stopしても止まらないことに気付きました。kubernetesを再起動すると直ったので、仮にそんな状況があれば試すのもいいかと思います。

ありがとうございました。