포스트

Docker와 컨테이너 관리 능력

Docker Compose 개념, 다중 컨테이너 통합 설정 파일 실행 및 관리 방법

For the English version of this post, see here.
Docker와 컨테이너 관리 능력

오늘 학습한 개념

  • Docker: 애플리케이션 실행 환경을 컨테이너로 묶어 관리하는 도구

  • 컨테이너: 실행 중인 애플리케이션 단위

  • 이미지: 컨테이너를 만들기 위한 설계도

  • Docker Compose: 여러 컨테이너를 하나의 설정 파일로 함께 실행하고 관리할 수 있게 해줌


Docker는 애플리케이션이 실행되는 환경을 하나의 독립된 공간으로 묶어주는 도구이다.

로컬 환경에서는 운영체제, Java 버전, DB 설정, 환경 변수 등이 사람마다 다를 수 있는데, Docker를 사용하면 실행 환경을 이미지로 정의하고 컨테이너로 실행할 수 있기 때문에 환경 차이로 인한 문제를 줄일 수 있다.

특히 백엔드 애플리케이션을 개발할 떄는 애플리케이션 서버 하나만 실행하는 경우보다 DB, Redis, 메시지 큐, 모니터링 도구 등 여러 구성 요소를 함께 실행해야 하는 경우가 많다. 이때 각각을 직접 docker run 명령어로 실행하면 포트, 환경 변수, 네트워크 설정을 일일이 기억해야 해서 관리가 복잡해진다.

Docker Compose는 이런 문제를 해결하기 위해 여러 컨테이너를 하나의 설정 파일로 정의하고 한 번에 실행할 수 있게 해준다.

이미지와 컨테이너의 차이

오늘 문제를 풀면서 가장 기본적으로 다시 확인한 개념은 이미지와 컨테이너의 차이였다.

이미지는 컨테이너를 만들기 위한 실행 템플릿 또는 설계도에 가깝다. 애플리케이션 코드, 실행에 필요한 라이브러리, 런타임 환경 등이 포함될 수 있다.

반면 컨테이너는 이 이미지를 바탕으로 실제 실행된 인스턴스이다.

비유하자면 이미지는 붕어빵 틀이고, 컨테이너는 그 틀로 찍어낸 붕어빵이라고 볼 수 있다. 같은 이미지로 여러 개의 컨테이너를 만들 수 있고, 각각의 컨테이너는 독립적으로 실행된다. 따라서 같은 애플리케이션 이미지를 사용하더라도 포트나 환경 변수를 다르게 지정하면 서로 다른 설정으로 컨테이너를 실행할 수 있다.

이 개념은 문제 풀이에서도 중요했다. 컨테이너를 삭제한다고 해서 이미지가 삭제되는 것은 아니며, 이미지는 남아 있기 때문에 같은 이미지로 다시 컨테이너를 생성할 수 있다. 즉, 컨테이너는 실행 단위이고 이미지는 실행을 위한 원본이라는 점을 구분해야 한다.

Docker compose를 사용하는 이유

Docker compose는 여러 컨테이너를 하나의 애플리케이션 묶음처럼 관리하기 위해 사용한다.

예를 들면 서비스 A와 서비스 B가 있고, 서비스 A가 서비스 B를 호출해야 하는 구조라면 두 컨테이너를 각각 실행하는 것만으로는 부족하다. 두 컨테이너가 같은 네트워크 안에 있어야 하고, 서로를 어떤 이름으로 호출할지도 정해야 한다.

docker run 명령어를 직접 사용할 수도 있지만, 컨테이너가 많아질수록 명령어가 길어지고 실수하기 쉬워진다. 포트 매핑, 환경 변수, 네트워크 연결, 실행 순서 등을 매번 직접 입력해야 하기 때문이다. Docker Compose를 사용하면 이런 설정을 YAML 파일 하나에 정리할 수 있고, docker compose up 명령어 하나로 여러 컨테이너를 실행할 수 있다.

이 점에서 Docker Compose는 단순히 편한 실행 도구가 아니라, 개발 환경을 문서화하고 재현 가능하게 만드는 도구라고 느꼈다. 팀원들이 같이 Compose 파일을 사용하면 동일한 조건으로 컨테이너를 실행할 수 있기 때문엥 협업에서도 장점이 크다.

포트 매핑의 의미

문제 풀이에서 헷갈릴 수 있는 부분 중 하나가 포트 매핑이었다.

컨테이너 내부에서 애플리케이션이 8080 포트로 실행되고 있다고 해서, 내 로컬 브라우저에서 무조건 localhost:8080으로 접근할 수 있는 것은 아니다. 외부에서 접근하려면 호스트 포트와 컨테이너 포트를 연결해줘야 한다.

예를 들어 18080:8080이라는 설정은 내 컴퓨터의 18080 포트로 들어온 요청을 컨테이너 내부의 8080 포트로 전달한다는 의미이다. 여기서 앞쪽은 호스트 포트, 뒤쪽은 컨테이너 포트이다.

이 개념을 이해하니 여러 컨테이너가 모두 내부적으로는 8080포트를 사용하더라도, 호스트 포트를 다르게 지정하면 동시에 실행할 수 있다는 점을 이해할 수 있었다. 반대로 같은 호스트 포트를 여러 컨테이너가 동시에 사용하려고 하면 포트 충돌이 발생한다.

환경 변수 설정

Docker Compose에서는 environment항목을 통해 컨테이너 내부로 환경 변수를 전달할 수 있다.

환경 변수는 애플리케이션이 실행될 때 필요한 설정값을 외부에서 주입하는 방식이다. 예를 들어 다른 서비스의 주소, DB 접속 정보, 프로파일 설정 등을 환경 변수로 전달할 수 있다.

