
Node.js는 다양한 환경에서 실행될 수 있지만, 환경에 따라 의존성 문제나 설정 문제가 발생할 수 있습니다. 이러한 문제를 해결하기 위해 도커(Docker)라는 컨테이너 기술을 사용할 수 있습니다.
도커는 애플리케이션과 그에 필요한 모든 것을 하나의 패키지로 만들어주는 컨테이너 기술입니다. 컨테이너는 애플리케이션을 실행하는 독립적인 공간으로, 호스트 OS의 자원을 공유하면서 격리된 환경에서 작동합니다. 컨테이너는 가볍고 빠르며, 어떤 환경에서도 동일하게 실행될 수 있습니다.
도커를 사용하면 Node.js 애플리케이션을 컨테이너화할 수 있습니다. 컨테이너화란 애플리케이션과 그에 필요한 모든 것을 하나의 이미지로 만들고, 이 이미지를 바탕으로 컨테이너를 생성하는 과정입니다. 컨테이너화를 통해 Node.js 애플리케이션의 배포와 관리를 쉽고 효율적으로 할 수 있습니다.
하지만 단일 컨테이너로 구성된 Node.js 애플리케이션은 한계가 있습니다. 예를 들어, 애플리케이션의 규모가 커지면 여러 개의 컨테이너로 분산해야 하는데, 이때 각 컨테이너의 생성과 관리, 네트워크 연결 등의 작업이 복잡해집니다. 또한, 애플리케이션의 가용성과 안정성을 보장하기 위해 컨테이너의 상태를 모니터링하고, 장애가 발생하면 자동으로 복구하는 등의 작업이 필요합니다.
이러한 문제를 해결하기 위해 컨테이너 오케스트레이션(Container Orchestration)이라는 기술을 사용할 수 있습니다. 컨테이너 오케스트레이션은 여러 개의 컨테이너로 구성된 애플리케이션을 자동으로 배치하고, 조정하고, 관리하는 기능을 제공합니다. 컨테이너 오케스트레이션을 통해 Node.js 애플리케이션의 확장성과 신뢰성을 높일 수 있습니다.
컨테이너 오케스트레이션을 구현하는 도구는 여러 가지가 있지만, 이 글에서는 쿠버네티스(Kubernetes)라는 도구를 사용해보겠습니다. 쿠버네티스는 구글이 개발한 오픈소스 컨테이너 오케스트레이션 플랫폼으로, 현재 가장 널리 사용되고 있습니다. 쿠버네티스는 클러스터(cluster)라는 단위로 컨테이너를 관리하며, 다양한 기능과 자원을 제공합니다.
이 글에서는 Node.js 애플리케이션을 도커로 컨테이너화하고, 쿠버네티스로 컨테이너 오케스트레이션하는 방법에 대해 알아보겠습니다.
Node.js 애플리케이션을 도커로 컨테이너화하기
Node.js 애플리케이션을 도커로 컨테이너화하는 방법은 다음과 같습니다.
Node.js 애플리케이션의 소스 코드를 준비합니다. 이 예제에서는 간단한 웹 서버를 구현한 app.js 파일을 사용합니다.
도커 이미지를 빌드하기 위한 Dockerfile 파일을 작성합니다. Dockerfile 파일은 도커 이미지의 생성 과정과 내용을 정의하는 파일입니다.
1. docker build 명령어를 사용하여 Dockerfile 파일을 바탕으로 도커 이미지를 빌드합니다.
2. docker run 명령어를 사용하여 빌드한 도커 이미지로 컨테이너를 생성하고 실행합니다.
Node.js 애플리케이션의 소스 코드 준비하기
먼저, Node.js 애플리케이션의 소스 코드를 준비해보겠습니다. 이 예제에서는 간단한 웹 서버를 구현한 app.js 파일을 사용합니다. app.js 파일은 다음과 같은 코드로 작성합니다.
// http 모듈을 불러옵니다.
const http = require('http');
// 웹 서버를 생성합니다.
const server = http.createServer((req, res) => {
// 응답 헤더와 본문을 작성합니다.
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, Docker!\n');
});
// 웹 서버를 8080번 포트에서 실행합니다.
server.listen(8080, () => {
console.log('Server running at http://localhost:8080/');
});
도커 이미지를 빌드하기 위한 Dockerfile 파일 작성하기
다음으로, 도커 이미지를 빌드하기 위한 Dockerfile 파일을 작성해보겠습니다. Dockerfile 파일은 도커 이미지의 생성 과정과 내용을 정의하는 파일입니다. Dockerfile 파일은 다음과 같은 구조로 작성합니다.
# 베이스 이미지를 지정합니다.
FROM base-image
# 이미지에 필요한 명령어들을 실행합니다.
RUN command1
RUN command2
...
# 컨테이너가 실행될 때 수행할 명령어를 지정합니다.
CMD command
다음은 Node.js 애플리케이션을 위한 Dockerfile 파일의 예시입니다.
# 베이스 이미지로 node:14-alpine을 사용합니다.
FROM node:14-alpine
# 애플리케이션의 작업 디렉토리를 /usr/src/app으로 설정합니다.
WORKDIR /usr/src/app
# 애플리케이션의 의존성 파일인 package.json과 package-lock.json을 복사합니다.
COPY package*.json ./
# npm install 명령어로 의존성 패키지들을 설치합니다.
RUN npm install
# 애플리케이션의 소스 코드를 복사합니다.
COPY . .
# 컨테이너가 실행될 때 node app.js 명령어로 애플리케이션을 실행합니다.
CMD [ "node", "app.js" ]
도커 이미지를 빌드하기
다음으로, docker build 명령어를 사용하여 Dockerfile 파일을 바탕으로 도커 이미지를 빌드해보겠습니다. docker build 명령어는 다음과 같은 구조로 사용합니다.
docker build -t image-name:tag .
docker run [options] image-name:tag [command]
docker run -d -p 8080:8080 node-app:1.0
이제 로컬호스트:8080 으로 접속하면 애플리케이션이 실행되는것을 확인할수 있습니다.
Node.js 애플리케이션을 쿠버네티스로 컨테이너 오케스트레이션하기
Node.js 애플리케이션을 쿠버네티스로 컨테이너 오케스트레이션하는 방법은 다음과 같습니다.
- 쿠버네티스 클러스터를 생성합니다. 쿠버네티스 클러스터는 여러 개의 노드(node)로 구성된 컨테이너의 집합입니다. 노드는 컨테이너를 실행하는 물리적 또는 가상의 서버입니다. 쿠버네티스 클러스터는 마스터(master) 노드와 워커(worker) 노드로 구분됩니다. 마스터 노드는 클러스터의 상태와 작업을 관리하고, 워커 노드는 실제로 컨테이너를 실행합니다.
- Node.js 애플리케이션을 위한 쿠버네티스 리소스를 정의합니다. 쿠버네티스 리소스는 쿠버네티스 클러스터에서 관리되는 객체로, YAML 형식의 파일로 정의할 수 있습니다. 쿠버네티스 리소스에는 다양한 종류가 있지만, 이 예제에서는 디플로이먼트(Deployment)와 서비스(Service)라는 두 가지 리소스를 사용합니다. 디플로이먼트는 컨테이너의 생성과 실행을 담당하고, 서비스는 컨테이너에 접근할 수 있는 네트워크 인터페이스를 제공합니다.
- kubectl 명령어를 사용하여 쿠버네티스 리소스를 클러스터에 배포합니다. kubectl 명령어는 쿠버네티스 클러스터와 통신하고, 리소스를 생성하고, 조회하고, 수정하고, 삭제하는 등의 작업을 수행할 수 있는 도구입니다.
- kubectl 명령어를 사용하여 쿠버네티스 클러스터와 리소스의 상태를 확인하고, 필요에 따라 스케일링하거나 업데이트하는 등의 작업을 수행합니다.
쿠버네티스 클러스터 생성하기
먼저, 쿠버네티스 클러스터를 생성해보겠습니다. 쿠버네티스 클러스터를 생성하는 방법은 여러 가지가 있지만, 이 예제에서는 디지털오션(DigitalOcean)에서 제공하는 관리형 쿠버네티스 서비스인 DOKS(DigitalOcean Kubernetes Service)를 사용해보겠습니다. DOKS는 쉽고 빠르게 쿠버네티스 클러스터를 생성하고 관리할 수 있게 해주는 서비스입니다.
DOKS를 사용하려면, 디지털오션 계정이 필요합니다.
쿠버네티스 클러스터를 생성한 후에는, kubectl 명령어를 사용하여 클러스터와 통신할 수 있도록 설정해야 합니다. 설정 방법은 다음과 같습니다.
- 디지털오션 대시보드에서 클러스터의 상세 페이지로 이동합니다.
- Download Config File 버튼을 클릭하여 클러스터의 구성 파일을 다운로드합니다.
- 다운로드한 구성 파일을 ~/.kube/config 경로에 저장합니다.
- 이제 kubectl get nodes 명령어를 사용하여 클러스터의 노드들을 조회할 수 있습니다.
kubectl get nodes

