React

Use Node.js with Docker and Docker Compose to improve DX 정리

Asset Type
File Type
When to use
2022/04/03
Created time
2022/04/03 17:15
Created by
Last edited time
2022/05/07 13:14
Next.js 앱을 배포하려고 조사하던 중 https://blog.logrocket.com/how-to-deploy-next-js-on-google-cloud-run/를 보면서 전제조건에 도커와 도커 컴포즈에 대한 실무 지식을 알고 있어야 한다기에 추천하는 글을 읽어보면서 번역하고 정리합니다.
지난 5년 간 Docker와 Node.js는 매우 인기있었습니다. 이 단계별 가이드에서 함께 사용해서 개발자 경험을 개선하는 방법을 알아봅니다.
이 튜토리얼에서 docker build를 효율적으로 사용하고 Docker compose를 활용하여 원활한 로컬 개발 환경을 구현하는 방법을 자세히 설명합니다.
예제로 demo Express.js application 를 사용합니다.(이 포스트도 문제가 있어 직접 실습하고 해결한 글은 여기입니다.)

전제 조건

1.
Node.js 및 npm의 기본 사항에 익숙해야 합니다. 우리는 최신 LTS 버전인 Node 14를 사용할 것입니다.
2.
Express.js 프레임워크의 기본 사항을 알고 있어야 합니다.
3.
Docker에 대한 작업 지식이 있어야 합니다.
이 튜토리얼은 셸이 있는 Linux 또는 macOS와 같은 Unix 계열 시스템 에서 실행되는 명령을 사용합니다 . 이것은 우리가 앱 설정에 직접 뛰어드는 압축된 게시물이 될 것이므로, 그렇게 생각한다면 Docker 및 Node에 대해 읽어볼 수 있습니다.
내가 공개 GitHub 리포지토리에 앱을 빌드한 방식을 sequence of six pull requests로 분석할 수 있습니다 .

Express 생성기로 새 Express.js 프로젝트 만들기

데모 애플리케이션을 생성하기 위해 Express 애플리케이션 생성기 를 사용할 것 입니다. 이를 위해 아래의 npx 스크립트를 실행합니다.
npx express-generator --view=pug --git <app-name>
Plain Text
복사
실행한 명령을 분석하기 위해 Express 생성기에 Express 앱을 생성하도록 요청했습니다. 
-view=pug 는 생성기 는 Pug view 엔진을 사용하도록 지시하고 —-git 파라미터는 .gitignore 파일을 추가하도록 요청합니다. 
물론 <app-name>는 응용 프로그램 이름으로 바꿔야 합니다. 
nodejs-docker-express 를 예시로 사용합니다.
아래와 같이 보여질 것입니다.

Express 앱 테스트

앱을 테스트하려면 먼저 npm install 로 필요한 모든 npm 모듈을 설치하도록 실행합니다. 
그런 다음 아래 명령을 실행하여 앱을 시작합니다.
DEBUG=nodejs-docker-express:* npm start
Plain Text
복사
그 후에 nodejs-docker-express:server Listening on port 3000와 같은 메시지가 표시되어야 합니다 . 위의 명령은 매우 간단합니다. 
값으로 호출된 환경 변수로 실행되어 서버에 자세한 디버그를 수행하도록 지시합니다.
Windows를 사용하는 경우, set DEBUG=nodejs-docker-express:* & npm start 를 사용해야 한다.
이제 브라우저를 열고 http://localhost:3000 를 입력 하면 아래와 같은 출력이 표시됩니다.
만세! 기본 Express.js 앱이 이미 실행 중입니다. 이제 Ctrl+c 로 커맨드 라인 창에서 서버를 중지할 수 있습니다 . 
Node.js 익스프레스 애플리케이션을 도커화하는 작업을 진행할 것입니다.\

Docker multi-stage build로 앱 Dockerize(도커화)하기

애플리케이션을 컨테이너화하면 많은 장점이 있습니다. 
우선 실행되는 플랫폼에 관계없이 동일하게 작동합니다. 
Docker 컨테이너를 사용하면 앱을 AWS Fargate, Google Cloud Run 또는 자체 Kubernetes 클러스터와 같은 플랫폼에 쉽게 배포할 수 있습니다.
Dockerfile부터 시작하겠습니다.
도커 파일은 Docker image가 구축되는Blueprint입니다.
빌드된 이미지가 실행 중일 때 Container라고 합니다.
이 게시물 은 이 세 가지 용어의 차이점을 잘 설명하고 있습니다. 
다음은 간단한 시각적 설명입니다.
따라서 프로세스는 매우 간단합니다.
Dockerfile에서 Docker 이미지를 빌드합니다. 
실행 중인 Docker 이미지를 Docker 컨테이너라고 합니다.

