ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Docker Compose는 켜졌는데 앱이 DB에 연결하지 못하는 이유
    Programming 2026. 10. 2. 07:50
    728x90
    반응형

    DB 실행, 준비 중, healthy를 시간순으로 놓고 checker가 건강 상태를 기다리는 그림
    service_healthy는 정해 둔 건강 검사 성공을 기다립니다. 앱 인증과 쿼리는 추가 확인 대상입니다.

    docker compose up 직후에는 앱이 DB 연결에 실패하고, 앱만 다시 시작하면 된다면 DB의 준비 시간을 먼저 살펴보세요. 컨테이너 실행과 서비스 준비를 구분하고 healthcheck·depends_on 조건·앱 재연결을 각 단계에 맞게 설계하세요.

    running은 프로세스가 실행 중이라는 상태입니다. DB가 초기화를 마치고 앱의 요청을 받을 수 있다는 뜻까지 포함하지는 않습니다. Compose의 기본 시작 순서는 이 간격을 자동으로 메워 주지 않습니다. Compose 시작 순서

    다만 기다리면 모든 연결 오류가 해결되는 것은 아닙니다. 앱에서 localhost를 DB 주소로 사용했거나 암호가 틀렸다면 별도의 수정이 필요합니다. 여기서는 2026년 9월 28일 확인한 Docker·PostgreSQL 문서를 바탕으로 시작 대기, 접속 주소, 실제 쿼리의 순서로 점검합니다. 아래 구성은 설명용이며 컨테이너를 실행한 측정 결과는 아닙니다.

    앱을 먼저 늦추기보다 기다릴 조건을 정합니다

    짧은 형태의 depends_on: [db]는 DB를 앱보다 먼저 시작하도록 합니다. DB가 접속을 받는 시점까지 기다리려면 DB의 healthcheck와 앱 쪽의 condition: service_healthy를 함께 정의해야 합니다.

    예를 들어 DB 프로세스가 켜진 뒤 데이터 디렉터리를 준비하는 동안 앱이 시작할 수 있습니다. 앱의 첫 접속은 실패하지만 잠시 뒤 재실행하면 성공합니다. 이 상황에서 고정된 sleep 10을 넣으면 지금 장비에서는 지나갈 수 있어도, 새 데이터 디렉터리나 느린 장비에서는 다시 부족해질 수 있습니다.

    상태 검사는 ‘10초가 지났는가’ 대신 ‘지금 연결을 받을 수 있는가’를 묻습니다. 건강 상태 검사라고 이름 붙였더라도 무엇을 실행하느냐에 따라 의미는 달라집니다. 빈 파일이 있는지 확인하는 검사와 실제 DB 접속 상태 검사는 같은 준비 조건이 아닙니다.

    두 컨테이너가 서로 다른 주소를 쓰는 예시

    다음은 DB와 일회성 확인 서비스만 담은 작은 Compose 예시입니다. 기존 프로젝트와 분리한 실습 디렉터리에서 읽어 보세요. 웹앱이나 운영 DB 설정을 대체하는 파일은 아닙니다.

    services:
      db:
        image: postgres:18
        environment:
          POSTGRES_USER: demo
          POSTGRES_PASSWORD:
            example-only
          POSTGRES_DB: demo
        healthcheck:
          test:
            - CMD
            - pg_isready
            - -h
            - 127.0.0.1
            - -U
            - demo
            - -d
            - demo
          interval: 5s
          timeout: 3s
          retries: 10
          start_period: 30s
    
      checker:
        image: postgres:18
        depends_on:
          db:
            condition:
              service_healthy
        command:
          - pg_isready
          - -h
          - db
          - -U
          - demo
          - -d
          - demo
    

    healthcheck는 DB 컨테이너 안에서 실행되므로 127.0.0.1을 검사합니다. 반면 checker는 별도 컨테이너여서 db라는 서비스 이름을 사용합니다. 같은 Compose 프로젝트의 기본 네트워크에서 서비스는 이 이름으로 서로를 찾습니다. 컨테이너끼리 통신하려고 호스트에 DB 포트를 공개할 필요는 없습니다. Compose 네트워크의 서비스 이름

    checker에서 localhost를 쓰면 checker 자신을 가리킵니다. 이 경우 DB가 아무리 오래 healthy여도 목적지가 잘못돼 연결되지 않습니다. 실제 앱의 연결 문자열에서도 호스트 이름과 포트를 먼저 대조해야 하는 이유입니다.

    암호와 검사 간격은 예시 값입니다. 30초는 PostgreSQL의 보편적인 준비 시간이나 측정값이 아닙니다. 운영에서는 자신의 이미지 버전과 초기화 시간에 맞춰 값을 정하고 자격 증명을 별도로 관리해야 합니다.

    pg_isready 성공 뒤에도 앱 접속은 남아 있습니다

    pg_isready의 종료 코드 0은 서버가 연결을 받아들이는 상태임을 나타냅니다. 1은 연결 거부, 2는 무응답, 3은 유효하지 않은 매개변수 등으로 시도하지 못한 경우입니다. 여기서 ‘거부’에는 서버 시작 중인 상태도 포함됩니다. PostgreSQL 18 pg_isready

    이 도구는 올바른 사용자 이름·암호·DB 이름이 없어도 서버 상태를 알아낼 수 있습니다. 따라서 위 checker가 성공했다는 사실은 앱의 암호나 테이블 권한까지 통과했다는 뜻이 아닙니다.

    실제 앱이 사용할 준비를 확인하려면 다음 단계에서 앱 계정으로 필요한 쿼리를 실행해야 합니다. 가령 로그인 화면이 사용자 테이블을 읽는다면 DB 서버의 접속 가능 여부 외에 계정 인증, 해당 DB 선택, 테이블 존재와 읽기 권한이 필요합니다. SELECT 1이 되더라도 아직 만들어지지 않은 사용자 테이블까지 확인한 것은 아닙니다.

    이런 앱은 마이그레이션 작업이 성공적으로 끝난 뒤 시작하도록 별도 의존성을 둘 수 있습니다. Compose의 service_completed_successfully는 일회성 작업이 성공하고 종료됐는지 기다리는 조건입니다. 이 조건을 쓰더라도 어떤 스키마 버전까지 적용했는지와 실패 시 시작을 막을지는 앱의 배포 절차에서 정해야 합니다.

    로그에서는 실패 시점과 실패 내용을 함께 읽습니다

    예시 파일을 실행하기 전에는 docker compose config로 최종 해석된 설정을 확인할 수 있습니다. 환경 변수나 여러 파일을 합치는 프로젝트에서는 편집한 YAML과 실제 적용 설정이 다를 수 있습니다. 출력에 자격 증명이 섞일 수 있으므로 그대로 공개 로그에 붙이지 마세요. Compose config

    별도 실습에서 확인한다면 DB가 healthy로 바뀐 시각과 checker의 실행·종료 순서를 봅니다. 예상 흐름은 DB 실행, 건강 검사 성공, checker 실행입니다. checker가 정상 종료한 뒤 목록에서 계속 running이 아니어도 이 예시의 목적에는 맞습니다. 한 번 검사하고 끝내도록 만든 서비스이기 때문입니다.

    실제 앱은 다음과 같이 나눠 읽을 수 있습니다.

    • 시작 직후 연결 거부: DB 로그와 건강 상태를 대조해 아직 초기화 중인지 확인합니다.
    • DB가 healthy인데 호스트를 못 찾음: 앱의 서비스 이름과 참여 네트워크를 봅니다.
    • 암호 인증 실패: 준비 시간을 늘리기보다 앱이 쓰는 사용자와 자격 증명을 대조합니다.
    • 테이블을 찾지 못함: 접속한 DB와 스키마, 마이그레이션 완료 여부를 확인합니다.

    이는 오류 문구만으로 확정하는 규칙이 아니라 조사할 위치를 고르는 순서입니다. 같은 앱에 주소 오류와 준비 시간 문제가 함께 있을 수도 있으므로 한 원인을 고친 뒤 남은 오류를 다시 읽습니다.

    실행 중에 끊기는 연결은 앱이 처리해야 합니다

    시작 때 DB가 healthy였어도 실행 중 재시작이나 연결 단절은 생길 수 있습니다. Compose의 시작 조건이 이미 열린 앱의 연결을 계속 복구해 주지는 않습니다.

    앱에는 재연결과 제한된 재시도, 실패 응답을 설계해야 합니다. 읽기 요청을 다시 보내는 경우와 결제를 기록하는 쓰기 요청을 다시 보내는 경우는 다릅니다. 서버가 작업을 마친 뒤 응답만 끊긴 상황에서 같은 쓰기를 무조건 반복하면 중복 처리될 수 있으므로, 재시도 가능한 작업과 중복 방지 방법을 함께 정합니다.

    긴 형태의 depends_on에 있는 restart: true도 의미를 확인하고 써야 합니다. 문서는 Compose가 명시적으로 의존 서비스를 갱신하거나 재시작할 때 연관 서비스를 재시작하는 동작으로 설명합니다. DB 프로세스의 모든 장애를 감시해 앱의 연결을 복구하는 설정으로 해석하면 안 됩니다. 의존 서비스 재시작 조건

    첫 실행만 실패한다면 healthcheck와 시작 조건을 맞추고, DB가 준비된 뒤에도 실패하면 주소·인증·스키마를 확인하세요. 마지막에는 DB 재시작 중 앱이 어떤 응답을 주고 연결을 되찾는지까지 별도로 점검해야 합니다.

    728x90
    반응형
Designed by Tistory.