본문으로 건너뛰기

© 2026 Molayo

HN분석2026. 05. 06. 21:17

2026 년에 평범한 Docker Compose 를 프로덕션에서 실행해야 하나요?

요약

본 기사는 2026년에도 Docker Compose가 프로덕션 워크로드를 실행하는 데 여전히 유효하지만, 운영상의 간극을 메우기 위한 노력이 필수적임을 강조합니다. 필자는 Docker Compose의 구조적 단순성(단일 노드 배포)이 장점인 동시에, 오래된 컨테이너 제거, 디스크 공간 관리, 로그 제한 등 운영자가 수동으로 처리해야 하는 여러 '특이점'을 야기한다고 지적합니다. 따라서 단순히 `docker compose up`만 실행하는 것을 넘어, `--remove-orphans`, 볼륨 정리(`docker volume prune`), 그리고 Docker 데몬 레벨에서 로그 크기를 제한하는 등의 적극적인 운영 관리가 필요하며, 이러한 간극을 메우는 것이 프로덕션 환경의 핵심 과제입니다.

핵심 포인트

  • Docker Compose는 구조적 단순성 덕분에 단일 노드 배포(엣지 박스 등)에 여전히 적합합니다.
  • Compose 사용 시 가장 흔한 문제는 오래된 컨테이너, 디스크 공간 부족, 로그 누적으로 인한 운영상의 간극에서 발생합니다.
  • 오래된 리소스 관리를 위해 `docker compose up` 실행 시 `--remove-orphans` 플래그를 사용하는 것이 권장됩니다.
  • 디스크 용량 문제를 해결하기 위해 주기적인 볼륨 정리(`docker volume prune`)와 이미지/컨테이너 감사 명령어를 숙지해야 합니다.
  • 로그 누적 방지를 위해 `/etc/docker/daemon.json`에 로그 회전 정책을 설정하여 데몬 레벨에서 제한하는 것이 중요합니다.

저는 Distr 에서 일하는 엔지니어인 필립입니다. 저희는 소프트웨어 및 AI 기업들이 자체 관리 환경으로 애플리케이션을 배포하도록 돕습니다.

저희 오픈소스 소프트웨어 배포 플랫폼은 GitHub(github.com/distr-sh/distr)에 제공되며, 고객 호스트에서 매일 Docker Compose 와 Docker Swarm 배포를 오케스트레이션합니다.

Docker Compose 호스트에서 본 대부분의 프로덕션 사고는 동일한 몇 가지 특이점 (quirks) 에서 비롯됩니다: 제거해야 할 오래된 컨테이너, 밤새 채워진 디스크, 문제를 감지한 후 아무런 조치도 취하지 않은 건강 체크, 새로운 곳으로 지시하는 :latest 태그, 또는 누구도 두 번 생각하지 않은 소켓 마운트. 이 중 어느 것도 Docker 의 버그가 아닙니다. 이는 dotCloud 라는 PaaS 회사에서 내부 도구로 시작하여 "내 머신에서는 작동한다"를 해결하기 위해 LXC 를 감싸고, 이제 많은 실제 비즈니스의 백엔드를 실행하는 도구의 의도적인 트레이드 오프입니다. 이 게시물은 각 명령어와 운영적 답변을 포함한 반복되는 사례들을 모았습니다.

간단한 답변: 네—2026 년에도 평범한 Docker Compose 는 실제 프로덕션 워크로드를 실행할 수 있지만, 스스로 남긴 운영적 간극을 처리해야만 합니다.

프로덕션에서 평범한 Docker Compose 의 위치

