DevOps
CI/CD·관측
설정 드리프트 정리: 값의 우선순위, 재생성, 감지와 예방

설정 드리프트 정리: 값의 우선순위, 재생성, 감지와 예방

들어가며

저장소에 있는 같은 설정으로 배포했는데, 개발 서버에서만 장애가 나는 경우가 있습니다. 코드도 같고 설정 파일도 같아 보이는데 결과가 다르다면, 실제로 프로세스가 받은 값이 서로 다를 가능성이 큽니다.

이런 현상을 **설정 드리프트(configuration drift)**라고 부릅니다. 이번 글에서는 드리프트가 생기는 경로를 Docker Compose 예제로 짚어 보고, 차이를 찾고 막는 방법을 정리합니다.

1. 설정 드리프트란

설정 드리프트는 실행 환경의 실제 설정이 **정본(source of truth)**과 조금씩 어긋나는 현상입니다. 정본은 설정의 기준으로 삼는 한 곳입니다. 보통 Git 저장소의 설정 파일이나 시크릿 저장소가 이 역할을 맡습니다.

어긋남은 대개 정본을 거치지 않은 변경에서 시작합니다. 서버에서 값을 직접 고치거나, 환경별 사본 중 한쪽만 수정하는 경우입니다. 기록이 정본에 남지 않으니 시간이 지날수록 차이가 쌓입니다.

설정 드리프트는 같은 레시피로 연 분점이 제각각 손맛을 더해 맛이 갈리는 일입니다. 본점 레시피에는 아무 기록도 남지 않습니다.

📘정본Git·시크릿 저장소🚚배포CI/CD 파이프라인✍️직접 수정서버 접속 후 편집🖥️서버 A정본과 같음🖥️서버 B정본과 다름🔎차이 비교드리프트 감지같은 설정기록 없음차이반영·복구
서버에서 바로 고치면 빠르지만 정본에 기록이 남지 않습니다. 차이를 찾은 뒤에는 정본에 반영할지 되돌릴지 사람이 판단합니다.

같은 설정을 내려도 서버 B 에 누군가 손을 댔다면 두 서버는 더 이상 같지 않습니다. 그래서 대응은 차이를 찾는 일과, 그 차이를 정본에 반영할지 되돌릴지 판단하는 일로 나뉩니다.

2. 파일을 고쳐도 값이 다른 이유: 값의 우선순위

같은 변수가 여러 곳에 있으면 파일을 제대로 고쳐도 다른 출처의 값이 이길 수 있습니다. Docker Compose 는 두 단계에서 값을 정합니다.

먼저 변수 치환(interpolation) 단계입니다. compose 파일 안의 ${ENDPOINT} 를 실제 값으로 바꾸는데, 이때 셸 환경변수가 프로젝트 .env 파일보다 우선합니다. 다음은 컨테이너에 환경변수를 넣는 단계입니다. 여기서는 docker compose run -e, environment, env_file, Dockerfile 의 ENV 순으로 위에 있는 쪽이 이깁니다.

값의 출처는 겹쳐 붙인 메모지입니다. 묶음마다 맨 위 메모만 읽힙니다.

1단계 치환셸 우선
셸 변수store:9000예전 값이 남기 쉬움
.envhttp://store:9000셸에 없을 때만
2단계 주입위가 이김
run -e실행 옵션
environment치환 결과store:9000 이 들어감
env_file파일 목록같은 이름이면 짐
이미지 ENVDockerfile
docker compose config 로 최종 값 확인치환 후 설정
위층이 아래층을 덮습니다. environment 에 변수 치환을 쓰면 두 규칙이 이어져 셸 값이 env_file 을 이깁니다. 값을 바로 바꾸기 편한 대신 출처가 보이지 않습니다.

두 규칙이 겹치면 이런 일이 생깁니다.

# compose.yaml
services:
  app:
    env_file: [.env]
    environment:
      ENDPOINT: ${ENDPOINT}
$ grep ENDPOINT .env
ENDPOINT=http://store:9000      # 파일은 고쳐져 있음
 
$ printenv ENDPOINT
store:9000                      # 셸에 예전 값이 남아 있음
 
$ docker compose config | grep ENDPOINT
      ENDPOINT: store:9000      # 셸 값이 최종값

파일은 맞지만 컨테이너는 셸의 예전 값을 받습니다. 치환까지 끝난 최종 설정은 docker compose config 로 확인합니다.

3. 고친 값은 언제 반영되나: restart 와 재생성

값의 출처를 바로잡았는데도 컨테이너가 예전 값을 들고 있는 경우가 있습니다. 컨테이너의 환경변수는 컨테이너를 만들 때 한 번 정해지기 때문입니다.

환경변수는 명찰에 인쇄한 이름과 같습니다. 이름이 바뀌면 명찰을 새로 뽑아야 합니다.

restart예전 값
  • 같은 컨테이너멈췄다 다시 켬
  • ENDPOINTstore:9000 그대로
  • 설정 다시 읽기하지 않음
