🧩 DevOps/Docker

[Docker] Docker 볼륨(Volume)

nayoungs 2022. 5. 10. 18:24
728x90

📌Index



✔️ Volume이란?

Docker 컨테이너(container)에 쓰여진 데이터(새로 추가한 rw layer)는

기본적으로 컨테이너가 삭제될 때 함께 사라지게 된다.

 

Docker에서 돌아가는 많은 애플리케이션(특히 MySQL과 같은 DB)은 컨테이너의 생명 주기와 관계없이

데이터를 영속적으로 저장해야하고,

많은 경우 여러 개의 Docker 컨테이너가 하나의 저장 공간을 공유해서 데이터를 읽거나 써야한다.

이렇게 Docker 컨테이너의 생명 주기와 관계없이 데이터를 영속적으로 저장할 수 있도록

별개의 공간인, Volume이 필요하다.

 

이미지의 Config.Volumes 선언되어 있으면, 자동으로 Docker 볼륨이 생성되고 마운트되며,

수동으로 마운트하는 것도 가능하다.

/var/lib/docker/volumes/[volume명]/_data가 실제 사용하는, 마운트된 디렉토리이다



✔️ 볼륨 방식 마운트

볼륨은 읽기-쓰기가 가능한 빈 저장소를 생성하는 것이 기본이고, 볼륨은 도커 데몬이 관리한다.

 

빈 볼륨 생성하기

docker volume create <NAME>

예시

$ docker volume create test-vol
$ docker volume ls             
DRIVER    VOLUME NAME
local     test-vol

빈 볼륨이므로 /var/lib/docker/volumes/test-vol/_data를 확인해도 아직은 아무것도 없다.

$ docker inspect test-vol           
[
    {
        "CreatedAt": "2022-05-15T12:52:47Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/test-vol/_data",
        "Name": "test-vol",
        "Options": {},
        "Scope": "local"
    }
]

 

볼륨 목록 확인하기

$ docker volume ls

 

볼륨 삭제하기

$ docker volume rm <NAME>

 

사용하지 않는 볼륨 삭제하기

사용되지 않는다는 것은 컨테이너에 마운트되지 않았다는 뜻이다.

$ docker volume prune

 

볼륨을 사용한 컨테이너

 

docker run-v옵션을 통해 볼륨을 연결할 수 있다.

 

ro : Read-Only 설정, default는 rw 이다.

 

<MOUNTPOINT>는 컨테이너 내부의 마운트할 위치로,

디렉토리가 만들어져있지 않더라도 알아서 만들게 된다.

$ docker run -v <VOL-NAME>:<MOUNTPOINT>[:ro] <IMAGE>

📋 예시1

$ docker run -itd -v test-vol:/test-vol ubuntu

생성된 컨테이너를 확인해보면 Mounts에서 Destination/test-vol으로 설정된 것을 확인할 수 있다.

$ docker inspect 5c 
...
 "Mounts": [
            {
                "Type": "volume",
                "Name": "test-vol",
                "Source": "/var/lib/docker/volumes/test-vol/_data",
                "Destination": "/test-vol",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }
        ],
        ...

컨테이너에 접속해서 /test-vol 디렉토리에 hello.txt 파일을 생성해보자.

$ docker exec -it 5c bash          
root@5c80ed5ed7c5:/# ls
bin   dev  home  lib32  libx32  mnt  proc  run   srv  test-vol  usr
boot  etc  lib   lib64  media   opt  root  sbin  sys  tmp       var
root@5c80ed5ed7c5:/# cd test-vol/
root@5c80ed5ed7c5:/test-vol# ls
root@5c80ed5ed7c5:/test-vol# echo "hello volume" > hello.txt
root@5c80ed5ed7c5:/test-vol# exit

/var/lib/docker/volumes/test-vol/_datahello.txt가 생성된 것을 확인할 수 있다.

$ sudo ls /var/lib/docker/volumes/test-vol/_data 
hello.txt

이때, 컨테이너를 지운다고해서 볼륨이 지워지는 것은 아니다.

즉, 컨테이너의 라이프 사이클과 볼륨의 라이프 사이클은 별개이다.

 

📋 예시2

볼륨은 다음과 같이 여러개를 지정하는 것도 가능하다. (볼륨은 여러개 있을 수 있다)