Node.js 애플리케이션을 위한 쿠버네티스 리소스 정의하기
다음으로, Node.js 애플리케이션을 위한 쿠버네티스 리소스를 정의해보겠습니다. 쿠버네티스 리소스는 YAML 형식의 파일로 정의할 수 있습니다. YAML 파일은 다음과 같은 구조로 작성합니다.
apiVersion: api-version
kind: resource-type
metadata:
name: resource-name
spec:
# resource-specific properties
- apiVersion: 쿠버네티스 API의 버전을 지정하는 필드로, 리소스의 종류에 따라 다른 값을 가집니다.
- kind: 리소스의 종류를 지정하는 필드로, 예를 들어 Deployment, Service, Pod 등이 있습니다.
- metadata: 리소스에 대한 메타데이터를 지정하는 필드로, 예를 들어 이름, 레이블, 주석 등이 있습니다.
- spec: 리소스의 상세한 속성을 지정하는 필드로, 리소스의 종류에 따라 다른 값을 가집니다.
이 예제에서는 디플로이먼트와 서비스라는 두 가지 리소스를 정의해보겠습니다.
디플로이먼트 정의하기
디플로이먼트는 컨테이너의 생성과 실행을 담당하는 리소스입니다. 디플로이먼트는 원하는 상태(desired state)를 정의하고, 쿠버네티스가 현재 상태(current state)와 일치하도록 조정합니다. 디플로이먼트는 포드(pod)라는 단위로 컨테이너를 관리합니다. 포드는 하나 이상의 컨테이너와 그에 필요한 자원과 네트워크를 가진 기본적인 쿠버네티스 객체입니다.
디플로이먼트를 정의하기 위해서는 다음과 같은 속성을 지정해야 합니다.
- replicas: 포드의 개수를 지정하는 속성으로, 기본값은 1입니다.
- selector: 디플로이먼트가 관리할 포드들을 선택하는 속성으로, 레이블(label)을 기준으로 선택합니다. 레이블은 키와 값으로 구성된 식별자입니다.
- template: 포드의 템플릿을 지정하는 속성으로, 포드가 생성될 때 참조됩니다. 템플릿에는 포드의 메타데이터와 스펙을 정의할 수 있습니다.
다음은 Node.js 애플리케이션을 위한 디플로이먼트의 예시입니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-app
spec:
replicas: 3 # 포드의 개수를 3개로 지정합니다.
selector:
matchLabels:
app: node-app # app이라는 레이블의 값이 node-app인 포드들을 선택합니다.
template:
metadata:
labels:
app: node-app # 포드에 app이라는 레이블을 부여하고, 값은 node-app으로 합니다.
spec:
containers: # 포드에 속한 컨테이너들을 정의합니다.
- name: node-app # 컨테이너의 이름을 node-app으로 합니다.
image: kamaln7/node-hello-app:1.0 # 컨테이너의 이미지를 도커 허브에서 다운로드한 이미지로 합니다.
ports: # 컨테이너가 사용하는 포트들을 정의합니다.
- containerPort: 3000 # 컨테이너가 사용하는 포트는 3000번입니다.
서비스 정의하기
서비스는 컨테이너에 접근할 수 있는 네트워크 인터페이스를 제공하는 리소스입니다. 서비스는 디플로이먼트와 마찬가지로 레이블을 기준으로 포드들을 선택하고, 선택된 포드들에 대해 로드 밸런싱(load balancing)과 서비스 디스커버리(service discovery)를 수행합니다. 로드 밸런싱은 서비스에 들어오는 요청을 여러 개의 포드에 균등하게 분배하는 기능입니다. 서비스 디스커버리는 서비스에 접근할 수 있는 IP 주소와 포트 번호를 자동으로 할당하는 기능입니다.
서비스를 정의하기 위해서는 다음과 같은 속성을 지정해야 합니다.
- type: 서비스의 타입을 지정하는 속성으로, 예를 들어 ClusterIP, NodePort, LoadBalancer 등이 있습니다. 각 타입은 서비스가 제공하는 네트워크 인터페이스의 범위와 방식에 따라 다릅니다. 이 예제에서는 NodePort 타입을 사용하겠습니다. NodePort 타입은 클러스터 외부에서 서비스에 접근할 수 있도록 각 노드의 특정 포트를 서비스에 연결하는 방식입니다.
- selector: 서비스가 관리할 포드들을 선택하는 속성으로, 레이블을 기준으로 선택합니다.
- ports: 서비스가 사용하는 포트들을 정의하는 속성으로, 여러 개의 포트를 사용할 수 있습니다. 각 포트는 다음과 같은 속성을 가집니다.
- port: 서비스가 사용하는 포트 번호
- targetPort: 서비스가 연결할 포드의 컨테이너 포트 번호
- nodePort: NodePort 타입일 경우, 각 노드에서 서비스에 연결할 포트 번호
다음은 Node.js 애플리케이션을 위한 서비스의 예시입니다.
apiVersion: v1
kind: Service
metadata:
name: node-app
spec:
type: NodePort # 서비스의 타입을 NodePort로 지정합니다.
selector:
app: node-app # app이라는 레이블의 값이 node-app인 포드들을 선택합니다.
ports: # 서비스가 사용하는 포트들을 정의합니다.
- port: 8080 # 서비스가 사용하는 포트는 8080번입니다.
targetPort: 3000 # 서비스가 연결할 포드의 컨테이너 포트는 3000번입니다.
nodePort: 30001 # 각 노드에서 서비스에 연결할 포트는 30001번입니다.
쿠버네티스 리소스 클러스터에 배포하기
다음은 Node 애플리케이션을 위한 쿠버네티스 리소스를 클러스터에 배포하는 명령어입니다.
kubectl create -f deployment.yaml # 디플로이먼트를 생성합니다.
kubectl create -f service.yaml # 서비스를 생성합니다.
쿠버네티스 클러스터와 리소스의 상태 확인하기
마지막으로, kubectl 명령어를 사용하여 쿠버네티스 클러스터와 리소스의 상태를 확인하고, 필요에 따라 스케일링하거나 업데이트하는 등의 작업을 수행해보겠습니다.
kubectl get: 쿠버네티스 리소스의 목록과 상태를 조회
kubectl describe: 쿠버네티스 리소스의 상세한 정보를 조회
kubectl logs: 쿠버네티스 리소스가 출력한 로그를 조회
kubectl exec: 쿠버네티스 리소스 내부에서 명령어를 실행하는 명령어
kubectl scale: 쿠버네티스 리소스의 스케일을 조정하는 명령어
kubectl apply: 쿠버네티스 리소스의 변경 사항을 적용하는 명령어
kubectl delete: 쿠버네티스 리소스를 삭제하는 명령어
이렇게 오늘은 Node 어플리케이션을 쿠버네티스로 컨테이너 오케스트레이션 하는 방법에 대해 알아보았습니다.
글이 꽤 길었네요. 다음부턴 분량조절을 잘하도록 하겠습니다 ^^;
'개발' 카테고리의 다른 글
Node.js 메모리관리법, 메모리 누수 예시 (0) | 2023.06.20 |
---|---|
React에서 애니메이션 만들어 사용하기 (with. Hooks) (0) | 2023.06.17 |
Node.js 이메일 발송 및 템플릿 처리 (0) | 2023.06.16 |
Node.js 용량이 큰 데이터 스트리밍으로 처리하기 (0) | 2023.06.16 |
node js 캐싱 성능최적화 (Redis, Memcached) (0) | 2023.06.16 |