4. Working with storage and volumes

2024. 3. 26. 16:59Docker

이 장에서 다루는 내용

  • 마운트 포인트 소개
  • 호스트와 컨테이너 사이에 데이터 공유하는 방법
  • 컨테이너 사이에 데이터 공유하는 방법
  • 임시, 메모리 내 파일 시스템 사용하기
  • 볼륨을 사용한 데이터 관리
  • 볼륨 플러그인을 사용한 고급 스토리지

이 책에서 지금까지 몇 가지 프로그램을 설치하고 실행해 보았습니다. 몇 가지 간단한 예제를 보았지만 실제 필드와 유사한 것은 실행해보지 않았습니다. 처음 세 장의 예제와 실제 필드의 차이는 실제 필드에서 프로그램들이 데이터를 다룬다는 것입니다. 이 장에서는 Docker 볼륨과 컨테이너를 사용하여 데이터를 관리할 때 사용할 전략을 소개합니다.

컨테이너 내부에서 데이터베이스 프로그램을 실행하는 것이 어떤 모습일지 생각해 보십시오. 소프트웨어를 이미지와 함께 패키징하고, 컨테이너를 시작할 때 빈 데이터베이스를 초기화할 수 있습니다. 프로그램이 데이터베이스에 연결하여 데이터를 입력할 때, 그 데이터는 어디에 저장되나요? 컨테이너 내부의 파일에 저장되나요? 컨테이너를 중지하거나 제거할 때 그 데이터에는 어떤 일이 발생하나요? 데이터베이스 프로그램을 업그레이드하고 싶을 때 데이터를 어떻게 이동시킬까요? 클라우드 머신이 종료될 때 그 저장소에는 어떤 일이 발생하나요?

다른 상황에서는 다른 컨테이너 안에서 몇 가지 다른 웹 애플리케이션을 실행하고 있다고 생각해 보십시오. 컨테이너가 지속되는 것보다 로그 파일을 어디에 작성해야 하나요? 문제를 해결하기 위해 그 로그에 어떻게 접근할 수 있나요? 로그 다이제스트 도구와 같은 다른 프로그램은 그 파일에 어떻게 접근할 수 있나요?

유니온 파일 시스템은 장기간 유지되는 데이터를 다루거나 컨테이너 간, 또는 컨테이너와 호스트 사이에 데이터를 공유하는 데 적합하지 않습니다. 이 모든 질문에 대한 답은 컨테이너 파일 시스템과 마운트 포인트를 관리하는 것과 관련이 있습니다.

 

4.1 File trees and mount points

다른 운영 체제와 달리, 리눅스는 모든 저장소를 단일 트리로 통합합니다. 디스크 파티션 또는 USB 디스크 파티션과 같은 저장 장치는 단일 트리의 특정 위치에 연결됩니다. 이러한 위치를 마운트 포인트라고 합니다. 마운트 포인트는 트리 내의 위치, 해당 지점에서 데이터에 대한 접근 속성(예: 쓰기 가능성), 그리고 해당 지점에 마운트된 데이터의 출처(예: 특정 하드 디스크, USB 장치 또는 메모리 백업 가상 디스크)를 정의합니다. 그림 4.1은 여러 저장 장치로 구성된 파일 시스템을 보여주며, 각 장치는 특정 위치와 접근 수준에 마운트됩니다.

그림 4.1 Storage devices attached to a filesystem tree at their mount point

 

마운트 포인트는 리눅스 환경에서 파일 트리가 구체적으로 어떻게 저장 장치에 매핑되는지를 몰라도 소프트웨어 및 사용자가 파일 트리를 사용할 수 있게 합니다. 이는 특히 컨테이너 환경에서 유용합니다. 각 컨테이너는 MNT 네임스페이스와 고유한 파일 트리 루트를 가지고 있습니다. 이에 대한 자세한 내용은 제6장에서 자세히 다루지만, 지금은 컨테이너가 생성된 이미지가 해당 컨테이너의 파일 트리 루트 또는 / 지점에 마운트되고 각 컨테이너가 다른 일련의 마운트 포인트를 가지고 있다는 것을 이해하는 것으로 충분합니다. 

로직은 다양한 저장 장치가 파일 트리의 다양한 지점에 마운트될 수 있다면, 컨테이너 파일 트리의 다른 지점에 비이미지 관련 저장 장치를 마운트할 수 있습니다. 이것이 바로 컨테이너가 호스트 파일 시스템의 저장 공간에 액세스하고 컨테이너 간에 저장 공간을 공유하는 방법입니다.

 

이 장의 나머지 부분에서는 컨테이너 내에서 스토리지와 마운트 포인트를 관리하는 방법에 대해 자세히 다룹니다. 가장 먼저 이해해야 할 것은 컨테이너에 마운트되는 세 가지 가장 일반적인 유형의 스토리지입니다:

  • Bind mounts
  • 메모리 내 스토리지
  • Docker 볼륨

이러한 스토리지 유형은 다양한 방식으로 사용될 수 있습니다. 그림 4.2는 컨테이너 파일 시스템의 예를 보여줍니다. 해당 파일 시스템은 이미지에서 파일을 시작하여, /tmp에 메모리 내 tmpfs를 추가하고, 호스트에서 구성 파일을 bind mount하며, 로그를 호스트의 Docker 볼륨에 기록합니다.

 

그림 4.2 Example of common container storage mounts

 

세 가지 유형의 마운트 포인트는 모두 docker run 및 docker create 하위 커맨에서 --mount 플래그를 사용하여 생성할 수 있습니다.

※Image FS란? Docker Image File System

가상 머신이나 컨테이너와 같은 격리된 환경에서 실행되는 애플리케이션의 경우, 실제 하드웨어 자원이나 운영 체제의 다른 소프트웨어 레이어에 접근하는 것은 중요한 도전과제입니다. 이런 환경에서 실행되는 애플리케이션들은 종종 자체적인 네트워크, 저장 공간, 및 컴퓨팅 리소스를 가지고 있으며, 이러한 리소스들은 일반적으로 호스트 시스템의 리소스를 기반으로 합니다. 이런 리소스들 사이의 연결 및 관리를 위해 오버레이 드라이버가 사용됩니다. 

오버레이 네트워크
Docker의 오버레이 네트워크는 이러한 컨셉의 좋은 예입니다. 오버레이 네트워크는 다른 호스트에 걸쳐있는 여러 Docker 컨테이너들이 서로 통신할 수 있도록 해주는 가상 네트워크입니다. 이 네트워크는 물리적 네트워크 위에 구축되며, 컨테이너가 마치 같은 물리적 네트워크 내에 있는 것처럼 통신할 수 있게 해줍니다. 이를 통해 개발자는 컨테이너를 물리적 위치에 관계없이 동일한 네트워크 내에 있는 것처럼 구성할 수 있습니다.

오버레이 드라이버의 기능
1. 자원 접근 중재: 오버레이 드라이버는 가상 환경 내의 애플리케이션과 실제 하드웨어 자원 사이에서 중재자 역할을 합니다. 이를 통해, 가상 환경에서 실행되는 애플리케이션이 필요로 하는 컴퓨팅 리소스, 스토리지, 네트워킹 등에 접근할 수 있게 해줍니다.
2. 투명성: 오버레이 드라이버는 애플리케이션과 실제 리소스 사이의 복잡성을 추상화합니다. 이는 애플리케이션이 마치 자체적인 독립적 환경 내에서 실행되는 것처럼 느끼게 하면서도, 필요한 리소스에는 여전히 접근할 수 있게 해줍니다.
3. 스케일링과 이동성: 컨테이너화된 애플리케이션의 경우, 오버레이 드라이버는 애플리케이션을 쉽게 스케일링하고, 필요에 따라 다른 호스트로 이동시킬 수 있는 유연성을 제공합니다. 이는 오버레이 네트워크를 통해, 컨테이너가 어디에 위치하든 상관없이 서로 통신할 수 있게 해주기 때문입니다.
4. 보안: 오버레이 네트워크는 네트워크 트래픽을 격리시켜, 다른 네트워크 세그먼트의 컨테이너들이 서로 통신하지 못하게 할 수 있습니다. 이는 멀티 테넌시 환경에서 특히 중요한 보안 기능입니다.

이처럼 오버레이 드라이버는 가상화된 환경에서의 리소스 관리와 네트워킹을 용이하게 하며, 컨테이너화된 애플리케이션의 배포, 관리, 스케일링을 효율적으로 만들어줍니다. Docker와 같은 컨테이너 오케스트레이션 도구들은 이러한 오버레이 드라이버를 활용하여 복잡한 애플리케이션을 쉽게 관리할 수 있는 강력한 기능을 제공합니다.

 

리눅스에서 tmpfs는 일시적인 파일 저장을 위한 임시 파일 시스템입니다. tmpfs는 주 메모리(RAM)와 스왑 공간을 사용하여 파일 시스템을 생성하며, 이로 인해 데이터 읽기/쓰기 작업이 매우 빠릅니다. tmpfs에 저장된 데이터는 휘발성이기 때문에 시스템이 재부팅되면 사라집니다. 이는 tmpfs가 일시적 데이터를 저장하기에 적합함을 의미하며, 디스크 I/O 부하를 줄이는 데 유용하게 사용됩니다.

