본문 바로가기

About/Docker

[Docker] Multi-stage build를 이용한 Nginx 웹 서버 이미지 만들기

다음 사이트의 예제를 참조하여 작성하였습니다.

progressivecoder.com/docker-multi-stage-build-for-running-react-application-on-nginx-server/

 

Docker Multi Stage Build for running React Application on Nginx Server

Docker Multi Stage Build lets you write multi stage Dockerfiles to cover build and run stages. We look at multi stage build for React app on Nginx

progressivecoder.com

 

지난 글에서 Docker의 Multis-stage에 대하여 알아보았고, 이를 Nginx 웹 서버에 응용하는 방법에 대하여 다루겠습니다.

 


1. Multi-stage Dockerfile

github.com/dashsaurabh/docker-react-application

 

dashsaurabh/docker-react-application

Multi Stage Docker Build for React Application on Nginx - dashsaurabh/docker-react-application

github.com

위의 github에서 예제 코드들을 다운로드 받습니다. 

다음과 같이 구성되어 있습니다.

docker-react-application-master
├── Dockerfile
├── Dockerfile.dev
├── package.json
├── package-lock.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
├── README.md
└── src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    └── serviceWorker.js

Dockerfile은 다음과 같이 구성되어 있습니다.

#Build Stage Start

#Specify a base image
FROM node:alpine as builder

#Specify a working directory
WORKDIR '/app'

#Copy the dependencies file
COPY package.json .

#Install dependencies
RUN npm install

#Copy remaining files
COPY . .

#Build the project for production
RUN npm run build

#Run Stage Start
FROM nginx

#Copy production build files from builder phase to nginx
COPY --from=builder /app/build /usr/share/nginx/html

코드를 2단계로 나누어 해석하자면 다음과 같습니다.

 

 

1. Build Stage
FROM node:alpine as builder

기본 이미지는 node:alpine 이미지 입니다. 위 이미지에 'builder'라는 별칭을 지정합니다.

 

WORKDIR '/app'

작업디레토리를 '/app' 로 이동합니다.

 

COPY package.json .

package.json파일을 작업 디렉토리로 복사합니다. npm의 모든 필수 종속성을 설치하려면 해당 파일이 필요합니다.

 

RUN npm install

npm install 명령어를 사용하여 종속성들을 설치합니다. 기본적인 node_modules를 설치합니다.

 

COPY . .

나머지 파일들을 작업 디렉토리에 복사합니다. 실제 어플리케이션 코드가 포함되어 있습니다.

 

RUN npm run build

npm run build 명령어를 실행하여 React 어플리케이션에 필요한 빌드 파일들을 준비합니다.
(클라이언트에 전달할 index.html 파일과 main.js 파일을 만듭니다.)

 

 

2. Run Stage

이제 빌드 스테이지가 끝나고 실행 런 스테이지를 시작 가능합니다. 

FROM nginx

우선 nginx 이미지를 기본 이미지로 사용합니다.

 

COPY --from=builder /app/build /usr/share/nginx/html

*중요*

nginx가 동작하기위해 필요한 파일들을 시스템으로 복사합니다 --from=builder 태그를 이용하여 빌드 단계의 이미지의 작업 디렉토리(/app/build)에서 nginx의 디렉토리로 파일을 복사합니다.

 

 

 

이제 Multi-stage 빌드용 Dockerfile와 파일이 모두 준비되었습니다.

또한 nginx의 기본이미지 자체가 80번 포트에서 웹서비스를 시작하기 때문에 Run-stage에서 RUN 명령이 필요하지 않습니다.

 

2. Build

다음과 같은 명령어를 이용하여 Multi-stage Dockerfile을 빌드합니다. 

(모든 종속성을 다운로드하기 때문에 시간이 조금 소요됩니다)

docker build -t my-react-app:1.0 .

빌드가 완료되면 다음 명령어를 입력하여 이미지를 확인합니다.

docker image ls

결과

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
my-react-app        1.0                 586455626728        48 seconds ago      134MB
<none>              <none>              ba81bc23c681        54 seconds ago      385MB
node                alpine              cf39b70292b8        6 days ago          113MB
nginx               latest              62d49f9bab67        2 weeks ago         133MB

기본 이미지로 사용된 node:alpin, nginx:latest 이미지가 다운로드 된것을 볼 수 있고,

<none>으로 표시된 이미지가 생성된 것을 볼 수 있는데, 이 이미지가 build-stage 에 사용된 이미지 입니다.

마지막으로 my-react-app:1.0 이미지가 생성된 것을 볼 수 있습니다.

 

빌드에 사용된 <none> 이미지는 385MB이지만, 실행에 사용될 my-react-app 이미지는 134MB 밖에 되지 않습니다.

(Multi-stage build의 장점!)

 

3. Run

이미지가 성공적으로 빌드되었으므로 다음과 같은 명령어를 사용하여 웹 서비스 컨테이너를 실행할 수 있습니다.

docker run -p 8080:80 my-react-app:1.0

 

host의 8080번 포트와 컨테이너의 80번 포트를 맵핑하였으므로, host의 8080번 포트를 통하여 웹서비스에 접근할 수 있습니다.

 

브라우저를 통해 접근하기 전 curl 명령어를 통하여 확인해보겠습니다.

curl localhost:8080

 

<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><title>React App</title><link href="/static/css/main.f9c67ae1.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={1:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="/static/js/2.b6097d3d.chunk.js"></script><script src="/static/js/main.453be307.chunk.js"></script></body></html>r

html 코드가 반환되는 것을 볼 수 있습니다.

 

웹 브라우저를 통해 접근해보겠습니다.

 

브라우저를 통한 localhost:8080 접근

정상적으로 웹페이지가 동작하는 것을 확인할 수 있습니다.

 


Dockerfile을 Multi-stage로 빌드하여 nginx 웹 서비스를 동작하는 방법에 대하여 다뤄봤습니다.