오늘 학습한 내용에서는 서비스 A가 서비스 B를 호출해야 하는 상황에서 서비스 B의 주소를 환경 변수로 넘기는 흐름이 중요했다. 이렇게 하면 애플리케이션 코드 안에 주소를 고정하지 않고, 실행 환경에 따라 다른 값을 주입할 수 있다.

이 방식은 실제 운영 환경에서도 중요하다고 느꼈다. 개발 환경, 테스트 환경, 운영 환경은 접속해야 하는 DB 주소나 외부 API 주소가 다를 수 있다. 이런 값을 코드에 직접 박아두면 환경이 바뀔 때마다 코드를 수정해야 하지만, 환경 변수로 분리하면 설정만 바꿔서 실행할 수 있다.

Docker Compose 네트워크와 서비스 이름 통신

오늘 가장 중요하게 이해한 개념 중 하나는 Docker Compose의 네트워크였다.

Docker Compose를 실행하면 기본적으로 Compose 파일이 있는 디렉토리 기준으로 브리지 네트워크가 생성되고, Compose에 정의된 서비스들이 그 네트워크 안에 함께 들어간다.

같은 Docker 네트워크 안에 있는 컨테이너들은 서로를 컨테이너 이름 또는 서비스 이름으로 호출할 수 있다. 예를 들어 서비스 A가 서비스 B를 호출할 때 IP 주소를 직접 몰라도 service-b와 같은 서비스 이름을 사용해 접근할 수 있다.

이 부분은 MSA 구조와도 연결된다. 마이크로서비스 환경에서는 여러 서비스가 독립적으로 실행되지만, 결국 서로 통신해야 한다. Docker Compose는 로컬 개발 환경에서 이런 서비스 간 통신 구조를 비교적 쉽게 구성할 수 있게 해준다.

만약 컨테이너 이름으로 호출이 되지 않는다면, 같은 네트워크에 속해 있는지 확인해야 한다. 직접 docker run으로 실행한 경우에는 네트워크 옵션을 빠뜨릴 수 있기 때문에, 이런 상황에서는 네트워크 설정을 먼저 의심해봐야 한다.

depends_on의 역할

Docker Compose에서 depends_on은 컨테이너 간 실행 순서를 지정하는 옵션이다.

예를 들어 애플리케이션 컨테이너가 DB 컨테이너에 의존한다면, DB 컨테이너가 먼저 생성된 뒤 애플리케이션 컨테이너가 실행되도록 설정할 수 있다.

다만 오늘 정리하면서 주의해야 할 점도 알게 되었다. depends_on은 컨테이너의 생성 순서를 보장하지만, 해당 서비스가 완전히 준비되었는지까지 보장하는 것은 아니다. 예를 들어 DB 컨테이너가 먼저 실행되었다고 해도 DB가 실제로 연결 가능한 상태가 되기까지는 시간이 걸릴 수 있다. 그래서 실제 환경에서는 헬스 체크나 재시도 로직 같은 추가적인 처리가 필요할 수 있다.

이 개념은 문제를 풀 때 단순히 ‘먼저 실행된다’ 정도로만 이해하면 부족하고, ‘생성 순서와 준비 완료는 다르다’는 점까지 구분해야 한다고 느꼈다.

docker compose up과 down

Docker Compose에서 가장 기본이 되는 명령어는 updown이다.

docker compose up은 Compose 파일에 정의된 서비스들을 실행한다. 여기에 -d 옵션을 붙이면 백그라운드에서 실행된다. 백그라운드 실행을 하면 터미널을 계속 점유하지 않기 때문에 개발 중에는 더 편리하게 사용할 수 있다.

반대로 docker compose down은 Compose로 실행한 컨테이너들을 정리한다. 여러 컨테이너를 한 번에 중지하고 제거할 수 있기 때문에, 직접 컨테이너 ID를 찾아서 하나씩 삭제하는 것보다 훨씬 편하다.

오늘 문제를 풀면서 느낀 점은 Docker Compose를 사용하면 단순히 컨테이너 실행만 편해지는 것이 아니라, 실행과 정리의 단위가 명확해진다는 것이다. 여러 컨테이너를 하나의 프로젝트 단위로 관리할 수 있기 때문에 복잡한 개발 환경에서도 실수를 줄일 수 있다.


느낀 점

오늘 학습 전에는 Docker가 단순히 “프로그램을 컨테이너로 실행하는 도구” 정도로 느껴졌다. 하지만 Docker Compose까지 함께 학습하면서 Docker가 개발 환경을 재현하고 여러 서비스를 안정적으로 관리하기 위한 도구라는 점을 더 명확히 이해하게 되었다.

특히 MSA처럼 여러 서비스가 나뉘어 있는 구조에서는 서비스 간 통신, 네트워크, 환경 변수 관리가 중요하다. Docker Compose는 이런 요소들을 하나의 설정 파일로 관리할 수 있게 해주기 때문에 로컬에서 MSA 구조를 연습하거나 팀 프로젝트 환경을 맞출 때 유용하다고 느꼈다.

또한 문제를 풀면서 이미지와 컨테이너, 호스트 포트와 컨테이너 포트, 컨테이너 생성 순서와 서비스 준비 완료처럼 비슷해 보이지만 정확히 구분해야 하는 개념들이 많다는 것을 알게 되었다. 앞으로 Docker를 사용할 때 단순히 명령어를 외우기보다, 각 설정이 어떤 문제를 해결하기 위한 것인지 생각하면서 사용해야겠다고 느꼈다.

댓글

궁금한 점, 피드백, 오류 제보를 남겨 주세요.