tmpfs의 특징
1. 고속 접근: 데이터가 RAM에 저장되기 때문에, 디스크 기반의 파일 시스템보다 훨씬 빠른 데이터 접근 속도를 제공합니다.
2. 휘발성: tmpfs에 저장된 데이터는 임시적이며, 시스템이 재시작되면 사라집니다. 이는 일시적인 데이터를 처리할 때 유용합니다.
3. 유연한 메모리 관리: tmpfs는 필요에 따라 메모리를 할당하고 해제합니다. 사용되지 않는 메모리는 다른 프로세스나 시스템 요구사항에 사용될 수 있습니다.
4. 사용자 설정 가능: 마운트 시점에 크기, 권한 등을 사용자가 설정할 수 있습니다.

사용 사례
1. 임시 파일 저장: 애플리케이션에서 생성되는 임시 파일이나 캐시를 저장하는 데 사용됩니다. 이는 시스템의 디스크 I/O 부하를 줄이고 성능을 향상시키는 데 도움이 됩니다.
2. 세션 데이터 저장: 웹 서버의 세션 데이터 같은, 일시적이지만 빠른 접근이 필요한 데이터를 저장하는 데 사용됩니다.
3. 데이터 처리: 데이터 분석이나 처리 과정에서 생성되는 임시 데이터 파일을 저장하는 데 사용할 수 있습니다. 처리가 완료되면 이 데이터는 자동으로 사라집니다.

사용 방법
tmpfs는 리눅스 시스템에서 다음과 같이 마운트하여 사용할 수 있습니다:
mount -t tmpfs -o size=512m tmpfs /path/to/directory
이 커맨드는 시스템의 /path/to/directory 위치에 크기가 512MB인 tmpfs 파일 시스템을 마운트합니다. 마운트된 tmpfs의 크기와 위치는 필요에 따라 조정할 수 있습니다. tmpfs의 사용은 일시적이고 빠른 데이터 접근이 필요한 경우에 매우 유용하며, 시스템의 전체적인 성능 향상에 기여할 수 있습니다.

 

4.2 Bind mounts

바인드 마운트는 호스트 파일 시스템 트리의 일부를 다른 위치[컨테이너 파일 시스템]에 다시 마운트하는 데 사용되는 마운트 포인트입니다. 컨테이너 작업 중에는 바인드 마운트가 호스트 파일 시스템의 사용자 지정 위치를 컨테이너 파일 트리의 특정 지점에 연결합니다. 바인드 마운트는 호스트가 컨테이너에서 실행 중인 프로그램에서 필요로 하는 파일이나 디렉토리를 제공하거나, 해당 컨테이너화된 프로그램이 사용자나 컨테이너 외부에서 실행 중인 프로그램에 의해 처리되는 파일 또는 로그를 생성하는 경우에 유용합니다.

그림 4.3의 예를 고려해 보겠습니다. 호스트에 민감한 구성이 필요한 웹 서버를 실행하고 있고, 접근 로그를 로그 배송 시스템에서 전달해야 한다고 가정해 보겠습니다. 웹 서버를 컨테이너에서 시작하고 구성 위치와 웹 서버가 로그를 작성하는 위치를 바인드 마운트할 수 있습니다.

Figure 4.3 Host files shared as a bind-mount volumes

 

여러분들이 직접 시도해 볼 수 있습니다. 먼저 placeholder 로그 파일을 만들고 example.conf라는 특별한 NGINX 구성 파일을 만듭니다. 다음 커맨드를 사용하여 파일을 생성하고 내용을 채웁니다:

touch ~/example.log
cat > ~/example.conf <<EOF
server {
 listen 80;
 server_name localhost;
 access_log /var/log/nginx/custom.host.access.log main;
 location / {
 root /usr/share/nginx/html;
 index index.html index.htm;
 }
}
EOF

 

cat (concatenate) 커맨드는 UNIX 및 UNIX 계열 시스템(리눅스 포함)에서 가장 기본적이면서도 강력한 텍스트 관련 유틸리티 중 하나입니다. 주로 텍스트 파일의 내용을 출력하거나, 여러 파일의 내용을 연결하여 출력하고, 새 파일을 생성하거나 기존 파일에 내용을 추가하는 데 사용됩니다.

기본 사용법
1. 단일 파일 내용 출력: 가장 간단한 사용 예는 단일 파일의 내용을 표준 출력(대부분의 경우 터미널 화면)으로 보내는 것입니다.
cat filename.txt
이 커맨드는 filename.txt의 내용을 화면에 표시합니다.

2. 여러 파일 내용 연결하여 출력: 여러 파일의 내용을 순서대로 출력하려면 파일 이름을 공백으로 구분하여 나열합니다.
cat file1.txt file2.txt file3.txt
이 커맨드는 file1.txt, file2.txt, file3.txt의 내용을 순서대로 화면에 출력합니다.

3. 새 파일 생성: cat 커맨드와 출력 리다이렉션(>)을 사용하여 새 파일을 생성하고 텍스트를 추가할 수 있습니다.
cat > newfile.txt
이후 입력하는 모든 텍스트는 newfile.txt에 저장됩니다. 입력을 종료하려면 Ctrl+D(EOF)를 사용합니다.

4. 기존 파일에 내용 추가: 출력 리다이렉션을 두 개 사용하여(>>) 기존 파일에 새로운 내용을 추가할 수 있습니다.
cat >> existingfile.txt
이 커맨드 새로운 텍스트를 existingfile.txt 파일의 끝에 추가합니다.


고급 사용법
표준 입력으로부터 파일 생성: cat을 사용하여 표준 입력(키보드)에서 받은 내용으로 파일을 생성할 수 있습니다. 이 방법은 스크립트나 다른 프로그램의 출력을 파일로 저장할 때 유용합니다.
파일 번호 라인 출력: cat -n을 사용하면 각 줄 앞에 줄 번호가 붙어 출력됩니다.
cat -n file.txt
빈 줄 제거: cat -s 옵션을 사용하면 파일에서 연속된 빈 줄을 하나의 빈 줄로 축소하여 출력합니다.

탭 문자 보이기: cat -T 옵션을 사용하면 탭 문자를 ^I로 표시하여 출력합니다.

주의사항
cat 커맨드는 텍스트 파일에 최적화되어 있으며, 이진 파일을 다룰 때는 예상치 못한 동작을 할 수 있습니다.
 큰 파일을 cat으로 출력할 때는 터미널 화면을 매우 빠르게 스크롤할 수 있으므로, less나 more 같은 페이저 프로그램과 함께 사용하는 것이 좋습니다.

cat 커맨드는 그 사용법이 단순함에도 불구하고 리눅스 시스템에서 텍스트 파일을 다룰 때 매우 중요하고 유용한 도구입니다.

 

이 구성 파일을 사용하여 서버를 시작하면 NGINX의 기본 사이트를 http://localhost/에서 제공하며 해당 사이트의 액세 로그가 컨테이너 내의 /var/log/nginx/custom.host.access.log 파일에 기록됩니다. 다음 커맨드는 새로운 구성이 서버의 구성 루트에 바인드 마운트된 상태로 NGINX HTTP 서버를 컨테이너에서 시작합니다:

CONF_SRC=~/example.conf; \
CONF_DST=/etc/nginx/conf.d/default.conf; \
LOG_SRC=~/example.log; \
LOG_DST=/var/log/nginx/custom.host.access.log; \
docker run -d --name diaweb \
 --mount type=bind,src=${CONF_SRC},dst=${CONF_DST} \
 --mount type=bind,src=${LOG_SRC},dst=${LOG_DST} \
 -p 80:80 \
 nginx:latest

 

이 컨테이너가 실행되면 웹 브라우저를 http://localhost/로 지정하여 NGINX hello-world 페이지를 볼 수 있으며, 컨테이너 로그 스트림 [docker logs diaweb의 실행으로 콘솔에 출력되는 로그]에서는 접근 로그를 볼 수 없습니다. 그러나 호스트 시스템 홈 디렉토리의 example.log 파일을 검사[cat ~/example.log]하면 해당 로그를 볼 수 있습니다.
이 예제에서는 --mount 옵션을 type=bind 옵션과 함께 사용했습니다. 다른 두 마운트 파라미인 src 및 dst는 호스트 파일 트리의 소스 위치와 컨테이너 파일 트리의 대상 위치를 정의합니다. 절대 경로를 지정해야 하지만, 이 예제에서는 명령을 읽기 쉽게 만들기 위해 셸 확장[$]과 셸 변수[CONF_SRC/CONF_DST/LOG_SRC/LOG_DST]를 사용했습니다.
이 예제는 볼륨의 중요한 기능을 다루고 있습니다. 컨테이너 파일 시스템에 볼륨을 마운트하면 해당 위치에서 이미지가 제공하는 내용이 대체됩니다. 디폴트로 nginx:latest 이미지는 /etc/nginx/conf.d/default.conf에 디폴트 구성을 제공하지만, bind 마운트를 해당 경로에 대해 만들면 이미지가 제공하는 내용이 호스트의 내용으로 대체됩니다. 이 동작은 나중에 챕터에서 다루는 다형적 컨테이너 패턴의 기초입니다.
이 사용 사례를 확장해 보면, NGINX 웹 서버가 구성 볼륨의 내용을 변경할 수 없도록 보장하고 싶을 수 있습니다. 신뢰할 수 있는 소프트웨어라도 취약점이 존재할 수 있으며, 웹 사이트에 대한 공격의 영향을 최소화하는 것이 가장 좋습니다. 다행히도 Linux는 마운트 지점을 읽기 전용으로 만드는 메커니즘을 제공합니다. 이를 위해 마운트 사양에 readonly=true 인수를 추가할 수 있습니다. 예를 들어, 예제에서는 다음과 같이 실행 명령을 변경해야 합니다:

