[Docker] docker volume-12

Docker 제공, volume 기술 이해

volume 기술 이해

  • Docker에서 제공하는 volume 기술은 컨테이너 애플리 케이션에서 생성되고 사용되는 데이터를 유지, 보존하기 위한 메커니즘을 제공한다. 컨테이너가 삭제되어도 volume은 독립적으로 운영되기 때문에 데이터를 유지한다.
  • volume 기술은 Docker HostOS와 컨테이너에서 직접 접근이 가능하다.
  • 일반적으로 컨테이너 내부의 데이터는 컨테이너의 생명 주기와 연관되어 컨테이너 종료시 삭제되지만, 이를 지속적으로 보존하기 위한 방법으로 volume 기술이 사용된다.

volume 방식

  • 3가지 volume기술을 제공한다. bind mount, volume, tmpfs mount이다.

bind mount

  • 호스트의 디렉토리, 파일을 컨테이너에 mount하여 사용한다.
  • 사전에 연결할 파일, 디렉토리를 생성하지 않고 자동으로 도커에 의해 생성되면 루트(root)사용자 소유가 된다.

“호스트 파일 시스템 절대경로”:”컨테이너 내부 경로”를 직접 연결(mount)하여 사용한다.

docker run .. -v /my-host:/app ..
docker run .. --mount type=bind,source=${PWD}/mydata,target=/var/log ..

첫번째 방식으로 주로 사용한다. -v을 사용해서 호스트위치:컨테이너위치 설정하면 된다.

volume을 사용하는 이유는 데이터의 보전을 위해서이다. 컨테이너는 생명주기가 있어 버전이 올라가거나 소스의 수정이 있을때마다 삭제하고 다시 생성하는데 그때 데이터의 유지를 위해서 사용한다. volume으로 연결해 놓으면 그 데이터가 호스트에 남아 있어서 컨테이너를 삭제해도 데이터가 유실되지 않는다.

 

docker volume

  • docker에서 권장하는 방법으로 “docker volume create 볼륨명”으로 볼륨을 생성한다.
  • docker volume 명령은 docker root dir(/var/lib/docker) 영역에 volume 영역을 만들어 컨테이너 내부 경로와 연결, 공유한다.
  • 볼륨 드라이버(vieux/sshfs plugin)를 통해 원격 호스트 및 클라우드 환경에 볼륨 내용을 저장하고 암호화가 가능하다.

docker volume create my-volume
docker run .. -v my-volume:/app ..
docker runn .. --mount source=my-volume,target=/app ..

사용법은 bind mount와 유사하다. docker volume create [volume name]을 실행해서 볼륨을 만들고 그 볼륨을 bind하면 된다.

tmpfs mount

  • Docker hostOS의 memory에서만 지속되고, 해당 컨테이너가 중지되면 tmpfs mount 연결 해제와 함께 기록된 데이터도 사라진다.
  • 이 방식은 컨테이너 간의 공유 설정은 안되고, Linux 기반 Docker에서만 지원된다.
  • 임시로 사용하고, 기록되지 않아야 되는 파일, 데이터 등을 사용할 경우에 유용하다.(host 영역 및 컨테이너의 write 영역에 파일이 기록되지 않음)

docker run .. -tmpfs /var/www/html
docker run .. -mount type=tmpfs,destination=/var/www/html ..

tmpfs mount는 bind와 달리 destination으로 컨테이너의 연결지점을 표현하고 있다. 이 방식은 컨테이너가 삭제 될때 데이터가 유실되는 방식이라서 특별한 경우가 아니면 사용될일이 없을거 같다.

 

bind mount 방식

[실습1] bind mount 연결 이해

mkdir bind01 bind02
echo 'fastcampus docker-1' > bind01/docker-1.txt
echo 'fastcampus docker-2' > bind02/docker-2.txt

docker run -it --name=bind-mount -v /home/kevin/fastcampus/ch08/bind01:/bind01 -v ${PWD}/bind02:/bind02 ubuntu:14.04 bash
df -ha
mount | grep bind

두개의 파일을 만들고 컨테이너에 연결합니다. 그리고 그 파일이 어떻게 마운트 되었는지 확인해봅시다.



ls
cat bind01/docker-1.txt
cat bind02/docker-2.txt

