[Docker]11. 도커 컴포즈[도커 기초 | Week 7]
DevOps/Docker

[Docker]11. 도커 컴포즈[도커 기초 | Week 7]

목차

Docker Compose

  - Docker Compose 설치 및 개요

    - Docker Compose 설치

    - docker-compose.yml

    - 실습

  - Docker Compose 명령어

  - docker-compose.yml 작성

    - springboot-mariadb 실습


Docker Compose

"Docker Compose"라는 새로운 용어가 등장했습니다. Compose는 구성하다 라는 뜻을 가진 영어 단어인데, 갑자기 도커와 함께 어울리게 된 이유는 무엇일까요? 이 수수께끼를 풀기 위해서 간단한 그림을 보여 드리겠습니다.

 

 

Python 기반의 웹 프레임워크 django, 관계형 DBMS Postgresql, Postgresql 모니터링 pgAdmin 이 세 가지를 모두 한꺼번에 컨테이너로 구성하려고 합니다. 각각을 컨테이너로 구동해야 되니까... 이미지를 받아오고 docker run 을 실행하고...다시 또 실행하고...포트는 어떻게 연결하지? 세팅하려는 모든 컨테이너를 각각 구성하려니 명령어를 어떻게 실행해야 할지, 필요한 계정은 어떻게 설정해야 할지 하나도 감이 잡히지 않습니다.

 

그래서 위와 같은 멀티 컨테이너를 구동하기 위한 구원투수가 나타났으니 그 이름이 바로 Docker Compose 입니다. Docker Compose 는 도커 컴포넌트 중의 하나로서, 여러 개의 컨테이너를 정의하고 실행하는 역할을 합니다. 기존에 학습했던 이미지 빌드용 파일인 Dockerfile 과 더불어 docker-compose.yml 이라는 새로운 설정 파일이 등장하는 데 사용법이 간결하고 직관적이기 때문에 크게 부담 갖지 않으셔도 됩니다. 오히려 이렇게 쉽게 컨테이너를 제어할 수 있다는 것에 감탄하게 됩니다.

 

Docker Compose 설치 및 개요

Docker Compose 설치

Docker Compose 를 사용하기 위해 먼저 설치를 진행하겠습니다. 설치 과정은 매우 간단합니다.

Ubuntu 가상 머신이 아닌 Docker Desktop을 설치한 경우 이미 Docker Compose 가 내장되어 있어 별도로 설치하실 필요 없습니다.

 

1) Docker Compose 다운로드

sudo curl -L "<https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$>(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

 

2) 실행 권한 부여

sudo chmod +x /usr/local/bin/docker-compose

 

3) 버전 확인

 

 

docker-compose --version

 

docker-compose.yml

The Official YAML Web Site

*.yml 혹은 *.yaml 이라는 파일 형식을 본 적이 있으신가요? 이는 YAML Ain't Markup Language 라는 문구의 약자를 따서 만들어졌다고 합니다. 원래는 Yet Another Markup Language 이라는 이름이었으나 이후 의미가 변경되었습니다. JSON 이나 XML 같이 시스템 간 데이터 교환을 위해 만들어졌으며, key-value 구조를 기본으로 합니다. 이외 문법적 중요사항은 다음과 같습니다.

  • 대소문자를 구분합니다.
  • 구조를 구분할 때 들여 쓰기로 탭 대신 스페이스를 사용합니다.
  • 값으로는 문자열(string), 숫자(number), 불린(boolean) 을 모두 취할 수 있습니다.
  • **: 바로 뒤는 한 칸을 떼고 작성합니다.**
  • 값을 나열하기 위해서는 - 를 입력한 후 한 칸을 떼고 사용합니다.
  • 주석 표기는 # 를 사용합니다.

Docker Compose 를 이용하기 위한 설정 파일인 docker-compose.yml 의 구조는 다음과 같습니다.

설치형 클라우드인 Nextcloud 와 관계형 DB postgresql 컨테이너를 함께 구성할 수 있도록 내용을 준비하였습니다.

 

version: "3.9"
services:
  db:
    image: postgres:alpine
    environment:
      - POSTGRES_PASSWORD=nextcloud
      - POSTGRES_DB=nextcloud
      - POSTGRES_USER=nextcloud
    restart: always
    volumes:
      - db_data:/var/lib/postgresql/data


  nc:
    image: nextcloud:apache
    environment:
      - POSTGRES_HOST=db
      - POSTGRES_PASSWORD=nextcloud
      - POSTGRES_DB=nextcloud
      - POSTGRES_USER=nextcloud
    ports:
      - "80:80"
    restart: always
    volumes:
      - nc_data:/var/www/html
    depends_on:
      - db
volumes:
  nc_data:
  db_data:

 