docker rm -f diaweb
CONF_SRC=~/example.conf; \
CONF_DST=/etc/nginx/conf.d/default.conf; \
LOG_SRC=~/example.log; \
LOG_DST=/var/log/nginx/custom.host.access.log; \
docker run -d --name diaweb \
 --mount type=bind,src=${CONF_SRC},dst=${CONF_DST},readonly=true \ 
 --mount type=bind,src=${LOG_SRC},dst=${LOG_DST} \
 -p 80:80 \
 nginx:latest

 

읽기 전용 마운트(readonly=true)를 생성함으로써 컨테이너 내부의 모든 프로세스가 볼륨의 내용을 수정하는 것을 방지할 수 있습니다. 이를 확인하기 위해 간단한 테스트를 실행해 볼 수 있습니다:

docker exec diaweb \
 sed -i "s/listen 80/listen 8080/" /etc/nginx/conf.d/default.conf

 

이 명령은 diaweb 컨테이너 내에서 sed 명령을 실행하고 구성 파일을 수정하려고 합니다. 그러나 파일이 읽기 전용으로 마운트되어 있기 때문에 명령이 실패합니다.
bind mount의 첫 번째 문제는 그들이 일반적으로 portable 컨테이너 description들을 특정 호스트의 파일 시스템에 묶는다는 것입니다. 컨테이너 description이 특정 위치의 콘텐츠에 의존하는 경우 해당 description은 해당 콘텐츠를 사용할 수 없거나 다른 위치에 있을 경우 해당 위치로 이동할 수 없습니다.
다음으로 중요한 문제는 다른 컨테이너와의 충돌 가능성을 만든다는 것입니다. 동일한 호스트 위치를 데이터 저장소로 사용하는 Cassandra의 여러 인스턴스를 시작하는 것은 좋지 않은 아이디어입니다. 이 경우 각 인스턴스가 동일한 파일 세트를 경쟁할 것입니다. 파일 잠금과 같은 다른 도구 없이는 데이터베이스의 손상이 발생할 가능성이 높습니다.
바인드 마운트는 워크스테이션, 특수한 관심을 가진 머신 또는 전통적인 구성 관리 도구와 결합된 시스템에서 적절한 도구입니다. 일반화된 플랫폼이나 하드웨어 풀에서 이러한 특정 바인딩을 피하는 것이 좋습니다.

 

바인드 마운트는 Docker 초기부터 있었습니다. 바인드 마운트는 볼륨에 비해 제한된 기능을 가집니다. 바인드 마운트를 사용할 때, 호스트의 파일이나 디렉토리가 컨테이너에 마운트됩니다. 이 파일이나 디렉토리는 호스트의 절대 경로로 참조됩니다. 반면에, 볼륨을 사용할 때는 호스트의 Docker 저장소 디렉토리 내에 새로운 디렉토리가 생성되고, Docker가 그 디렉토리의 내용을 관리합니다.
파일이나 디렉토리가 Docker 호스트에 이미 존재할 필요는 없습니다. 아직 존재하지 않는 경우, 요구에 따라 생성됩니다. 바인드 마운트는 매우 빠른 성능을 제공하지만, 호스트의 파일 시스템에 특정 디렉토리 구조가 사용 가능해야 한다는 제한이 있습니다. 새로운 Docker 애플리케이션을 개발 중이라면, 명명된 볼륨을 사용하는 것을 고려해보세요. Docker CLI 커맨드를 사용해 바인드 마운트를 직접 관리할 수는 없습니다.

 

바인드 마운트를 사용할 때, 호스트 파일 시스템 트리의 특정 부분(즉, 특정 디렉토리나 파일)을 컨테이너의 파일 시스템 내의 다른 위치에 마운트함으로써, 호스트 시스템과 컨테이너 간의 파일 공유를 가능하게 합니다.

바인드 마운트의 기능과 사용 사례
1. 파일 공유: 호스트 시스템의 파일이나 디렉토리를 컨테이너 내부에서 직접 접근할 수 있도록 합니다. 이는 개발 과정에서 코드나 리소스 파일을 컨테이너에서 즉시 사용할 수 있게 하여, 개발과 테스트의 효율성을 높일 수 있습니다.
2. 데이터 보존: 컨테이너가 생성한 데이터를 컨테이너가 삭제되어도 보존하고 싶을 때 사용할 수 있습니다. 예를 들어, 로그 파일이나 데이터베이스 파일을 호스트 시스템에 저장하여, 컨테이너가 삭제된 후에도 해당 데이터에 접근할 수 있게 합니다.
3. 구성 파일 공유: 호스트 시스템에 있는 구성 파일을 컨테이너와 공유하여, 동일한 구성을 여러 컨테이너가 사용할 수 있도록 합니다. 이는 중앙 집중식 구성 관리를 용이하게 합니다.

바인드 마운트 사용 예
다음은 Docker 컨테이너에서 바인드 마운트를 사용하는 기본적인 예시 명령어입니다:
docker run -v /host/path:/container/path -it image_name
이 명령어는 호스트 시스템의 /host/path 디렉토리를 컨테이너의 /container/path 위치에 마운트합니다. 이로써, 컨테이너 내부에서 실행되는 프로그램은 호스트의 해당 디렉토리에 있는 파일을 마치 자신의 파일 시스템 내부에 있는 것처럼 사용할 수 있게 됩니다.

바인드 마운트는 호스트 시스템의 파일 시스템 트리의 일부를 컨테이너 내의 다른 위치에 마운트하는 기술로, 호스트와 컨테이너 간의 파일 공유 및 데이터 관리에 매우 유용합니다. 이는 개발, 데이터 보존, 구성 관리 등 다양한 용도로 활용될 수 있습니다.

 

 

4.3 In-memory storage[tmpfs mounts]

볼륨과 바인드 마운트를 사용하면 호스트 기기와 컨테이너 간에 파일을 공유하여 컨테이너가 중지된 후에도 데이터를 유지할 수 있습니다.
리눅스에서 Docker를 실행 중이라면, 세 번째 옵션인 tmpfs 마운트를 사용할 수 있습니다. tmpfs 마운트로 컨테이너를 생성하면, 컨테이너는 컨테이너의 쓰기 가능 레이어 외부에서 파일을 생성할 수 있습니다.
볼륨과 바인드 마운트와 달리, tmpfs 마운트는 임시적이며 호스트 메모리에만 유지됩니다. 컨테이너가 중지되면 tmpfs 마운트는 제거되고, 거기에 쓰여진 파일들은 유지되지 않습니다.

tmpfs 마운트의 제한 사항
볼륨과 바인드 마운트와 달리, 컨테이너 간에 tmpfs 마운트를 공유할 수 없습니다.
이 기능은 리눅스에서 Docker를 실행 중일 때만 사용할 수 있습니다.
tmpfs에 권한을 설정하는 것은 컨테이너 재시작 후 권한이 재설정될 수 있습니다. 일부 경우에는 uid/gid를 설정하는 것이 해결책이 될 수 있습니다.

 

 

대부분의 서비스 소프트웨어와 웹 애플리케이션은 개인 키 파일, 데이터베이스 비밀번호, API 키 파일 또는 기타 민감한 구성 파일을 사용하며, 업로드 버퍼링 공간이 필요합니다. 이러한 경우에는 이미지에 그러한 유형의 파일을 포함하지 않거나 디스크에 쓰지 않는 것이 중요합니다. 대신에 메모리 내 스토리지를 사용해야 합니다. 메모리 내 스토리지를 컨테이너에 추가하는 방법은 특수한 유형의 마운트를 사용하는 것입니다.
mount 플래그에서 type 옵션을 tmpfs로 설정하세요. 이것은 컨테이너의 파일 트리에 기반한 메모리 기반 파일 시스템을 마운트하는 가장 쉬운 방법입니다. 다음 명령을 고려해 보세요:

docker run --rm \
 --mount type=tmpfs,dst=/tmp \
 --entrypoint mount \
 alpine:latest -v

 

--entrypoint 옵션
Docker에서 docker run 명령어를 사용할 때 --entrypoint 옵션은 컨테이너가 시작될 때 실행되는 기본 명령어를 오버라이드(덮어쓰기)할 수 있게 해줍니다. Docker 이미지는 보통 ENTRYPOINTCMD 지시자를 사용하여 이미지가 실행될 때 어떤 프로세스를 시작할지 정의합니다. ENTRYPOINT는 컨테이너가 실행될 때 디폴트로 실행되는 커맨드나 스크립트를 설정하고, CMDENTRYPOINT에 전달될 디폴트 아규먼트를 제공합니다. 그러나 docker run 명령어를 사용할 때 --entrypoint 옵션을 지정하면, Dockerfile 내에서 정의된 ENTRYPOINT를 무시하고 새로운 엔트리포인트를 지정할 수 있습니다.

--entrypoint 옵션 사용 예
다음은 docker run 명령어를 사용하여 --entrypoint 옵션으로 엔트리포인트를 설정하는 예시입니다:
docker run --entrypoint /bin/bash my_image 
이 커맨드는 my_image 이미지를 사용하여 컨테이너를 실행할 때, Dockerfile에 정의된 ENTRYPOINT 대신 /bin/bash를 엔트리포인트로 설정합니다. 이를 통해 사용자는 컨테이너가 시작될 때 실행되는 프로세스를 동적으로 제어할 수 있습니다.

