[docker-swarm] docker swarm 고급 (2)

업데이트:     Updated:

카테고리:

태그:

🎯Docker Swarm 고급 (2)

  • config
  • secret

config 란?

  • config를 사용하면 서비스 이미지 또는 실행중인 컨테이너 외부에 설정 파일과 같은 민감하지 않은 정보를 저장할 수 있습니다.
  • config는 암호화되지 않으면서 Memory를 사용하지 않고 컨테이너의 파일시스템에 직접 마운트된다는 점을 제외하면 secret과 유사한 방식으로 동작합니다.
  • config는 언제든지 서비스에서 추가하거나 제거할 수 있으며 서비스에서 공유할 수 있습니다.
  • config는 환경 변수 또는 레이블과 함께 사용할 수 있습닏나.
  • config는 일반 문자열 또는 이진 데이터(최대 500kb)일 수 있습니다.

Docker에서 config를 관리하는 방법

  • Swarm에 config을 추가하면 Docker는 상호 TLS 연결을 통해 config를 Swarm 관리자에게 보냅니다.
  • config는 암호화된 Raft 로그에 저장되고 전체 Raft 로그는 다른 관리자에게 복제되어 고가용성을 보장합니다.
  • 새로 만들거나 실행 중인 서비스에 config에 대한 액세스 권한을 부여하면 config가 컨테이너에 파일로 마운트됩니다.
  • 리눅스 컨테이너에서 config는 기본적으로 /<config-name>에 마운트됩니다.
  • 윈도우 컨테이너에서 config는 기본적으로 C:\ProgramData/Docker\configs에 모두 마운트되고 C:\<config-name>과 같이 원하는 위치에 심볼릭 링크를 생성합니다.
  • config는 소유권과 권한을 설정할 수 있습니다. 이러한 설정은 windows 컨테이너에서는 무시됩니다.
    • 소유권을 설정하지 않은 경우 기본적으로 컨테이너를 실행한 user와 user의 기본 그룹이 설정됩니다.
    • 권한을 설정하지 않은 경우 기본적으로 (0444) 권한을 가지며, 이떄 umask 값의 영향을 받습니다.
  • 서비스 업데이트를 통해 config에 대한 액세스를 추가하거나 취소할 수 있습니다.
  • 노드는 Swarm 관리자 노드이거나 config에 대한 액세스 권한이 부여된 서비스 task를 실행 중인 노드에서만 config에 액세스할 수 있습니다.
  • 실행 중인 서비스에서 사용 중인 config는 제거할 수 없습니다. 실행 중인 서비스를 중단하지 않고 config을 제거하는 방법은 config 교체을 참고하세요.
  • config을 보다 쉽게 업데이트하거나 롤백하려면 config 이름에 버전 번호나 날짜를 추가하는 것이 좋습니다.

docker config 명령

# TODO 1. Docker에 config을 추가합니다. 
$ echo "This is a config" | docker config create my-config -

# docker config create 명령에서 config를 읽을 파일을 나타내는 마지막 인수로 '-'가 쓰였기 때문에  표준 입력을 읽습니다.

# TODO 2. redis 서비스를 만들고 config에 대한 액세스 권한을 부여합니다.
$ docker service create --name redis \
--config my-config \
redis:alpine

# 기본적으로 컨테이너는 '/my-config'에서 config 를 액세스할 수 있지만 'target' 옵션을 사용하여 파일 이름을 커스터마이징할 수 있습니다.

# TODO 3. docker service ps를 사용하여 task를 확인합니다.
$ 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

# TODO 4.1 docker ps를 사용하여 서비스 task 컨테이너의 ID를 가져옵니다.
$ docker ps --filter name=redis -q

5cb1c2348a59

# TODO 4.2 docker exec를 사용하여 컨테이너에 연결하고 config 파일을 확인합니다.
$ 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

# TODO 4.3 docker exec를 사용하여 컨테이너에 연결하고 config 파일의 내용을 확인합니다.
$ docker container exec $(docker ps --filter name=redis -q) cat /my-config

This is a config

# config 파일은 기본적으로 0444 권한을 갖기 때문에 모든 사용자가 읽을 수 있습니다.

# TODO 5. 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

# redis 서비스가 실행 중이고 config에 대한 액세스 권한이 있기 때문에 삭제에 실패합니다.

# TODO 6. redis 서비스를 업데이트하여 실행 중인 서비스에서 config에 대한 액세스를 제거합니다.
$ docker service update --config-rm my-config redis

# TODO 7. 서비스를 중지 및 제거하고 Docker에서 config를 제거합닏나.
$ docker service rm redis
$ docker config rm my-config

config 교체

# TODO 1. index.html 파일을 생성합니다.
$ echo "helloworld-v1" > index.html

# TODO 2. index.html 파일을 기반으로 index.html-v1 config를 생성합니다.
$ docker config create index.html-v1 index.html

