Infra

[nginx] docker-compose command override envsubst 미실행 ${VAR}

jykim23 2026. 6. 25. 21:43
반응형

부제: command를 덮어썼더니 ${VAR}가 그대로 남았다

nginx 설정을 템플릿으로 두고, 환경변수를 채워 렌더하는 방식을 쓰고 있었다. TLS 인증서 경로에 도메인을 변수로 넣어 뒀다.

ssl_certificate /etc/letsencrypt/live/${SSL_DOMAIN}/fullchain.pem;

그런데 어느 순간 ${SSL_DOMAIN}이 치환되지 않고 글자 그대로 남았다. nginx는 ${SSL_DOMAIN}이라는 이름의 디렉터리에서 인증서를 찾으려다 실패했고, 443(HTTPS)이 뜨지 못했다.

증상

디스크의 템플릿은 멀쩡했다. ${SSL_DOMAIN}을 채우는 단계가 그냥 안 돌았다. 변수 치환(envsubst)이 통째로 건너뛰어진 것이다.

근본 원인 — command를 덮어쓰면 치환 단계도 같이 사라진다

nginx 계열 이미지는 컨테이너가 뜰 때 entrypoint에서 템플릿을 렌더하는 초기화 단계를 돈다. 그런데 이 초기화는 보통 컨테이너의 실행 명령(CMD)이 기본값(nginx 기동)일 때만 동작한다.

우리는 인증서 갱신을 위해 command를 직접 덮어쓰고 있었다. 주기적으로 reload를 돌리는 루프를 넣으려고.

# 문제가 된 형태 (요지) — command를 통째로 교체
command: sh -c "while :; do sleep 12h; nginx -s reload; done & nginx -g 'daemon off;'"

이렇게 기본 실행 명령을 갈아치우자, 이미지가 기대하던 "기본 nginx 기동" 조건이 깨졌다. 그래서 envsubst 초기화 단계가 발동하지 않았고, 템플릿이 렌더되지 않은 채 nginx가 떴다. 편의를 위해 command를 덮어쓴 것이, 그 앞에 있던 치환 단계까지 건너뛰게 만든 셈이다.

정리한 방식

덮어쓴 command 안에서 치환 단계를 명시적으로 먼저 호출했다. 초기화에 맡기지 말고, 내가 직접 부른다.

# 치환을 명시적으로 먼저 돌리고, 그다음 reload 루프 + nginx 기동
command: sh -c "sh /docker-entrypoint.d/20-envsubst-on-templates.sh;
                while :; do sleep 12h; nginx -s reload; done & nginx -g 'daemon off;'"

command를 덮어쓸 거면, 원래 기본 실행이 알아서 해주던 일(여기선 envsubst)이 무엇인지 확인하고 그걸 내 command 안에 챙겨 넣어야 했다.

force-recreate 글과 이어지는 함정

전에 적은 "설정을 바꿨는데 컨테이너가 옛 설정을 들고 있던" 문제와 뿌리가 같다. 둘 다 nginx 설정이 컨테이너의 기동 과정과 어긋나는 함정이다.

  • 그때: 디스크 설정은 바뀌었는데 컨테이너가 재기동 안 됨 → --force-recreate
  • 이번: 컨테이너는 떴는데 설정 렌더(치환) 단계가 건너뛰어짐 → command에 치환을 명시

설정 파일을 "썼다"와 그게 "실행 중인 nginx에 반영됐다"는 다른 사건이라는 걸, 두 번 다른 방식으로 배웠다.

정리

  • 이미지의 entrypoint 초기화는 보통 기본 실행 명령일 때만 동작한다
  • command를 덮어쓰면 그 초기화(여기선 envsubst 치환)가 조용히 사라질 수 있다
  • 덮어쓸 거면, 기본 실행이 대신 해주던 일을 직접 command 안에 챙겨 넣는다
  • ${VAR}가 리터럴로 남아 보이면, 변수 값이 아니라 치환 단계가 돌았는지부터 의심한다

편의로 덮어쓴 한 줄이, 그 앞에 있던 보이지 않는 단계를 같이 지웠다.

반응형