PromleeBlog
sitemap
aboutMe

posting thumbnail
Docker 심화 멀티 컨테이너 관리와 Docker Compose - DevOps 컨테이너에서 클러스터까지 3편
Docker Advanced Multi-Container Apps with Compose - DevOps from Containers to Clusters Part 3

📅

🚀

들어가기 전에 🔗

지난 시간에는 Dockerfile을 이용해 우리만의 웹 페이지가 담긴 Nginx 이미지를 직접 만들어보았습니다.
이제 우리는 어디서든 동일하게 실행되는 웹 서버 패키지를 갖게 된 셈입니다.
하지만 실제 서비스는 웹 서버 하나만으로 동작하지 않습니다.
보통 웹 서버(프론트엔드), API 서버(백엔드), 데이터베이스 등 여러 컴포넌트가 서로 통신하며 하나의 서비스를 이룹니다.
이번 시간에는 이렇게 여러 개의 컨테이너를 어떻게 연결하고, 또 어떻게 한 번에 쉽게 관리할 수 있는지 알아보겠습니다.


먼저 우리가 만든 이미지를 다른 사람과 공유할 수 있는 Docker Hub에 대해 배우고, 컨테이너들이 서로 대화하는 방법인 네트워크 개념을 살펴봅니다.
마지막으로, 여러 컨테이너를 설계도 한 장으로 관리하게 해주는 강력한 도구,
Docker Compose
를 이용해 Nginx와 Node.js API 서버를 함께 띄우는 실습을 진행하겠습니다.

🚀

내가 만든 이미지 공유하기: Docker Hub 🔗

Dockerfile로 만든 my-custom-nginx 이미지는 지금까지 내 컴퓨터에만 존재했습니다.
만약 팀 동료와 이 이미지를 공유하거나 다른 서버에서 사용하고 싶다면 어떻게 해야 할까요?
이럴 때 사용하는 것이 바로
Docker Hub
와 같은 이미지 저장소(Registry)입니다.

Docker Hub: 이미지들의 앱스토어 🔗

Docker Hub는 Docker가 공식적으로 운영하는, 전 세계의 Docker 이미지들이 모여있는 거대한 저장소입니다.
마치 스마트폰의 앱스토어처럼, 우리는 이곳에서 필요한 이미지를 검색해서 다운로드(docker pull) 받을 수 있고, 내가 만든 이미지를 업로드(docker push)하여 다른 사람과 공유할 수도 있습니다.
우리가 1편에서 hello-world, 2편에서 nginx 이미지를 별다른 설정 없이 바로 사용할 수 있었던 것도 모두 Docker Hub에 기본적으로 등록된 공식 이미지였기 때문입니다.
https://hub.docker.com/_/hello-world
https://hub.docker.com/_/nginx
Docker Hub - Nginx
Docker Hub - Nginx
비공개로 이미지를 관리하고 싶다면 유료 플랜을 사용하거나, 회사 내부에 별도의 Private Registry를 구축하여 사용할 수도 있습니다.

🚀

컨테이너는 어떻게 서로 대화할까? 🔗

여러 개의 컨테이너를 실행하면, 이들은 기본적으로 서로 격리되어 통신할 수 없습니다.
컨테이너들이 서로 대화하기 위해서는
네트워크
라는 통로를 만들어 연결해주어야 합니다.
Docker는 몇 가지 네트워크 드라이버를 제공하는데, 가장 대표적인 것은 bridge 입니다.

bridge 네트워크: 우리집 공유기처럼 🔗

bridge는 가장 흔하게 사용되는 네트워크 모드입니다.
마치 우리 집에 있는 인터넷 공유기를 상상하면 쉽습니다.
하나의 공유기에 연결된 여러 대의 노트북과 스마트폰은 192.168.0.x 와 같은 내부 IP 주소를 할당받아 서로 자유롭게 통신할 수 있습니다.
하지만 공유기 외부, 즉 인터넷에서는 이 내부 IP 주소를 직접 알 수 없습니다.
Docker의 bridge 네트워크도 마찬가지입니다.
하나의 bridge 네트워크에 연결된 컨테이너들은 각자의 내부 IP와 컨테이너 이름을 가지고 서로 통신할 수 있지만, 외부에서는 직접 접근할 수 없습니다.
외부에서 특정 컨테이너에 접근하려면 2편에서 배운
포트 매핑
(-p 옵션)을 통해 문을 열어주어야 합니다.
포트 맵핑
포트 맵핑

🚀

Nginx와 Node.js 서버 연동하기 🔗