base stage 설정

Dockerfile이 어떻게 보이는지 확인할 시간입니다.
보너스로 multi-stage builds를 활용하여 빌드를 더 빠르고 효율적으로 만들 것입니다.
FROM node:14-alpine as base WORKDIR /src COPY package*.json / EXPOSE 3000 FROM base as production ENV NODE_ENV=production RUN npm ci COPY . / CMD ["node", "bin/www"] FROM base as dev ENV NODE_ENV=development RUN npm install -g nodemon && npm install COPY . / CMD ["nodemon", "bin/www"]
Docker
복사
위의 Dockerfile에서는 다단계 빌드를 사용합니다. 
기본, 프로덕션 및 개발의 세 단계로 구성됩니다. 
기본 단계에는 개발과 프로덕션 모두에서 공통적인 사항이 있습니다. 
그래픽으로 다음과 같이 표현할 수 있습니다.
이것은 당신에게 상속을 생각 나게합니까? Docker 이미지에 대한 일종의 상속입니다. 우리는 슬림한 생산 단계와 보다 기능이 풍부한 개발 중심의 개발 단계를 사용하고 있습니다.
한 줄씩 살펴보겠습니다.
FROM node:14-alpine as base // line 1, 도커에게 공식 도커 노드 알파인 이미지 버전 14를 사용하게 합니다.
Docker
복사
먼저 Docker에게 마지막 LTS 버전인 공식 Docker Node Alpine 이미지 버전 14를 사용하도록 지시합니다. 
이 이미지는 DockerHub 에서 공개적으로 사용할 수 있습니다 . 
우리는 공식 Node.js Docker 이미지의 알파인 변형을 사용하고 있습니다.
기본 이미지의 경우 345MB에 비해 40MB가 조금 안되기 때문입니다 .
또한 이 Dockerfile은 다단계 빌드를 사용하기 때문에 as base 를 지정합니다. 
네이밍은 당신에게 맡깁니다.
나중에 빌드 프로세스에서 "extended"될 것이기 때문에 based을 사용하고 있습니다.
WORKDIR /src COPY package*.json / EXPOSE 3000
Docker
복사
WORKDIR 는 설정 후 실행 되는 후속 RUN 명령에 대한 컨텍스트를 설정합니다. 
더 나은 Docker 빌드 캐싱으로 더 빠른 빌드를 얻기 위해 package.jsonpackage-lock.json 파일 만 컨테이너에 복사합니다.
다음 줄은 컨테이너의 포트 3000 으로 EXPOSE 입니다. 
Node.js Express 웹 서버가 기본적으로 실행되는 포트입니다. 
위의 단계는 dev stages와 production stages 모두에 공통적입니다.
이제 production target stage가 어떻게 구축되는지 살펴볼 수 있습니다.

production stage 설정

FROM base as production ENV NODE_ENV=production RUN npm ci COPY . / CMD ["node", "bin/www"] // base에 ENV 변수를 설정하고 RUN한다. 그다음 /src에 복사한 뒤 노드 명령과 함께 bin/www로 웹서버를 실행한다.
Docker
복사
production 단계에서는 Docker가 base stage에서 시작하도록 지시하는 라인에서 base stage에서 중단한 부분을 계속 진행합니다. 
그 후 Docker에 호출된 환경 변수 NODE_ENVproduction로 설정합니다.
변수를 production로 설정하면 성능이 3배 향상 되고 캐시된 뷰와 같은 다른 이점도 있다고 합니다. 
npm install 을 실행하면 주요 종속성만 설치되고 개발 종속성은 제외됩니다. 
이러한 설정은 프로덕션 환경에 적합합니다.
다음으로  npm install대신 npm ci 실행합니다.  
npm ci 는 지속적인 통합 및 배포를 목표로 합니다.
또한 일부 사용자 지향 기능을 우회(bypasses)하기 때문에 npm install보다 훨씬 빠릅니다.
npm ci 이 작동하려면 package-lock.json 파일이 필요합니다 .
그런 다음 코드를 /src 에 복사합니다 . 이것이 작업 디렉토리이기 때문입니다. 
여기에서 우리가 가지고 있는 사용자 정의 코드를 컨테이너에 복사합니다. 
결과적으로 Node 명령과 함께 bin/www을 실행하여 웹 서버를 실행합니다.
다단계 빌드를 활용하기 때문에 개발 단계에서만 개발에 필요한 구성 요소를 추가할 수 있습니다. 
어떻게 되는지 살펴보겠습니다.
FROM base as dev ENV NODE_ENV=development RUN npm install -g nodemon && npm install COPY . / CMD ["nodemon", "bin/www"]
Docker
복사
프로덕션과 마찬가지로 dev도 기본 단계에서 "extending"합니다. 
이 단계에서는 NODE_ENV환경 변수를 development로 설정합니다 . 
그런 다음 nodemon 을 설치합니다 . 
파일이 변경될 때마다 nodemon은 서버를 다시 시작하여 개발 경험을 훨씬 원활하게 만듭니다.
그런 다음, dev 종속성이 있는 경우에도 이를 설치 하는 npm install를 수행 합니다. 
현재 , package.json 에 개발 종속성은 없습니다. 
예를 들어, Jest를 사용하여 앱을 테스트하는 경우, 이는 개발 의존 관계 중 하나입니다.
2개의 명령어는 &&과 함께 조합되어 있습니다.
이렇게 하면 fewer Docker layers 되어 빌드 캐시에 적합합니다.
이전 단계와 마찬가지로 코드를 /src 에서 컨테이너에 복사합니다 . 
하지만 이번에는 개발 환경인 만큼 파일이 변경될 때마다 다시 시작하기 위해 nodemon으로 웹 서버를 실행합니다.

