[Docker] compose 설치, 사용 방법 및 장점

도커는 모든 종속성을 하나의 컨테이너로 만들어 애플리케이션을 격리된 환경에서 실행하게 해줍니다. 하지만 일반적으로 애플리케이션을 지원하기 위해서는 데이터베이스와 로드밸런싱과 같은 여러 서비스가 필요합니다.  여러 서비스를 설정하는 데 도움을 주는게 Docker compose입니다.

docker compose란

YAML 기반의 다중 컨테이너 애플리케이션을 실행합니다. YAML 파일은 컨테이너를 배포하는 데 필요한 모든 구성을 포함하고 있습니다. docker compose는 docker swarm과 통합되어 컨테이너를 빌드하고 배포할 수 있습니다. docker compose를 사용하면 각 컨테이너는 단일 호스트에서 실행되도록 구성됩니다.

docker compose 설치

docker compose는 docker engine에 의존합니다. 설정에 따라 docker engine이 로컬 또는 원격으로 설치되어 있는지 확인해야 합니다. Docker for Mac 및 Windows와 같은 데스크탑 시스템에는 docker compose가 사전 설치되어 있습니다.

Linux 시스템에서 docker compose를 설치하기 전에 docker를 먼저 설치해야 합니다.

docker compose 설치 단계

1. docker compose의 최신 버전을 설치하고 패키지 관리자를 업데이트합니다.

apt-get update

sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

apt-get update

apt-get install docker-ce docker-ce-cli containerd.io

apt-get install docker-compose-plugin

Ubuntu OS에서는 apt-get이고 Linux OS에서는 yum이 됩니다.

2.docker compose가 성공적으로 설치되었는지 버전을 확인합니다.

docker-compose version

docker 컨테이너

애플리케이션의 모든 라이브러리와 종속성을 패키징한 경량 리눅스 기반 시스템으로 미리 빌드되어 실행 준비가 되어있습니다. 많은 조직들이 VM에서 컨테이너로 이동하고 있고, 컨테이너는 가볍고 사용 및 유지 관리가 간단합니다. 실제 애플리케이션에서는 많은 수의 컨테이너들이 있습니다. 인증, 로그인, 데이터베이스, 결제등의 서비스를 위한 각각의 컨테이너가 있습니다. 컨테이너는 단일 목적만을 가지는 것이 선호됩니다. 각각의 컨테이너를 위해 별도의 dockerfile을 작성하고 구성 및 네트워크를 관리하는 docker compose를 가지고 쉽게 관리합니다.

docker compose의 장점

각 서비스에 대해 별도의 컨테이너를 가지는데 각 컨테이너는 dockerfile를 필요로 합니다. 서비스가 수백 개 있으면 dockerfile도 수백 개 필요하여 개별적으로 관리하기가 힘듭니다. docker compose를 통해서 다중 컨테이너 애플리케이션을 정의하고 실행할 수 있습니다. docker hub에서 미리 빌드된 이미지를 사용하는 대신 사용자 정의 이미지를 사용하는 경우 해당 이미지를 별도의 dockerfile에서 정의해야 합니다.

docker compose 기능

  • 모든 서비스는 단일 호스트에서 격리된 상태로 실행
  • 변경 사항이 있을 때만 컨테이너 재생성
  • 볼륨 데이터는 새 컨테이너를 생성할 때 리셋되지 않고 보존
  • 환경 내에서 변수와 구성을 이동
  • 환경 간의 상호작용 쉽게 하기 위해 가상 네트워크를 생성

docker compose 사용 방법(예제)

간단한 프로젝트를 생성하여 사용 방법에 도움이 되도록 하겠습니다. 과일 목록을 반환하는 RESTful API를 생성하겠습니다. java spring boot를 사용하겠습니다. reat로 해당 서비스를 요청하여 브라우저에 표시하도록 합니다. 두 서비스는 각각의 컨테이너에서 실행됩니다.

먼저 전체 프로젝트를 위한 별도의 디렉토리를 만들겠습니다.

mkdir fruitListProject

그리고 디렉토리 안으로 이동합니다.

cd fruitListProject

product 서버를 위한 디렉토리를 만듭니다.

