Docker Configs로 설정 데이터 저장하기

Docker Configs로 설정 데이터 저장하기

Docker swarm 서비스에서 비밀번호 같은 민감한 정보가 아닌 설정 파일들을 어떻게 안전하게 관리할 수 있는지 알려드릴게요. 이미지나 실행 중인 컨테이너 안에 설정을 욱여넣는 대신, configs를 이용하면 이미지를 최대한 범용적으로 유지하면서도 필요한 설정을 서비스에 주입할 수 있어요. 이 기능은 secrets와 비슷하지만, 저장 시 암호화되지 않고 RAM 디스크 없이 컨테이너 파일시스템에 바로 마운트된다는 차이가 있어요.

출처: 공식문서

본문

configs란 무엇인가요?

Docker swarm service configs를 사용하면 설정 파일 같은 민감하지 않은 정보를 서비스 이미지나 실행 중인 컨테이너 밖에 보관할 수 있어요. 이렇게 하면 이미지를 최대한 범용적으로 유지할 수 있고, 컨테이너에 설정 파일을 바인드 마운트하거나 환경 변수를 사용할 필요가 없어져요.

Configs는 secrets와 비슷하게 동작하지만, 저장 시 암호화되지 않고 RAM 디스크를 사용하지 않고 컨테이너의 파일시스템에 직접 마운트된다는 차이가 있어요. Configs는 언제든 서비스에 추가하거나 제거할 수 있고, 여러 서비스가 하나의 config를 공유할 수도 있어요. 환경 변수나 라벨과 함께 사용하면 더욱 유연하게 활용할 수 있답니다. Config 값은 일반 문자열이나 바이너리 콘텐츠(최대 500kb)로 저장할 수 있어요.

참고

Docker configs는 스웜 서비스에서만 사용할 수 있고, 독립 실행형 컨테이너에서는 사용할 수 없어요. 이 기능을 사용하려면 컨테이너를 스케일 1인 서비스로 실행하도록 조정해보세요.

Configs는 Linux 서비스와 Windows 서비스 모두에서 지원돼요.

Windows 지원

Docker는 Windows 컨테이너에서도 configs를 지원하지만, 구현 방식에 몇 가지 차이가 있어요. 아래 예제에서도 그 차이를 확인할 수 있으니, 다음 사항들을 꼭 기억해두세요.

  • Windows 컨테이너에는 사용자 지정 target을 가진 config 파일이 직접 바인드 마운트되지 않아요. Windows는 디렉터리가 아닌 파일의 바인드 마운트를 지원하지 않기 때문이에요. 대신 컨테이너 안의 모든 config는 C:\ProgramData\Docker\internal\configs에 마운트돼요. 이 경로는 구현 세부사항이므로 애플리케이션이 의존하면 안 돼요. 그리고 그 위치에서 컨테이너 안의 원하는 target으로 가리키는 심볼릭 링크가 생성돼요. 기본 target은 C:\ProgramData\Docker\configs예요.
  • Windows 컨테이너를 사용하는 서비스를 만들 때는 configs에 대해 UID, GID, mode 옵션을 지정할 수 없어요. 현재 configs는 컨테이너 안에서 관리자와 system 액세스 권한을 가진 사용자만 접근할 수 있어요.
  • Windows에서는 --credential-specconfig://<config-name> 형식과 함께 사용하여 서비스를 생성하거나 업데이트해요. 이렇게 하면 컨테이너가 시작되기 전에 gMSA 자격 증명 파일이 노드에 직접 전달돼요. 작업자 노드의 디스크에는 gMSA 자격 증명이 기록되지 않아요. 자세한 내용은 Deploy services to a swarm을 참고하세요.

Docker가 configs를 관리하는 방법

스웜에 config를 추가하면 Docker는 상호 TLS 연결을 통해 config를 스웜 매니저에게 보내요. Config는 암호화된 Raft 로그에 저장되고, 전체 Raft 로그는 다른 매니저들에게 복제돼요. 그래서 configs도 스웜 관리 데이터와 동일한 고가용성 보장을 받아요.

