docker inspect 명령어 한 번이면 환경 변수에 담긴 DB 비밀번호가 평문으로 노출된다. Docker Compose의 Secrets 기능은 이 문제를 파일 마운트 방식으로 해결한다. 환경 변수 대신 파일로 민감한 값을 전달하면, 노출 경로가 줄고 접근 제어도 명확해진다.
환경 변수는 편리하지만 보안 측면에서 취약점이 있다.
| 방식 | docker inspect 노출 | 자식 프로세스 상속 | 로그 유출 위험 |
|---|---|---|---|
| 환경 변수 | O (평문) | O | O |
| Secrets (파일) | X | X | 낮음 |
docker inspect <container>를 실행하면 Env 배열에 모든 환경 변수가 평문으로 나타난다. Secrets는 이 배열에 포함되지 않는다.
docker-compose.yml에서 최상위 secrets 블록으로 시크릿을 선언한다. file 소스는 호스트의 파일을 읽어 시크릿으로 등록하는 방식이다.
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txt
services:
app:
image: my-app:latest
secrets:
- db_password
- api_key
./secrets/db_password.txt 파일에 비밀번호 값만 한 줄로 저장한다. 파일 끝 개행 문자에 주의해야 한다.
printf 'supersecret' > ./secrets/db_password.txt
file 소스: 호스트 파일 경로를 지정하면 Compose가 파일 내용을 읽어 시크릿으로 등록한다. Docker Swarm 환경에서는 external 소스로 클러스터 시크릿을 참조할 수 있다.
시크릿이 마운트된 컨테이너 안에서는 /run/secrets/<secret_name> 경로로 파일을 읽을 수 있다.
Host Container
───────────────────── ──────────────────────────
./secrets/db_password.txt → /run/secrets/db_password
./secrets/api_key.txt → /run/secrets/api_key
애플리케이션 코드에서는 파일을 직접 읽어 값을 사용한다.
import fs from 'fs';
function readSecret(name: string): string {
const path = `/run/secrets/${name}`;
return fs.readFileSync(path, 'utf-8').trim();
}
const dbPassword = readSecret('db_password');
const apiKey = readSecret('api_key');
.trim으로 파일 끝의 개행 문자를 제거하는 것이 안전하다.
소스 파일 관리가 핵심이다. ./secrets/ 디렉토리는 반드시 .gitignore에 추가해야 한다.
# .gitignore
secrets/
*.secret
파일 권한도 최소화한다.
chmod 600 ./secrets/db_password.txt
컨테이너 내 마운트는 tmpfs 기반 메모리 파일시스템에 이루어지므로 디스크에 기록되지 않는다. 컨테이너가 종료되면 시크릿 파일도 사라진다.
tmpfs: 디스크가 아닌 메모리에 파일시스템을 구성하는 방식. /run/secrets/는 이 방식으로 마운트되어 재시작 후 잔존하지 않는다.
운영 환경에서는 Vault, AWS Secrets Manager, GCP Secret Manager 같은 외부 시크릿 관리 도구와 연동하는 방식이 권장된다. Compose의 Secrets는 개발·스테이징 환경에서 환경 변수보다 안전한 중간 단계로 활용하기에 적합하다.