특이점 목록 전에 청중에 대한 짧은 말입니다. Docker Compose 는 멀티 컨테이너 애플리케이션을 연결하는 선언적인 방법입니다: 하나의 YAML 파일은 서비스, 그들 사이의 네트워크, 공유 볼륨, 필요한 환경 변수, 그리고—서비스 구성을 덮어쓰거나 패치하는 패턴을 통해—애플리케이션이 기대하는 디스크 구성을 설명합니다. docker compose up 호스트를 해당 파일과 일치시킵니다. 프로덕션의 최선점은 바로 그 것을 기반으로 한 단일 노드 배포입니다: 벤더가 멀티 컨테이너 애플리케이션을 고객 환경에 푸시하고, 긴 꼬리 서비스를 실행하는 내부 팀이 클러스터를 정당화하지 않으며, 소매점 위치의 엣지 박스입니다. 발자국은 작고, 운영 오버헤드는 낮으며, 유능한 운영자는 하나의 docker-compose.yaml 에서 전체 스택을 추론할 수 있습니다. Compose 에서는 제어 평면이 없습니다: 호스트를 감시하는 스케줄러, 상태를 재적용하는 리컨실러, 다른 곳에서 업데이트를 푸시하는 운영자가 없습니다. docker compose up 한 번 실행하고 종료합니다.

그 구조적 단순성이 바로 특이점이 물리는 이유입니다. Compose 는—or 호스트를 실행하는 누구든—다른 것이什么都不 하는 운영 작업을 수행할 것이라고 가정하며, 고객에게 Compose 파일을 보내면 안전한 가정은 고객이 하지 않을 것입니다. 이 게시물의 나머지 부분은 Compose 가 수행하는 것과 프로덕션 호스트가 실제로 필요로 하는 것 사이의 간극을 닫는 것에 관한 것입니다: 수동으로 또는 스스로를 위해 수행하는 에이전트를 통해. 이미 간극이 너무 넓다고 결론지었다면 다음 단계와 비교하고 싶다면 저희 Docker Compose vs Kubernetes 분해기를 읽으세요.

수정 사항이 하나의 플래그입니다:

플래그는 Compose 에 다음을 알려줍니다: 이 프로젝트에 한 번 포함되었으나 현재 파일에는 없는 컨테이너는 제거해야 합니다. 프로젝트용 네트워크는 각 up
에서 동일하게 재조정되므로, 유실된 네트워크도 사라집니다. 볼륨은 예외입니다—Compose 는 데이터를 보호하기 위해 기본적으로 이름 지정된 볼륨을 보존하며, 제거된 서비스를 사용하던 볼륨을 제거하는 서비스별 플래그가 없습니다. 해당 공간을 되찾으려면 수동으로 수행해야 합니다: docker volume ls --filter dangling=true

로 후보를 나열하고 docker volume rm

이름으로 삭제하거나, 프로젝트의 볼륨을 대량으로 지우려는 경우 docker compose down -v

를 사용하세요. 삭제 전에 감사하려면 Docker 가 프로젝트 이름과 연결하는 모든 것을 나열하세요:

Distr 의 Docker 에이전트는 각 Compose Up

콜에서 RemoveOrphans: true

를 전달하므로, 고객 호스트는 배포 업데이트에 걸쳐 유실된 컨테이너가 누적되지 않습니다. 이 단일 플래그는 "8080 포트에서 이전 버전이 여전히 응답 중"이라는 반복되는 지원 티켓을 제거했습니다.

Docker 이미지와 컨테이너 로그 정리 및 제한

docker compose pull

은 이전 이미지를 디스크에 유지합니다. 기본 json-file

로그 드라이버를 사용하는 모든 컨테이너는 /var/lib/docker/containers/<id>/<id>-json.log

에 무제한 JSON 을 작성합니다. 바쁜 호스트에서는 이 것이 장애의 가장 일반적인 원인 중 하나입니다: 디스크가 가득 차고 Docker 는 로그, 메타데이터, 이미지 레이어 등 아무것도 쓸 수 없게 되며, 그 결과 컨테이너는 혼란스러운 방식으로 실패하기 시작합니다.

먼저 감사 명령어를 배우세요:

-v