새로 생성했거나 실행 중인 서비스에 config에 대한 액세스 권한을 부여하면, config는 컨테이너 안에 파일로 마운트돼요. Linux 컨테이너에서 마운트 지점의 기본 위치는 /<config-name>이에요. Windows 컨테이너에서는 모든 config가 C:\ProgramData\Docker\configs에 마운트되고, 원하는 위치(기본값은 C:\<config-name>)를 가리키는 심볼릭 링크가 생성돼요.

config의 소유권(uidgid)을 숫자 ID 또는 사용자/그룹 이름으로 설정할 수 있고, 파일 권한(mode)도 지정할 수 있어요. 다만 Windows 컨테이너에서는 이러한 설정이 무시돼요.

  • 설정하지 않으면 config는 컨테이너 명령을 실행하는 사용자(보통 root)와 그 사용자의 기본 그룹(역시 보통 root)이 소유해요.
  • 설정하지 않으면 config는 전 세계가 읽을 수 있는 권한(mode 0444)을 가져요. 단, 컨테이너 안에 umask가 설정되어 있으면 그 값의 영향을 받아요.

언제든 서비스를 업데이트해서 추가 config에 대한 액세스 권한을 부여하거나 특정 config에 대한 액세스 권한을 취소할 수 있어요.

노드가 config에 접근할 수 있는 경우는 두 가지예요. 노드가 스웜 매니저이거나, config에 대한 액세스 권한이 부여된 서비스 태스크를 실행 중일 때예요. 컨테이너 태스크가 실행을 멈추면 해당 컨테이너에 공유되었던 configs는 메모리 파일시스템에서 마운트 해제되고 노드 메모리에서도 제거돼요.

config에 대한 액세스 권한이 있는 태스크 컨테이너를 실행하는 동안 노드가 스웜과의 연결을 잃으면, 태스크 컨테이너는 여전히 configs에 접근할 수 있지만 노드가 스웜에 다시 연결될 때까지 업데이트를 받을 수 없어요.

개별 config는 언제든 추가하거나 조회할 수 있고, 모든 config를 나열할 수도 있어요. 하지만 실행 중인 서비스가 사용 중인 config는 제거할 수 없어요. 실행 중인 서비스를 중단하지 않고 config를 제거하는 방법은 Rotate a config를 참고하세요.

configs를 더 쉽게 업데이트하거나 롤백하려면 config 이름에 버전 번호나 날짜를 붙이는 것도 좋은 방법이에요. 특정 컨테이너 안에서 config의 마운트 지점을 제어할 수 있기 때문에 이렇게 관리하기가 더 수월해져요.

스택을 업데이트하려면 Compose 파일을 수정한 다음 docker stack deploy -c <new-compose-file> <stack-name>을 다시 실행하면 돼요. 파일에 새 config를 사용하면 서비스가 새 config를 사용하기 시작해요. 단, config는 불변이에요. 기존 서비스의 파일을 변경할 수는 없고, 다른 파일을 사용하려면 새 config를 만들어야 해요.

docker stack rm을 실행하면 앱을 중지하고 스택을 내릴 수 있어요. 이때 docker stack deploy로 같은 스택 이름으로 생성된 모든 config가 제거돼요. 서비스가 참조하지 않는 config나 docker service update --config-rm 후에 남아 있던 config까지 모두 제거된다는 점을 유의하세요.

예제

이번에는 Docker configs를 어떻게 사용하는지 단계별 예제로 살펴볼게요.

참고

이 예제들은 설명을 쉽게 하기 위해 단일 엔진 스웜과 스케일 없는 서비스를 사용해요. 예제는 Linux 컨테이너를 사용하지만, Windows 컨테이너도 configs를 지원해요.

Compose 파일에서 configs 정의하고 사용하기

docker stack 명령은 Compose 파일에서 configs를 정의하는 것을 지원해요. 하지만 docker compose에서는 configs 키가 지원되지 않아요. 자세한 내용은 Compose file reference를 참고하세요.

간단한 예제: configs 시작하기

