Docker에서 환경 변수 마스터하기: 설정 vs. 시크릿

환경 변수를 마스터하여 안전하고 유연한 Docker 배포를 구현하세요. 이 포괄적인 가이드는 일반적인 애플리케이션 설정을 위한 환경 변수 사용과 API 키, 비밀번호 같은 민감한 데이터를 안전하게 관리하는 것 사이의 중요한 차이점을 명확히 설명합니다. 민감하지 않은 설정을 전달하는 실제적인 방법을 배우고, 환경 변수를 통해 시크릿을 노출하는 심각한 위험을 이해하며, 강력하고 암호화된 시크릿 관리를 위해 Docker 시크릿과 Compose를 활용하는 방법을 알아보세요. Docker 지식을 향상하고 애플리케이션을 보호하세요.

34 조회수

Docker 환경 변수 마스터하기: 설정 vs. 시크릿

Docker는 개발의 다양한 단계를 거쳐 애플리케이션을 구축, 배포 및 실행하는 방식을 혁신하여 일관된 환경을 제공합니다. 컨테이너화된 애플리케이션을 관리하는 근본적인 측면은 구성을 설정하는 것이며, 환경 변수는 이를 위한 주요 메커니즘입니다. 그러나 모든 데이터가 동일한 것은 아닙니다. 일부 구성은 무해하지만 API 키 또는 데이터베이스 자격 증명과 같은 다른 정보는 매우 민감합니다. 이 두 범주를 혼동하면 심각한 보안 취약점으로 이어질 수 있습니다.

이 글에서는 일반 구성에 환경 변수를 사용하는 것과 "시크릿"으로 일반적으로 알려진 민감한 데이터를 관리하는 적절하고 안전한 방법 사이의 중요한 차이점을 자세히 살펴봅니다. Docker 컨테이너에 구성을 전달하는 다양한 방법, 시크릿을 일반 환경 변수로 취급하는 내재된 위험, 안전한 시크릿 관리를 위한 Docker의 전용 솔루션을 소개합니다. 마지막까지 각 접근 방식을 언제 어떻게 사용할지에 대한 명확한 이해를 얻어 애플리케이션이 유연하고 안전하게 유지되도록 할 것입니다.

구성을 위한 환경 변수 이해하기

환경 변수는 Docker 컨테이너에서 실행되는 애플리케이션을 포함하여 애플리케이션에 런타임 구성을 전달하는 간단하고 널리 채택된 방법입니다. 이를 통해 Docker 이미지를 다시 빌드하지 않고도 애플리케이션의 동작을 수정할 수 있으므로 컨테이너를 더 유연하고 이식성 있게 만들 수 있습니다. 이는 애플리케이션 포트 번호, 디버그 플래그 또는 타사 서비스 URL과 같이 민감하지 않고 동적인 설정에 이상적입니다.

구성 변수 전달 방법

Docker는 컨테이너에 환경 변수를 정의하고 주입하는 여러 가지 방법을 제공합니다.

1. Dockerfile의 ENV 지시문

ENV 지시문은 컨테이너가 실행될 때 컨테이너 내부에서 사용할 수 있는 기본 환경 변수를 설정합니다. 이는 변경될 가능성이 적거나 애플리케이션에 대한 유용한 기본값을 제공하는 변수에 적합합니다.

FROM alpine:latest

ENV APP_PORT=8080
ENV DEBUG_MODE=false

COPY ./app /app
WORKDIR /app
CMD ["/app/start.sh"]

팁: ENV는 기본값을 설정하지만 런타임에 이를 재정의할 수 있습니다.

2. docker run과 함께 -e 또는 --env 플래그 사용

단일 컨테이너를 시작할 때 -e 또는 --env 플래그를 사용하여 환경 변수를 직접 전달할 수 있습니다. 이는 임시 테스트 또는 Dockerfile 기본값과 다른 특정 설정을 제공하는 데 일반적입니다.

docker run -d -p 80:8080 --name my_app_instance \n  -e APP_PORT=80 \n  -e DEBUG_MODE=true \n  my_app_image:latest

