點燈坊

失くすものさえない今が強くなるチャンスよ

將 Node 包進 Nginx Image

Sam Xiao's Avatar 2021-10-31

在 Microservice 架構下,理論上 Nginx 與 Node 應該各自放在不同 Container 下,但有時 API 就是要控制 Nginx 或其他執行檔如 FFmpeg,此時將 Node 與 Nginx 包在同一個 Image 會更方便。

Version

Nginx 1.18.0
Node 12.15.0

Dockerfile

FROM nginx:stable-alpine
WORKDIR /usr/src/app

COPY default.conf /etc/nginx/conf.d/
COPY package.json .
COPY index.js .
COPY entrypoint.sh /

RUN apk add nodejs -q
RUN apk add yarn -q
RUN yarn install
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

第 1 行

FROM nginx:stable-alpine

nginx:stable-alpine 為基底。

第 2 行

WORKDIR /usr/src/app

將預設目錄設定在 /usr/src/app,也就是 Node 相關 *.js 的所在目錄。

第 4 行

COPY default.conf /etc/nginx/conf.d/

將自定義了 Nginx configuration 檔複製進 image。

第 5 行

COPY package.json .

package.json 複製進 image。

第 6 行

COPY index.js .

將 Node 所有檔案複製進 image。

第 7 行

COPY entrypoint.sh /

將自定義的 entrypoint.sh 複製進 image。

為什麼要使用 script ? 稍後會解釋

第 9 行

RUN apk update
RUN apk add nodejs -q
RUN apk add yarn -q

安裝 Node 與 Yarn。

在 alpine 內只能使用 apk,不能使用 apt-get

12 行

RUN yarn install

根據 package.json 安裝 NPM package,如 express

13 行

RUN chmod +x /entrypoint.sh

賦予 entrypoint.sh 有執行權利。

15 行

ENTRYPOINT ["/entrypoint.sh"]

最後執行 entrypoint.sh

Nginx Configuration

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;
  }
}

採用預設的 Nginx 設定檔。

Node

index.js

let express = require('express')

let app = express()
let port = 3000

app.get('/hello', (req, res) => {
  res.send('world')
})

app.listen(port, _ => console.log(`Node listening on port ${port}`))

使用 Express 實現簡單的 RESTful GET API。

Shell Script

entrypoint.sh

#!/bin/sh
node index.js &
nginx -g 'daemon off;'

由於 container 必須啟動 Node 與 Nginx,因此必須透過 entrypoint.sh 啟動。

因為已經設定好 WORKDIR/usr/src/app,可直接以 node 執行 index.js

但還得啟動 Nginx,只好在 node 最後加上 &,表示在背景執行,最後執行 Nginx。

Docker Compose

version: "3"

services:
  nginx:
    image: nginx-node:0.1.0
    container_name: MyNginx
    restart: always
    ports:
      - "80:80"
      - "3000:3000"

Nginx 使用了 port 80,而 Node 使用了 port 3000,須在 docker-compose.yml 特別設定。

NPM Script

{
  "name": "nginx-node",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "api": "node index.js",
    "docker:build": "docker build -t $npm_package_name:$npm_package_version .",
    "docker:save": "docker save $npm_package_name:$npm_package_version | gzip > $npm_package_name-$npm_package_version.tar.gz",
    "docker:login": "docker exec -it MyNginx sh",
    "docker:log": "docker logs MyNginx",
    "docker:rmi": "docker rmi $npm_package_name:$npm_package_version",
    "docker:up": "docker-compose up -d",
    "docker:down": "docker-compose down"
  },
  "dependencies": {
    "express": "^4.17.1"
  }
}

將常用 Docker command 以 NPM script 管理。

Conclusion

  • 若其他 server 也想將 Node 包進去,也可參考此文方式