# TODO 3. index.html-v1 config을 사용하여 Nginx 서비스를 생성합니다.
$ docker service create --name my-nginx \
--replicas 3 \
--config src=index.html-v1,target=/usr/share/nginx/html/index.html \
--publish published=80,target=80
nginx:latest

# TODO 4. curl을 사용하여 config가 잘 반영됐는지 확인합니다.
$ curl localhost
helloworld-v1

# TODO 5. index.html 파일을 수정합니다.
$ echo "helloworld-v2" > index.html

# TODO 6. 수정된 index.html 파일을 기반으로 index.html-v2 config을 생성합니다.
$ docker config create index.html-v2 index.html

# TODO 7. 이전 config 대신 새로운 config를 사용하도록 서비스를 업데이트합니다.
$ docker service update \
--config-rm index.html-v1 \
--config-add source=index.html-v2,target=/usr/share/nginx/html/index.html \
my-nginx

# TODO 8. 서비스 업데이트가 완료되고 config가 수정됐는지 curl을 통해 확인합니다.
$ curl localhost
helloworld-v2

# TODO 9. 기존 config를 삭제합니다.
$ docker config rm index.html-v1

secret 란?

  • secret은 암호, SSH 개인 키, SSL 인증서 등 민감한 정보를 액세스가 필요한 컨테이너에만 안전하게 전송할 수 있다.
  • secret은 Docker Swarm에서 유휴 상태 및 전송 시에 암호화됩니다.
  • secret은 명시적 액세스 권한이 부여된 서비스에서 해당 서비스 task가 실행되는 동안에만 접근할 수 있습니다.
  • secret을 사용하여 설정 파일과 같은 민감하지 않은 데이터를 관리할 수도 있습니다. 그러나 Docker는 이러한 데이터를 저장하기 위한 config를 지원합니다.

Docker에서 secret을 관리하는 방법

  • Swarm에 secret을 추가하면 Docker는 상호 TLS 연결을 통해 Swarm 관리자에게 secret을 보냅니다.
  • secret은 Raft 로그에 저장되고 전체 Raft 로그는 다른 관리자에게 복제되어 고가용성을 보장합니다.
  • 새로 생성되거나 실행 중인 서비스에 secret에 대한 액세스 권한을 부여하면 복호화된 secret이 컨테이너의 인메모리 파일시스템 내에 마운트됩니다.
  • 리눅스 컨테이너에서 마운트 위치는 기본적으로 /run/secrets/<secret_name> 입니다.
  • 윈도우 컨테이너에서 마운트 위치는 기본적으로 C:\ProgramData\Docker\secrets 입니다.
  • 마운트 위치는 사용자가 지정한 위치로 변경할 수 있습니다.
  • 언제든지 secret에 대한 액세스 권한을 부여하거나 취소하도록 서비스를 업데이트할 수 있습니다.
  • 노드는 노드가 Swarm 관리자이거나 secret에 대한 액세스 권한이 부여된 서비스 task가 실행 중인 노드에서만 secret에 접근할 수 있습니다.
  • task 실행이 중지되면 해당 task에 공유된 secret이 해당 컨테이너의 인메모리 파일시스템에서 마운트 해제되고 노드의 메모리에서 플러시됩니다.
  • 실행 중인 서비스에서 사용 중인 secret은 제거할 수 없습니다. 실행 중인 서비스를 중단하지 않고 secret을 제거하는 방법은 암호 교체를 참고하세요.
  • secret을 보다 쉽게 업데이트하거나 롤백하려면 secret 이름에 버전 번호 또는 날짜를 추가하는 것이 좋습니다.

docker secret 명령

# TODO 1. Docker에 secret을 추가합니다. 
$ echo "This is a secret" | docker secret create my_secret -

# 읽을 파일을 나타내는 docker secret create 명령의 마지막 인수가 '-'로 설정되었기 때문에 이 명령은 표준 입력으로부터 읽습니다.

# TODO 2. redis 서비스를 만들고 secret에 대한 액세스 권한을 부여합니다.
$ docker service create --name redis \
--secret my_secret \
redis:alpine

# 기본적으로 컨테이너는 /run/secrets/<secret_name> 에서 secret에 액새스할 수 있지만 'target' 옵션을 사용하여 파일 이름을 사용자 지정할 수 있습니다.

# TODO 3. docker service ps 명령을 사용하여 task가 문제 없이 실행되고 있는 지 확인합니다.
$ docker service ps redis
ID             NAME      IMAGE          NODE        DESIRED STATE   CURRENT STATE            ERROR     PORTS
oof8fjabuodd   redis.1   redis:alpine   manager02   Running         Running 13 seconds ago

# TODO 4.1 docker ps 명령을 사용하여 서비스 task 컨테이너의 ID를 가져옵니다.
$ docker ps --fliter name=redis -q
f593f75ddc4e

# TODO 4.2 docker container exec 명령을 사용하여 컨테이너에 연결하고 secret 파일을 읽습니다.
$ docker container exec $(docker ps --filter name=redis -q) ls -l /run/secrets/my_secret
-r--r--r--    1 root     root            15 Jan 27 12:43 /run/secrets/my_secret