docker inspect --format="{{ .HostConfig.Binds }}" bind-mount
조금 더 자세히 살펴 보기 위해서 위의 실행문들을 입력해서 확인 해보았습니다.


docker stop bind-mount
docker rm bind-mount
cat bind01/docker-1.txt
cat bind02/docker-2.txt

컨테이너를 삭제한후에 연결해 놓았던 파일들이 유지 되는지 확인해봅시다.  잘 유지되네요.

[실습2] bind mount, 설정 파일 공유(전달)

redis 컨테이너의 비밀번호를 호스트에서 설정하고  설정파일을 공유해서 컨테이너를 만들겠습니다.

cd ~/fastcampus/ch08
mkdir redis-conf && cd &_
curl -O https://raw.githubusercontent.com/redis/redis/7.0/redis.conf

vi redis.conf
requirepass pass123#

강의 자료 폴더로 이동해서 폴더를 하나 만들고 redis.conf파일을 다운받습니다. 그리고 1037번 라인에 위의 비밀번호를 입력합니다.


 

docker run -itd --name=redis-container -p 6379:6379 -h=redis-db -v ./data:/data -v ./redis.conf:/usr/local/conf/redis.conf redis:7 redis-server /usr/local/conf/redis.conf
ll

./data:/data,  ./redis.conf:/usr/local/conf/redis.conf을 연결합니다. 그런데 ./data 폴더를 만들지 않았습니다. 이렇게 없는 폴더를 연결시키면 docker가 자동생성을 해줍니다. 하지만 그 권한이 root권한이죠. 

 

docker exec -it redis-container bash
redis-cli
set item docker1
exit

redis-container에 접속해서 redis-cli를 실행해서 데이터를 저장하려 했으나 에러가 발생했습니다. 권한 문제로 보입니다. 전에 설정했던 비밀번호가 필요한거 같습니다.

 

redis-cli -a pass123#
set item1 docker1
set item2 docker2

get item1
get item2

redis-cli -a pass123#를 실행해서 접속하면 데이터를 crud할수 있습니다.  item1, item2를 저장하고 조회해 봅시다.

 

[실습3] file bind mount

폴더, 설정 파일을 앞에서 마운트 했었습니다. 이번에는 컨테이너 내부의 .bash_history 파일을 호스트의 파일과 연결하겠습니다. 이 파일은 사용자가 로그아웃하면 그동한 입력한 내용들을 저장해 놓는 곳입니다. 사용자가 컨테너를 종료하면 그때 까지입력한 내용을 호스트의 .bash_history에 저장해줍니다.

docker run -it -v ~/.bash_history:/root/.bash_history --rm centos:8 /bin/bash
echo 'docker volume test' > volume.txt
ls
rm volume.txt
df -h
tail ~/.bash_history
exit

tail ~/.bash_history

컨테이너를 만들고 몇가지 실행문들을 실행하고 tail ~/.bash_history를 실행하면 방금 입력한 내용이 아닌 history가 나옵니다. 이것은 아직 로그오프를 하지 않아서입니다.  exit로 컨테이너를 죽인후 다시 해보면 방금 입력한 내용이 업데이트 된것을 알수 있습니다.

date
docker run --rm ubuntu:14.04 date
docker run --rm -v /etc/localtime:/etc/localtime ubuntu:14.04 date

hostos의 시간과 컨테이너의 시간이 맞지 않으면 -v /etc/localtime:/etc/localtime 사용해서 시간을 동기화 해주면된다. 하지만 매번 설정 넣는것보단 도커 파일을 수정해주는게 더 효율적이다.

FROM ubuntu:20.04
ENV TZ Asia/Seoul
# TimeZone 설정
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

 

docker volume 방식

[실습1] docker volume 생성과 연결

docker volume create mydb-data
docker volume ls
docker volume inspect mydb-data

docker run -d --name mydb -e MYSQL_ROOT_PASSWORD=password1 -e MYSQL_DATABASE=fastcampus -v mydb-data:/var/lib/mysql arm64v8/mysql

docker ps
sudo ls /var/lib/docker/volumes/mydb-data/_data
docker exec -it mydb bash
df-ha

docker stop mydb
docker rm mydb

sudo ls /var/lib/docker/volumes/mydb-data/_data
docker volume rm mydb-data

 