정상적으로 컨테이너가 구동된 이후 브라우저를 통해 접속하면 다음과 같이 관리자 계정을 생성하는 창이 나옵니다. 원하시는 값을 입력해주시고 하단의 Finish setup 버튼을 누르시면 됩니다.

 

계정 생성이 완료되고 필요한 모듈의 설치가 완료되면 대시보드가 뜹니다. 이제 이 컨테이너를 여러분들만의 구글 드라이브, 네이버 클라우드처럼 사용하실 수 있습니다.

 


실습

mkdir sample

cd sample

nano docker-compose.yml

 

docker compose 파일을 작성하고 아래처럼 up 하게 되면 

아래처럼 빌드와 실행이 되어서 composed 된 컨테이너들이 실행하게 됩니다.

(동일한 디렉토리에서 up 해야 합니다)

 

sudo docker-compose up

 

 

 

 

compose-up 이 된 상태에서 서버 ip로 접속하게 되면 아래와 같이 위에서 yml로 띄어준 컨테이너에 접근 가능합니다.

 

 

compose-up 한 서버 콘솔에서는 이동할 때마다 로그가 기록되는 것을 확인할 수 있습니다.

 

 

너무 신기하네요!!

 

sudo docker ps

sudo docker image ls

sudo docker network ls

 

명령어를 통해서

 

현재 실행 중인 컨테이너와 이미지를 확인하고 

네트워크 상태도 확인했습니다.

 

 

sudo docker-compose stop 명령어로 도커 컴포즈로 올린 것을 멈출 수 있습니다.

 


Docker Compose 명령어

Docker Compose에서 사용되는 명령은 기존에 사용하던 Docker 명령어와 비슷하지만 기능적으로 다른 것들이 있어 혼동하지 않도록 주의해야 합니다.

명령어는 기본적으로 docker-compose.yml 파일이 위치한 곳의 경로에서 실행합니다.

 

1) up - 멀티 컨테이너 생성 및 실행

컨테이너의 이름은 별도로 설정하지 않으면 [docker-compose.yml 파일이 위치한 디렉토리명]_[서비스명]_[번호] 의 형태로 정의됩니다.

sudo docker-compose up
이름 기능
-d, --detach  컨테이너를 백그라운드에서 실행합니다.
—-build  컨테이너를 생성하기 전에 이미지를 빌드합니다.
—-no-build  실행 대상 이미지가 존재하지 않더라도 빌드하지 않습니다.
--abort-on-container-exit  여러 컨테이너들 중 하나라도 종료되면 모두 종료됩니다.
주의❗️—-detach 와 함께 사용할 수 없습니다.

 

2) ps - 컨테이너 조회

docker ps 혹은 docker container ls 를 사용해도 실행 중인 컨테이너를 조회할 수 있습니다. 다만, docker-compose ps를 통해 조회했을 때의 출력 양식에서 약간 차이가 납니다.

sudo docker-compose ps
이름 기능
-q, —-quiet  컨테이너 ID만 출력합니다.
—-services  정의된 서비스 명을 출력합니다.
-a, —-all  종료된 컨테이너를 포함하여 모든 컨테이너를 출력합니다.

 

3) run - 컨테이너 내부에서 명령 실행

명령어를 사용할 때 주의할 점은 run 명령어 실행의 인수를 컨테이너명이 아닌 docker-compose.yml 에 정의된 서비스명으로 입력해야 하는 것입니다.

sudo docker-compose run [서비스명] [실행 대상 명령]
sudo docker-compose run db bash

 

4) start - 생성되어 있는 컨테이너 실행

sudo docker-compose start

 

5) stop - 생성되어 있는 컨테이너 종료

sudo docker-compose stop

 

6) down - 컨테이너 종료 및 삭제

sudo docker-compose down

 


docker-compose.yml 작성

1) version

version: "3.9"

 

Docker Compose 파일은 최상단에 버전을 정의하도록 되어있습니다. 각 버전별로 명령어 혹은 표기법이 다르기 때문에 정상적으로 작동하지 않는 경우 버전을 체크해보아야 합니다.

https://docs.docker.com/compose/compose-file/compose-file-v3/

 

Compose file version 3 reference

 

docs.docker.com

 

2) services

services:
  db: 
    ...
  nc: 
    ...

서비스는 Compose에서 실행할 컨테이너라고 생각하시면 이해가 빠릅니다. 각 서비스 별로 그에 맞는 환경의 컨테이너를 구성하기 위해 내부에 다양한 옵션을 추가합니다. 혼동하지 말아야 하는 것은 서비스명과 컨테이너명은 다른 개념이라는 것입니다.

 

만약 원하는 컨테이너명을 설정하고 싶다면 각 서비스 하위에 container_name 키와 설정하려는 값을 추가하면 됩니다.

 

3) image

services:
  db:
    image: postgres:alpine
    ...
  nc:
    image: nextcloud:apache
    ...

 