3. Docker Compose의 env_file

여러 서비스에 걸쳐 여러 환경 변수를 관리하는 경우, 특히 docker-compose.yml 파일에 정의된 경우 env_file 옵션이 매우 편리합니다. 이를 통해 하나 이상의 .env 파일에서 변수를 로드하여 docker-compose.yml을 더 깔끔하게 유지할 수 있습니다.

docker-compose.yml:

version: '3.8'
services:
  webapp:
    image: my_app_image:latest
    ports:
      - "80:8080"
    env_file:
      - ./config/app.env

./config/app.env:

APP_PORT=8080
DEBUG_MODE=false
API_ENDPOINT=https://api.example.com/v1

4. Docker Compose의 environment

또는 docker-compose.yml의 서비스 섹션 내에서 환경 변수를 직접 정의할 수 있습니다. 이는 적은 수의 변수나 단일 서비스에만 해당하는 변수에 선호되는 경우가 많습니다.

version: '3.8'
services:
  webapp:
    image: my_app_image:latest
    ports:
      - "80:8080"
    environment:
      APP_PORT: 8080
      DEBUG_MODE: false

시크릿에 환경 변수를 사용할 때의 함정

환경 변수는 구성에 훌륭하지만 데이터베이스 암호, API 키 또는 개인 SSH 키와 같은 민감한 데이터를 관리하는 데 근본적으로 안전하지 않습니다. 이는 특히 개발 환경에서 종종 간과되는 중요한 보안 취약점입니다.

환경 변수가 시크릿에 안전하지 않은 이유:

  1. docker inspect를 통한 가시성: Docker 호스트에 액세스할 수 있는 사람은 누구나 docker inspect <container_id>를 사용하여 실행 중인 컨테이너의 환경 변수를 쉽게 볼 수 있습니다. 이는 시크릿이 일반 텍스트로 명확하게 표시됨을 의미합니다.

    ```bash

    시크릿 노출 예시 (프로덕션에서는 절대 이렇게 하지 마세요)

    docker run -d -e DB_PASSWORD=mysecretpassword --name insecure_app nginx:latest

    누구나 비밀번호를 볼 수 있습니다.

    docker inspect insecure_app | grep DB_PASSWORD
    ```

  2. 프로세스 스누핑: 컨테이너 내에서 다른 프로세스나 사용자(여러 사용자가 있는 경우)는 특히 애플리케이션이 루트로 실행되거나 권한이 상승된 경우 환경 변수를 읽을 수 있습니다.

  3. 로깅 및 기록: 환경 변수는 부주의하게 로그, CI/CD 파이프라인 기록 또는 셸 기록에 포함되어 우발적인 노출로 이어질 수 있습니다.

  4. 이미지 레이어: Dockerfile에서 시크릿과 함께 ENV를 사용하는 경우 해당 시크릿은 이미지 레이어에 구워지고 나중에 unset하려고 해도 해당 레이어에 남아 있습니다. 이로 인해 시크릿 자체에서 시크릿을 검색할 수 있습니다.

  5. 우발적 공유: 시크릿이 포함된 .env 파일이나 docker-compose.yml 파일은 종종 버전 관리 시스템에 커밋되거나 부적절하게 공유되어 광범위한 노출을 초래합니다.

경고: 민감한 정보를 일반 환경 변수로 취급하는 것은 일반적인 보안 실수입니다. 항상 환경 변수가 호스트 및 컨테이너 내에서 공개적으로 표시될 수 있다고 가정해야 합니다.

Docker에서 시크릿을 안전하게 관리하기

민감한 데이터에 대한 환경 변수의 보안 부족 문제를 해결하기 위해 Docker는 주로 Docker Secrets(Docker Swarm용) 및 secrets 기능이 있는 Docker Compose와 같은 외부 도구를 통해 전용 시크릿 관리 기능을 제공합니다(Docker Swarm 시크릿을 활용하거나 단순히 파일을 마운트할 수 있음).

