Nuxt3 with docker Nginx deploy ( 二 )

Stephen Chen
12 min readDec 26, 2023
Generate form Midjourney

此篇是 Nuxt3 with docker ( ㄧ ) 的後續

上篇文章已經把 Nuxt3 Docker 相關的檔案都弄好了,由於在 Server 部署部分不太可能直接把 3000 port 給客戶去使用,通常會加一層 web service 來 Proxy

我自己是使用 Nginx,所以此篇會著重在如何改成可以使用 Nginx 的版本

而文章的最後我們會有兩個 running 的 container,一個是 Nginx 一個是 Nuxt3

Source code

第一篇而來的當前目錄結構

第一篇是屬於沒有 Nginx 的版本

Nginx container

開始正文,首先我們先處理 Nginx container 的部分,新增一個 folder 底下兩個檔案

Nginx 下的 Dockerfile

# nginx/Dockerfile

FROM nginx:1.19.2-alpine

EXPOSE 80

COPY nginx.conf /etc/nginx/nginx.conf

Nginx 下的 nginx.conf

註:以下非 docker 的 Nginx directive 就不解釋,下面是我自己調整過後來的 XD

#nginx/nginx.conf

events {
worker_connections 4096;
}

http {

# Rate limiting, be ware of this setting as it can block you out of your own site
limit_req_zone $binary_remote_addr zone=rate_limiting:10m rate=30r/s;

server {

# listen 80
listen 80 default_server;
listen [::]:80 default_server;

server_name localhost;

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;

# Adjust buffer sizes and timeouts to handle client requests efficiently:
client_body_buffer_size 128k;
client_max_body_size 10m;
client_header_buffer_size 1k;
large_client_header_buffers 4 4k;
send_timeout 30s;

# Adjust keepalive to conserve server resources:
keepalive_timeout 65;
keepalive_requests 100;

# For security, disable server tokens to prevent Nginx from revealing its version in responses:
server_tokens off;

gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types
application/atom+xml
application/geo+json
application/javascript
application/x-javascript
application/json
application/ld+json
application/manifest+json
application/rdf+xml
application/rss+xml
application/xhtml+xml
application/xml
font/eot
font/otf
font/ttf
image/svg+xml
text/css
text/javascript
text/plain
text/xml;

location / {

# burst:
# This parameter allows bursts of up to 50 requests to be queued
limit_req zone=rate_limiting burst=50 nodelay;

# ❗❗❗❗❗❗
proxy_pass http://frontend:3000;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}

這邊分享比較重要的這一行

proxy_pass http://frontend:3000;

首先當 User 訪問瀏覽器的時候 request 會先到 Nginx 的 container ,然後在proxy_pass 到當前 frontend 這 docker container,而 response 則是 vice versa

以此文的情境我們的 Nginx container 會透過這樣的方式把 reqeust proxy 到 Nuxt3 的 container

基本上 container 是透過 network interface 來溝通彼此

Nuxt3 container

設定完 Nginx 之後,接著來處理 Nuxt3 部分

首先我們會新增一個 prod.Dockerfile 還有修改原先的兩個 docker-compose

分別對應到 dev 跟 prod 版本的

Dev 版本的 docker-compose.yml

version: '3.8'
services:
frontend:
container_name: frontend
build:
# 新增這一行,指定到 Dockerfile folder 裡面的 Dockerfile
dockerfile: Dockerfile/Dockerfile
context: .
args:
- NODE_VERSION=${DOCKER_NODE_VERSION}
ports:
# 把本機的 port 跟 container 的 port 綁定
- '${DOCKER_HOST_MACHINE_PORT}:3000'
volumes:
- .:/app

Dev 版本的 Dockerfile

ARG NODE_VERSION

FROM node:${NODE_VERSION}-slim as base

WORKDIR /app

# By default, just keep the container running
CMD ["tail", "-f", "/dev/null"]

Dev 版本的兩個檔案幾乎跟之前一樣,只差別指定 Dockerfile 的路徑而已

Prod 版本的 docker-compose.prod.yml

version: '3.8'
services:
frontend:
container_name: frontend
restart: on-failure
build:
dockerfile: Dockerfile/prod.Dockerfile
context: .
args:
- NODE_VERSION=${DOCKER_NODE_VERSION}
ports:
# 把本機的 port 跟 container 的 port 綁定
- '${DOCKER_HOST_MACHINE_PORT}:3000'
tty: true
stdin_open: true

# nginx
nginx:
container_name: nginx
restart: on-failure
build: ./nginx
ports:
- '80:80'

這邊會在 prod 的版本新增新增 nginx service

Prod 版本的 Dockerfile

# ---
# Base stage
ARG NODE_VERSION

FROM node:${NODE_VERSION}-slim as base

MAINTAINER "example@gmail.com"

# This can influence how the application behaves, particularly in terms of logging, performance optimizations
ENV NODE_ENV=production

WORKDIR /app

# ---
# Build stage
FROM base as build

COPY ../package.json .
COPY ../yarn.lock .

# 安裝 yarn
# --prefer-offline
#
# use network only if dependencies are not available in local cache
#
# --frozen-lockfile
#
# don't generate a lockfile and fail if an update is needed
#
# --non-interactive
#
# do not show interactive prompts
#
# --production=false
#
# install devDependencies
RUN yarn install \
--prefer-offline \
--frozen-lockfile \
--non-interactive \
--production=false

COPY . .

RUN yarn build

# ---
# Run stage
FROM base

# Copy only the files needed to run the app
COPY --from=build /app/.output /app/.output

CMD [ "node", ".output/server/index.mjs" ]

上面會分成三個 staging

分別是 Base,Build 以及 Run,也就是所謂的 Multi-stage builds

其原理就是在 build 的過程中可能會產生一堆過渡期的檔案,不會被 copy 到最終的 image 裡面,這樣就可以讓最終被使用的 image 比較小達到優化的效能

拆解 Prod 版本的 Dockerfile 之 Base 的 staging

# ---
# Base stage
ARG NODE_VERSION

FROM node:${NODE_VERSION}-slim as base

MAINTAINER "example@gmail.com"

# This can influence how the application behaves, particularly in terms of logging, performance optimizations
ENV NODE_ENV=production

WORKDIR /app

拆解 Prod 版本的 Dockerfile 之 Build 的 staging

# ---
# Build stage
FROM base as build

COPY ../package.json .
COPY ../yarn.lock .

# 安裝 yarn
# --prefer-offline
#
# use network only if dependencies are not available in local cache
#
# --frozen-lockfile
#
# don't generate a lockfile and fail if an update is needed
#
# --non-interactive
#
# do not show interactive prompts
#
# --production=false
#
# install devDependencies
RUN yarn install \
--prefer-offline \
--frozen-lockfile \
--non-interactive \
--production=false

COPY . .

RUN yarn build

此 build staging 會透過 FROM statements 來重新 build 一個新的 image 叫 build

FROM base as build

拆解 Prod 版本的 Dockerfile 之 Run 的 staging

# ---
# Run stage
FROM base

# Copy only the files needed to run the app
COPY --from=build /app/.output /app/.output

CMD [ "node", ".output/server/index.mjs" ]

而最終我們只需要 .output 裡面的檔案,所以我們也只 Copy .output 裡面的檔案,這樣就回到上面所說的 Multi-stage builds 把最終的 image size 變小

結論

此兩篇文章只有一個目的,就是要完整走一遍從本機開發到 Server 部署的階段,然後不想要本機有任何套件管理器 ( yarn …etc ) 或者 node 安裝的情況。

雖然第一篇文章最終 prod 部署的版本會在此篇有大幅度的修正,但這也是我自己在 try & error 整個 Docker 的流程

Source code

Reference

--

--