Storage in Docker
시스템에 도커를 설치하면 `/var/lib/docker/` 디렉토리가 생성된다. 하위에 있는 `aufs`, `containers`, `images`, `volumes` 와 같은 하위 디렉토리에는 관련 데이터들이 저장된다.
Layered architecture
도커 파일의 모든 커맨드 라인은 `docker build` 커맨드가 실행될 때 실행된다.
도커 파일을 통해 빌드하여 생성되는 이미지 레이어는 읽기만 하고 수정할 수 없다. (수정하는 방법은 새로 생성하는 방법 뿐이다.)
- 이미지 레이어 자체에서는 파일들이 수정되지 않지만, 파일(ex. 소스 코드 등)을 수정하고자하면 자동으로 Read & Write 가능한 컨테이너 레이어에 해당 파일의 사본이 생성되고, 이를 수정할 수 있다. (COPY-ON-WRITE mechanism)
- 실행된 컨테이너의 이미지 레이어는 `docker build` 커맨드를 통해 이미지를 재구축하기 전까지는 항상 동일하다.
`docker run`을 통해 해당 이미지에 기반을 둔 컨테이너를 실행할 때 이 레이어들에 기반을 둔 컨테이너를 만들고, 이미지 레이어 상단에 Read & Write이 가능한 새 레이어를 생성한다.
- Container Layer에는 애플리케이션의 로그 파일이나 컨테이너에 의해 생성된 임시 파일, 해당 컨테이너의 사용자에 의해 수정된 파일 등이 저장된다.
- 컨테이너가 파괴되면 이 레이어와 레이어에 저장된 모든 변경 사항도 파괴된다.
- 이미지 레이어는 이 이미지를 통해 생성된 모든 컨테이너가 공유한다.
도커는 이미지를 구축(빌드)할 때 레이어드 아키텍처 안에 빌드한다. 도커 파일에서의 각 라인(each line of the instruction docker file)은 도커 이미지에 새로운 레이어를 생성한다. (파일 시스템상 이전 레이어와 다른 변경사항이 존재한다면)
` Dockerfile`
FROM ubuntu
RUN apt-get update && apt-get -y install python
RUN pip install flask & flask-mysql
COPY . /opt/source-code
ENTRYPOINT FLASK_APP=/opt/source-code/app.py flask run
docker build Dockerfile -t yhlee/my-custom-app
위의 도커 파일을 빌드하면 아래와 같은 레이어들이 생성된다.
Layer 1. Base Ubuntu layer (120 MB)
Layer 2. Changes in apt packages (306 MB)
Layer 3. Changes in pip packages (6.3 MB)
Layer 4. Source code (229MB)
Layer 5. Update Entrypoint (0B)
각 레이어는 이전 레이어의 변화(파일 시스템 상의 변화)만 저장한다. 때문에, 파일 시스템에 생기는(새로 설치하는) 변화 만큼의 용량이 곧 레이어의 크기가 된다.
- 즉, Layer 1의 용량은 우분투 기본 이미지의 용량이며, Layer 2의 용량은 apt 패키지를 업데이트하는 (패키지들을 새로 설치하는) 만큼의 용량이다.
만약 아래의 새로운 도커 파일을 빌드한다면, 앞의 세 레이어는 같기 때문에 빌드되지 않으며, 캐시에 남아있는 ‘첫 번째 애플리케이션에 쓰인 3 계층’을 재사용한다.
FROM Ubuntu
RUN apt-get update && apt-get -y install python
RUN pip install flask flask-mysql
COPY app2.py /opt/source-code
ENTRYPOINT FLASK_APP=/opt/source-code/app2.py flask run
docker build Dockerfile2 -t yhlee/my-custom-app-2
생성되는 레이어는 아래와 같다. 앞의 세 레이어는 이전에 캐시에 남아있는 레이어를 재사용함으로써 도커는 보다 빨리 레이어를 생성할 수 있고, 디스크 공간도 효율적으로 절약할 수 있다.
Layer 1. Base Ubuntu layer (0 MB)
Layer 2. Changes in apt packages (0 MB)
Layer 3. Changes in pip packages (0 MB)
Layer 4. Source code (229 MB)
Layer 5. Update Entrypoint (0 B)
Storage drivers
💡 도커는 스토리지 드라이버(Storage Driver)를 이용해 Layered architecture를 가능하게 한다.
도커는 OS에 기반해 자동으로 사용 가능한 최고의 스토리지 드라이버를 선택한다.
- Ex) 우분투의 기본 스토리지 드라이버는 AUFS이지만 해당 드라이버는 페도라나 센토스에서는 사용 불가하다. (이 OS들에서는 Device Mapper가 더 좋다.
Cf. Common Storage drivers
- AUFS
- ZFS
- BTRFS
- Device Mapper
- Overlay
- Overlay2
Volumes
Volume drivers
볼륨은 스토리지 드라이버가 아닌 볼륨 드라이버 플러그인에 의해 다뤄진다.(handle)
default는 Local 이다.
- 로컬 볼륨 드라이버는 Docker Host를 도와 볼륨을 생성하고, 해당 볼륨의 데이터를 var/lib/docker/volumes 경로에 저장하도록 한다.
많은 볼륨 드라이버들이 서드 파티 솔루션에 볼륨을 생성할 수 있도록 허가한다.
Cf. Common Volume drivers
- Local
- Azure File Storage
- Convoy
- DigitalOcean Block Storage
- Flocker
- gce-docker
- GlusterFS
- NetApp
- RexRay
- Portworx
- VMware vSphere Storage
일부 볼륨 드라이버는 다른 Storage driver를 지원한다.
Docker 컨테이너를 실행할 때 Amazon EBS의 볼륨을 프로비저닝하기 위해 REX-Ray EBS와 같은 특정 볼륨 드라이버를 사용할 수 있다.
아래와 같이 실행할 경우 컨테이너가 생성되고 AWS Cloud에서 볼륨이 연결되어, 컨테이너가 종료되어도 데이터는 클라우드에 존재하게 된다.
docker run -it \\
--name mysql
--volume-driver rexray/ebs
--mount src=ebs-vol, target=/var/lib/mysql
mysql
Volume 유형
Volume mounting
💡 볼륨을 마운트하는 방법이다.
간혹 컨테이너에 생성된 데이터를 DB에 저장하고 싶다면 컨테이너에 영구적인 볼륨(Persistent Volume)을 추가(생성)할 수 있다. 해당 볼륨에 대한 디렉토리는 `/var/lib/docker/volumes/` 하위에 볼륨명으로 된 폴더로 생성된다.
docker volume create [volumne-name]
해당 볼륨을 컨테이너 내부에 마운트한다. (`-v` : rewrite layer)
# '-v' option is old version
docker run -v [volume_name]:[location inside cluster] [image-name]
# example
docker run -v [my_volume]:/var/lib/mysql mysql
# '/var/lib/mysql' is the default location where mySQL stores data.
이렇게 되면 새로운 컨테이너가 생성되고, 조금 전에 생성한 경로의 볼륨(`/var/lib/mysql/` 하위에 존재)을 마운트한다.
- 따라서 데이터베이스에 의해 쓰이는 모든 데이터는 도커 호스트 상에 생성된 볼륨에 저장된다. (= 컨테이너가 파괴되어도 데이터는 살아 있다.)
Bind mounting
💡 도커 호스트 상의 어떠한 경로든 볼륨으로 삼을 수 있는 방법이다.
만일, 도커 호스트 상에 존재하는 외부 스토리지(`/data` )가 이미 존재한다고 가정해보자. `/var/lib/docker/volumes` 하위에 저장하지 않고, `/data` 하위에 저장하고 싶은 경우, 볼륨명이 아닌 전체 경로를 명시해야한다.
# old version('-v')
docker run -v /data/mysql:/var/lib/mysql mysql
# new version
docker run \
--mount type=volume, source=/data/mysql, target=/var/lib/mysql mysql
CSI(Container Storage Interface)
CRI(Container Runtime Interface)
과거에는 쿠버네티스의 컨테이너 런타임 엔진으로써 도커가 단독으로 이용되었다. (이 때문에 도커가 동작하는데 필요한 코드들이 쿠버네티스 소스 코드에 속해있었다.) 하지만 rkt, cri-o 등의 다른 컨테이너 런타임이 등장함에 따라 Kubernetes 소스 코드에 의존하지 않고 다양한 컨테이너 런타임과 함께 작동하도록 지원을 개방하고 확장하는 것이 중요해지면서 CRI가 등장하게 되었다.
CRI는 쿠버네티스와 같은 오케스트레이션 솔루션이 도커와 같은 컨테이너 런타임과 통신하는 방식을 정의하는 표준이다. 때문에, 새로 개발되는 컨테이너 런타임 인터페이스는 CRI 표준을 따를 수 있고, 그럴 경우 해당 컨테이너 런타임은 쿠버네티스 개발자들과 협업하거나 쿠버네티스 소스 코드를 만지지 않고도 쿠버네티스와 함께 동작할 것이다.
CNI(Container Network Interface)
여러 네트워크 솔루션을 지원하기 위해 개발된 것으로, 많은 벤더(공급업체)들은 CNI표준에 기반한 플러그인을 개발하여 손쉽게 쿠버네티스와 호환되게 할 수 있다.
CSI
위의 여러 표준들과 같이 CSI 역시 여러 스토리지 솔루션을 지원하기 위해 개발되었다.
CSI를 사용하면 쿠버네티스와 호환되도록 자체 스토리지용 드라이버를 직접 작성할 수 있다. CSI는 쿠버네티스의 특정 표준이 아닌 보편적인 표준이다. 따라서, 일단 구현되면, 지원되는 플러그인을 이용해 어떤 컨테이너 오케스트레이션 툴도 스토리지 공급업체와 호환되도록 한다.
현재 Kubernetes, Cloud Foundry, Mesos가 CSI에 동참하고 있다.
종류
RPC
컨테이너 오케스트레이터가 호출할 RPC 세트 또는 원격 프로시저 호출을 의미하며 이는 스토리지 드라이버에 의해 구현되어야 한다.
Ex. 컨테이너 오케스트레이터가 RPC 호출 → 스토리지 드라이버가 해당 RPC를 구현 및 처리
파드가 생성되고 볼륨이 필요할 때 컨테이너 오케스트레이터(이 경우 쿠버네티스)는 볼륨 생성 RPC를 호출하고 볼륨 이름과 같은 세부 정보 집합을 전달해야 한다. 스토리지 드라이버는 이 RPC를 구현하고 해당 요청을 처리하고 스토리지 배열에 새 볼륨을 프로비저닝하고 작업 결과를 반환해야 한다.
마찬가지로, 컨테이너 오케스트레이터는 볼륨이 삭제될 때 볼륨 삭제 RPC를 호출해야 하며, 스토리지 드라이버는 해당 호출이 이루어질 때 배열에서 볼륨을 해제하는 코드를 구현해야 한다.
Cf. 사양에는 호출자가 어떤 매개 변수를 보내야 하는지, 솔루션이 무엇을 받아야 하는지, 어떤 오류 코드를 교환해야 하는지 자세히 설명되어 있다.
'Docker' 카테고리의 다른 글
Docker) 도커 이미지, 컨테이너 조회 및 삭제 명령어 (1) | 2022.11.20 |
---|---|
Docker) Ubuntu에 Docker 설치하기 (0) | 2022.10.19 |
Docker) CentOS에 Docker 설치하기(VM 설치 및 네트워크 설정, SSH 통신) (1) | 2022.09.20 |
Docker) Docker 설치하기(VirtualBox 설치, VM 생성) (0) | 2022.09.04 |