이 명령어는 이미지, 컨테이너, 볼륨, 빌드 캐시별로 총합을 분해하여, 일반적으로 범람자를 찾는 데 충분합니다. 거기서부터 타겟팅된 정리 명령어:

docker volume prune -f

도 존재하며 실제로 유용하지만, 실행하기 전에 다음 aside 를 읽으세요.

디스크 이야기의 다른 반은 로그입니다. daemon 레벨에서 한 번에 제한하세요: /etc/docker/daemon.json

에:

systemctl restart docker

후 모든 새로운 컨테이너는 10 MB 로 로그를 회전시키고 최대 세 개의 회전 파일을 유지하며—컨테이너 당 30 MB 상한선, "디스크가 사라질 때까지"가 아닌 것입니다. 기존 컨테이너는 새 기본값을 적용하기 위해 재구축해야 합니다.

이것은 출시 전에 올바르게 해결할 가치가 있는 주제 중 하나입니다.

Distr 의 Docker 에이전트에서는 정리가 내장되어 있습니다: 각 배포 타겟에는 성공적인 업데이트 후 이전 버전의 이미지를 자동으로 제거하는 컨테이너 이미지 정리 설정이 있으며, 실패 시 재시도합니다. 성공에만 발동하므로 문제가 발생하면 이전 이미지가 디스크에 남아있고 롤백이 필요합니다.

Docker 건강 체크는 비정상 컨테이너를 다시 시작하지 않습니다

이것은 가장 많은 사람을 놀라게 합니다. Dockerfile 에 HEALTHCHECK

을 추가하거나 Compose 의 서비스 블록에 healthcheck:

블록을 추가하고, 컨테이너가 healthy

에서 unhealthy

로 넘어가는 것을 보이지만… 아무 일도 일어나지 않습니다. Docker 엔진은 상태를 보고합니다. 이를 행동하지는 않습니다. restart: unless-stopped

은 컨테이너 종료에 의해 트리거되며, 비정상 상태로 표시되는 것이 아닙니다.

Docker 가 실제로 어떻게 생각하는지 확인하세요:

상태, 실패 횟수, 마지막 몇 개의 probe 출력—you'll see the status, the streak of failures, and the last few probe outputs—useful information that is silently ignored by the engine.

엔진에 의해 침묵적으로 무시되는 유용한 정보입니다.

이것에 대한 세 가지 답변이 있습니다:

**자동 복구 사이드카 실행.**커뮤니티 표준은 willfarrell/docker-autoheal

입니다: Docker 소켓을 마운트하고 unhealthy

를 감시하는 작은 컨테이너입니다.

오류가 발생한 컨테이너를 재시작합니다. autoheal=true 라벨을 붙여 컨테이너를 선택하거나 (AUTOHEAL_CONTAINER_LABEL=all 을 설정하여 모든 것을 모니터링), Docker Swarm 에서 실행합니다. Swarm 은 기본적으로 불건강한 작업을 재시작합니다. 이미 Swarm 을 고려 중이라면, 이것이 더 좋은 이유 중 하나입니다.

Distr 를 사용하세요. 각 Distr Docker 에이전트는 적응형 autoheal 서비스를 함께 배포합니다. "모든 컨테이너를 autoheal 활성화" 토글은 배포 타겟 생성 시 기본적으로 켜져 있으므로, 고객 측의 불건강한 컨테이너 재시작은 별도의 설정 없이도 발생합니다.

어떤 경로를 선택하든 결론은 같습니다: HEALTHCHECK 를 실행하지 않는 것은 상태 표시등일 뿐, 자체 치유 시스템이 아닙니다.

Digest 를 사용하여 Docker 이미지를 고정하는 대신 :latest