무시하지 마세요. .dockerignore!

.gitignore 없이 Git을 사용하지 않는 것처럼. , Docker를 사용할 때 파일 gitignore을 추가하는 것이 좋습니다 . 
Docker 이미지에 포함하고 싶지 않은 파일을 무시하는 데 사용됩니다. Docker 이미지를 작게 유지하고 관련 없는 파일 변경 사항을 무시하여 빌드 캐시를 보다 효율적으로 유지하는 데 도움이 됩니다. 이것이 우리 파일 .dockerignore의 모습입니다:
.git node_modules
Docker
복사
매우 간단합니다. 우리는 폴더와 호스트에서 Docker 컨테이너로 .git 와 node_modules폴더를 복사하지 않도록 Docker에 지시합니다. 
npm ci 나 npm install실행 하거나 컨테이너 내부에서 일관성을 유지하는 데 도움이 됩니다.

Docker Compose를 추가하고 빌드 대상을 잊지 마세요.

이제 Docker와 함께 Node.js Express 앱을 실행하는 데 필요한 대부분의 항목이 있습니다. 
다음으로 모든 것을 함께 붙일 필요가 있는 것은 Docker Compose입니다.
Compose 를 사용하면 단일 또는 여러 컨테이너로 애플리케이션을 쉽게 실행할 수 있습니다. 
컨테이너를 빌드하거나 실행하기 위해 매우 긴 명령을 기억할 필요가 없습니다. 
docker-compose builddocker-compose up를 실행할 수 있는 한 응용 프로그램은 쉽게 실행됩니다.
좋은 점은 도커 설치 시 사전 설치되어 있다는 점입니다. 개발 환경에서 주로 사용됩니다.
아래 docker-compose.yml는 이 튜토리얼의 프로젝트 루트에 있는 파일입니다.
version: '3.8' services: web: build: context: ./ target: dev volumes: - .:/src command: npm run start:dev ports: - "3000:3000" environment: NODE_ENV: development DEBUG: nodejs-docker-express:*
Docker
복사
먼저 Docker Compose의 버전을 지정합니다.
이 경우 Docker 엔진 19.0.3에서 지원하는 최신 버전인 3.8입니다. 
이를 통해 다단계 Docker 빌드를 사용할 수도 있습니다.
다음으로 사용 중인 서비스를 지정합니다. 
이 자습서에서는 web이라는 서비스가 하나만 있습니다 . 
현재 디렉토리 context의 빌드 와 dev로 설정된 중요한 빌드 매개변수 target가 있습니다. 
이것은 dev 단계에서 Docker 이미지를 빌드하고 싶다는 것을 Docker에 알려줍니다.
그런 다음 Docker 볼륨을 지정합니다 .
Docker 컨테이너에 있는 호스트 /src의 로컬 디렉터리 ./에서 변경 사항을 복사하고 동기화하도록 Docker에 지시합니다 . 
이것은 호스트 시스템에서 파일을 변경할 때 유용하며 컨테이너 내부에도 즉시 반영됩니다.
결과적으로 다음과 같이 파일 package.json에 추가되는 명령 npm run start:dev을 사용합니다.
"start:dev": "nodemon ./bin/www"
Plain Text
복사
그래서 nodemon으로 웹 서버를 시작하려고 합니다. 
개발 환경이므로 파일을 저장할 때마다 서버를 다시 시작합니다.
다음으로 호스트 시스템의 포트 3000을 컨테이너 포트 3000과 매핑합니다.
컨테이너를 구축할 때 포트 3000을 노출했으며 웹 서버도 3000에서 실행됩니다.
마지막으로 몇 가지 환경 변수를 설정합니다. 
첫째, 자세한 오류를 보고 뷰 캐싱을 수행하지 않기를 원하기 때문에 development로 NODE_ENV설정됩니다. 
그런 다음 디버그를 *로 설정하여 Express 웹 서버에 모든 것에 대한 자세한 디버그 메시지를 출력하도록 지시합니다.