$ docker run -itd -v abc:/abc -v xyz:/xyz: ubuntu
$ docker volume ls                               
DRIVER    VOLUME NAME
local     abc
local     xyz
$ docker inspect <container>
...
"Mounts": [
            {
                "Type": "volume",
                "Name": "abc",
                "Source": "/var/lib/docker/volumes/abc/_data",
                "Destination": "/abc",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            },
            {
                "Type": "volume",
                "Name": "xyz",
                "Source": "/var/lib/docker/volumes/xyz/_data",
                "Destination": "/xyz",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }
        ],
...

 

📋 예시3

아래의 명령을 실행해보자

$ docker run --name wp-db -d -v wp-db-vol:/var/lib/mysql mysql:5.7
b63565b20242657ef8ca4a91f346754fbbf1a8a9c1237db48fd37ce270d0ba9c

mysql 컨테이너에 환경변수를 설정하지 않았지만, 정상적으로 실행되었다.

왜 환경 변수 설정없이도 잘 실행되는 것일까❔

$ docker ps

CONTAINER ID   IMAGE       COMMAND                  CREATED         STATUS         PORTS                 NAMES
b63565b20242   mysql:5.7   "docker-entrypoint.s…"   4 seconds ago   Up 3 seconds   3306/tcp, 33060/tcp   wp-db
$ docker exec b6 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=95348375c379
GOSU_VERSION=1.14
MYSQL_MAJOR=5.7
MYSQL_VERSION=5.7.38-1debian10
HOME=/root

wp-db를 살펴보면 환경변수가 없는 것을 확인할 수 있는데,

원래 mysql 또는 mariadb 컨테이너를 실행하기 위해서는 root 패스워드 설정이 중요하고, 이미지 내에는 root 패스워드가 없기 때문에 환경변수로 root 패스워드를 제공해줘야한다.

그러나 여기서는 컨테이너를 띄울 때 volume 즉 데이터베이스를 제공했기 때문에 그 내부에 root 패스워드가 있어 별도로 루트 패스워드를 설정할 필요 없이 컨테이너가 실행된 것이다.


다음과 같이 기존 패스워드로는 정상적으로 접속할 수 있지만

$ docker exec -it b6 mysql -u wpadm -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.38 MySQL Community Server (GPL)

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

패스워드를 변경한 뒤, 변경한 패스워드를 통해 로그인하려하면 접근이 불가능하다.

-e로 지정한 환경 변수는 무시되고, 볼륨에 있는 내용을 참조하고 있기 때문이다.

$ docker run --name wp-db2 -d -e MYSQL_ROOT_PASSWORD=qwer1234 -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wpadm -e MYSQL_PASSWORD=qwer1234 --restart always --cpus 0.5 --memory 1000m -v wp-db-vol:/var/lib/mysql mysql:5.7
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
389245782e838f364604f3dab72790b772046eb93e2d967de58f534db140857e
$ docker ps
CONTAINER ID   IMAGE       COMMAND                  CREATED         STATUS         PORTS                 NAMES
37887g412411   mysql:5.7   "docker-entrypoint.s…"   3 seconds ago   Up 3 seconds   3306/tcp, 33060/tcp   wp-db2
b63565b20242   mysql:5.7   "docker-entrypoint.s…"   5 minutes ago   Up 5 minutes   3306/tcp, 33060/tcp   wp-db

심지어 다시 변경 전의 비밀번호로 접속하려고해도 접속이 불가능하다.

$ docker exec -it 37 mysql -u wpadm -p
Enter password:
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)

결론: 이미 데이터베이스(볼륨)이 있는 상태에서 데이터베이스 환경변수를 설정해주면 오류가 발생한다.

 

또한 여기서 알 수 있는 한가지 사실이 더 있다.

 

하나의 볼륨을 서로 다른 컨테이너가 동시에 마운트해서 사용할 수 있다는 것이다. (여기서는 wp-db, wp-db2)

VM을 사용할 때는 이런 구성을 하기 위해 NFS를 사용했어야했다.

그리고 NFS는 동시에 읽기는 가능하나, lock 기능으로 인해 동시에 쓰기는 불가능 하다.

그러나 이러한 방식(컨테이너가 마운트 공유)은 lock 기능이 없고, 동시에 파일의 쓰기 작업도 가능하다.

다만 데이터가 깨지거나, 문제가 발생하거나 어떠한 결과를 초래할 지는 모른다.

 

📋 예시4

아래의 명령을 실행해보자. roRead-Only 옵션이다.

$ docker run -itd -v abc:/abc --name u1 ubuntu
$ docker run -itd -v abc:/abc:ro --name u2 ubuntu