이 간단한 예제는 몇 가지 명령만으로 configs가 어떻게 동작하는지 보여줘요. 실제 활용 예제를 보려면 고급 예제: Nginx 서비스에서 configs 사용하기로 계속 진행해보세요.

  1. Docker에 config를 추가해요. docker config create 명령은 마지막 인자가 -로 설정되어 있으면 표준 입력을 읽어요.
$ echo "This is a config" | docker config create my-config -
  1. redis 서비스를 만들고 config에 대한 액세스 권한을 부여해요. 기본적으로 컨테이너는 /my-config에서 config에 접근할 수 있지만, target 옵션을 사용하면 컨테이너 안의 파일 이름을 바꿀 수 있어요.
$ docker service create --name redis --config my-config redis:alpine
  1. docker service ps를 사용해서 태스크가 문제없이 실행되고 있는지 확인해요. 정상적으로 동작한다면 출력은 다음과 비슷할 거예요.
$ docker service ps redis

ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
bkna6bpn8r1a redis.1 redis:alpine ip-172-31-46-109 Running Running 8 seconds ago
  1. docker ps를 사용해서 redis 서비스 태스크 컨테이너의 ID를 알아내요. 그러면 docker container exec로 컨테이너에 접속해서 config 데이터 파일의 내용을 읽을 수 있어요. 이 파일은 기본적으로 모든 사용자가 읽을 수 있고, config 이름과 같은 이름을 가져요. 아래 첫 번째 명령은 컨테이너 ID를 찾는 방법이고, 두 번째와 세 번째 명령은 셸 완성 기능을 이용해 자동으로 처리하는 방법이에요.
$ docker ps --filter name=redis -q

5cb1c2348a59

$ docker container exec $(docker ps --filter name=redis -q) ls -l /my-config

-r--r--r-- 1 root root 12 Jun 5 20:49 my-config

$ docker container exec $(docker ps --filter name=redis -q) cat /my-config

This is a config
  1. config를 제거해볼게요. redis 서비스가 실행 중이고 config에 접근하고 있기 때문에 제거는 실패해요.
$ docker config ls

ID NAME CREATED UPDATED
fzwcfuqjkvo5foqu7ts7ls578 hello 31 minutes ago 31 minutes ago

$ docker config rm my-config

Error response from daemon: rpc error: code = 3 desc = config 'my-config' is
in use by the following service: redis
  1. 서비스를 업데이트해서 실행 중인 redis 서비스에서 config에 대한 액세스 권한을 제거해요.
$ docker service update --config-rm my-config redis
  1. 3단계와 4단계를 다시 반복해서 서비스가 더 이상 config에 접근할 수 없는지 확인해요. service update 명령이 서비스를 다시 배포하기 때문에 컨테이너 ID는 달라져요.
$ docker container exec -it $(docker ps --filter name=redis -q) cat /my-config

cat: can't open '/my-config': No such file or directory
  1. 서비스를 중지하고 제거한 다음, Docker에서 config도 제거해요.
$ docker service rm redis

$ docker config rm my-config

간단한 예제: Windows 서비스에서 configs 사용하기

이 예제는 Microsoft Windows 10에서 Windows 컨테이너를 실행하는 Docker for Windows의 Microsoft IIS 서비스에서 configs를 사용하는 방법을 보여줘요. 웹페이지를 config에 저장하는 아주 단순한 예제예요.

이 예제는 PowerShell이 설치되어 있다고 가정해요.

  1. 다음 내용을 새 파일 index.html로 저장해요.
<html lang="en">
 <head><title>Hello Docker</title></head>
 <body>
 <p>Hello Docker! You have deployed a HTML page.</p>
 </body>
</html>
  1. 아직 스웜을 초기화하지 않았다면 초기화하거나 조인해요.
docker swarm init
  1. index.html 파일을 homepage라는 이름의 스웜 config로 저장해요.
docker config create homepage index.html
  1. IIS 서비스를 만들고 homepage config에 대한 액세스 권한을 부여해요.