이미지는 말 그대로 컨테이너로 실행할 대상 이미지를 설정하는 것입니다. Dockerfile에서 FROM을 통해 베이스 이미지를 설정했던 것 기억나시나요? 그 과정이 image 라는 키의 값을 작성하는 것으로 바뀌었다고 보시면 됩니다.

 

4) build

services:
  db:
    ...
  nc:
    build:
      context: .
      dockerfile: Dockerfile
    ...

이미 로컬에 이미지가 있거나 Docker Hub에 이미지가 있다면 이미지명과 태그만으로 쉽게 내려받아 컨테이너를 구성할 수 있습니다. 하지만 일반적으로 작성한 Dockerfile 에서 빌드된 이미지를 기반으로 컨테이너를 실행하죠.

 

Compose 에서는 실행할 이미지명 대신에 빌드할 Dockerfile 의 정보를 입력하여 이를 빌드하고 바로 이미지로 사용할 수 있습니다. dockerfile 옵션을 사용하면 파일의 이름이 Dockerfile 이 아닌 것도 빌드 대상으로 지정할 수 있으며 경로 역시 동일 경로가 아닌 다른 경로를 지정할 수 있습니다.

 

5) command

services:
  db:
     ...
  nc:
    build:
      context: .
      dockerfile: Dockerfile
    command: java -jar app.jar

생성된 컨테이너에 어떤 명령을 내릴지 세팅합니다. 보통 컴파일러나 특정 언어로 작성된 애플리케이션을 명령어로 실행해야 하는 경우에 사용됩니다.

 

6) ports

services:
  db:
    ...
  nc:
    ports: "80:80"
    ...

포트 포워딩을 설정하는 항목으로 docker run -p 80:80 와 동일한 기능을 한다고 보시면 됩니다. 다만, yaml 파일에서는 XX:YY의 형식이 시간 값으로 해석될 수 있기 때문에 안전하게 따옴표 처리를 하시는 것을 권장합니다.

 

7) depends_on

services:
  db:
    ...
  nc:
    depends_on: 
      - db
    ...

특정 서비스가 먼저 시작되면 이어서 시작할 수 있도록 설정하는 명령어입니다. 위 예제 소스를 보면 nc라는 서비스에 db depends_on 으로 걸려있는데, 이는 db 서비스가 시작되면 nc 서비스가 시작되도록 순서를 정하는 것입니다. 다만, db 가 완전히 초기화 되어 리스닝 상태까지 도달했는지는 확인하지 않습니다. 단순히 시작이 되었느냐, 아니냐 만을 가지고 서비스를 시작합니다.

 

8) environment

services:
  db:
    ...
  nc:
    environment:
      - POSTGRES_HOST=db
      - POSTGRES_PASSWORD=nextcloud
      - POSTGRES_DB=nextcloud
      - POSTGRES_USER=nextcloud
    ...

환경변수를 설정하는 항목이며, DB 계정 및 초기 DB 세팅 등에 주로 사용됩니다. 이외 필요에 따라 각 컨테이너별 환경변수를 할당할 수 있습니다. docker run -e 와 유사한 기능이라고 볼 수 있습니다.

 

9) volumes

services:
  db:
    volumes:
      - db_data:/var/lib/postgresql/data 
    ...
  nc:
    volumes:
      - nc_data:/var/www/html
    ...
 volumes:
   nc_data:
   db_data:
 ...

볼륨을 세팅하는 항목이며, docker run -v 와 같은 역할을 합니다. 컨테이너가 삭제되어도 데이터 유실이 되지 않도록 호스트의 일부 영역을 할당하게 되며, [볼륨명]:[할당할 호스트 경로] 를 작성하면 됩니다. 더불어 services 와 같은 위계에 volumes 를 작성하고 그 하위에 서비스에 설정한 볼륨명을 작성해줍니다.

 

10) restart

services:
  db:
    ...
  nc:
    ...
    restart: always
    ...

서비스 재시작을 설정할 수 있습니다. 기본값은 재시작을 하지 않는 것이지만 웹서비스의 경우 재시작을 always로 설정하는 경우가 많습니다. 이는 db 가 완전히 리스닝이 가능한 상태가 되기 전에 웹 서비스에서 Connection을 생성하려는 경우 에러가 발생해 서비스가 비정상 종료가 되기 때문입니다. 재시작을 하다가 db 가 Connection 생성이 가능한 상태가 되면 웹 서비스가 정상적으로 실행됩니다.

 

11) expose

services:
  db:
    ...
    expose:
      - 5432
    ...
  nc:
    ...

Dockerfile 에도 EXPOSE 라는 명령어가 있었죠. 실제로 포트 포워딩을 수행하지 않고 어떤 포트를 개방해야 하는지 명시하는 역할을 했었습니다. docker-compose.yml 에서는 조금 다른 역할을 하기 때문에 주의해야 합니다.

 