ro 옵션을 주지 않은 u1RW : true로 설정되어있고,

$ docker inspect u1
"Mounts": [
            {
                "Type": "volume",
                "Name": "abc",
                "Source": "/var/lib/docker/volumes/abc/_data",
                "Destination": "/abc",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }
        ],

ro 옵션을 준 u2RW : false 로 설정되어있는 것을 확인할 수 있다.

즉, 쓰기가 불가능하다.

$ docker inspect u2
"Mounts": [
            {
                "Type": "volume",
                "Name": "abc",
                "Source": "/var/lib/docker/volumes/abc/_data",
                "Destination": "/abc",
                "Driver": "local",
                "Mode": "ro",
                "RW": false,
                "Propagation": ""
            }
        ],



✔️ 바인드 방식 마운트

바인드 방식은 컨테이너에게 제공할 볼륨을 호스트의 특정 디렉토리를 지정하는 방식이다. 호스트의 디렉토리를 컨테이너에게 제공하는 것으로, 미리 데이터를 채워서 제공이 가능하다.

바인드 볼륨은 도커 데몬이 관리하지 않고, 사용자가 직접 관리한다.

bind 방식의 볼륨은 .MountsTypebind로 되어있다.

 

<ABSOLUTE_PATH> : 경로를 반드시 절대경로로 작성해야한다

$ docker run -v <ABSOLUTE_PATH>:<MOUNTPOINT>[:ro] <IMAGE>

마운트 방식과의 차이는 절대경로를 적는가, 볼륨 이름을 적는가의 차이이다.

 

📋 예시

먼저 실습을 위한 contents 디렉토리를 만들고 본 디렉토리에 hello.html 파일을 생성해보자

$ mkdir contents
$ cd contents
$ echo "hello bind volume" > hello.html

그리고 다음과 같이 절대경로로 바인드 마운트 한 뒤,

$ docker run -itd -v /home/vagrant/contents:/abc ubuntu

/abc 디렉토리를 확인해보면 hello.html 이 있는 것을 확인할 수 있다.

$ docker exec -it d3e bash              
root@d3e21b276e15:/# ls abc
hello.html

또한, .MountsTypebind로 되어있는 것을 확인할 수 있다.

볼륨 바인드 방식은 volume으로 설정되어있다.

$ docker inspect <container>
...
  "Mounts": [
            {
                "Type": "bind",
                "Source": "/home/vagrant/contents",
                "Destination": "/abc",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],
...

 

디렉토리 전체 마운트하기

httpd는 기본적으로 /usr/local/apache2/working directory이고 /usr/local/apache2/htdocshttpdDocumentRoot directory이다.

$ docker run -d -v /home/vagrant/contents:/usr/local/apache2/htdocs httpd

 

⭐ 파일 마운트하기

파일을 파일로 마운트 하는 것이다. file mounting

$ docker run -d -v /home/vagrant/contents/hello.html:/usr/local/apache2/htdocs/hello.html httpd

디렉토리에 기존의 것은 유지되고, 파일만 마운트 된다.

$ docker exec -it 664 bash
root@664e1253a597:/usr/local/apache2# ls htdocs/
hello.html  index.html

디렉토리를 마운트하면 디렉토리 전체가 마운트 되기 때문에 기존의 파일이 가려진다.

기존의 파일들이 있지만 접근불가(안보이는) 것으로, 마운트를 해제하면 다시 접근할 수 있다.


그러나 파일을 마운트하면 특정 파일만 마운트되기 때문에 기존에 존재하던 파일이 가려지지 않는다.

따라서 기존에 어떤 디렉토리에 index.html 파일이 있고 추가적으로 다른 파일을 마운팅 시키고 싶다면 파일명을 지정해서 마운팅할 수 있다.

실제로 file mounting은 설정 파일을 제공할 때 많이 사용한다.

파일만을 추가하는 것이 목적이라면 파일 마운팅 방식을 사용해야한다.

참고로 볼륨 방식은 파일 마운팅이 불가능하다.



✔️ 사용 용도

바인드 방식: 설정파일, 기타 파일을 제공하기 위한 목적

  • 이미 존재하는 파일을 제공할 때 사용한다.

볼륨 방식 : 데이터를 저장하기 위한 빈 디렉토리 제공하기 위한 목적

  • 빈 볼륨을 제공할 때 사용한다.

 

 

 

 

 

참고

 

 

728x90