docker service create
 --name my-iis
 --publish published=8000,target=8000
 --config src=homepage,target="\inetpub\wwwroot\index.html"
 microsoft/iis:nanoserver
  1. http://localhost:8000/로 IIS 서비스에 접속해보세요. 첫 번째 단계의 HTML 콘텐츠가 제공되어야 해요.

  2. 서비스와 config를 제거해요.

docker service rm my-iis

docker config rm homepage

예제: 템플릿 config 사용하기

템플릿 엔진을 사용해서 콘텐츠가 생성되는 config를 만들려면 --template-driver 파라미터에 엔진 이름을 인자로 지정하면 돼요. 템플릿은 컨테이너가 생성될 때 렌더링돼요.

  1. 다음 내용을 새 파일 index.html.tmpl로 저장해요.
<html lang="en">
 <head><title>Hello Docker</title></head>
 <body>
 <p>Hello {{ env "HELLO" }}! I'm service {{ .Service.Name }}.</p>
 </body>
</html>
  1. index.html.tmpl 파일을 homepage라는 이름의 스웜 config로 저장해요. --template-driver 파라미터에 golang 템플릿 엔진을 지정해요.
$ docker config create --template-driver golang homepage index.html.tmpl
  1. Nginx를 실행하는 서비스를 만들고 환경 변수 HELLO와 config에 대한 액세스 권한을 부여해요.
$ docker service create \
 --name hello-template \
 --env HELLO="Docker" \
 --config source=homepage,target=/usr/share/nginx/html/index.html \
 --publish published=3000,target=80 \
 nginx:alpine
  1. 서비스가 정상적으로 동작하는지 확인해요. Nginx 서버에 접근할 수 있고, 올바른 출력이 제공되는지 확인해보세요.
$ curl http://0.0.0.0:3000

<html lang="en">
 <head><title>Hello Docker</title></head>
 <body>
 <p>Hello Docker! I'm service hello-template.</p>
 </body>
</html>

고급 예제: Nginx 서비스에서 configs 사용하기

이 예제는 두 부분으로 나뉘어요. 첫 번째 부분은 사이트 인증서를 생성하는 내용으로, Docker configs와 직접적인 관련은 없어요. 하지만 두 번째 부분에서 사이트 인증서를 secrets로 저장하고 Nginx 설정을 config로 저장해서 사용할 수 있도록 준비하는 과정이에요. 이 예제에서는 config의 target 위치나 파일 권한(mode) 같은 옵션을 설정하는 방법도 보여줘요.

사이트 인증서 생성하기

사이트용 루트 CA와 TLS 인증서, 키를 생성해요. 프로덕션 사이트라면 Let’s Encrypt 같은 서비스를 사용해서 TLS 인증서와 키를 생성하는 것이 좋을 수 있어요. 하지만 이 예제에서는 명령줄 도구를 사용해요. 이 단계는 조금 복잡하지만, Docker secret으로 저장할 무언가를 만들기 위한 준비 단계일 뿐이에요. 이 하위 단계들을 건너뛰고 싶다면 Let's Encrypt 사용하기로 사이트 키와 인증서를 생성한 다음, 파일 이름을 site.keysite.crt로 지정하고 Nginx 컨테이너 설정하기로 건너뛰어도 돼요.

  1. 루트 키를 생성해요.
$ openssl genrsa -out "root-ca.key" 4096
  1. 루트 키를 사용해서 CSR을 생성해요.
$ openssl req \
 -new -key "root-ca.key" \
 -out "root-ca.csr" -sha256 \
 -subj '/C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA'
  1. 루트 CA를 구성해요. 새 파일 root-ca.cnf를 만들고 다음 내용을 붙여넣어요. 이 설정은 루트 CA가 중간 CA가 아닌 리프 인증서만 서명하도록 제한해요.
[root_ca]
basicConstraints = critical,CA:TRUE,pathlen:1
keyUsage = critical, nonRepudiation, cRLSign, keyCertSign
subjectKeyIdentifier=hash
  1. 인증서에 서명해요.