사용 시 주의사항
  --entrypoint 옵션을 사용할 때, 커맨드 뒤에 오는 모든 아규먼트는 엔트리포인트로 지정된 프로그램[커맨드]에 대한 아규먼트로 처리됩니다. 이는 CMD 지시자에 정의된 디폴트 아규먼트를 오버라이드합니다.
  --entrypoint 옵션은 이미지 내에서 정의된 ENTRYPOINT를 완전히 대체합니다. 따라서 컨테이너 내에서 실행하려는 프로그램에 필요한 모든 아규먼트를 명확히 지정해야 합니다.
 이 옵션은 컨테이너의 실행 환경을 유연하게 조정할 필요가 있을 때 매우 유용할 수 있으나, 이미지가 의도한 대로 작동하지 않게 만들 수 있으므로 주의해서 사용해야 합니다.

--entrypoint 옵션을 사용함으로써, 사용자는 디폴트로 설정된 컨테이너의 시작 프로세스를 변경할 수 있으며, 디버깅이나 특정 애플리케이션의 실행 요구 사항을 맞추는 데 유용하게 활용할 수 있습니다.

 

이 커맨드는 Empty tmpfs 디바이스를 생성하고 이를 새 컨테이너의 파일 트리의 /tmp에 연결합니다. 이 파일 트리 하위에 생성된 파일은 디스크가 아닌 메모리에 기록됩니다. 더 나아가, 마운트 포인트는 일반적인 작업 부하에 대한 합리적인 디폴트값으로 생성됩니다. 이 커맨드를 실행하면 컨테이너의 모든 마운트 포인트 목록이 표시됩니다. 이 목록에는 다음과 같은 라인이 포함됩니다:

tmpfs on /tmp type tmpfs (rw,nosuid,nodev,noexec,relatime)

 

마운트 포인트는 일반적인 작업 부하에 대한 합리적인 디폴트 값으로 생성의 의미

"작업 부하에 대한 합리적인 디폴트 값으로 생성"이라는 표현에서, "작업 부하(workload)"는 컨테이너가 처리해야 할 작업량 또는 수행해야 할 태스크의 종류와 양을 의미합니다. 이 문장에서는, tmpfs 마운트(메모리에 기반한 파일 시스템)가 생성될 때, 일반적인 사용 사례 또는 대부분의 작업 부하에 적합하도록 설정된 "합리적인 디폴트 값"으로 구성된다는 뜻입니다.
합리적인 디폴트 값으로 구성된다는 것은, 예를 들어, 메모리 사용량, 보안 설정, 파일 시스템의 특성 등에 있어서 대부분의 사용자나 애플리케이션에 적합한 레벨로 자동 설정된다는 의미입니다. 이는 개발자나 시스템 관리자가 특정 상황에 맞게 세부 설정을 조정할 필요 없이, 바로 사용할 수 있는 환경을 제공하려는 의도를 반영합니다.
예를 들어, tmpfs 마운트는 일반적으로 RAM과 스왑 영역을 사용하여 데이터를 저장하는데, 이때 시스템은 메모리 사용량을 관리하는 디폴트 정책을 적용하여, 너무 많은 메모리를 소비하지 않도록 합니다. 또한, 일부 보안 관련 설정이나 파일 시스템 권한 등도 대부분의 애플리케이션에서 바로 사용할 수 있도록 디폴트 값으로 설정됩니다. 이런 접근 방식은 사용자가 특별한 요구 사항이 없는 경우, 빠르고 쉽게 시스템을 설정하고 실행할 수 있게 해줍니다. 그러나 특정 요구 사항이나 성능 최적화가 필요한 경우, 사용자는 이러한 설정을 조정할 수 있습니다.

 

위 커맨드 라인은 마운트 포인트 구성을 설명합니다. 왼쪽에서 오른쪽으로 순서대로 다음을 나타냅니다:

  • tmpfs 디바이스가 /tmp에 있는 트리에 마운트됩니다.
  • 해당 디바이스에는 tmpfs 파일 시스템이 있습니다.
  • 트리는 읽기/쓰기가 가능합니다.
  • 이 트리의 모든 파일에서 suid 비트는 무시됩니다.
  • 이 트리의 파일은 특수 장치로 해석되지 않습니다.
  • 이 트리의 파일은 실행될 수 없습니다.
  • 파일 접근 시간은 현재 수정 또는 변경 시간보다 오래된 경우에만 업데이트됩니다.

또한 tmpfs 디바이스는 기본적으로 크기 제한이 없으며 world-writable하며(8진수로 파일 권한이 1777입니다) 사이즈 제한을 추가하고 파일 모드를 변경할 수 있는 두 가지 추가 옵션이 있습니다: tmpfs-size 및 tmpfs-mode입니다.

docker run --rm \
 --mount type=tmpfs,dst=/tmp,tmpfs-size=16k,tmpfs-mode=1770 \
 --entrypoint mount \
 alpine:latest -v

 

이 명령은 /tmp에 마운트된 tmpfs 디바이스를 16KB로 제한하고, 컨테이너 내의 다른 사용자에게는 읽기 권한이 없도록 합니다.

 

4.4 Docker volumes

Docker 컨테이너에서 생성되고 사용되는 데이터를 지속적으로 저장하는 데에는 볼륨이 선호되는 메커니즘입니다. 바인드 마운트가 호스트 머신의 디렉토리 구조와 운영 체제에 의존하는 반면, 볼륨은 완전히 Docker에 의해 관리됩니다. 볼륨은 바인드 마운트보다 여러 가지 이점이 있습니다:

⦁ 볼륨은 바인드 마운트보다 백업하거나 마이그레이션하기가 더 쉽습니다.
Docker CLI 커맨드나 Docker API를 사용하여 볼륨을 관리할 수 있습니다.

⦁ 볼륨은 리눅스와 윈도우 컨테이너 모두에서 작동합니다.
⦁ 볼륨은 여러 컨테이너 간에 더 안전하게 공유될 수 있습니다.
⦁ 볼륨 드라이버를 통해 볼륨을 원격 호스트나 클라우드 제공자에 저장하거나, 볼륨의 내용을 암호화하거나, 다른 기능을 추가할 수 있습니다.
⦁ 새 볼륨은 컨테이너에 의해 내용이 사전 채워질 수 있습니다.
⦁ Docker Desktop에서의 볼륨은 Mac과 Windows 호스트에서의 바인드 마운트보다 훨씬 더 높은 성능을 제공합니다.

또한, 볼륨은 컨테이너의 쓰기 가능 레이어에 데이터를 지속적으로 저장하는 것보다 종종 더 나은 선택입니다. 왜냐하면 볼륨은 그것을 사용하는 컨테이너의 크기를 증가시키지 않으며, 볼륨의 내용은 주어진 컨테이너의 생명주기 밖에 존재하기 때문입니다.

 

도커 볼륨은 도커가 관리하는 named 파일 시스템 트리입니다. 호스트 파일 시스템의 디스크 저장소나 클라우드 저장소와 같은 다른 exotic 백엔드로 구현할 수 있습니다. 도커 볼륨에 대한 모든 작업은  docker volume 하위 커맨드 세트를 사용하여 수행할 수 있습니다. 볼륨을 사용하는 것은 바인드 마운트로 지정할 수 있는 파일 시스템의 특수한 위치에서 저장소를 분리하는 방법입니다.
예를 들어 웹 서버와 로그 포워딩 컨테이너 예제를 섹션 4.2에서 볼륨을 사용하여 로그에 대한 액세스를 공유하도록 변환한다면, 이 쌍[웹 서버와 로그 포워딩 컨테이너]은 디스크의 정적 위치와 충돌할 수 있는 다른 소프트웨어를 고려하는 것 없이 어떤 머신에서든 실행할 수 있습니다. 이 예제는 그림 4.4와 같이 보일 것이며, 컨테이너는 location-example 볼륨을 통해 로그를 읽고 쓸 것입니다.
도커 볼륨을 생성하고 검사하는 것은 docker volume createdocker volume inspect 하위 명령을 사용하여 수행할 수 있습니다. 디폴트로 도커는 local  볼륨 플러그인을 사용하여 볼륨을 생성합니다. 디폴트 동작은 Docker 엔진의 제어 하에 있 호스트 파일 시스템의 일부에 볼륨의 내용을 저장하기 위한 디렉토리를 생성할 것입니다.

local 볼륨 플러그인은 Docker에 내장된 디폴트 볼륨 드라이버 중 하나로, 컨테이너에서 사용할 볼륨을 생성, 관리 및 마운트하는 기능을 제공합니다. 이 플러그인을 사용하여 생성된 볼륨은 Docker 호스트의 로컬 파일 시스템에 저장됩니다. local 볼륨 플러그인은 볼륨의 생명주기(생성, 삭제, 마운트, 언마운트 등)를 관리하며, 특별한 네트워크 스토리지나 외부 API 호출 없이도 동작합니다.

Local 볼륨 플러그인의 주요 기능
  볼륨 생성: 사용자가 명시적으로 볼륨을 생성하거나, 컨테이너를 실행할 때 볼륨을 자동으로 생성할 수 있습니다.