docker volume 방식은 도커가 volume을 관리 합니다. 위치는 /var/lib/docker/volumes 아래입니다. mysql 컨테이너를 올리고 df -ha를 사용해서 잘 마운트 되었는지 확인했습니다. 그리고 컨테이너를 삭제하고 volume이 데이터를 유지하는지 확인해 보았습니다.

 

[실습2] 암시적 docker volume, 볼륨 생성 없이 사용하는 방법

docker run -d --name mydb -e MYSQL_ROOT_PASSWORD=password1 -e MYSQL_DATABASE=fastcampus -v /var/lib/mysql arm64v8/mysql
docker volume ls

-v옵션을 사용할때 호스트의 경로를 정의 하지 않으면 임의의 이름으로 자동 생성된다. 하지만 알아보기 어려워서 추천하지 않는 방법이다.

 

[실습3] data container

여러개의 컨테이너에서 데이터를 공유하고자 할때 데이터 컨테이너 혹은 볼륨 컨테이너를 사용해서 공유 할수 있습니다.

[실습3-1] data container, 암시적 docker volume

docker create -v /share-data --name=share-container ubuntu:14.04
docker ps -a | grep share

docker run -it --volumes-from share-container --name=data-1 ubuntu:14.04 bash
df –h

docker ps -a | grep data
docker run -it --volumes-from share-container --name=data-2 ubuntu:14.04 bash

cat /share-data/data-1.txt

docker create -v /share-data –name=share-container ubuntu:14.04 실행해서 하나의 컨테이너를 생성해줍니다. 여기서 데이터 컨테이너는 생성만 해주면 됩니다. run으로 운영될 필요가 없습니다. 이걸 –volumes-from 옵션으로 다른 컨테이너에서 사용하면 됩니다. 

 

[실습3-2] data container, bind mount

cd ~
docker rm data-1
docker create -v $(pwd)/share-volume:/share-data --name=share-container ubuntu:14.04
docker run -it --volumes-from share-container:ro --name=data-2 ubuntu:14.04 bash
echo 'testing data container' > /share-data/data-1.txt
cat /share-data/data-1.txt
exit

ls share-volume/

docker run -it --volumes-from share-container:ro --name=data-2 ubuntu:14.04 bash
cat /share-data/data-1.txt
echo 'testing data container2' >> /share-data/data-2.txt

data-container를 만들고 bind 한후 컨테이너 내부에서 파일을 생서합니다. 컨테이너를 종료한후 파일의 존재하는지 확인합니다.
docker run -it –volumes-from share-container:ro –name=data-2 ubuntu:14.04 bash  여기서 보면 :ro 라는 옵션을 사용했습니다. read only라는 의미로 컨테이너 내부에서 수정이 못하고 읽는 것만 가능 하다는 의미입니다.

 

[실습] 데이터 지속성을 위한 Volume 구성

[실습1] DB container의 데이터 지속성을 위한 Volume 구성

docker run -itd --name=mydb -e MYSQL_ROOT_PASSWORD=pass123# -e MYSQL_DATABASE=fastcampus -v ${PWD}/mydb-data:/var/lib/mysql arm64v8/mysql
docker ps
docker exec -it mydb bash
cat /etc/os-release
df -h

mysql -uroot -p
show databases;
use fastcampus;

create table dockerclass (classid int, classname varchar(50));
insert into dockerclass values (10,'docker container CI/CD');
select * from dockerclass;
exit
ls -l /var/lib/mysql/fastcampus/

볼륨을 연결한 mysql 컨테이너를 올리고 테이블을 만들고 인서트를 해보자. 아래에서 컨테이너를 지우고 다른 컨테이너에서 데이터가 유지 되는지 확인 보는 실습이다.

 

docker stop mydb
docker rm mydb
ls mydb-data/

ocker run -it --name=mydb -e MYSQL_ROOT_PASSWORD=pass123# -e MYSQL_DATABASE=fastcampus -v ${PWD}/mydb-data:/var/lib/mysql -d arm64v8/mysql
docker exec -it mydb bash

mysql -uroot -p
use fastcampus;
show tables;
select * from dockerclass;

기존 컨테이너를 삭제하고 새로운 컨테이너를 볼륨연결해보았다. 예상대로 데이터가 잘 유지 되어있다. 추가로 동일 이미지에 대한 버전변경도 데이터가 유지된다.

 