expose 에서 지정된 포트를 통해 통신을 가능케 하는 것은 맞습니다. 하지만, 호스트 OS에서의 접근은 불가능하고 연결된 타 서비스(컨테이너)와의 통신만 가능합니다. Compose에서 연결된 서비스라는 것은 docker-compose.yml 에 작성된 services 하위의 항목을 가리킵니다. 이들은 동일한 네트워크 대역에 위치하므로 기본적으로 통신이 가능한 상태입니다.

 


 Spring Boot + MariaDB 실습

 

DB mariadb

 

container_name 으로 컨테이너명을 custom 할 수 있습니다. 

 

image 로는 mariadb, latest 버전으로 사용했습니다.

 

ports 는 " " 안에 써줘야 합니다. yml파일을 parsing 할 때 시간으로도 해석하는 경우도 있다고 합니다.

(db ports로 3306을 사용)

 

environment, 환경 변수는 docker hub에서 사용하고자 하는 이미지를 검색해보면 알 수 있습니다.

여기서는 MARIADB_ROOT_PASSWORD: root 마리아 db 패스워드를 root로 설정했고 MARIADB_DATABASE(마리아 db이름): testdb 로 설정했습니다.

 

https://hub.docker.com/_/mariadb

 

Mariadb - Official Image | Docker Hub

We and third parties use cookies or similar technologies ("Cookies") as described below to collect and process personal data, such as your IP address or browser information. You can learn more about how this site uses Cookies by reading our privacy policy

hub.docker.com

 

restart: always 항상 컨테이너가 재시작되게 설정해줍니다.

 

 

 

springboot 작성

 

sampleapp 이라는 이름으로 지어주고

 

container_name은 springboot

 

build 라는 명령어를 통해 yml파일이 현재 위치한 경로에 있는 Dockerfile로 바로 build후 컨테이너를 구성합니다.

그래서 build: . 명령어를 작성합니다.

주의할 점은 반드시 Dockerfile 위치를 확인해야 합니다.

 

environment, 환경변수로는 mariadb에 접속해야 하기 때문에 아래와 같이 설정해줍니다.

MARIADB_ADDRESS: mariadb (접속할 db주소로 mariadb 설정), MARIADB_USERNAME: root, MARIADB_PASSWORD: root (root password는 위와 같이 설정해 놓았음) 

 

ports는 8080으로 열어줍니다. springboot 포트로 자주 쓰입니다.

 

종종 mariadb server가 올라가기 전에 springboot가 먼저 동작하게 될 때가 있는데, 그럴 때는 down 하게 됩니다. 그것을 방지하기 위해 항상 동작할 수 있도록 restart 설정을 always로 해줍니다.

 

depends_on설정은 mariadb, 바로 위 컨테이너가 시작되고 나서 sampleapp이 실행되도록 합니다.

* 여기서 순서는 컨테이너로 올라가는 순서를 의미하며, depends_on이 컨테이너가 내부적으로 동작하고 나서 그다음 컨테이너를 올리는 것이 아님, 즉, 컨테이너로 올리는 순서를 의미한다.

(mariadb 서버가 사용 가능하기 전에도 sampleapp이 올라갈 수 있음)

 

잘 작성된 모습

yml 파일 작성 후

sudo docker-compose up 명령어로 올려봅시다

 

 

잘 구동되네요!! 직접 브라우저로 확인해봅시다.

 

 

 

 

지금 buildspringboot소스는 Api를 포함합니다.

 

총 5개의 기능을 제공합니다.

 

post를 해야 data가 들어가기 때문에 직접 post를 해야 get으로 post들을 확인할 수 있습니다.

 

json data로 작성 후 밑의 excute를 눌러 실행해보면

아래처럼 server에서 db에 insert 되는 것을 확인할 수 있습니다.

get으로 확인해보면 아래처럼 가져와서 볼 수 있습니다.

 

직접 내부 서버에 data가 어떻게 들어갔는지 확인을 해보고 싶어서 아래와 같이 확인했습니다.

 

 

sudo docker exec -it mariadb bash 해당 도커 컨테이너에 bash로 접근합니다.

mysql -u root -p 아까 입력했던 비밀번호로 들어갑니다.

 

show databases; 현재 db확인

use [db명] 그 db로 들어가기

show tables; 들어간 db의 현재 table를 확인해줍니다.

 

아까 위에서 입력한 값이 정확하게 들어가 있는 것을 확인할 수 있었습니다.

 

 

이처럼 도커 컴포즈를 사용하여 api서버를 비교적 쉽게 구동시킬 수 있어, 생산성 향상에 많은 기여를 했습니다.

 

나중에 프로젝트할 때 꼭 도커 컴포즈를 사용해서 구동시켜 봐야겠습니다!!