데이터 지속성: 컨테이너가 삭제되어도 볼륨에 저장된 데이터는 보존되어, 다른 컨테이너에서 해당 볼륨을 재사용할 수 있습니다.
성능: 데이터가 로컬 스토리지에 저장되므로, 네트워크 스토리지를 사용할 때보다 일반적으로 더 빠른 입출력 성능을 제공합니다.
간단한 관리: Docker CLI나 Docker API를 통해 볼륨을 쉽게 생성, 조회, 삭제할 수 있습니다.

사용 예시 Docker CLI를 사용하여 local 볼륨 플러그인으로 볼륨을 생성하는 예시는 다음과 같습니다:
docker volume create my_volume
이 명령은 my_volume이라는 이름의 볼륨을 생성하며, 볼륨은 호스트의 Docker 관리 디렉토리 내에 위치한 특정 경로에 저장됩니다. 이후, docker run 커맨드의 -v 또는 --mount 옵션을 사용하여 컨테이너와 볼륨을 연결할 수 있습니다.

주의사항
local 볼륨 플러그인으로 생성된 볼륨은 Docker 호스트에 종속적입니다. 따라서, 볼륨 데이터를 다른 호스트로 이전하려면 추가 작업이 필요합니다.
데이터 백업 및 복원 절차를 수립하는 것이 중요합니다, 특히 중요한 데이터를 다룰 때는 더욱 그렇습니다.

local 볼륨 플러그인은 Docker 컨테이너의 데이터 지속성을 위한 간단하면서도 효과적인 솔루션을 제공합니다.

 

예를 들어, 다음 두 명령은 location-example이라는 볼륨을 생성하고 볼륨 호스트 파일 시스템 트리의 위치를 표시합니다:

docker volume create \     --> Creates the volume
 --driver local \          --> Specifies the “local” plugin
 --label example=location \ --> Adds a volume label
 location-example
docker volume inspect \     --> Inspects the volume
 --format "{{json .Mountpoint}}" \  --> Selects the location on the host
 location-example

 

Docker 볼륨을 생성할 때 --label 옵션을 사용하면 해당 볼륨에 메타데이터를 추가할 수 있습니다. 레이블(labels)은 키-값(key-value) 쌍으로 구성되며, 볼륨을 분류하거나, 관리하기 쉽게 정보를 부여하는 용도로 사용됩니다. 이 메타데이터는 Docker API나 CLI를 통해 조회할 수 있으며, 볼륨을 필터링하는 데에도 사용할 수 있습니다.
위 커맨드에서 --label example=location은 example이라는 키에 location이라는 값을 가진 레이블을 볼륨에 추가하라는 의미입니다. 이 레이블은 볼륨에 임의의 의미를 부여할 수 있으며, 사용자가 볼륨을 쉽게 식별하고 관리할 수 있도록 도와줍니다. 예를 들어, 여러 볼륨 중에서 특정 용도나 위치, 프로젝트 등을 나타내는 볼륨을 빠르게 찾아내기 위해 사용할 수 있습니다.

Figure 4.4 Sharing files between containers with a volume

 


Nginx의 로그 데이터를 저장하는 볼륨에 example=location 레이블을 추가하여 위 예제를 수정하겠습니다. 이를 통해 볼륨을 더 명확하게 식별할 수 있게 됩니다.

1단계: 로그 데이터를 저장할 볼륨 생성
example=location 레이블을 추가하여 nginx_logs 볼륨을 생성합니다. 이 레이블은 볼륨이 로그 데이터를 저장하는 위치를 나타내는 예제용 메타데이터로 사용됩니다.

docker volume create --label example=location nginx_logs


2단계: Nginx 컨테이너 실행과 볼륨 마운트

example=location 레이블이 추가된 nginx_logs 볼륨을 Nginx 컨테이너의 /var/log/nginx 디렉토리에 마운트합니다. 이렇게 하면 Nginx 컨테이너가 생성하는 로그 파일들이 nginx_logs 볼륨에 저장됩니다.

docker run -d -p 8080:80 --name my_nginx \
  -v nginx_logs:/var/log/nginx \
  nginx


3단계: 로그 데이터 확인

볼륨에 저장된 로그 파일에 접근하려면, 볼륨이 마운트된 컨테이너의 셸에 접속합니다.

docker exec -it my_nginx bash


컨테이너 내에서 로그 디렉토리로 이동하여 로그 파일을 확인합니다.

cd /var/log/nginx
cat access.log
cat error.log

 

※ 위 커맨드들로 실제 로그를 확인할 수 없습니다.

다음 커맨드로 nginx의 로그를 확인할 수 있습니다.

docker logs <컨테이너 id 또는 이름>

 

4단계: 레이블을 사용하여 볼륨 필터링

생성된 볼륨 중 example=location 레이블을 가진 볼륨을 찾기 위해 다음과 같은 Docker 커맨드를 사용할 수 있습니다.

docker volume ls --filter "label=example=location"

 

이 커맨드는 example=location 레이블을 가진 모든 볼륨을 나열합니다. 이 방법을 통해 특정 레이블을 기준으로 볼륨을 빠르게 식별하고 관리할 수 있습니다.


이 예제에서는 example=location 레이블을 가진 nginx_logs 볼륨을 생성하고, 이 볼륨을 Nginx 컨테이너의 로그 디렉토리에 마운트하여 로그 데이터를 저장하는 방법을 수정했습니다. 레이블을 사용함으로써 볼륨 관리를 더 용이하게 하고, 볼륨의 용도를 명확히 할 수 있습니다.


4.4.1 Volumes provide container-independent data management

시맨틱으로, 볼륨은 임의의 컨테이너와 독립적인 범위나 라이프 사이클을 가지는 데이터를 분할하고 공유하기 위한 도구입니다. 이는 볼륨을 파일을 공유하거나 기록하는 컨테이너화된 시스템 디자인의 중요한 부분으로 만듭니다. 컨테이너와 범위 또는 액세스가 다른 데이터의 예로는 다음과 같은 것들이 있습니다:

  • 데이터베이스 소프트웨어 vs 데이터베이스 데이터
  • 웹 애플리케이션 vs 로그 데이터
  • 데이터 처리 애플리케이션 vs 입력 및 출력 데이터
  • 웹 서버 vs 정적 콘텐츠
  • 제품 vs 지원 도구

볼륨은 관심사의 분리를 가능하게 하고 아키텍처 구성 요소에 대한 모듈성을 만듭니다. 이러한 모듈성은 더 큰 시스템의 일부를 이해하고 구축하고 지원하며 재사용하기 쉽게 만듭니다.
이렇게 생각해보세요: 이미지는 프로그램과 같이 상대적으로 정적인 파일을 포장하고 배포하는 데 적합합니다. 반면에 볼륨은 동적 데이터나 특수화를 보유합니다. 이 구분은 이미지를 재사용 가능하게 하고 데이터를 간단하게 공유할 수 있도록 합니다. 상대적으로 정적이고 동적인 파일 공간의 분리로 인해 애플리케이션 또는 이미지 작성자는 다형적 및 구성 가능한 도구와 같은 고급 패턴을 구현할 수 있습니다.
다형적 도구는 일관된 인터페이스를 유지하지만 서로 다른 작업을 수행할 수 있는 여러 구현이 있는 도구입니다. 예를 들어, 일반 애플리케이션 서버와 같은 애플리케이션을 고려해보세요. 예를 들어, Apache Tomcat은 네트워크에서 HTTP 인터페이스를 제공하고 받은 모든 요청을 플러그 가능한 프로그램으로 전달하는 애플리케이션입니다. Tomcat은 다형적 동작을 가지고 있습니다. 볼륨을 사용하면 이미지를 수정하지 않고도 컨테이너에 동작을 주입할 수 있습니다. 반면에 MongoDB나 MySQL과 같은 데이터베이스 프로그램을 고려해보세요. 데이터베이스의 가치는 그 안에 포함된 데이터에 의해 정의됩니다. 데이터베이스 프로그램은 항상 동일한 인터페이스를 제공하지만 볼륨으로 주입할 수 있는 데이터에 따라 완전히 다른 가치를 갖습니다. 다형적 컨테이너 패턴은 섹션 4.5.1의 주제입니다.
더 근본적으로, 볼륨은 애플리케이션과 호스트의 관심사를 분리할 수 있습니다. 어느 시점에는 이미지가 호스트에 로드되고 그것을 기반으로 컨테이너가 생성됩니다. Docker는 실행 중인 호스트에 대해 거의 알지 못하며 컨테이너에 사용 가능해야 할 파일에 대해서만 주장할 수 있습니다. 도커는 마운트된 네트워크 저장소나 혼합 회전 및 고체 하드 드라이브와 같은 호스트별 시설을 활용할 방법이 없습니다. 그러나 호스트에 대한 지식을 가진 사용자는 볼륨을 사용하여 컨테이너의 디렉터리를 해당 호스트의 적절한 저장소에 매핑할 수 있습니다. 이제 볼륨이 무엇이며 왜 중요한지를 알게 되었으므로 실제 예제에서 볼륨을 사용해볼 수 있습니다.

 

4.4.2 Using volumes with a NoSQL database

