핵심 정리
# docker-compose.yml
services:
app:
image: my-app
secrets:
- db_password
environment:
DB_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txt갈리는 기준
어떤 민감 정보 전달 방식을 먼저 떠올리면 되나
| 상황 | 먼저 떠올릴 선택 |
|---|---|
| 런타임 시크릿을 파일로 주입 | Docker Secrets |
| 개발 환경에서 로컬 파일로 시크릿 테스트 | secrets.file |
| 빌드 중 토큰 전달 | docker build --secret |
| Dockerfile에 직접 넣으려는 유혹 | 피해야 할 패턴 |
Docker Secrets와 환경변수는 노출 경로가 다르다
환경변수는 docker inspect로 조회할 수 있고, 자식 프로세스에 상속되며, 로그나 디버깅 출력에 섞일 수 있다. 반면 Secrets 계열 기능은 보통 파일 경로를 통해 값을 읽게 만들어 노출 경로를 줄이는 쪽에 가깝다. 다만 "항상 같은 방식으로 tmpfs에 안전하게 올라온다"처럼 단정하면 안 된다. Swarm secrets, BuildKit build secrets, Compose의 로컬 file 기반 secrets는 동작 방식과 적용 범위가 서로 다르므로, 현재 실행 환경에서 실제로 어떤 방식으로 마운트되는지 문서와 동작을 함께 확인하는 편이 안전하다.
# 환경변수: docker inspect로 값이 노출됨
docker run -e DB_PASSWORD=secret123 my-app
docker inspect my-app | grep DB_PASSWORD
# "DB_PASSWORD=secret123" 그대로 보임
# Docker Secrets: 파일로만 접근, inspect에서 값 미노출
docker secret create db_password ./password.txt
docker service create --secret db_password my-app/run/secrets/에 파일로 마운트되는 방식
Docker Secrets는 컨테이너 내부의 /run/secrets/시크릿명 경로에 파일로 마운트된다. 앱은 파일을 읽어서 값을 가져온다. 파일 경로를 환경변수로 알려주는 _FILE 접미사 패턴이 많이 쓰인다.
# 컨테이너 안에서 시크릿 파일 확인
docker exec my-app cat /run/secrets/db_password
# _FILE 패턴: 앱이 파일 경로를 읽어 값을 로드
# (postgres, mysql 공식 이미지 등이 지원하는 패턴)# 앱 코드에서 파일로 읽기
import os
secret_file = os.environ.get("DB_PASSWORD_FILE", "/run/secrets/db_password")
with open(secret_file) as f:
db_password = f.read().strip()Compose의 secrets 키는 실행 환경에 따라 읽어야 한다
file은 로컬 파일에서 시크릿 값을 읽어 들이는 개발용 진입점으로 많이 쓰인다. external은 Docker Swarm 또는 외부 시스템에서 이미 생성된 시크릿을 참조할 때 쓴다. 개발 환경에서는 file이 편하고, 운영 환경에서는 external이나 별도 시크릿 관리 체계를 연결하는 구성이 더 자연스럽다. 다만 로컬 Compose의 secrets 지원 범위와 실제 마운트 방식은 배포 방식에 따라 차이가 날 수 있으므로, 운영 설계에서는 "Swarm secret처럼 완전히 같은 보안 특성을 준다"라고 가정하지 않는 편이 맞다.
services:
db:
image: postgres:17
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt # 개발: 로컬 파일
# 운영: 이미 생성된 Swarm secret 참조
# db_password:
# external: true환경변수로 시크릿을 전달할 때의 노출 위험
환경변수는 /proc/PID/environ을 통해 같은 호스트의 루트 권한 사용자가 읽을 수 있다. 또한 앱이 크래시 덤프를 생성하거나 env를 로그에 출력하면 시크릿이 노출된다. 환경변수는 편의성은 높지만 완전한 격리를 보장하지 않는다.
# 실행 중인 컨테이너의 환경변수 조회 (루트 권한으로 가능)
docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' my-app
# DB_PASSWORD=secret123 ← 그대로 노출
# /proc를 통한 노출 경로 (호스트에서)
# cat /proc/<PID>/environ | tr '\0' '\n' | grep PASSWORD환경 변수와 Secrets는 노출 경로가 다르다
간단한 개발 설정은 환경 변수로도 충분하지만, 비밀번호와 API 키처럼 유출 비용이 큰 값은 Secrets 쪽이 더 안전합니다. 환경 변수는 앱과 디버깅 도구가 쉽게 읽을 수 있는 대신 노출 면이 넓고, Secrets는 파일로 읽어야 해서 다루는 코드는 조금 늘지만 노출 경로를 줄일 수 있습니다.
체크포인트
| 상황 | 적합한 선택 |
|---|---|
| 개발 환경에서 간편하게 시크릿 관리 | secrets.file + /run/secrets/ 파일 읽기 |
| 운영 환경에서 시크릿 중앙 관리 | Docker Swarm secrets 또는 외부 Vault |
| 빌드 시 필요한 토큰 (npm, pip 등) | docker build --secret |
| 절대 해서는 안 되는 방식 | Dockerfile에 ENV SECRET=... 하드코딩 |
주의할 점
ENV API_KEY=...처럼 Dockerfile에 민감값을 직접 쓰면 이미지 레이어와 히스토리에 오래 남을 수 있다. 배포 전에 값을 지워도 이미 push된 이미지나 캐시에 남아 있을 수 있으므로, 시크릿은 빌드 타임과 런타임에 외부에서 주입하는 쪽이 안전합니다. .dockerignore와 .gitignore에 시크릿 파일을 추가하는 것도 함께 검토해야 합니다.
ENV API_KEY=secret123
# docker history나 이미지 추출로 값이 남을 수 있음참고 링크
2 sources