이제 실제 시나리오를 통해 두 개의 컨테이너를 연결해보겠습니다.
사용자가 Nginx 웹 서버에 접속하면, Nginx가 뒤에서 실행 중인 Node.js API 서버를 호출하여 데이터를 받아와 보여주는 구조입니다.
실습에 앞서 전체 파일 구조는 다음과 같습니다.
📦 my-docker-project
 ┣ 📂 api-server
 ┃ ┣ 📜 Dockerfile
 ┃ ┣ 📜 package.json
 ┃ ┗ 📜 server.js
 ┣ 📂 my-nginx-page
 ┃ ┣ 📜 Dockerfile
 ┃ ┣ 📜 index.html
 ┃ ┗ 📜 nginx.conf
 ┗ 📜 docker-compose.yml

1. 간단한 Node.js API 서버 준비 🔗

먼저 API 서버 역할을 할 간단한 Node.js 코드를 준비합니다.
api-server 라는 폴더를 새로 만들고, 그 안에 server.jspackage.json, Dockerfile을 작성합니다.
/api-server/server.js
const http = require('http');
const server = http.createServer((req, res) => {
  if (req.url === '/api') {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello from API Server!');
  } else {
    res.writeHead(404);
    res.end();
  }
});
server.listen(3000, () => console.log('API Server is running on port 3000'));
/api-server/Dockerfile
FROM node:18-alpine
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD [ "node", "server.js" ]
/api-server/package.json
{
  "name": "api-server",
  "version": "1.0.0",
  "description": "A simple API server",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "http": "^0.0.1-security"
  }
}

2. Nginx 설정 파일 준비 🔗

이제 Nginx가 /api 요청을 받으면 Node.js API 서버로 전달하도록 설정해야 합니다.
이 역할을
리버스 프록시(Reverse Proxy)
라고 합니다.
2편에서 만들었던 my-nginx-page 폴더에 nginx.conf 파일을 만들고 아래와 같이 작성합니다.
/my-nginx-page/nginx.conf
server {
    listen 80;
    location / {
        root   /usr/share/nginx/html;
        index  index.html;
    }
    location /api {
        proxy_pass http://api-server:3000;
    }
}
/my-nginx-page/Dockerfile
FROM nginx
 
COPY ./index.html /usr/share/nginx/html/index.html
  # Nginx 설정 파일을 컨테이너에 복사합니다.
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
 
RUN chmod 644 /usr/share/nginx/html/index.html

3. Docker Compose로 한 번에 관리하기 🔗

예전 방식이라면 docker network create, docker run api-server..., docker run nginx... 등 여러 명령어를 순서대로 입력해야 합니다.
하지만
Docker Compose
를 사용하면 이 모든 과정을 하나의 파일로 깔끔하게 정의할 수 있습니다.
프로젝트의 최상위 폴더에 docker-compose.yml 파일을 만들고 아래와 같이 작성해봅시다.
docker-compose.yml
version: '3.8'
services:
  nginx:
    build: ./my-nginx-page
    ports:
      - "8080:80"
    depends_on:
      - api-server
  
  api-server:
    build: ./api-server
이제 터미널에서 docker-compose.yml 파일이 있는 위치로 이동한 뒤, 아래 명령어 하나만 실행하면 됩니다.
docker-compose up --build
이 명령어는 docker-compose.yml 파일을 읽어 api-servernginx 이미지를 빌드하고, 두 개의 컨테이너를 생성하여 실행합니다.
이제 브라우저에서 http://localhost:8080/api 로 접속해보세요.
"Hello from API Server!" 라는 메시지가 보인다면 성공입니다.
api 통신 성공
api 통신 성공
컨테이너들을 중지하고 싶을 때는 Ctrl + C를 누르거나, 다른 터미널에서 docker-compose down 명령어를 입력하면 됩니다.

🚀

결론 🔗

오늘은 하나의 서비스를 구성하는 여러 컨테이너를 어떻게 관리하는지 배웠습니다.
Docker Hub를 통해 이미지를 공유하는 방법을 알아보았고, 컨테이너 간의 통신을 가능하게 하는 bridge 네트워크의 개념을 이해했습니다.
무엇보다 가장 중요한 것은,
Docker Compose
라는 강력한 도구를 사용해 Nginx와 Node.js API 서버로 구성된 멀티 컨테이너 애플리케이션을 단 하나의 yml 파일과 명령어로 손쉽게 관리하는 방법을 익힌 것입니다.
이제 우리는 복잡한 애플리케이션도 Docker를 이용해 쉽고 반복 가능하게 배포할 수 있는 능력을 갖추게 되었습니다.

🖐️
하지만 Docker Compose는 여전히 한 대의 컴퓨터(단일 호스트)에서 동작한다는 한계가 있습니다.
만약 우리 서비스가 엄청난 인기를 끌어 수십, 수백 대의 서버로 확장해야 한다면 어떻게 해야 할까요?
이러한 대규모 컨테이너 환경을 관리하기 위한 기술이 바로 다음 시간에 배울
쿠버네티스(Kubernetes)
입니다.

참고 🔗