아파치 카산드라 프로젝트는 내장 클러스터링, 최종 일관성 및 선형 쓰기 확장성을 제공하는 열 기반 데이터베이스입니다. 현대 시스템 디자인에서 인기 있는 선택지이며 Docker Hub에 공식 이미지가 제공됩니다. 카산드라는 다른 데이터베이스와 마찬가지로 데이터를 디스크에 있는 파일로 저장합니다. 이 섹션에서는 공식 카산드라 이미지를 사용하여 단일 노드 카산드라 클러스터를 생성하고 키스페이스를 만들고, 컨테이너를 삭제한 다음 해당 키스페이스를 다른 컨테이너의 새로운 노드에서 복구합니다.
먼저 카산드라 데이터베이스 파일을 저장할 볼륨을 생성하여 시작합니다. 이 볼륨은 로컬 머신의 디스크 공간을 사용하며 Docker 엔진이 관리하는 파일 시스템의 일부입니다.

docker volume create \
 --driver local \
 --label example=cassandra \
 cass-shared

 

이 볼륨은 어떤 컨테이너와도 연결되어 있지 않습니다. 단지 컨테이너에서 액세스할 수 있는 디스크의 명명된 부분입니다. 방금 생성한 볼륨의 이름은 cass-shared입니다. 이 경우에는 볼륨에 example 키와 값이 cassandra인 레이블을 추가했습니다. 볼륨에 레이블 메타데이터를 추가하면 나중에 볼륨을 조직화하고 정리하는 데 도움이 됩니다. 새로운 카산드라 컨테이너를 생성할 때 이 볼륨을 사용하게 될 것입니다.

docker run -d \
 --volume cass-shared:/var/lib/cassandra/data \ --> Mounts the volume into the container
 --name cass1 \
 cassandra:4.0.7

 

도커가 Docker Hub에서 cassandra:4.0.7 이미지를 가져온 후, cass-shared 볼륨이 /var/lib/cassandra/data에 마운트된 새로운 컨테이너를 생성합니다. 다음으로 cassandra:4.0.7 이미지에서 컨테이너를 시작하지만 Cassandra 클라이언트 도구인 CQLSH를 실행하고 실행 중인 서버에 연결합니다.

docker run -it --rm \
 --link cass1:cass \
 cassandra:4.0.7 cqlsh cass

 

이제 CQLSH 명령줄에서 Cassandra 데이터베이스를 검사하거나 수정할 수 있습니다. 먼저 docker_hello_world라는 키스페이스를 찾아보겠습니다.

select * from system_schema.keyspaces where keyspace_name = 'docker_hello_world';

 

Cassandra는 빈 리스트환해야 합니다. 이는 데이터베이스가 예제에 의해 수정되지 않았음을 의미합니다. 이제 다음 명령어로 해당 키스페이스를 생성하세요:

create keyspace docker_hello_world with replication = { 'class' : 'SimpleStrategy', 'replication_factor': 1
};

 

이제 데이터베이스를 수정했으므로 동일한 쿼리를 다시 실행하여 결과를 확인하고 변경 사항이 적용되었는지 확인할 수 있습니다. 다음 명령은 이전에 실행했던 명령과 동일합니다:

select * from system_schema.keyspaces where keyspace_name = 'docker_hello_world';

 

이번에는 Cassandra가 키스페이스를 생성할 때 지정한 속성을 가진 단일 항목을 리턴해야 합니다.

cqlsh> SELECT * FROM system_schema.keyspaces WHERE keyspace_name = 'docker_hello_world';

 keyspace_name      | durable_writes | replication
--------------------+----------------+-------------------------------------------------------------------------------------
 docker_hello_world |           True | {'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '1'}

(1 rows)

 

Cassandra 노드에 연결하고 수정했다고 확신하면 CQLSH 프로그램을 종료하여 클라이언트 컨테이너를 중지하세요.

# Leave and stop the current container
quit

 

클라이언트 컨테이너는 --rm 플래그와 함께 생성되어 커맨드가 중지될 때 자동으로 제거되었습니다. 이 예제의 첫 번째 부분을 정리하기 위해 생성한 카산드라 노드를 중지하고 제거하세요.

docker stop cass1
docker rm -vf cass1

 

docker rm 명령어의 -vf 옵션은 다음과 같은 의미를 갖습니다:
1 -v: 컨테이너와 연결된 익명 볼륨도 함께 제거합니다. 컨테이너가 실행될 때 자동으로 생성된 볼륨까지 함께 제거됩니다.
2 -f: 강제로 컨테이너를 제거합니다. 컨테이너가 실행 중이거나 중지되지 않은 경우에도 강제로 제거됩니다. 따라서 이 커맨드는 컨테이너 'cass1'과 해당하는 볼륨을 함께 강제로 제거합니다.

 

위의 명령어를 실행한 후에는 생성한 Cassandra 클라이언트와 서버가 모두 삭제됩니다. 만약 당신이 만든 수정이 유지되었다면, 그 유일한 위치는 cass-shared 볼륨일 것입니다. 이를 확인하기 위해 다음 단계를 반복해서 테스트할 수 있습니다. 새로운 Cassandra 노드를 생성하고 클라이언트를 연결한 다음 키스페이스를 쿼리합니다. 아래 그림은 시스템을 설명하고 당신이 만들어낼 것을 보여줍니다.

 

다음 세 개의 명령어는 데이터 복구를 테스트합니다.

docker run -d \
 --volume cass-shared:/var/lib/cassandra/data \
 --name cass2 \
 cassandra:4.0.7
 
docker run -it --rm \
 --link cass2:cass \
 cassandra:4.0.7 \
 cqlsh cass
 
select * 
from system_schema.keyspaces 
where keyspace_name = 'docker_hello_world';

 

이 세트의 마지막 커맨드는 싱글 엔트리를 리턴하며, 이는 이전 컨테이너에서 생성한 키스페이스와 일치합니다. 이는 이전 주장을 확인하고 볼륨이 어떻게 지속 가능한 시스템을 생성하는 데 사용될 수 있는지를 보여줍니다. 다음으로 넘어가기 전에 CQLSH 프로그램을 종료하고 작업 공간을 정리하세요. 볼륨 컨테이너도 제거하는 것을 잊지 마세요.

quit
docker rm -vf cass2 cass-shared

 

그림 4.5 Cassandra로 볼륨에 영속화된 데이터를 생성하고 복구하는 주요 단계

 

Cassandra로 볼륨에 영속화된 데이터를 생성하고 복구하는 주요 단계들은 다음과 같습니다:

  1. 볼륨 생성: 먼저 Docker에서 볼륨을 생성합니다. 이 볼륨은 Cassandra 데이터 파일을 저장하는 데 사용될 것입니다.
  2. Cassandra 서버 실행: 볼륨과 연관된 Cassandra 이미지를 사용하여 새로운 컨테이너를 실행합니다. 이 컨테이너는 Cassandra 서버를 호스트하고, 볼륨을 통해 데이터를 저장합니다.
  3. Cassandra 클라이언트 연결: Cassandra 데이터베이스를 수정하거나 조회하기 위해 클라이언트를 연결합니다. 이 클라이언트는 쿼리를 실행하고 데이터를 검색할 수 있습니다.
  4. 데이터 수정 및 확인: 클라이언트를 사용하여 Cassandra 키스페이스를 만들거나 수정한 후, 데이터를 저장하고 변경사항을 확인합니다.
  5. Cassandra 서버 중지 및 제거: 데이터 변경사항이 올바르게 저장되었는지 확인한 후, Cassandra 서버 컨테이너를 중지하고 제거합니다.
  6. 데이터 복구: 이전에 생성한 볼륨을 다시 사용하여 새로운 Cassandra 서버 컨테이너를 실행합니다.
  7. Cassandra 클라이언트 재연결 및 데이터 확인: 새로운 Cassandra 서버 컨테이너에 클라이언트를 다시 연결하고, 이전에 작성한 데이터가 여전히 유지되었는지 확인합니다.

이러한 단계를 통해 Cassandra와 볼륨을 사용하여 데이터를 생성하고 복구할 수 있습니다.

docker volume rm [볼륨 이름]

 

docker volume ls

 

이 예제는 볼륨을 사용하는 한 가지 방법을 보여주지만, 볼륨이 작동하는 방식, 사용되는 패턴 또는 볼륨 수명 주기를 관리하는 방법에 대해 자세히 다루지 않습니다. 이 장의 나머지 부분에서는 사용 가능한 유형부터 시작하여 각 볼륨 측면을 보다 심층적으로 탐구합니다.

 

4.5 Shared mount points and sharing files

여러 컨테이너들 간에 동일한 파일 세트에 대한 액세스를 공유하는 것은 볼륨의 가치가 가장 명확하게 드러나는 곳입니다. 바인드 마운트와 볼륨 기반 접근 방법을 비교해 보겠습니다. 바인드 마운트는 컨테이너 간에 디스크 공간을 공유하는 가장 명확한 방법입니다. 다음 예제에서 이를 확인할 수 있습니다:

$ LOG_SRC=~/web-logs-example
$ mkdir ${LOG_SRC} 
$ docker run --name plath -d \ 
> --mount type=bind,src=${LOG_SRC},dst=/logs \ 
> intheeast0305/ch4_writer_a 

$ docker run --rm \
> --mount type=bind,src=${LOG_SRC},dst=/logs \ 
> alpine:latest \ 
> head /logs/logA 

$cat ${LOG_SRC}/logA 

$docker rm -f plath

PowerShell에서는 명령어와 변수 구문이 다르므로 쉘 스크립트를 PowerShell 스크립트로 변환해야 합니다. 아래는 주어진 쉘 스크립트를 PowerShell 스크립트로 변환한 것입니다:

$LOG_SRC = "$env:USERPROFILE\web-logs-example"
New-Item -ItemType Directory -Path $LOG_SRC | Out-Null
docker run --name plath -d `
 --mount type=bind,source=$LOG_SRC,destination=/data `
 dockerinaction/ch4_writer_a
docker run --rm `
 --mount type=bind,source=$LOG_SRC,destination=/data `
 alpine:latest `
 head /data/logA
Get-Content "${LOG_SRC}\logA"
docker rm -f plath


PowerShell에서는 변수를 ${}로 묶지 않고 사용하며, Next Line을 나타내는 것으로 백틱(`)을 사용합니다. 또한 New-Item 명령어를 사용하여 디렉토리를 생성하고, Get-Content를 사용하여 파일 내용을 읽습니다.