Docker Secrets (Docker Swarm 모드)

Docker Secrets는 서비스 작업을 위해 민감한 데이터를 안전하게 전송하고 저장하는 방법을 제공하는 Docker Swarm 모드와 통합된 기능입니다. 시크릿은 다음과 같습니다.

  • Swarm 관리자의 Raft 로그에서 휴지 상태로 암호화됩니다.
  • 승인된 서비스 작업으로 안전하게 전송됩니다.
  • 환경 변수로 노출되는 대신 컨테이너 파일 시스템의 일반적으로 /run/secrets/<secret_name>메모리 내 파일로 마운트됩니다.
  • 명시적으로 액세스 권한이 부여된 서비스에서만 액세스 가능합니다.

Docker Secrets 사용 방법 (Swarm 모드)

  1. Swarm 초기화 (아직 안 한 경우):
    bash docker swarm init

  2. 시크릿 생성: 시크릿은 파일 또는 표준 입력에서 생성됩니다.
    bash echo "my_secure_db_password" | docker secret create db_password_secret - echo "SG.your_api_key_here" | docker secret create sendgrid_api_key -

  3. 시크릿으로 서비스 배포: 서비스는 이름으로 시크릿을 참조합니다. Docker는 시크릿을 컨테이너에 마운트합니다.
    bash docker service create --name my-webapp \n --secret db_password_secret \n --secret sendgrid_api_key \n my_app_image:latest

  4. 컨테이너에서 시크릿 액세스: 애플리케이션은 마운트된 파일 경로에서 시크릿을 읽습니다.
    ```python

Python 애플리케이션 코드에서 (다른 언어도 유사)

with open('/run/secrets/db_password_secret', 'r') as f:
db_password = f.read().strip()

with open('/run/secrets/sendgrid_api_key', 'r') as f:
sendgrid_key = f.read().strip()
```

Docker Compose 및 시크릿 (단일 호스트 또는 Swarm용)

Docker Compose 버전 3.1 이상에서는 secrets 섹션을 도입하여 docker-compose.yml 내에서 시크릿을 정의하고 참조할 수 있습니다. Swarm 모드에서 실행될 때 Compose는 Docker Swarm의 네이티브 시크릿을 활용합니다. Swarm 없이 단일 호스트에서 실행될 때 Compose는 Swarm에서 제공하는 휴지 상태 암호화 없이 호스트에서 컨테이너로 파일을 안전하게 마운트함으로써 시크릿을 지원합니다.

docker-compose.yml에서 secrets 사용하기

  1. 시크릿 정의: 외부 파일을 참조하거나 외부 시크릿(미리 생성된 Swarm 시크릿)으로 만들어 시크릿을 정의할 수 있습니다.

    ```yaml

    docker-compose.yml

    version: '3.8'

    services:
    webapp:
    image: my_app_image:latest
    ports:
    - "80:8080"
    secrets:
    - db_password
    - sendgrid_api_key

    secrets:
    db_password:
    file: ./secrets/db_password.txt # 비밀번호가 포함된 호스트의 파일 경로
    sendgrid_api_key:
    external: true # 미리 존재하는 Docker Swarm 시크릿 'sendgrid_api_key'를 참조
    ```

  2. 로컬 시크릿 파일 생성 (file 사용 시):
    bash mkdir secrets echo "my_local_db_password" > ./secrets/db_password.txt

  3. Compose로 배포: docker compose up -d는 서비스를 배포하여 컨테이너 내부의 /run/secrets/<secret_name>에서 시크릿을 사용할 수 있도록 합니다.

    ```bash

    컨테이너 내부에서 ./secrets/db_password.txt의 내용은 다음과 같습니다:

    /run/secrets/db_password

    ```

올바른 도구 선택: 구성 vs. 시크릿

구성에 환경 변수를 사용할지 전용 시크릿 관리 솔루션을 사용할지 결정하는 것은 다음과 같은 한 가지 주요 질문에 달려 있습니다.

