✔️ Docker Volume 개요
이미지는 하나 이상의 layer로 구성되어 있다.
이미지의 레이어는 어떻게 확인하는가 ?
1) 이미지를 받을 때(pull) 확인할 수 있다.
2) docker inspect <IMAGE>
명령 실행 후 layer에서 확인할 수 있다.
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:e59fc94956120a6c7629f085027578e6357b48061d45714107e79f04a81a6f0c"
]
},
이미지는 immutable이므로 절대 바뀌지 않는다.
이미지로 컨테이너를 만들게 되면 기존 이미지의 내용이 변하지 않고 컨테이너로 생성된다.
즉 이미지는 read-only 상태이며 쓰기 작업을 할 수 없다.
만약 그 위에 read/write가 가능한 empty layer를 얹는다고 해보자.
그리고 그 layer을 변경되거나 새로 만들어지는 모든 것들이 기록되는 영역으로 두고 새롭게 컨테이너로 만든다고 하자
만약 해당 컨테이너를 지워버리면 모든 변경 사항은 컨테이너와 함께 사라지게 된다.
하지만 어떠한 컨테이너들은 변경 사항을 잃어서는 안되는데 가장 대표적인 것은 데이터베이스이다.
데이터베이스는 레코드를 CRUD 해야한다. 이때 mysql이나 mariadb는 local의 var/lib/mysql/ 에 데이터가 저장되어야 한다.
우리가 방금 추가한 empty layer에는 실제로 이러한 데이터들이 들어가야 한다.
하지만 이 컨테이너를 삭제하면 DB의 데이터도 같이 지워진다.
그렇다면 매번 컨테이너를 삭제하기위해서는 디비를 백업하는 그런 귀찮은 과정을 거쳐야 하는 것일까?
결론부터 말하면 컨테이너의 LifeCycle과는 별개의 storage를 구현해야한다.
새로 추가한 rw 가능한 empty layer 영역이 아니라 별개의 공간에 저장해야하는데 이를 Volume
이라고 한다.
이로서 컨테이너를 지워도 Volume에는 영향이 없다.
MySQL과 같은 DB는 이런 Volume을 필요로 한다.
우선 mysql 이미지를 살펴보자
docker inspect mysql:5.7 --format='{{ .Config.Volumes }}'
vagrant@docker ~ docker inspect mysql:5.7 --format='{{ .Config.Volumes }}'
map[/var/lib/mysql:{}]
format으로 확인해봤을 때 이미지 내에 Volumes 가 선언되어 있고 리스트의 개수가 1개이기 때문에 볼륨 1개가 자동으로 생성된다.
vagrant@docker ~ docker run --name wp-db -d -e MYSQL_ROOT_PASSWORD=P@ssw0rd -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wpadm -e MYSQL_PASSWORD=P@ssw0rd --restart always --cpus 0.5 --memory 1000m mysql:5.7
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
fb162625662e435c6290e8750c5ae3b40004a42930305f66bdafbcc877794b61
vagrant@docker ~ docker volume ls
DRIVER VOLUME NAME
local 69635f52d0792e15984fa4d998ec34d5755ab927ccd17f2dfc6767f503904d14
VOLUME_NAME을 보면 69635f52d0792e15984fa4d998ec34d5755ab927ccd17f2dfc6767f503904d14
이름으로 생성된 것을 알 수 있다.
볼륨의 이름이 랜덤하게 만들어졌는데 이것이 바로 map[/var/lib/mysql:{}]으로부터 만들어진 것이다.
어떻게 같음을 증명할 수 있는가 ?
docker inspect wp-db
"Mounts": [
{
"Type": "volume",
"Name": "69635f52d0792e15984fa4d998ec34d5755ab927ccd17f2dfc6767f503904d14",
"Source": # 도커 호스트의 위치 "/var/lib/docker/volumes/69635f52d0792e15984fa4d998ec34d5755ab927ccd17f2dfc6767f503904d14/_data",
"Destination": "/var/lib/mysql", # 컨테이너의 마운트 위치
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
docker inspect
하면 Mounts 항목이 있는데 이것이 볼륨과 관련된 부분이다.69635f52d0792e15984fa4d998ec34d5755ab927ccd17f2dfc6767f503904d14
볼륨이 /var/lib/mysql 에 Mount 되었다.
컨테이너의 기본적인 정보는 이미지에서 가져오는 것이므로 자동으로 마운트되어 볼륨으로 만들어진다.
참고로 우리가 볼륨의 이름을 지정할 수도 있다.
정확히 마운트 된 내용을 확인하기 위해 해당 위치로 이동해보자
vagrant@docker ~ sudo -i
root@docker:~# cd /var/lib/docker/volumes/69635f52d0792e15984fa4d998ec34d5755ab927ccd17f2dfc6767f503904d14/_data
root@docker:/var/lib/docker/volumes/69635f52d0792e15984fa4d998ec34d5755ab927ccd17f2dfc6767f503904d14/_data# ls
auto.cnf client-cert.pem ib_logfile0 ibtmp1 private_key.pem server-key.pem
ca-key.pem client-key.pem ib_logfile1 mysql public_key.pem sys
ca.pem ib_buffer_pool ibdata1 performance_schema server-cert.pem wordpress
root@docker:/var/lib/docker/volumes/69635f52d0792e15984fa4d998ec34d5755ab927ccd17f2dfc6767f503904d14/_data#
vagrant@docker ~ docker exec -it wp-db bash
root@fb162625662e:/# cd /var/lib/mysql
root@fb162625662e:/var/lib/mysql# ls
auto.cnf client-cert.pem ib_logfile0 ibtmp1 private_key.pem server-key.pem
ca-key.pem client-key.pem ib_logfile1 mysql public_key.pem sys
ca.pem ib_buffer_pool ibdata1 performance_schema server-cert.pem wordpress
도커의 볼륨 디렉토리의 파일 구성이 컨테이너 내의 /var/lib/mysql 디렉토리의 파일 구성과 같은 것을 알 수 있다.
이미지의 Config.Volumes
선언되어 있으면, 자동으로 Docker 볼륨이 생성되고 마운트된다.
✔️ 볼륨 방식 마운트
볼륨 : 읽기 - 쓰기가 가능한 빈 저장소를 생성한다.
빈 볼륨 생성
docker volume create <NAME>
볼륨 목록
docker volume ls
볼륨 삭제
docker volume rm <NAME>
사용하지 않는 볼륨 삭제
docker volume prune
사용하지 않는 컨테이너에 마운트되지 않음
볼륨을 사용한 컨테이너
docker run -v <VOL-NAME>:<MOUNTPOINT> <IMAGE>
지정한 볼륨 이름이 없으면 생성
vagrant@docker ~ docker volume create --help
Usage: docker volume create [OPTIONS] [VOLUME]
Create a volume
Options:
-d, --driver string Specify volume driver name (default "local")
--label list Set metadata for a volume
-o, --opt map Set driver specific options (default map[])
vagrant@docker ~
default "local" : 도커의 호스트에 있는 로컬에 있는 디렉토리를 사용한다는 뜻이다.
vagrant@docker ~ docker volume create test-vol
test-vol
vagrant@docker ~ docker volume ls
DRIVER VOLUME NAME
local test-vol
vagrant@docker ~ docker inspect test-vol
[
{
"CreatedAt": "2022-05-09T02:28:10Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/test-vol/_data",
"Name": "test-vol",
"Options": {},
"Scope": "local"
}
]
vagrant@docker ~ sudo ls /var/lib/docker/volumes/test-vol/_data
아직은 빈 볼륨이므로 아무것도 없다.
-v
, 또는 --volume
옵션을 이용해 볼륨을 연결할 수 있다.
- 앞 : 호스트 볼륨의 이름
- 뒤 : 컨테이너 내부의 마운트 위치
vagrant@docker ~ docker run -itd -v test-vol:/test-vol ubuntu
vagrant@docker ~ docker inspect 1d
"Mounts": [
{
"Type": "volume",
"Name": "test-vol",
"Source": "/var/lib/docker/volumes/test-vol/_data",
"Destination": "/test-vol",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
],
vagrant@docker ~ docker exec -it 1d bash
root@1d5aec0e9c31:/# pwd
/
root@1d5aec0e9c31:/# 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@1d5aec0e9c31:/# cd test-vol/
root@1d5aec0e9c31:/test-vol# ls
root@1d5aec0e9c31:/test-vol# echo "hello world" > hello.txt
root@1d5aec0e9c31:/test-vol# ls
hello.txt
root@1d5aec0e9c31:/test-vol# cat hello.txt
hello world
root@1d5aec0e9c31:/test-vol# exit
exit
vagrant@docker ~ sudo ls /var/lib/docker/volumes/test-vol/_data
hello.txt
vagrant@docker ~ sudo cat /var/lib/docker/volumes/test-vol/_data/hello.txt
hello world
vagrant@docker ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1d5aec0e9c31 ubuntu "bash" 2 minutes ago Up 2 minutes pensive_goodall
vagrant@docker ~ docker rm -f 1d
1d
vagrant@docker ~ docker volume ls
DRIVER VOLUME NAME
local test-vol
vagrant@docker ~ docker run -itd -v test-vol:/test ubuntu
e694c691ca518d7b6f4d94aa03f1b4e8f0b8070770f99bf0e8389c5e5b4db69a
vagrant@docker ~ docker exec e6 ls /test
hello.txt
vagrant@docker ~ docker exec e6 cat /test/hello.txt
hello world
컨테이너를 삭제한다고 볼륨이 삭제되는 것이 아니다.
vagrant@docker ~ docker run -itd -v abc:/abc ubuntu
cc9fc19d7cc471439dadb8b3d972f732eb0cbc6b20e584b1d415fa6ad23b9b00
vagrant@docker ~ docker volume ls
DRIVER VOLUME NAME
local abc
local test-vol
참고로 볼륨을 미리 만들어놓지 않아도 없으면 알아서 만들어준다.
물론 생성된 볼륨은 처음에는 비어있는 상태이다.
vagrant@docker ~ docker run -itd -v abc:/abc -v xyz:/xyz ubuntu
447910a18ed6975d0baa11e2e42f3a4e3ae529c061bb92a8ddf2eece3721fcca
vagrant@docker ~ docker volume ls
DRIVER VOLUME NAME
local abc
local test-vol
local xyz
vagrant@docker ~ docker inspect 44
"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": ""
}
],
볼륨은 여러개일 수도 있다.
vagrant@docker ~ docker run --name wp-db2 -d -v wp-db-vol:/var/lib/mysql mysql:5.7
95348375c37992827a06e1f45f654b15de45c0e732eed658b32ffe7804bc21b3
vagrant@docker ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
95348375c379 mysql:5.7 "docker-entrypoint.s…" 4 seconds ago Up 3 seconds 3306/tcp, 33060/tcp wp-db2
mysql 컨테이너에 환경변수 설정을 하지 않았지만 정상적으로 실행되었다.
왜 환경변수 설정 없이도 실행이 되는 것일까 ?
vagrant@docker ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
95348375c379 mysql:5.7 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 3306/tcp, 33060/tcp wp-db2
vagrant@docker ~ docker exec 95 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-db2를 살펴보면 환경변수가 없다.
mysql 또는 mariadb 컨테이너를 띄우기 위해서는 루트 패스워드 설정이 중요한데 이미지 내에는 루트 패스워드가 없기 때문에 루트 패스워드를 환경 변수로 제공해야 한다.
하지만 우리가 컨테이너를 띄울 때 volume 즉 데이터베이스를 제공했기 때문에 그 내부에 루트의 패스워드가 있어 별도로 루트 패스워드를 설정할 필요가 없이 컨테이너가 실행됐던 것이다.
vagrant@docker ~ docker exec 95 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
vagrant@docker ~ docker exec -it 95 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>
기존의 패스워드로는 정상적으로 접속할 수 있으나,
vagrant@docker ~ docker run --name wp-db3 -d -e MYSQL_ROOT_PASSWORD=qwer1234 -e MYSQL_DATABASE=wordpress -e MYSQL_U
SER=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
vagrant@docker ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
389245782e83 mysql:5.7 "docker-entrypoint.s…" 4 seconds ago Up 3 seconds 3306/tcp, 33060/tcp wp-db3
95348375c379 mysql:5.7 "docker-entrypoint.s…" 5 minutes ago Up 5 minutes 3306/tcp, 33060/tcp wp-db2
vagrant@docker ~ docker exec -it 38 mysql -u wpadm -p
Enter password:
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
변경한 패스워드를 통해 로그인하려고 하면 접근이 불가하다.
볼륨에 있는 내용을 참조하고 있기 때문이다. 심지어는 변경 전의 비밀번호로 접속하려고 해도 접속이 불가능하다.
docker logs 38
2022-05-09T02:53:06.367712Z 0 [ERROR] InnoDB: Unable to lock ./ibdata1 error: 11
2022-05-09T02:53:06.367748Z 0 [Note] InnoDB: Check that you do not already have another mysqld process using the same InnoDB data or log files.
로그 파일을 살펴보면 error가 발생했음을 알 수 있다.
vagrant@docker ~ docker inspect wp-db3
"Mounts": [
{
"Type": "volume",
"Name": "wp-db-vol",
"Source": "/var/lib/docker/volumes/wp-db-vol/_data",
"Destination": "/var/lib/mysql",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
],
그리고 여기서 한가지 알 수 있는 것은
vagrant@docker ~ docker inspect wp-db2
"Mounts": [
{
"Type": "volume",
"Name": "wp-db-vol",
"Source": "/var/lib/docker/volumes/wp-db-vol/_data",
"Destination": "/var/lib/mysql",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
],
하나의 볼륨이 있고 이 볼륨을 서로 다른 컨테이너가 동시에 마운트해서 쓸 수 있다는 것이다.
VM을 사용할 때는 이런 구성을 하기위해 NFS를 사용했어야 했다.
NFS를 사용할 때는 동시에 동일한 파일에 쓰기 작업을 할 수 없었다. 왜냐하면 NFS는 lock 기능을 가지고 있기 때문이다.
그런데 이 방식은 NFS가 아니기 때문에 lock 기능이 없고 누군가가 동시에 파일을 쓰기에 시도하면 가능하다.
하지만 어떤 결과를 만들어낼지 모른다. 데이터가 깨질 수도 있고 문제가 발생할 수도 있다.
vagrant@docker ~ docker run -itd -v abc:/abc --name u1 ubuntu
7e72220f4f9e417a89a66d1643a767b4b57e76b0c7a073340203b2015ce360ba
vagrant@docker ~ docker run -itd -v abc:/abc:ro --name u2 ubuntu
7cc6742ef36963ae0cd1fd30c3421ae19ea10bbcfb98c97fbc7edae6ef4533d8
ro
옵션은 read-only라는 의미이다.
vagrant@docker ~ docker inspect u1
"Mounts": [
{
"Type": "volume",
"Name": "abc",
"Source": "/var/lib/docker/volumes/abc/_data",
"Destination": "/abc",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
],
vagrant@docker ~ docker inspect u2
"Mounts": [
{
"Type": "volume",
"Name": "abc",
"Source": "/var/lib/docker/volumes/abc/_data",
"Destination": "/abc",
"Driver": "local",
"Mode": "ro",
"RW": false,
"Propagation": ""
}
],
vagrant@docker ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7cc6742ef369 ubuntu "bash" About a minute ago Up About a minute u2
7e72220f4f9e ubuntu "bash" About a minute ago Up About a minute u1
vagrant@docker ~ docker exec -it u1 bash
root@7e72220f4f9e:/# cd abc/
root@7e72220f4f9e:/abc# ls
root@7e72220f4f9e:/abc# touch a b c
root@7e72220f4f9e:/abc# ls
a b c
root@7e72220f4f9e:/abc# exit
exit
vagrant@docker ~ docker exec -it u2 bash
root@7cc6742ef369:/# cd abc/
root@7cc6742ef369:/abc# ls
a b c
root@7cc6742ef369:/abc# touch x y z
touch: cannot touch 'x': Read-only file system
touch: cannot touch 'y': Read-only file system
touch: cannot touch 'z': Read-only file system
root@7cc6742ef369:/abc# rm a
rm: cannot remove 'a': Read-only file system
필요시에 따라 볼륨에 rw, ro 설정을 할 수 있다. 기본값은 rw이며 ro로 변경하고자 할 때만 명시해주면 된다.
mysql을 보면 조금 다르다.
$ docker run --name some-mysql -v /my/own/datadir:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
앞에 보면 경로로 되어있다. 이것은 호스트의 경로이다. my/own/datadir
이 방식은 bind 방식이라고 한다.
도커의 볼륨의 마운트에는 2가지 방식이 있다.
1) Bind : 볼륨을 도커 데몬이 관리하지 않는다. → docker volume 명령으로 볼 수 없다.
호스트의 디렉토리를 사용하는 것이므로 도커와는 무관하다.
2) Volume : 볼륨을 도커 데몬이 관리한다.
✔️ 바인드 방식 마운트
바인드 방식은 컨테이너에게 제공할 볼륨을 호스트의 특정 디렉토리로 지정하는 방식
호스트의 디렉토리를 컨테이너에게 제공 : 미리 데이터를 채워서 제공이 가능하다.
docker run -v <ABSOLUTE_PATH>:<MOUNTPOINT>[:ro] <IMAGE>
디렉토리 전체 마운트
docker run -d -v /home/vagrant/contents:/usr/local/apache2/htdocs httpd
파일 마운트
docker run -d -v /home/vagrant/contents/hello.html:/usr/local/apache2/htdocs/hello.html httpd
✔️ 사용 용도
- 바인드 : 설정파일, 기타 파일을 제공하기 위한 목적
- 볼륨 : 데이터를 저장하기 위한 빈 디렉토리 제공하기 위한 목적
vagrant@docker ~ ls
centos7 centos7.tar hello-world.tar
vagrant@docker ~ mkdir contents
vagrant@docker ~ cd contents
vagrant@docker ~/contents echo "hello bind volume" > hello.html
vagrant@docker ~/contents
vagrant@docker ~/contents ls
hello.html
vagrant@docker ~/contents cat hello.html
hello bind volume
vagrant@docker ~/contents
도커 데몬이 관리하지 않는 볼륨으로 호스트의 디렉토리를 컨테이너에게 제공할 수 있다.
vagrant@docker ~/contents docker run -itd -v /home/vagrant/contents:/abc ubuntu
aa0a551f379165240f6571eb01bf9372db580f73bad88957809b918fba4db9c0
vagrant@docker ~/contents docker exec -it aa bash
root@aa0a551f3791:/# cd /abc/
root@aa0a551f3791:/abc# ls
hello.html
root@aa0a551f3791:/abc# cat hello.html
hello bind volume
root@aa0a551f3791:/abc#
중요한 것은 경로는 반드시 절대경로를 사용해야 한다는 것이다.
docker inspect aa
"Mounts": [
{
"Type": "bind",
"Source": "/home/vagrant/contents",
"Destination": "/abc",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
vagrant@docker ~/contents docker run -itd -v abc:/abc ubuntu
vagrant@docker ~/contents docker inspect 12
"Mounts": [
{
"Type": "volume",
"Name": "abc",
"Source": "/var/lib/docker/volumes/abc/_data",
"Destination": "/abc",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
],
바인드 방식은 이미 존재하는 파일을 제공할 때 사용하고 볼륨 방식은 빈 볼륨을 제공할 때 사용한다.
vagrant@docker ~/contents docker run -d -v /home/vagrant/contents/hello.html:/usr/local/apache2/htdocs/hello.html ht
tpd
188e94dec23742cbe3e21ad55e13a8d035637a819853bbff0e2c6284b671d03c
vagrant@docker ~/contents docker exec -it 18 bash
root@188e94dec237:/usr/local/apache2# cd htdocs/
root@188e94dec237:/usr/local/apache2/htdocs# ls
hello.html index.html
root@188e94dec237:/usr/local/apache2/htdocs# exit
exit
디렉토리를 마운트하면 디렉토리 전체가 마운트 되기 때문에 기존의 파일이 가려진다.
그러나 파일을 마운트하면 특정 파일만 마운트된다. 기존에 존재하던 파일이 가려지지 않는다.
이 개념은 file mounting 이라는 개념으로 자주 사용된다.
그래서 기존에 어떤 디렉토리에 index.html 파일이 있고 추가적으로 다른 파일을 마운팅 시키고 싶다면 파일명을 지정해서 마운팅할 수 있다.
실제로 file mounting은 설정 파일을 제공할 때 많이 사용한다.
파일만을 추가
하는 것이 목적이라면 파일 마운팅 방식을 사용해야한다.
'DevOps > Docker' 카테고리의 다른 글
[Docker] Docker Network - 2 (0) | 2022.05.10 |
---|---|
[Docker] Docker Network - 1 (0) | 2022.05.10 |
[Docker] Docker 컨테이너 관리 명령어 (3) (0) | 2022.05.07 |
[Docker] Docker 컨테이너 관리 명령어 (2) (0) | 2022.05.07 |
[Docker] Docker 컨테이너 관리 명령어 (1) (0) | 2022.05.07 |
영차영차 성장 블로그
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!