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하면 끝입니다.
오늘은 볼륨 사용법을 다루어 보았습니다.