본문 바로가기

About/Docker

[Docker] Dockerfile 컨테이너 시스템 콜 시그널 설정(node.js 컨테이너)

다음 도서를 참고하여 작성하였습니다.

www.aladin.co.kr/shop/wproduct.aspx?ItemId=166082298

 

완벽한 IT 인프라 구축을 위한 Docker

컨테이너 기술을 사용한 애플리케이션 실행 환경 플랫폼인 ‘Docker’를 사용하여 인프라를 구축하기 위한 입문서. 제2판에서는 Docker의 새로운 기능 및 명령과 더불어, 멀티호스트 환경에서 실행

www.aladin.co.kr


STOPSIGNAL

컨테이너를 종료할 때에 송신하는 시그널을 설정하려면 STOPSIGNAL 명령을 사용합니다

 

STOPSIGNAL 명령의 구문은 다음과 같습니다.

 

STOPSIGNAL [시그널]

 

STOPSIGNAL 명령에는 시그널 번호(9 등) 또는 시그널명(SIGKILL 등)을 지정할 수 있습니다.

Docker에서 어플리케이션을 실행하면 Signal을 사용하여 호스트 머신에서 어플리케이션과 통신하거나, 구성 요소를 다시 불러오거나, 종료된 프로그램을 정리하거나 실행 파일을 조정하는 일을 할 수 있습니다.

 


SIGNAL에 관하여

OS에서 Signal이란, 프로세스 간 통신의 한 형태 입니다. SIGNAL 형태의 어떠한 조건이 발생했음을 커널에 알리기 위함입니다. 프로세스에 SIGNAL이 발생되면 프로세스가 중단되고 SIGNAL Handler가 호출됩니다. 만약 Handler가 없는 경우에는 기본 Handler가 호출됩니다.

 

예를 들어 node.js에서 SIGTERM 신호에 대한 설정입니다.

process.on('SIGTERM', function(){
	console.log('Process 종료 중 ... ');
})

 

또한 Docker Description에 따르면 docker stop 명령어 사용 시

내부적에서는 SIGTERM 신호를 받으며 잠시 후에 SIGKILL 신호를 받고 종료합니다.
첫 번째 신호는 Dockerfile의 STOPSIGNAL 명령어 docker run 실행 시 --stop-signal 옵션을 통해 변경할 수 있습니다.

 

다음은 자주 사용되는 신호의 목록입니다. (참조 : Programming Interface : A Linux and UNIX System Programming Handbook  )

SIGKILL, SIGSTOP을 제외한 모든 SIGNAL은 Process에 의해 intercept 될 수 있습니다.

 

node.js STOPSIGNAL 예시

 

node.js를 사용하여 3000번 포트에서 수신하는 HTTP 서버를 만들었으며, SIGINT 및 SIGTERM에 대한 SIGNAL Handler를 설정하였습니다. Handler는 간단하게 어떤 신호로 프로세스가 종료되었는지 출력하는 함수입니다.

 

파일명은 sig_test.js입니다.

//sig_test.js
'use strict';
var http = require('http');
var server = http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(3000, '0.0.0.0');
console.log('server started');
var signals = {
  'SIGINT': 2,
  'SIGTERM': 15
};
function shutdown(signal, value) {
  server.close(function () {
    console.log('server stopped by ' + signal);
    process.exit(128 + value);
  });
}
Object.keys(signals).forEach(function (signal) {
  process.on(signal, function () {
    shutdown(signal, signals[signal]);
  });
});

package.json은 다음과 같이 작성합니다.

{
  "name": "node_signal_test_app",
  "version": "1.0.0",
  "description": "node_signal_test_app",
  "main": "sig_test.js",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index"
  },
  "repository": {
  },
  "author": "JingoKim",
  "license": "MIT",
  "bugs": {
    "url": "kimjingo.tistory.com"
  },
  "homepage": "kimjingo.tistory.com"
}

 

 

다음은 io.js를 기반으로 한 Dockerfile입니다.

FROM iojs:onbuild
COPY ./sig_test.js ./sig_test.js
COPY ./package.json ./package.json
EXPOSE 3000
ENTRYPOINT ["node", "sig_test"]

 

이 때 Dockerfile에서 ENTRYPOINT 혹은 RUN 명령을 exec 형식으로 사용해야합니다. 

(shell 형식으로 작성하게 되면 /bin/sh의 하위 명령으로 실행되기 때문에 컨테이너의 PID은 shell이 되고 애플리케이션에서 docker kill 신호를 수신하지 않습니다.)

 

이제 이미지를 빌드합니다.

docker build -t node_signal_test_app .

빌드 결과

이제 이미지를 통해 컨테이너를 실행합니다.

docker run -it -p 3000:3000 --name node_signal_test_app node_signal_test_app

터미널을 하나 더 실행하여 서버가 정상 작동하는 지 확인합니다.

# curl localhost:3000
Hello World

정상 작동 확인 후 새 터미널에서 docker stop(혹은 docker kill)을 통해 컨테이너를 중지시킵니다.

docker stop node_signal_test_app
혹은
docker kill --signal="SIGTERM" node_signal_test_app

그러면 실행 중인 컨테이너가 종료되며 다음과 같이 출력됩니다.

SIGTERM에 의한 종료

SIGTERM 신호에 의해 종료됐다고 나오네요.

 

다시 한 번 컨테이너를 실행하고 이번에는 컨테이너에서 Ctrl+C 를 통해 종료시켜봅니다.

SIGINT에 의한 종료

이번에는 SIGINT 신호에 의해 종료됐다고 출력됩니다.

 


Docker에서 시스템 콜 시그널을 다루는 법에 대하여 다뤄봤습니다.

 

참조 사이트