Docker 태그는 변형 가능한 참조입니다. myapp:1.4 는 현재 레지스트리에 있는 것입니다; 내일은 재 푸시 후 다른 레이어를 지칭할 수 있습니다. :latest 는 가장 나쁜 사례로, 실제로는 "가장 최근에 푸시된 것"을 의미하는 경우가 많습니다. 또한 무의미한 기본값입니다: Compose 파일에서 자격이 없는 image: nginximage: nginx:latest 로 처리되므로, 심지어 Compose 파일에서도 단어 'latest' 를 입력하지 않아도 우연히 해당됩니다. 결과적으로, 프로덕션 환경에서는 동일한 태그를 5 분간 다른 호스트가 가져와서 서로 다른 코드를 실행할 수 있습니다.

해결책은 콘텐츠 주소용 디지스트로 고정하는 것입니다. 모든 이미지는 하나씩 있으며, Docker 는 태그가 들어갈 곳 어디에든 이를 허용합니다.

이미지를 이미 다운로드한 경우 디지스트를 찾기 위해:
또는, 로컬 Docker 설치에서 원격 레지스트리에 대해 다운로드하지 않은 경우:

Compose 파일에서 태그를 디지스트로 교체하세요:
디지스트에 대한 풀은 레지스트리가 해당 바이트를 더 이상 보유하지 않으면 빠르게 실패합니다. 이는 정확히 원하는 것입니다: 무의미한 드리프트가 큰 오류가 됩니다. 동일한 이미지 참조는 docker stack deploy 에서, docker run 에서, 그리고 Kubernetes 매니페스트에서 작동합니다.

고객이 출판된 이미지에서 추출할 수 있는 것 (그리고 이미지 위생이 재현성 이상으로 중요한 이유) 을 확인하기 위해, Docker 및 Kubernetes 배포에서 소스 코드와 IP 를 보호하는 가이드를 확인하세요. 여전히 레지스트리를 선택 중이라면, 컨테이너 레지스트리 비교가 트레이드 오프를 설명합니다.

/var/run/docker.sock 마운팅이 보안 리스크인 이유

/var/run/docker.sock 을 마운트한 컨테이너는 Docker API 를 호출할 수 있으며, Docker API 는 호스트의 루트 파일시스템을 마운트하는 권한 있는 컨테이너를 실행할 수 있습니다. 즉: 소켓을 가진 모든 컨테이너는 호스트에서 유효한 루트 권한을 가집니다. 이는 Docker 버그가 아니라, 소켓의 위협 모델입니다. 이 접근 권한을 부여하는 줄은 Compose 파일에 하나의 바인드 마운트이며, 이를 생각하지 않고 쉽게 추가할 수 있습니다.

실용적인 위생:
**소켓을 마운트한 컨테이너를 재고하세요.**에이전트, CI 러너, 모니터링 사이드카, 컨테이너 관리 UI — 목록을 짧고 의도적으로 유지하세요.가능한 경우 루트리스 Docker 를 실행하세요.dockerd-rootless-setuptool.sh install 는 일반 사용자로서 Docker 데몬을 설정합니다. 손상된 소켓 마운트 컨테이너의 폭발 반경은 "전체 호스트"에서 "이 사용자 계정"으로 줄어듭니다.**소켓 프록시를 고려하세요.**Tecnativa 의 docker-socket-proxy 프로젝트는 해당 컨테이너가 필요로 하는 API 의 필터링된 하위 집합을 노출합니다 (예: 읽기 전용 containersevents)

모니터링용) 대신 전체 소켓을 사용합니다.**소켓 마운팅 이미지를 최소화하세요.**표면이 작고, 라이브러리가 적으며, 진입 경로가 적습니다.

Distr Docker 에이전트는 실제로 소켓을 마운트합니다—it 는 호스트에서 Compose 와 Swarm 을 오케스트레이션하기 위해 반드시 해야 합니다. 우리는 고객 보안 팀이 설치 전에 검토할 수 있도록 Docker 에이전트 문서에 이 경계를 공개적으로 문서화했습니다. 에이전트는 Hub 에 JWT 로 인증되며, 설치 비밀은 한 번만 표시되고 저장되지 않습니다.

고객 호스트 간 Docker Compose 배포 업데이트

docker compose pull && docker compose up -d