이 예제에서는 logA라는 파일에 싱글 텍스트 라인(This is a single line written by the container.)을 작성하는 plath라는 컨테이너와 파일의 상단 부분을 보는 다른 컨테이너를 생성했습니다. 이러한 컨테이너들은 공통의 bind-mount 정의를 공유합니다. 컨테이너 외부에서는 생성한 디렉토리의 내용을 나열하거나 새 파일을 보는 것으로 변경 사항을 볼 수 있습니다. 이 접근 방식의 주요 단점은 모든 관련 컨테이너가 호스트 파일 경로의 정확한 위치에 동의해야 하며, 해당 위치에서 파일을 읽거나 조작하려는 다른 컨테이너와 충돌할 수 있다는 것입니다.
이제 이 bind-mount 예제를 볼륨을 사용하는 예제와 비교해 보겠습니다. 다음 명령은 이전 예제와 동일하지만 호스트별 종속성이 없습니다.

$ docker volume create \ 
> --driver local \ 
> logging-example 

$ docker run --name plath -d \ 
> --mount type=volume,src=logging-example,dst=/logs \ 
> intheeast0305/ch4_writer_a 

$ docker run --rm \
> --mount type=volume,src=logging-example,dst=/logs \ 
> alpine:latest \ 
> head /logs/logA 