$ openssl x509 -req -days 3650 -in "root-ca.csr" \
 -signkey "root-ca.key" -sha256 -out "root-ca.crt" \
 -extfile "root-ca.cnf" -extensions \
 root_ca
  1. 사이트 키를 생성해요.
$ openssl genrsa -out "site.key" 4096
  1. 사이트 인증서를 생성하고 사이트 키로 서명해요.
$ openssl req -new -key "site.key" -out "site.csr" -sha256 \
 -subj '/C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost'
  1. 사이트 인증서를 구성해요. 새 파일 site.cnf를 만들고 다음 내용을 붙여넣어요. 이 설정은 사이트 인증서가 서버 인증에만 사용되고 인증서 서명에는 사용할 수 없도록 제한해요.
[server]
authorityKeyIdentifier=keyid,issuer
basicConstraints = critical,CA:FALSE
extendedKeyUsage=serverAuth
keyUsage = critical, digitalSignature, keyEncipherment
subjectAltName = DNS:localhost, IP:127.0.0.1
subjectKeyIdentifier=hash
  1. 사이트 인증서에 서명해요.
$ openssl x509 -req -days 750 -in "site.csr" -sha256 \
 -CA "root-ca.crt" -CAkey "root-ca.key" -CAcreateserial \
 -out "site.crt" -extfile "site.cnf" -extensions server
  1. site.csrsite.cnf 파일은 Nginx 서비스에 필요하지 않지만, 나중에 새 사이트 인증서를 생성하려면 필요해요. root-ca.key 파일은 잘 보호해두세요.

Nginx 컨테이너 설정하기

  1. HTTPS로 정적 파일을 제공하는 아주 기본적인 Nginx 설정을 만들어요. TLS 인증서와 키는 Docker secrets로 저장해서 쉽게 회전할 수 있게 해요.

현재 디렉터리에 새 파일 site.conf를 만들고 다음 내용을 넣어요.

server {
 listen 443 ssl;
 server_name localhost;
 ssl_certificate /run/secrets/site.crt;
 ssl_certificate_key /run/secrets/site.key;

 location / {
 root /usr/share/nginx/html;
 index index.html index.htm;
 }
}
  1. 키와 인증서를 나타내는 두 개의 secrets를 생성해요. 500KB보다 작은 파일이라면 무엇이든 secret으로 저장할 수 있어요. 이렇게 하면 키와 인증서를 사용하는 서비스로부터 분리할 수 있어요. 이 예제에서는 secret 이름과 파일 이름이 같아요.
$ docker secret create site.key site.key

$ docker secret create site.crt site.crt
  1. site.conf 파일을 Docker config로 저장해요. 첫 번째 파라미터는 config 이름이고, 두 번째 파라미터는 읽을 파일이에요.
$ docker config create site.conf site.conf

configs를 나열해보세요.

$ docker config ls

ID NAME CREATED UPDATED
4ory233120ccg7biwvy11gl5z site.conf 4 seconds ago 4 seconds ago
  1. Nginx를 실행하는 서비스를 만들고 두 secrets와 config에 대한 액세스 권한을 부여해요. mode를 0440으로 설정해서 파일이 소유자와 소유 그룹만 읽을 수 있고, 다른 사용자는 읽을 수 없도록 해요.
$ docker service create \
 --name nginx \
 --secret site.key \
 --secret site.crt \
 --config source=site.conf,target=/etc/nginx/conf.d/site.conf,mode=0440 \
 --publish published=3000,target=443 \
 nginx:latest \
 sh -c "exec nginx -g 'daemon off;'"

실행 중인 컨테이너 안에는 이제 다음 세 개의 파일이 존재해요.

  • /run/secrets/site.key
  • /run/secrets/site.crt
  • /etc/nginx/conf.d/site.conf
  1. Nginx 서비스가 실행 중인지 확인해요.
$ docker service ls

ID NAME MODE REPLICAS IMAGE
zeskcec62q24 nginx replicated 1/1 nginx:latest

$ docker service ps nginx

NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
nginx.1.9ls3yo9ugcls nginx:latest moby Running Running 3 minutes ago
  1. 서비스가 정상적으로 동작하는지 확인해요. Nginx 서버에 접근할 수 있고, 올바른 TLS 인증서가 사용되고 있는지 확인해보세요.
$ curl --cacert root-ca.crt https://0.0.0.0:3000

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
 body {
 width: 35em;
 margin: 0 auto;
 font-family: Tahoma, Verdana, Arial, sans-serif;
 }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support, refer to
<a href="https://nginx.org">nginx.org</a>.<br/>
Commercial support is available at
<a href="https://www.nginx.com">www.nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
$ openssl s_client -connect 0.0.0.0:3000 -CAfile root-ca.crt

CONNECTED(00000003)
depth=1 /C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA
verify return:1
depth=0 /C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost
verify return:1
---
Certificate chain
 0 s:/C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost
 i:/C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA
---
Server certificate
-----BEGIN CERTIFICATE-----
…
-----END CERTIFICATE-----
subject=/C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost
issuer=/C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA
---
No client certificate CA names sent
---
SSL handshake has read 1663 bytes and written 712 bytes
---
New, TLSv1/SSLv3, Cipher is AES256-SHA
Server public key is 4096 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
 Protocol : TLSv1
 Cipher : AES256-SHA
 Session-ID: A1A8BF35549C5715648A12FD7B7E3D861539316B03440187D9DA6C2E48822853
 Session-ID-ctx:
 Master-Key: F39D1B12274BA16D3A906F390A61438221E381952E9E1E05D3DD784F0135FB81353DA38C6D5C021CB926E844DFC49FC4
 Key-Arg : None
 Start Time: 1481685096
 Timeout : 300 (sec)
 Verify return code: 0 (ok)
  1. 다음 예제로 계속 진행하지 않는다면, 이 예제 실행 후 nginx 서비스와 저장된 secrets, config를 정리해요.
$ docker service rm nginx

$ docker secret rm site.crt site.key

$ docker config rm site.conf

이제 Nginx 서비스의 설정을 이미지에서 분리해서 구성했어요. 완전히 같은 이미지로도 별도의 설정을 가진 여러 사이트를 실행할 수 있고, 커스텀 이미지를 만들 필요도 없어요.

예제: config 회전하기

config를 회전하려면 먼저 현재 사용 중인 config와 다른 이름으로 새 config를 저장해요. 그런 다음 서비스를 다시 배포하면서 기존 config를 제거하고, 컨테이너 안의 같은 마운트 지점에 새 config를 추가해요. 이 예제는 앞선 예제를 바탕으로 site.conf 설정 파일을 회전하는 방법을 보여줘요.

  1. 로컬에서 site.conf 파일을 수정해요. index 줄에 index.php를 추가하고 파일을 저장해요.
server {
 listen 443 ssl;
 server_name localhost;
 ssl_certificate /run/secrets/site.crt;
 ssl_certificate_key /run/secrets/site.key;

 location / {
 root /usr/share/nginx/html;
 index index.html index.htm index.php;
 }
}
  1. site.conf를 사용해서 site-v2.conf라는 새 Docker config를 만들어요.
$ docker config create site-v2.conf site.conf
  1. nginx 서비스를 업데이트해서 이전 config 대신 새 config를 사용하도록 해요.
$ docker service update \
 --config-rm site.conf \
 --config-add source=site-v2.conf,target=/etc/nginx/conf.d/site.conf,mode=0440 \
 nginx
  1. docker service ps nginx를 사용해서 nginx 서비스가 완전히 다시 배포되었는지 확인해요. 배포가 완료되면 이전 site.conf config를 제거할 수 있어요.
$ docker config rm site.conf
  1. 정리를 위해 nginx 서비스와 secrets, configs를 제거해요.
$ docker service rm nginx

$ docker secret rm site.crt site.key

$ docker config rm site-v2.conf

이제 이미지를 다시 빌드할 필요 없이 nginx 서비스의 설정을 업데이트했어요.

더 알아보기 (Learn more)