mkdir product

그리고 spring initializr 사이트를 이용하여 api 서버를 구성합니다.

과일 리스트를 반환하는 api를 만듭니다.

package com.example.demo;

import java.util.Arrays;
import java.util.List;

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@CrossOrigin("*")
public class FruistController {
	@GetMapping("/fruits")
	public List<String> getFruits() {
		return Arrays.asList("Mango","Apple","Orange","Banana");
	}
}

그리고 application.properties에서 server.port=8080를 추가합니다.

그리고 gradlew build를 합니다.

./gradlew build

그러면 build/blis에 snapshot이 생깁니다. 해당 snapshot을 이용하여 도커 서버를 구성합니다.

product 폴더에서  그런 다음 Dockerfile을 사용하여 서비스를 추가로 구성합니다.

FROM openjdk:17-jdk-slim
VOLUME /tmp
COPY build/libs/demo-0.0.1-SNAPSHOT.jar product.jar
ENTRYPOINT ["java", "-jar", "/product.jar"]

FROM은 도커가 hub에서 다운로드할 이미지 이름과 버전을 지정합니다.

VOLUME은 지정된 경로로 마운트 지점이 생성됩니다.

COPY는 현재 로컬에 있는 파일을 도커 안에 위치로 복사합니다.

ENTRYPOINT 도커 컨테이너의 진입점을 설정합니다. 컨테이너가 시작될 때 해당 명령을 실행하도록 지시합니다.

 

다시 fruitListProject안에서 프론트를 위한 react를 만듭니다.

npx create-react-app website

website에 들어가서 app.js를 수정합니다.

import './App.css';
import { useEffect, useState } from 'react';

function App() {
  const [fruits, setFruits] = useState([])

  useEffect(() => {
    fetch('http://localhost:8080/fruits')
      .then(r => r.json())
      .then(d => setFruits(d))
  }, [])

  return (
    <div className="App">
      <h1>Fruit list</h1>
      <ul>
        {fruits.map((e,i) => (
          <li key={i}>{e}</li>
        ))}
      </ul>
    </div>
  );
}

export default App;

그리고 website 폴더에서 Dockerfile생성합니다.

FROM node:16

WORKDIR /usr/src/app

COPY package*.json ./
RUN npm install

COPY . .

EXPOSE 3000
CMD ["npm","start"]

FROM은 도커 컨테이너에 사용할 기본 이미지를 지정합니다.

WORKDIR은 컨테이너 내부의 작업 디렉터리를 설정합니다. 이 후 명령을 이 디렉터리에서 실행됩니다.

json 파일을 COPY한 후 npm install 명령을 실행합니다. 그리고 현재 폴더를 복사합니다. .dockerignore에서 설정한 파일들은 제외합니다.

EXPOSE는 도커에게 컨테이너가 런타임 시 포트 3000에서 수신 대기한다고 알려줍니다.

CMD는 컨테이너가 실행될 때 실행할 명령을 설정합니다.

 

.dockerignore를 생성합니다.

node_modules

다시 fruitListProject안에서 docker-compose.yml를 생성합니다.

version: "3"

services:
    fruit-service:
        build: ./product
        ports:
            - "8080:8080"
    website:
        build: ./website
        ports:
            - "3000:3000"
        depends_on:
            - fruit-service

build: ./product fruit-service 이미지를 빌드하는데 사용되는 Dockerfile이 포함되어야 합니다.

ports를 통해서 호스트의 포트와 컨테이너 포트를 매핑합니다.

depends_on를 통해서 website 서비스 이전에 fruit-service가 시작되었는지 확인합니다. 

 

입력 후 다음과 같은 명령어를 실행합니다.

docker-compose up --build

--build를 사용하여 컨테이너를 시작하기 전에 서비스에 대한 이미지를 다시 빌드합니다. 그러면 이미지가 최신 변경 사항으로 재구축되고 up 명령과 같이 사용하여 서비스가 최신 버전의 코드로 시작되는지 확인합니다.

 

그리고 localhost:3000으로 들어가면 다음과 같이 과일 목록을 볼 수 있습니다.

중지하려면 다음과 같은 명령어를 입력하세요

docker-compose stop