[실습2] Web container의 log 유지 및 실시간 확인을 위한 Volume 구성

mkdir -p /home/kevin/nginx-log
docker run -d --name=myweb -v /home/kevin/nginx-log:/var/log/nginx - p 8011:80 nginx:1.25.0-alpine
docker ps | grep myweb
ls nginx-log/

tail -f nginx-log/access.log
curl http://192.168.56.201:8011/

cd nginx-log/
awk '$4>"[16/Jun/2023:05:14:58]" && $4<"[16/Jun/2023:05:15:31]"' access.log | awk '{ print $1 }' | sort | uniq -c | sort -r | more

로그를 볼륨연결해서 살펴보았다.
awk ‘$4>”[16/Jun/2023:05:14:58]” && $4<“[16/Jun/2023:05:15:31]”‘ access.log | awk ‘{ print $1 }’ | sort | uniq -c | sort -r | more
위의 실행문은 두 기간 사이의 로그를 출력하는 명령어이다.

 

[실습] volume 사용량 제한 구성

이제 실습을 해오면서 느꼈을 수도 있을거 같습니다. volume이 사용량 제한이 없다는 것입니다. 제한 없이 사용하다 보면 용량부족으로 문제가 발생할수도 있기에 이번 실습에서는 그 사용량을 제한하는 방법을 살펴보겠습니다.

[실습1-1] 컨테이너 rootfs(/) 영역 제한

docker run -it -v /home/kevin/myvolume:/webapp --name=mycontainer ubuntu:14.04 bash
df -h

/, /webapp경로의 마운트 되어있는 크기를 보면 호스트의 할당량 전부가 마운트되어있습니다. 이걸 이제 제한해 보겠씁니다.스크린샷은 처음 진행할때 못 찍어서 hostos2에서 찍었습니다.

mount | grep xfs
docker run -it -v /home/kevin/myvolume:/webapp --name=mycontainer --storage-opt size=1G ubuntu:14.04 bash

sudo vi /etc/default/grub
10 GRUB_CMDLINE_LINUX_DEFAULT="quiet splash rootflags=uquota,pquota"

sudo vi /etc/fstab
UUID=b2f0ef34-291f-411d-aa75-9a46d72c7401 /var/lib/docker xfs defaults,pquota 0 0

sudo reboot
mount | grep xfs

noquata 일때는 용량 제한이 안됩니다. prjquota로 변경하겠습니다.

 

docker run -it -v /home/kevin/myvolume:/webapp --name=mycontainer --storage-opt size=1G ubuntu:14.04 bash
df -h

–storage-opt size=1G을 적용해서 /경로가 1G로 제한 되는걸 확인했습니다.

 

[실습1-2] 컨테이너 rootfs(/) 영역 제한

모든 컨테이너에 이러한 옵션을 적용하는 거는 불편합니다. 전역으로 설정을 적용하는 방법을 알아봅시다.

sudo vi /etc/docker/daemon.json
{ "insecure-registries": ["192.168.56.101:5000"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "30m",
    "max-file": "10"
  },
  "storage-opts": ["overlay2.size=500m"]
}

sudo systemctl restart docker.service
sudo systemctl status docker.service

docker run -it -v /home/kevin/myvolume:/webapp --name=mycontainer ubuntu:14.04 bash
df -h

적용 하고 실행해보면 500M로 제한이 됨을 알수 있습니다.

 

[실습2] 컨테이너 Volume 공간 사용량 제한

sudo su -
dd if=/dev/zero of=tmpdisk.img count=512 bs=1M
mkfs.xfs tmpdisk.img
fdisk -l tmpdisk.img

mkdir -p /home/kevin/volume-quota
mount -o loop tmpdisk.img /home/kevin/volume-quota
df -h

chown -R kevin.kevin /home/kevin/volume-quota
exit

컨테이너에 마운트 되는 Volume을 제한 하기 위해서는 리눅스 명령어들을 이용해야 합니다. 먼저 루트사용자로 512M의 이미지를 만들고 파일시스템을 만듭니다. 그걸 마운트하고 권한을 kevin에게 줍니다.

 

docker run -it -v /home/kevin/volume-quota:/webapp --name=myvolume ubuntu:14.04 bash

마운트된 경로를 bind mount하면 끝입니다.

오늘은 볼륨 사용법을 다루어 보았습니다.

Leave A Reply

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다