Docker 및 Docker Compose로 앱 테스트

필요한 부분을 모두 설정했으므로 이제 Docker 이미지 빌드를 계속해 보겠습니다. 
BuildKit 을 사용하여 Docker 빌드를 최적화합니다 . 
Docker 이미지는 BuildKit을 활성화하면 훨씬 빠르게 빌드 됩니다. 
작동을 볼 시간 — 다음 명령을 실행하십시오.
COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose build
Plain Text
복사
Error 발생, docker-compose는 도커에 사전설치된다고 하는데 인식이 되지 않는다.
다른 에러가 발생합니다. 아래의 글 참고합니다.
docker daemon을 돌려주어야 한다는 것입니다.
하지만 이 방식은 다시 에러 발생합니다. mac m1 환경입니다. https://www.lainyzine.com/ko/article/how-to-install-docker-for-m1-apple-silicon/ 또는 공식문서를 참고합니다. 여기서는 전자를 봅니다.
사이트 방문해서 설치합니다.
간단히 도커 동작 확인
실행한 nginx 컨테이너를 웹 브라우저 포트로 접근해서 확인합니다.
접속 로그가 찍힙니다.
메뉴바에서 도커 데몬 확인을 종종 하게 됩니다.
다시 본론으로 돌아와서, 확인합니다. 직접 확인한 것과
COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose build
Plain Text
복사
여기에서 Compose에 BuildKit을 사용하여 Docker 이미지를 빌드하도록 지시합니다. 일정 시간이 지나면 실행되고 아래와 같이 Docker 이미지를 빌드해야 합니다.
실제와 포스트에서의 마지막 메시지가 다릅니다.
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them 는 어떻게 해결하나요?
하면 안됩니다. docker scan --login 을 입력합니다. (https://forums.docker.com/t/unable-to-docker-scan-failed-to-get-dockerscanid/117745/4)
사이트가 켜졌고 회원가입하고 나니 CLI를 Authenticated할지 허용하는 버튼을 보여줍니다. 클릭합니다.
다시 빌드해봤는데, 성공이라는 메시지도, docker scan하라는 메시지도 뜨지 않습니다.
에러를 해결하고, 다시 돌아왔습니다.
따라서 Docker 이미지는 약 14초 만에 빌드되었으며 BuildKit을 사용하면 훨씬 더 빨라졌습니다. 이미지를 실행해 보겠습니다.
docker-compose up
Plain Text
복사
아래와 같은 결과가 나와야 합니다.
그 후, 브라우저에서 http://localhost:3000 치면 다음이 표시되어야 합니다.
엄청난! 우리 앱은 Docker와 함께 잘 실행되고 있습니다. 
이제 파일을 변경하고 올바르게 반영되는지 확인하겠습니다.

복구를 위해 파일 변경 nodemon에서 다시 시작

우리의 목표는 테스트로 "Welcome to Express"를 "Welcome to Express with Docker"로 변경하는 것입니다. 이렇게 하려면 routes/index.js파일의 line 6에서 변경하고 아래와 같이 표시해야 합니다.
res.render('index', { title: 'Express with Docker' });
JavaScript
복사
파일을 저장하자마자 웹 서버가 다시 시작되는 것을 볼 수 있습니다. 이것은 Docker 볼륨과 nodemon이 예상대로 올바르게 작동하고 있음을 분명히 보여줍니다.
그의 시점에서 http://localhost:3000 실행 중인 브라우저 탭을 새로 고치면 다음 이 표시됩니다.
만세! Docker Compose가 구성된 로컬 환경에서 Docker로 Express 앱을 성공적으로 실행했습니다. 등을 두드려주세요!

다음 단계 및 결론

Docker Compose는 여러 컨테이너를 시작하는 데 매우 유용합니다. 
따라서 애플리케이션의 데이터 소스로 Mongo 또는 MySQL 을 추가하려는 경우 파일 docker-compose의 다른 서비스로 쉽게 추가할 수 있습니다 .
이 자습서의 목적을 위해 단일 컨테이너가 실행 중인 Docker가 있는 Node.js에만 초점을 맞춥니다.
Node.js와 docker는 아주 잘 어울립니다. 
docker-compose를 사용하면 개발 경험이 훨씬 부드러워집니다. 
이 튜토리얼을 빌드 기반으로 사용하여 Docker 및 Node.js로 고급 기능을 시도해 볼 수 있습니다.