컨테이너 내에서 Node.js 디버깅
Node.js 프로젝트에 Docker 파일을 추가하면 해당 애플리케이션을 컨테이너 내에서 디버깅할 수 있는 작업 및 실행 구성이 추가됩니다. 그러나 Node.js를 둘러싼 방대한 생태계로 인해 이러한 작업은 모든 애플리케이션 프레임워크나 라이브러리를 수용할 수 없으므로 일부 애플리케이션은 추가 구성이 필요합니다.
컨테이너 진입점 구성
Container Tools 확장 기능은 package.json의 속성을 통해 컨테이너의 진입점, 즉 컨테이너 내에서 디버그 모드로 애플리케이션을 시작하는 명령줄을 추론합니다. 확장 기능은 먼저 scripts 객체에서 start 스크립트를 찾습니다. 이를 찾고 node 또는 nodejs 명령으로 시작하는 경우 이를 사용하여 디버그 모드로 애플리케이션을 시작하는 명령줄을 빌드합니다. 찾지 못했거나 인식되는 node 명령이 아닌 경우 package.json의 main 속성이 사용됩니다. 둘 다 찾지 못했거나 인식되지 않는 경우 컨테이너를 시작하는 데 사용되는 docker-run 작업의 dockerRun.command 속성을 명시적으로 설정해야 합니다.
일부 Node.js 애플리케이션 프레임워크에는 애플리케이션을 관리하는 CLI가 포함되어 있으며 start 스크립트에서 애플리케이션을 시작하는 데 사용되어 기본 node 명령을 모호하게 합니다. 이러한 경우 Container Tools 확장 기능은 시작 명령을 추론할 수 없으므로 시작 명령을 명시적으로 구성해야 합니다.
Nest.js 애플리케이션의 진입점 구성 예시
{
"tasks": [
{
"type": "docker-run",
"label": "docker-run: debug",
"dependsOn": ["docker-build"],
"dockerRun": {
"command": "nest start --debug 0.0.0.0:9229"
},
"node": {
"enableDebugging": true
}
}
]
}
Meteor 애플리케이션의 진입점 구성 예시
{
"tasks": [
{
"type": "docker-run",
"label": "docker-run: debug",
"dependsOn": ["docker-build"],
"dockerRun": {
"command": "node --inspect=0.0.0.0:9229 main.js"
},
"node": {
"enableDebugging": true
}
}
]
}
애플리케이션 진입 페이지로 브라우저 자동 실행
Container Tools 확장 기능은 디버거에서 시작된 후 애플리케이션의 진입점으로 브라우저를 자동으로 실행할 수 있습니다. 이 기능은 기본적으로 활성화되어 있으며 launch.json의 디버그 구성의 dockerServerReadyAction 객체를 통해 구성됩니다.
이 기능은 애플리케이션의 여러 측면에 따라 달라집니다.
- 애플리케이션은 디버그 콘솔에 로그를 출력해야 합니다.
- 애플리케이션은 "서버 준비" 메시지를 기록해야 합니다.
- 애플리케이션은 찾아볼 수 있는 페이지를 제공해야 합니다.
기본 설정은 Express.js 기반 애플리케이션의 경우 작동할 수 있지만, 다른 Node.js 프레임워크는 이러한 측면 중 하나 이상에 대한 명시적 구성이 필요할 수 있습니다.
애플리케이션 로그가 디버그 콘솔에 기록되도록 보장
이 기능은 애플리케이션이 연결된 디버거의 디버그 콘솔에 로그를 작성하는지에 따라 달라집니다. 그러나 모든 로깅 프레임워크가 디버그 콘솔에 기록되는 것은 아니며, 콘솔 기반 로거를 사용하도록 구성된 경우에도 마찬가지입니다(일부 "콘솔" 로거는 실제로 콘솔을 우회하고 stdout에 직접 기록하므로).
솔루션은 로깅 프레임워크에 따라 다르지만 일반적으로 콘솔에 *실제로* 기록하는 로거를 생성/추가해야 합니다.
Express 애플리케이션을 디버그 콘솔에 기록하도록 구성하는 예시
기본적으로 Express.js는 콘솔을 우회할 수 있는 debug 로깅 모듈을 사용합니다. 이는 로그 함수를 명시적으로 콘솔의 debug() 메서드에 바인딩하여 해결할 수 있습니다.
var app = require('../app');
var debug = require('debug')('my-express-app:server');
var http = require('http');
// Force logging to the debug console.
debug.log = console.debug.bind(console);
또한 debug 로거는 DEBUG 환경 변수를 통해 활성화된 경우에만 로그를 기록하며, 이는 docker-run 작업에서 설정할 수 있습니다. (Docker 파일을 애플리케이션에 추가할 때 이 환경 변수는 기본적으로 *로 설정됩니다.)
{
"tasks": [
{
"type": "docker-run",
"label": "docker-run: debug",
"dependsOn": ["docker-build"],
"dockerRun": {
"env": {
"DEBUG": "*"
}
},
"node": {
"enableDebugging": true
}
}
]
}
애플리케이션이 "준비"되었을 때 구성
확장 기능은 애플리케이션이 디버그 콘솔에 Listening on port <number> 형식의 메시지를 기록할 때 HTTP 연결을 수신할 "준비"가 되었다고 판단합니다. Express.js가 기본적으로 그렇게 하듯이 말입니다. 애플리케이션이 다른 메시지를 기록하는 경우 launch.json의 디버그 구성에서 dockerServerReadyAction 객체의 pattern 속성을 해당 메시지와 일치하는 JavaScript 정규식으로 설정해야 합니다. 정규식에는 애플리케이션이 수신 대기하는 포트에 해당하는 캡처 그룹이 포함되어야 합니다.
예를 들어, 애플리케이션이 다음 메시지를 기록한다고 가정해 봅시다.
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
debug('Application has started on ' + bind);
}
디버그 실행 구성(launch.json)의 해당 pattern은 다음과 같습니다.
{
"configurations": [
{
"name": "Containers: Node.js Launch",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"platform": "node",
"dockerServerReadyAction": {
"pattern": "Application has started on port (\\d+)"
}
}
]
}
포트 번호를 위한
(\\d+)캡처 그룹과\d문자 클래스의 백슬래시에 대한 JSON 이스케이프 문자로서의\사용에 유의하십시오.
애플리케이션 진입 페이지 구성
기본적으로 Container Tools 확장 기능은 브라우저의 "메인" 페이지를 엽니다(애플리케이션에서 결정되는 방식). 브라우저가 특정 페이지로 열려야 하는 경우, 디버그 실행 구성의 dockerServerReadyAction 객체의 uriFormat 속성을 Node.js 형식 문자열로 설정해야 하며, 포트가 대체될 위치를 나타내는 단일 문자열 토큰이 있어야 합니다.
기본 페이지 대신 about.html 페이지를 열기 위한 디버그 실행 구성(launch.json)의 해당 uriFormat은 다음과 같습니다.
{
"configurations": [
{
"name": "Containers: Node.js Launch",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"platform": "node",
"dockerServerReadyAction": {
"uriFormat": "https://:%s/about.html"
}
}
]
}
컨테이너 소스 파일을 로컬 작업 영역에 매핑
기본적으로 Container Tools 확장 기능은 실행 중인 컨테이너의 애플리케이션 소스 파일이 /usr/src/app 폴더에 있다고 가정하며, 디버거는 해당 파일을 열린 작업 영역의 루트로 다시 매핑하여 컨테이너에서 Visual Studio Code로 중단점을 번역합니다.
애플리케이션 소스 파일이 다른 위치(예: 다른 Node.js 프레임워크는 다른 규칙을 가짐)에 있는 경우, 작업 영역 내 또는 컨테이너 내에 있든, 디버그 실행 구성의 node 객체의 localRoot 및 remoteRoot 속성 중 하나 이상을 각각 작업 영역 및 컨테이너 내의 루트 소스 위치로 설정해야 합니다.
예를 들어, 애플리케이션이 대신 /usr/my-custom-location에 있는 경우 해당 remoteRoot 속성은 다음과 같습니다.
{
"configurations": [
{
"name": "Containers: Node.js Launch",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"platform": "node",
"node": {
"remoteRoot": "/usr/my-custom-location"
}
}
]
}
문제 해결
node_modules 누락으로 인해 컨테이너 이미지가 빌드 또는 시작되지 않음
Docker 파일은 종종 이미지 빌드 시간 또는 이미지 크기를 최적화하도록 구성됩니다. 그러나 모든 Node.js 애플리케이션 프레임워크가 일반적인 Node.js Docker 파일 최적화를 모두 지원하는 것은 아닙니다. 특히 일부 프레임워크의 경우 node_modules 폴더는 애플리케이션 루트 폴더의 바로 아래 폴더여야 하는 반면, Container Tools 확장 기능은 node_modules 폴더가 부모 또는 조상 수준에 있는 Docker 파일을 스캐폴드합니다(일반적으로 Node.js에서 허용됨).
해결 방법은 Dockerfile에서 해당 최적화를 제거하는 것입니다.
FROM node:lts-alpine
ENV NODE_ENV=production
WORKDIR /usr/src/app
COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"]
# Remove the `&& mv node_modules ../` from the RUN command:
# RUN npm install --production --silent && mv node_modules ../
RUN npm install --production --silent
COPY . .
EXPOSE 3000
RUN chown -R node /usr/src/app
USER node
CMD ["npm", "start"]