$ sudo cat $(docker volume inspect \
> --format "{{json .Mountpoint}} logging-example)"/logA 

$ docker stop plath

 

바인드 마운트를 기반으로 하는 공유와는 달리, 명명된 볼륨은 컨테이너가 호스트 파일 시스템에 대한 어떠한 지식도 없이 파일을 공유할 수 있게 합니다. 볼륨이 특정 설정이나 플러그인을 사용해야 하는 경우를 제외하고는, 첫 번째 컨테이너가 마운트하기 전에 볼륨이 존재할 필요가 없습니다. 도커는 run이나 create 명령에서 디폴트 값을 사용하여 자동으로 볼륨을 생성합니다. 그러나 중요한 점은 호스트에 존재하는 명명된 볼륨은 해당 볼륨 의존성을 가진 다른 모든 컨테이너에 의해 재사용되고 공유된다는 것입니다.

이러한 이름 충돌 문제는 익명 볼륨과 컨테이너 간의 마운트 포인트 정의 상속을 사용하여 해결할 수 있습니다.

 

4.5.1 Anonymous volumes and the volumes-from flag

익명 볼륨은 docker volume create 하위 명령어를 사용하여 사용하기 전에 이름이 지정되지 않거나, docker run 또는 docker create 명령을 사용하여 디폴트로 동적으로 생성됩니다. 볼륨이 생성되면 1b3364a8debb5f653d1ecb9b190000622549ee2f812a4fb4ec8a83c43d87531b와 같은 고유 식별자가 부여됩니다. 이러한 식별자는 인간 친화적인 이름 대신 사용되며, 수동으로 종속성을 조합하는 경우 더 어려울 수 있지만, 볼륨 이름 충돌을 제거해야 하는 경우에 유용합니다. Docker 커맨드 라인은 이름으로 볼륨을 참조하는 대신 마운트 종속성을 지정하는 다른 방법을 제공합니다.

docker run 명령은 --volumes-from 플래그를 제공하는데, 이는 하나 이상의 소스 컨테이너로부터 마운트 정의를 새 컨테이너로 복사합니다. 이 플래그는 여러 번 설정하여 여러 소스 컨테이너를 지정할 수 있습니다. 이 플래그와 익명 볼륨을 결합하여 호스트에 독립적인 방식으로 풍부한 공유 상태 관계를 구축할 수 있습니다. 다음 예를 고려해 보겠습니다.

 docker run --name fowler \
    --mount type=volume,dst=/library/PoEAA \
    --mount type=bind,src=/tmp,dst=/library/DSL \
    alpine:latest \
    echo "Fowler collection created."
 docker run --name knuth \
    --mount type=volume,dst=/library/TAoCP.vol1 \
    --mount type=volume,dst=/library/TAoCP.vol2 \
    --mount type=volume,dst=/library/TAoCP.vol3 \
    --mount type=volume,dst=/library/TAoCP.vol4.a \
    alpine:latest \
    echo "Knuth collection created"
 
 
 docker run --name reader \        (1)
    --volumes-from fowler \        (1)
    --volumes-from knuth \         (1)
    alpine:latest ls -l /library/
    

 docker inspect --format "{{json .Mounts}}" reader  (2)

(1)  Lists all volumes as they were copied into new container

(2)  Checks out volume list for reader

 

이 예제에서, 익명 볼륨과 바인드 마운트 볼륨을 정의한 두 개의 컨테이너를 생성했습니다. --volumes-from 플래그를 사용하지 않고 이를 세 번째 컨테이너와 공유하려면, 이전에 생성된 컨테이너를 조사한 다음, Docker에서 관리하는 호스트 디렉토리로 바인드 마운트 볼륨을 설정해야 합니다. 하지만 --volumes-from 플래그를 사용하면 Docker가 이러한 작업을 대신 처리합니다. 이 플래그는 참조된 소스 컨테이너에 정의된 모든 마운트 포인트를 새 컨테이너에 복사합니다. 이 경우, reader라는 이름의 컨테이너는 fowlerknuth에서 정의된 모든 마운트 포인트를 복사했습니다.

볼륨은 직접 또는 간접적으로 복사할 수 있습니다. 즉, 다른 컨테이너에서 볼륨을 복사할 경우, 해당 컨테이너가 다른 컨테이너에서 복사한 볼륨도 함께 복사됩니다. 이전 예제에서 생성된 컨테이너를 사용하는 경우, 다음과 같은 결과를 얻을 수 있습니다.

 docker run --name aggregator \     (1)
    --volumes-from fowler \
    --volumes-from knuth \
    alpine:latest \
    echo "Collection Created."
 docker run --rm \                  (2)
    --volumes-from aggregator \
    alpine:latest \
    ls -l /library/

 

(1)  Creates an aggregation

(2)  Consumes volumes from a single source and lists them

 

복사된 볼륨은 항상 동일한 마운트 지점을 가집니다. 이는 세 가지 상황에서 --volumes-from을 사용할 수 없음을 의미합니다.

첫 번째 상황에서는, 생성 중인 컨테이너가 공유 볼륨을 다른 위치에 마운트해야 할 경우 --volumes-from을 사용할 수 없습니다. 이 옵션은 마운트 지점을 다시 매핑하는 도구를 제공하지 않으며, 지정된 컨테이너에서 표시된 마운트 지점을 복사하고 병합하는 기능만 제공합니다. 예를 들어, 이전 예제에서 student가 라이브러리를 /school/library와 같은 위치에 마운트하려고 한다면, 그렇게 할 수 없습니다.

두 번째 상황은 볼륨 소스가 서로 충돌하거나 새 볼륨 사양과 충돌할 때 발생합니다. 하나 이상의 소스가 동일한 마운트 지점을 가진 관리되는 볼륨을 생성하는 경우, 이를 사용하는 컨테이너는 여러 볼륨 정의 중 하나만 받게 됩니다.

 

 docker run --name chomsky --volume /library/ss \
    alpine:latest echo "Chomsky collection created."
 docker run --name lamport --volume /library/ss \
    alpine:latest echo "Lamport collection created."
 docker run --name student \
    --volumes-from chomsky --volumes-from lamport \
    alpine:latest ls -l /library/
 docker inspect -f "{{json .Mounts}}" student

 

예제를 실행하면 docker inspect의 출력에서 마지막 컨테이너가 /library/ss에 단일 볼륨만 나열되어 있음을 확인할 수 있습니다. 이 값은 다른 두 컨테이너 중 하나와 동일합니다. 각 소스 컨테이너가 동일한 마운트 지점을 정의하고, 두 개를 새 컨테이너로 복사하려고 하면 경합 상태(race condition)가 발생합니다. 이 경우 두 복사 작업 중 하나만 성공할 수 있습니다.

이 제한 사항의 실제 사례는 여러 웹 서버의 볼륨을 하나의 컨테이너로 복사하여 점검하려고 할 때 발생할 수 있습니다. 이러한 서버들이 동일한 소프트웨어를 실행하거나 공통 구성을 공유하는 경우가 많기 때문에(컨테이너화된 시스템에서 이는 흔한 일입니다), 이들 서버는 동일한 마운트 지점을 사용할 가능성이 높습니다. 이 경우 마운트 지점이 충돌하여 필요한 데이터의 일부만 접근할 수 있게 됩니다.

세 번째로 --volumes-from을 사용할 수 없는 상황은 볼륨의 쓰기 권한을 변경해야 할 때입니다. --volumes-from은 전체 볼륨 정의를 복사하기 때문에, 소스가 읽기/쓰기 권한으로 마운트된 볼륨을 가지고 있고, 이를 읽기 전용으로만 공유하려는 경우 --volumes-from을 사용할 수 없습니다.

--volumes-from 플래그를 사용하여 볼륨을 공유하는 것은 휴대성이 높은 애플리케이션 아키텍처를 구축하는 데 중요한 도구지만, 몇 가지 제한 사항을 동반합니다. 이 중에서 가장 까다로운 것은 파일 권한 관리입니다.

볼륨을 사용하면 컨테이너를 호스트 머신의 데이터 및 파일 시스템 구조와 분리할 수 있으며, 이는 대부분의 프로덕션 환경에서 매우 중요합니다. Docker가 관리하는 볼륨을 위해 생성한 파일과 디렉토리는 여전히 관리 및 유지보수가 필요합니다. 다음 섹션에서는 Docker 환경을 깔끔하게 유지하는 방법을 설명합니다.

 

4.6 Cleaning up volumes

이 시점에서, 꽤 많은 컨테이너와 볼륨을 cleaningup해야 할 것입니다. 시스템에 존재하는 모든 볼륨을 확인하려면 docker volume list 서브커맨드를 실행하면 됩니다. 출력 결과에는 각 볼륨의 유형과 이름이 나열됩니다. 이름을 지정하여 생성된 볼륨은 해당 이름으로 표시됩니다. 익명 볼륨은 식별자로 표시됩니다.

익명 볼륨은 두 가지 방법으로 cleaningup할 수 있습니다. 첫째, 익명 볼륨은 생성된 컨테이너가 자동으로 cleaningup 될 때 자동으로 삭제됩니다. 이는 docker run --rm 또는 docker rm -v 플래그를 통해 컨테이너가 삭제될 때 발생합니다. 둘째, docker volume remove 명령을 사용하여 수동으로 삭제할 수도 있습니다.

docker volume create --driver=local
 # Outputs:
 # 462d0bb7970e47512cd5ebbbb283ed53d5f674b9069b013019ff18ccee37d75d
 docker volume remove \
    462d0bb7970e47512cd5ebbbb283ed53d5f674b9069b013019ff18ccee37d75d
 # Outputs:
 # 462d0bb7970e47512cd5ebbbb283ed53d5f674b9069b013019ff18ccee37d75d

 

익명 볼륨과 달리, 이름이 지정된 볼륨(named volumes)은 항상 수동으로 삭제해야 합니다. 이러한 동작은 컨테이너가 분할되거나 주기적인 데이터를 수집하는 작업을 수행할 때 유용할 수 있습니다. 다음을 고려해 보세요:

 for i in amazon google microsoft; \
 do \
 docker run --rm \
    --mount type=volume,src=$i,dst=/tmp \
    --entrypoint /bin/sh \
    alpine:latest -c "nslookup $i.com > /tmp/results.txt"; \
 done

 

이 커맨드는 세 개의 개별 컨테이너에서 amazon.com, google.com, microsoft.com에 대한 DNS 조회를 수행하고, 결과를 각각의 다른 볼륨에 기록합니다. 이 볼륨들은 amazon, google, microsoft라는 이름으로 생성됩니다. 비록 컨테이너는 자동으로 정리되지만, 이름이 지정된 볼륨(named volumes)은 그대로 남아 있게 됩니다. 

명령을 실행한 후, docker volume list를 실행하면 새로 생성된 볼륨을 확인할 수 있습니다. 

이름이 지정된 이러한 볼륨을 삭제하는 유일한 방법은 docker volume remove 명령에서 해당 이름을 명시적으로 지정하는 것입니다.

 docker volume remove \
    amazon google microsoft

 

remove 서브커맨드는 볼륨 이름과 식별자를 지정하는 목록 인수를 지원합니다. 앞서 언급된 명령은 세 개의 이름이 지정된 볼륨을 모두 삭제합니다.

볼륨 삭제에는 한 가지 제약 조건이 있습니다. 현재 사용 중인 볼륨은 삭제할 수 없습니다. 보다 구체적으로, 어떠한 상태의 컨테이너에도 연결된 볼륨은 삭제할 수 없습니다. 만약 이를 시도하면, Docker 명령은 "volume is in use"라는 메시지와 해당 볼륨을 사용하는 컨테이너의 식별자를 표시하며 응답합니다.

모든 또는 일부 삭제 가능한 볼륨을 제거하려고 할 때, 어떤 볼륨이 제거 가능한지 확인하는 것은 번거로울 수 있습니다. 이는 주기적인 유지보수 작업의 일환으로 자주 발생합니다. 이를 위해 docker volume prune 명령이 사용됩니다.

옵션 없이 docker volume prune을 실행하면 확인을 요청하고, 삭제 가능한 모든 볼륨을 삭제합니다. 제공된 볼륨 라벨을 사용하여 후보군을 필터링할 수도 있습니다:

docker volume prune --filter example=cassandra

 

이 명령은 확인을 요청하며 이전 Cassandra 예제에서 생성한 볼륨을 삭제합니다. 이러한 정리 절차를 자동화하는 경우, 확인 단계를 생략하고 싶다면 --force 또는 -f 플래그를 사용하세요:

docker volume prune --filter example=location --force

 

볼륨을 이해하는 것은 실제 컨테이너 작업에서 매우 중요하지만, 로컬 디스크에서 볼륨을 사용하는 것은 문제가 될 수 있습니다. 클러스터 환경에서 소프트웨어를 실행하는 경우, 소프트웨어가 볼륨에 저장하는 데이터는 작성된 디스크에 남아 있습니다. 컨테이너가 다른 머신으로 이동하면 이전 볼륨에 있는 데이터에 접근할 수 없게 됩니다. 이러한 문제는 볼륨 플러그인을 사용하여 해결할 수 있습니다.

 

 4.7  Advanced storage with volume plugins

Docker는 디폴트 엔진 기능을 확장하기 위한 수단으로 볼륨 플러그인 인터페이스를 제공합니다. 이는 여러 스토리지 관리 도구에서 구현되었으며, 현재 사용자는 독점적인 클라우드 블록 스토리지, 네트워크 파일 시스템 마운트, 특수 스토리지 하드웨어, Ceph 및 vSphere 스토리지와 같은 온프레미스 클라우드 솔루션을 포함한 다양한 백엔드 스토리지를 자유롭게 사용할 수 있습니다.

이 커뮤니티 및 공급업체 플러그인은 한 머신에 파일을 디스크에 쓰고 다른 머신에서 이를 의존하는 데 관련된 어려운 문제를 해결하는 데 도움을 줍니다. 플러그인은 적절한 Docker 플러그인 서브커맨드를 사용하여 간단히 설치, 구성 및 제거할 수 있습니다.

이 텍스트에서는 Docker 플러그인에 대해 자세히 다루지 않습니다. 플러그인은 항상 환경에 따라 다르며, 유료 리소스를 사용하거나 특정 클라우드 제공업체를 추천하지 않고는 데모하기 어렵습니다. 플러그인을 선택하는 것은 통합하려는 스토리지 인프라에 따라 달라지지만, 일부 프로젝트는 이를 어느 정도 단순화합니다. REX-Ray(https://github.com/rexray/rexray)는 여러 클라우드 및 온프레미스 스토리지 플랫폼에서 볼륨을 제공하는 인기 있는 오픈 소스 프로젝트입니다. 컨테이너 작업에서 보다 정교한 볼륨 백엔드가 필요할 때가 되었다면, Docker Hub에서 최신 제공사항을 확인하고 REX-Ray의 현재 상태를 살펴보는 것이 좋습니다.

 

Summary

Docker를 배우는 데 있어 첫 번째 주요 장벽 중 하나는 이미지의 일부가 아닌 파일을 다루고, 이를 다른 컨테이너나 호스트와 공유하는 방법을 이해하는 것입니다. 이 장에서는 다음과 같은 내용을 포함하여 마운트 지점에 대해 깊이 다룹니다:

  • 마운트 지점은 여러 장치의 파일 시스템을 단일 파일 트리에 연결할 수 있도록 합니다. 각 컨테이너는 고유한 파일 트리를 가집니다.
  • 컨테이너는 바인드 마운트를 사용하여 호스트 파일 시스템의 일부를 컨테이너에 연결할 수 있습니다.
  • 민감하거나 임시 데이터가 디스크에 기록되지 않도록 하기 위해 인메모리 파일 시스템을 컨테이너 파일 트리에 연결할 수 있습니다.
  • Docker는 익명 또는 이름이 지정된 볼륨을 통해 스토리지를 참조합니다.
  • 볼륨은 적절한 docker volume 서브커맨드를 사용하여 생성, 목록화, 삭제할 수 있습니다.
  • 볼륨은 호스트 파일 시스템의 일부로, Docker가 지정된 위치에 컨테이너에 마운트합니다.
  • 볼륨은 자체적인 수명 주기를 가지며 주기적으로 정리해야 할 수도 있습니다.
  • Docker는 적절한 볼륨 플러그인이 설치된 경우 네트워크 스토리지 또는 기타 더 정교한 도구를 지원하는 볼륨을 제공합니다.

 

 

'Docker' 카테고리의 다른 글

6 Limiting risk with resource controls  (0) 2024.04.03
5 Single-host networking  (0) 2024.03.29
3. Software installation simplified  (0) 2024.03.25
2. Running software in containers  (0) 2024.03.20
1. Welcome to Docker  (0) 2024.03.20