데이터가 민감한가?

  • 그렇다면 (민감한 데이터): Docker Swarm에서 Docker Secrets를 사용하거나 유사한 시크릿 관리 시스템(예: Kubernetes Secrets, HashiCorp Vault)을 사용합니다. 단일 호스트 Compose 설정의 경우 secrets 섹션을 사용하여 파일을 안전하게 마운트합니다.
  • 아니라면 (민감하지 않은 구성): 환경 변수(Dockerfile의 ENV, -e 플래그, env_file 또는 Compose의 environment 사용)를 사용합니다.
기능 환경 변수 (구성용) Docker Secrets (민감한 데이터용)
목적 민감하지 않은 애플리케이션 구성 민감한 데이터 (암호, API 키)
가시성 docker inspect, ps -e를 통해 보임 파일로 마운트; docker inspect에 없음
보안 민감한 데이터에 대해 안전하지 않음 암호화, 안전한 전송 및 저장
앱에서 액세스 os.environ (또는 유사)에서 읽음 /run/secrets/<secret_name> 파일에서 읽음
관리 대상 Docker 런타임, Docker Compose Docker Swarm, Docker Compose
사용 사례 포트 번호, 디버그 플래그, 민감하지 않은 URL 데이터베이스 암호, API 토큰, 개인 키

둘 다에 대한 모범 사례

구성 (환경 변수)의 경우:

  • ENV를 사용하여 Dockerfile에 유용한 기본값을 제공합니다. 이렇게 하면 이미지를 바로 실행할 수 있고 예상되는 변수를 명확하게 문서화할 수 있습니다.
  • 가능한 경우 구성을 외부화합니다. 대규모 배포의 경우 docker compose 또는 외부 구성 서비스와 함께 .env 파일을 사용합니다.
  • 환경(개발, 스테이징, 프로덕션) 간에 변경될 수 있는 값은 하드코딩하지 마세요.
  • README.md 또는 애플리케이션 문서에 예상 값과 함께 모든 구성 옵션을 문서화합니다.

시크릿 (Docker Secrets 및 그 이상)의 경우:

  • 절대 시크릿을 커밋하지 마세요 (예: 시크릿이 포함된 .env 파일, db_password.txt). Git과 같은 버전 관리 시스템에.
  • 시크릿을 정기적으로 순환합니다. 이렇게 하면 시크릿이 손상된 경우 노출 창을 최소화할 수 있습니다.
  • 최소 권한을 부여합니다. 서비스가 반드시 필요한 시크릿에만 액세스할 수 있도록 합니다.
  • 시크릿 값 로깅을 피하세요. 애플리케이션 및 인프라 로깅이 시크릿 내용을 출력하지 않도록 합니다.
  • 대규모 엔터프라이즈급 배포의 경우 감사, 동적 시크릿 생성 및 IAM(ID 및 액세스 관리) 통합과 같은 고급 기능을 제공하는 HashiCorp Vault, AWS Secrets Manager 또는 Azure Key Vault와 같은 전용 시크릿 관리 솔루션을 고려하세요.

결론

Docker에서 환경 변수를 마스터한다는 것은 단순히 전달하는 방법을 아는 것 이상입니다. 일반 구성과 민감한 시크릿의 근본적인 차이를 이해하는 것을 의미합니다. 환경 변수는 애플리케이션 구성에 비교할 수 없는 유연성을 제공하지만 민감한 데이터에는 본질적으로 안전하지 않습니다.

Swarm 환경 내에서 민감한 정보를 위해 Docker Secrets를 활용하거나 단일 호스트 배포를 위해 Docker Compose의 secrets 기능을 신중하게 사용함으로써 컨테이너화된 애플리케이션의 보안 상태를 크게 향상시킬 수 있습니다. 항상 올바른 도구를 사용하여 보안을 우선시하고 모범 사례를 준수하며 민감한 데이터가 우발적인 노출로부터 보호되도록 하십시오. 이러한 규율 있는 접근 방식은 더 강력하고 유지 관리 가능하며 안전한 Docker 배포로 이어질 것입니다.