$ docker compose restart app
up -d새 값
  • 새 컨테이너Recreated
  • ENDPOINThttp://store:9000
  • 설정 다시 읽기만들 때 한 번
$ docker compose up -d app
restart 는 빠르지만 만들 때 정해진 값을 그대로 씁니다. 재생성은 잠깐 멈추는 대신 바뀐 설정을 읽습니다.

docker compose restart 는 같은 컨테이너를 다시 켤 뿐이라 변경이 반영되지 않습니다. docker compose up -d 는 설정이 바뀐 서비스의 컨테이너를 새로 만듭니다. 출력에 Recreated 가 보이면 새 값입니다.

$ docker compose restart app            # 예전 값 그대로
$ docker compose up -d app              # 바뀐 서비스만 재생성
 ✔ Container app  Recreated
$ docker compose up -d --force-recreate app   # 무조건 새로 만듦

값이 의심스러우면 --force-recreate 로 예전 값 가설부터 지우고 다음 원인을 봅니다.

4. 드리프트를 찾고 막는 방법

드리프트 감지는 정본에서 기대하는 값과 실제 값을 비교하는 일입니다. 무엇을 보느냐에 따라 쓰는 도구가 다릅니다.

차이를 찾는 일은 설계도와 실제 건물을 맞대 보는 일입니다. 어느 층을 볼지에 따라 줄자가 다릅니다.

확인 수단치환 반영실행 중 값클라우드
파일 열기cat .env
compose config적용될 값재생성 전엔 다를 수 있음
docker inspect컨테이너 값
terraform plan코드 vs 리소스
확인됨 못 봄 조건부
설정 파일만 보면 가장 쉽지만 실제 값을 놓칩니다. 실행 중인 값을 보는 도구일수록 정확한 대신 비밀값 원문이 드러날 수 있어 다루기 조심스럽습니다.

클라우드 리소스는 terraform plan 이, 실행 중인 컨테이너 값은 docker inspect 가 차이를 보여 줍니다. 막는 쪽에서는 세 가지를 함께 씁니다.

  • 모든 변경을 정본에서 시작해 배포 파이프라인으로만 내보냅니다.
  • 배포는 CI 처럼 깨끗한 환경에서 돌려 개인 셸의 값이 끼어들지 못하게 합니다.
  • **불변 인프라(immutable infrastructure)**로 전환해 서버를 고치지 않고 새로 만들어 교체합니다. 수동 변경이 쌓일 틈이 줄어듭니다.

엄격하게 막을수록 긴급 수정도 절차를 타야 해서 느려집니다. 그래서 직접 수정을 금지하기보다 고친 뒤 정본에 반영하는 규칙을 두는 경우가 많습니다.

헷갈리기 쉬운 점

  • 설정 파일을 열어 보면 실제 값을 안다는 오해. 셸 변수가 파일을 이길 수 있으니 프로세스가 받은 값을 확인해야 합니다.
  • 프로젝트 .env 와 env_file 은 같은 것이라는 오해. .env 는 compose 파일의 치환용이고, env_file 은 컨테이너에 넣는 목록입니다. 같은 파일을 가리켜도 역할이 다릅니다.
  • restart 하면 환경변수도 새로 읽힌다는 오해. 환경변수는 컨테이너를 만들 때 정해지므로 재생성해야 반영됩니다.

면접에서 자주 받는 질문

차이를 찾으면 바로 정본대로 덮어쓰면 되나요?

먼저 그 값을 누가 왜 바꿨는지 확인합니다. 장애 대응 중의 긴급 수정이라면 정본에 올리고, 실수라면 서버를 되돌립니다. 확인 없이 덮어쓰면 그 값에 의존하던 쪽이 바로 깨질 수 있습니다.

셸에 남은 값이 끼어들지 못하게 하려면 어떻게 하나요?

environment 에서 ${VAR} 치환을 빼고 env_file 로만 값을 넣는 방법이 있습니다. 배포는 CI 처럼 깨끗한 환경에서 돌리고, 마지막에는 docker compose config 로 최종 값을 확인합니다.

실행 중인 비밀값을 노출하지 않고 어떻게 확인하나요?

원문 대신 길이나 앞 몇 글자만 봅니다. 정본과 비교할 때는 양쪽 값의 해시를 맞춰 봅니다. docker inspect 결과에는 원문이 그대로 나오므로 로그에 남기지 않습니다.

정리

  • 설정 드리프트는 실제 설정이 정본과 어긋나는 현상이고, 정본을 거치지 않은 변경에서 시작합니다.
  • 파일이 맞아도 값의 우선순위나 컨테이너 재생성 여부 때문에 실제 값은 다를 수 있습니다.
  • 정본에서만 배포하고, 정기적으로 차이를 비교하고, 서버는 고치기보다 새로 만듭니다.

확인할 대상은 설정 파일이 아니라 프로세스가 실제로 받은 값입니다.