$ docker container exec $(docker ps --filter name=redis -q) cat /run/secrets/my_secret
This is secret

# 기본적으로 컨테이너의 인모메리 파일시스템에 마운트된 secret의 파일 이름은 secret 이름과 같고 모든 사람이 읽을 수 있는 권한이 부여됩니다.

# TODO 5. secret을 제거합니다.
$ docker secret ls
ID                          NAME        DRIVER    CREATED          UPDATED
p8r88mgp0a7y0krmw2f6y75ab   my_secret             11 minutes ago   11 minutes ago

$ docker secret rm my_secret
Error response from daemon: rpc error: code = InvalidArgument desc = secret 'my_secret' is in use by the following service: redis

# redis 서비스가 실행 중이고 secret에 액세스할 수 있기 때문에 제거에 실패합니다.

# TODO 6. redis 서비스를 업데이트하여 실행 중인 서비스에서 secret에 대한 액세스를 제거합니다.
$ docker service update --secret-rm my_secret redis

# TODO 7. 재배포된 서비스에서 secret에 접근할 권한이 없는지 확인합니다.
$ docker container exec $(docker ps --filter name=redis -q) ls -l /run/secrets/my_secret
ls: /run/secrets/my_secret: No such file or directory

# TODO 8. 서비스를 중지 및 제거하고 Docker에서 secret을 제거합니다.
$ docker service rm redis
$ docker secret rm my_secret

암호 교체

# TODO 1. MySQL에 대한 임의의 영문자와 숫자로 조합된 secret을 생성합니다.
$ openssl rand -base64 20 | docker secret create mysql_password -
ln3k4tpp5l9c60oezq0zwsinr

$ openssl rand -base64 20 | docker secret create mysql_root_password -
xal6giwy2wluo5cxohxnbpssl

$ docker secret ls
D                          NAME                  DRIVER    CREATED          UPDATED
ln3k4tpp5l9c60oezq0zwsinr   mysql_password                  39 seconds ago   39 seconds ago
xal6giwy2wluo5cxohxnbpssl   mysql_root_password             25 seconds ago   25 seconds ago

# TODO 2. mysql 서비스를 생성합니다.
$ docker service create \
--name mysql \
--replicas 1 \
--mount type=volume,source=mydata,destination=/var/lib/mysql \
--secret source=mysql_root_password,target=mysql_root_password \
--secret source=mysql_password,target=mysql_password \
-e MYSQL_ROOT_PASSWORD_FILE="/run/secrets/mysql_root_password" \
-e MYSQL_PASSWORD_FILE="/run/secrets/mysql_password" \
-e MYSQL_USER="admin"
-e MYSQL_DATABASE="test" \
mysql:latest

# TODO 3. 새 secret을 생성하고 이름은 mysql_password_v2로 합니다.(해당 예제에서는 mysql_root_password는 변경하지 않습니다.)
$ openssl rand -base64 20 | docker secret create mysql_password_v2 -

# TODO 4. 이전 secret과 새로 생성한 secret에 대한 액세스 권한을 모두 부여하도록 mysql 서비스를 업데이트합니다.
$ docker service update \
--secret-rm mysql_password \
mysql

$ docker service update \
--secret-add source=mysql_password,target=old_mysql_password
--secret-add source=mysql_password_v2,target=mysql_password \
mysql

# TODO 5. MySQL 서비스에서 이전 secret에 대한 액세스 권한을 취소하고 Docker에서 이전 secret을 제거합니다.
$ docker service update \
--secret-rm mysql_password \
mysql

$ docker secret rm mysql_password

이미지 빌드에서 secret 지원

  • 민감한 데이터가 환경 변수로 필요한 컨테이너를 개발하는 경우 secret을 활용하도록 이미지를 조정하는 것이 좋습니다.
  • 이를 수행하는 한 가지 방법은 컨테이너를 생성할 때 환경 변수를 파일에서도 읽을 수 있도록 하는 것입니다.
  • 예를 들어, WordPress 컨테이너를 시작할 때 필요한 환경 변수인 WORDPRESS_DB_PASSWORD는 파일에서도 해당 값을 읽을 수 있도록 WORDPRESS_DB_PASSWORD_FILE 환경 변수가 포함되도록 이미지가 업데이트 되었습니다.

docker-compose에서 secret 사용

version: "3.9"

services:
   db:
     image: mysql:latest
     volumes:
       - db_data:/var/lib/mysql
     environment:
       MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD_FILE: /run/secrets/db_password
     secrets:
       - db_root_password
       - db_password

   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
     secrets:
       - db_password


secrets:
   db_password:
     file: db_password.txt
   db_root_password:
     file: db_root_password.txt

volumes:
    db_data:

📌출처

Docker Docs


👍 개인 공부 기록용 블로그입니다. 오류나 조언이 있으시면 언제든지 댓글 혹은 메일로 남겨주시면 감사하겠습니다! 😄

맨 위로 이동하기

docker 카테고리 내 다른 글 보러가기

댓글남기기