는 호스트에 SSH 를 통해 로그인한 경우 좋은 명령어입니다. 고객 규모—방화벽 뒤에 수 개의 자체 관리 환경, 각각 고유한 변경 통제 프로세스를 가진 경우—that 이 수동 프로세스는 확장되지 않습니다. Docker 는 다른 곳에서 실행 중인 호스트로 새 매니페스트를 푸시하는 내장 메커니즘이 없습니다. Docker Hub 웹훅은 이미지가 푸시될 때 CI 재빌드를 트리거할 수 있지만, 고객의 네트워크에 도달하여 그들의 docker compose 를 호출하지는 않습니다.

일반적인 우회 방법과 비용:

**Watchtower:**레지스트리에 일정을 맞춰 조회하고, 새 이미지를 다운로드하며, 컨테이너를 재구성합니다. 설정은 쉽지만 제어하기 어렵습니다. 단계적 롤아웃이 없고, 롤백 경로가 없으며, 당신의 측면에서 제한된 가시성—you 는 고객이 티켓을 제출할 때 업데이트했음을 알게 됩니다.**Bastion + SSH + Ansible/스크립트:**10 명의 고객에는 작동합니다. 50 명에서는 무너집니다, 특히 3 명이 에어-게이핑되고 4 명이 자체 변경 통제 주기를 실행할 때입니다. 모든 운영자는 공유 키와 유지보수 창력정을 함께 살아야 합니다.**풀 기반 에이전트.**Distr 가 선택한 형태입니다. 에이전트는 고객 호스트에서 실행되며, 5 초마다 알려진 엔드포인트를 조회하고, Hub 가 어떤 상태여야 하는지 로컬 Compose 상태와 비교합니다. 에이전트는 상태를 보고하므로, 당신의 대시보드에서 고객이 어떤 버전에 있는지 볼 수 있습니다. 에이전트 자체가 업데이트가 필요할 때, 그것은 자체를 교체하는 동안 실행하지 않으므로 별도의 컨테이너를 생성하여 스왑을 수행합니다.

이 패턴은 고유하지는 않습니다—Kubernetes 운영자 및 GitOps 도구는 동일한 일을 하지만 Compose 사용자는 종종 이를 나쁘게 재발명합니다. 만약 당신이 하나를 구축한다면, 적어도 롤백, 상태 보고, 버전을 고정하는 방법을 제공하거나, 당신은 너가 볼 수 없는 방식으로 드래프트되는 플레트를 얻을 것입니다.

또 다른 주목할 점: 애플리케이션과 함께 반복적인 예약 작업은 native Compose 답변도 없습니다.如果你的 스택에는 일일 정리, 주기적 보고서, 또는 해트비트 스타일의 작업이 포함된다면, 인앱 스케줄러는 하나의 옵션이지만, 결국 커버할 수 없는 경우로 이어집니다 (크로스 서비스 작업, 단일 컨테이너를 넘어서야 하는 작업). 제가 본 세 가지 패턴을 고객 배포에서 생존하는 경우, Compose cron jobs 가이드를 확인하세요.

Docker Compose 성장: Kubernetes vs Swarm

단일 노드 Compose 배포가 스스로를 초과하면, 대부분의 팀의 현실적인 다음 단계는 Kubernetes 입니다. 생태계는 크며, 운영 패턴은 잘 문서화되어 있고, 채용할 인력 풀이 실제로 존재합니다. 측면 비교를 위해, 우리의 Docker Compose vs Kubernetes 비교를 읽으세요.

Docker Swarm 은 다른 옵션—it 는 Compose YAML 형식을 재사용하고, 박스에 탑재되며, 위의 몇 가지 이상점을 직접 해결합니다 (it 는 불건강한 작업을 재시작하며, update_config 를 사용하여 업데이트를 롤아웃합니다)

AI 자동 생성 콘텐츠

본 콘텐츠는 HN AI Posts의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
2

댓글

0