도커 볼륨
도커 이미지 => 컨테이너 생성 (읽기 전용)
컨테이너의 변경 사항만 별도로 저장
=> 각 컨테이너의 정보 보존
ex)
도커 컨테이너 (쓰기 가능)
mysql 이미지 (읽기 전용)
생성된 이미지는 변경 X, 컨테이너 계층에 원래 이미지에서 변경된 정보를 저장
이미지: 애플리케이션 파일
컨테이너: 운용하며 생기는 데이터 저장
but 컨테이너를 삭제하면 정보도 같이 삭제.
도커 컨테이너는 생성과 삭제가 매우 쉬워서 복구 불가.
=> 이를 방지하기 위해 데이터를 영속적으로 활용할 수 있는 도커 볼륨 활용
호스트 볼륨 공유
DB 컨테이너를 삭제해도 데이터는 삭제되지 않게 설정을 해보겠습니다.
docker run -d \
--name wordpressdb_hostvolume \
-e MYSQL_ROOT_PASSWORD=password \
-e MYSQL_DATEBASE=wordpress \
-v /home/wordpress_db:/var/lib/mysql \
mysql
-v: 호스트의 /home/wordpress_db 디렉터리와 컨테이너의 /var/lib/mysql 디렉터리를 공유
[호스트의 공유 디렉터리]:[컨테이너의 공유 디렉터리]
*맥에서는 /home이 아니라 /Users/계정이름 경로를 적어야 제대로 작동됩니다.
docker run -d \
-e MYSQL_ROOT_PASSWORD=password \
--name wordpress_hostvolume \
--link wordpressdb_hostvolume:mysql \
-p 80 \
wordpress
워드프레스 컨테이너에 -p 옵션으로 컨테이너의 80 포트를 외부에 노출했으므로,
wordpress_hostvolume 컨테이너의(DB) 호스트 포트로 워드프레스 컨테이너에 접속 가능
/var/lib/mysql: MySQL 데이터베이스를 저장하는 기본 디렉터리
미리 wordpress_db 디렉터리를 호스트에 생성하지 않았어도, 도커는 자동으로 이를 생성하고 mysql 구동에 필요한 각종 파일을 공유합니다.
docker stop wordpress_hostvolume wordpressdb_hostvolume
docker rm wordpress_hostvolume wordpressdb_hostvolume
명령어를 입력해서 두 컨테이너를 멈추고 삭제했습니다.
그럼에도 여전히 mysql 컨테이너가 사용한 데이터가 그대로 남아있습니다.
-v 옵션을 써서 컨테이너의 디렉터리를 호스트와 공유했기 때문입니다.
컨테이너의 /var/lib/mysql = 호스트의 /home/wordpress_db
동기화되는 것이 아니라 완전히 같은 디렉터리입니다.
즉, 컨테이너의 파일이 호스트의 파일로 복사된 것입니다.
(디렉터리뿐만 아니라 단일 파일 단위의 공유도 가능)
그렇다면 이미 호스트와 컨테이너에 파일이 존재하는 경우 두 디렉터리를 공유하면 어떻게 될까요?
이미지에 원래 존재하던 디렉터리에 호스트의 볼륨을 공유하면 컨테이너의 디렉터리 자체가 덮어씌워집니다.
-v 옵션을 통한 호스트의 볼륨 공유는 호스트의 디렉터리를 컨테이너의 디렉터리에 마운트 합니다.
볼륨 컨테이너
-v 옵션으로 볼륨을 사용하는 컨테이너를 다른 컨테이너와 공유할 수 있습니다.
컨테이너를 생성할 때 --volume-from 옵션을 설정하면 -v 옵션을 적용한 컨테이너의 볼륨 디텍 터리를 공유할 수 있습니다.
이 방법은 직접 공유하는 것이 아니라 -v 옵션을 적용한 컨테이너를 통해 공유하는 것입니다.
docker run -i -t \
--name volume_override \
-v /Users/en/wordpress_db:/home/test \
ubuntu:14.04
이를 테스트하기 위해 앞서 만들어놨던 wordpressdb 컨테이너에서 공유한 파일을 -v 옵션으로 가져왔습니다.
ocker run -i -t \
--name volumes_from_container \
--volumes-from volume_override \
ubuntu:14.04
그리고 -volumes-from 옵션을 사용해서 위에서 만든 volume_override 컨테이너의 볼륨 디렉터리를 공유받았습니다.
실행결과 wordpressdb 컨테이너가 가지고 있던 mysql 실행파일들이 ubuntu 이미지를 사용한 volumes_from_container에 공유된 것을 확인할 수 있습니다.
원본 컨테이너가 가지고 있던 파일을 -v 옵션을 적용한 볼륨 컨테이너가 공유받고,
그 볼륨 컨테이너에서 -volumes-from 옵션을 사용한 컨테이너가 공유를 받게 됩니다.
즉, 여러 개의 컨테이너가 동일한 컨테이너에 --volumes-from 옵션을 사용해 볼륨을 공유할 수 있습니다.
호스트에서 볼륨만 공유하고 별도의 역할을 담당하지 않는 '볼륨 컨테이너'로서 활용하는 것도 가능해집니다.
즉, 볼륨을 사용하려는 컨테이너에 -v 옵션 대신 --volumes-from 옵션을 사용함으로써 볼륨 컨테이너에 연결해 데이터를 간접적으로 공유하는 방식입니다.
volume 명령어
도커의 volume 명령어를 통해서 볼륨을 만들고, 만들어진 볼륨을 확인해 봤습니다.
볼륨을 생성할 때 플러그인 드라이버를 설정해 여러 종류의 스토리지 백엔드를 쓸 수 있지만,
기본 제공되는 드라이버인 local을 사용합니다.
이 볼륨은 로컬 호스트에 저장되며 도커 엔진에 의해 생성되고 삭제됩니다.
호스트와 볼륨을 공유하기 위해 -v [볼륨의 이름]:[컨테이너의 공유 디렉터리] 형식으로 코드를 작성했습니다.
볼륨의 컨테이너의 /root/ 디렉터리에 마운트 하므로 /root 디렉터리에 파일을 쓰면 해당 파일이 볼륨에 저장됩니다.
위의 설정으로 /root/volume 디렉터리가 생겼습니다.
다른 컨테이너에서 myvolume을 사용해 확인해 보겠습니다.
같은 파일인 volume이 존재하는 걸 확인할 수 있습니다.
이처럼 도커 볼륨도 여러 개의 컨테이너에 공유되어 활용할 수 있습니다.
볼륨은 디렉터리 하나에 상응하는 단위로 도커 엔진에서 관리합니다.
도커 볼륨도 호스트 볼륨 공유와 마찬가지로 호스트에 저장함으로써 데이터를 보존하지만,
파일이 실제로 어디에 저장되는지 사용자는 알 필요가 없습니다.
docker inspect 명령어를 사용하면 myvolume 볼륨이 실제로 어디에 저장되는지 알 수 있습니다.
컨테이너, 이미지, 볼륨 등 도커의 모든 구성단위의 정보를 확인할 때 사용되며,
정보를 확인할 종류를 명시하기 위해 --type 옵션에 image, volume 등을 입력합니다.
Driver는 볼륨이 쓰는 드라이버를, Label은 볼륨을 구분하는 라벨을 나타내며,
Mountpoint는 해당 볼륨이 실제로 호스트의 어디에 저장됐는지 의미합니다.
docker volume create 명령을 입력하지 않아도 -v 옵션을 입력할 때 이를 수행하도록 설정할 수 있습니다.
컨테이너에서 공유할 디렉터리의 위치를 -v 옵션에 입력하면 해당 디렉터리에 대한 볼륨을 자동으로 생성합니다.
ls 명령어로 확인하면 이름이 무작위의 16진수 형태인 볼륨이 자동으로 생성된 것을 확인할 수 있습니다.
docker container inspect [컨테이너 이름]
insepct 명령어는 컨테이너의 상세 정보를 출력하는데 이를 통해 볼륨 리스트 중 어떤 볼륨을 사용하는지 확인할 수 있습니다.
이렇게 도커 볼륨을 생성하고 삭제하다 보면 불필요한 볼륨들이 남게 됩니다.
컨테이너를 삭제한다고 볼륨이 같이 삭제되지는 않기 때문입니다.
docker volume prune
이를 통해 사용하지 않는 볼륨을 삭제할 수 있습니다.
이처럼 컨테이너가 아닌 외부에 데이터를 저장하고, 컨테이너는 그 데이터로 동작하도록 설계하는 것을 스테이트리스(stateless)하다고 말합니다.
컨테이너 자체는 상태가 없고 상태를 결정하는 데이터는 외부로부터 제공받습니다.
컨테이너가 삭제돼도 데이터는 보존되므로 스테이트리스한 컨테이너 설계는 도커를 사용할 때 매우 바람직한 설계입니다.
이와 반대로 컨테이너가 데이터를 저장하고 있어 상태가 있는 경우 스테이트풀(stateful)하다고 합니다.
스테이트풀한 컨테이너 설계는 컨테이너 자체에서 데이터를 보관하므로 지양하는 것이 좋습니다.
도커 네트워크 구조
도커는 컨테이너에 내부 IP를 순차적으로 할당하며, 이 IP는 컨테이너를 재시작할 때마다 변경될 수 있습니다.
내부 IP는 도커가 설치된 호스트, 즉 내부 망에서만 쓸 수 있는 IP이므로 외부와 연결이 필요합니다.
이 과정은 컨테이너를 시작할 때마다 호스트에 veth... 라는 네트워크 인터페이스를 제공함으로써 이뤄집니다.
도커는 각 컨테이너에 외부와의 네트워크를 제공하기 위해 컨테이너마다 가상 네트워크 인터페이스를 호스트에 생성하며,
이 인터페이스의 이름은 veth로 시작합니다.
veth 인터페이스는 사용자가 직접 생성할 필요는 없으며 컨테이너가 생성될 때 도커 엔진이 자동으로 생성합니다.
도커가 설치된 호스트에서 ifconfig나 ip addr과 같은 명령어로 네트워크 인터페이스를 확인하면,
실행 중인 컨테이너의 수만큼 veth로 시작하는 인터페이스가 생성된 것을 알 수 있습니다.
eth0은 공인 IP or 내부 IP가 할당되어 실제로 외부와 통신할 수 있는 호스트의 네트워크 인터페이스입니다.
veth로 시작하는 인터페이스는 컨테이너를 시작할 때 생성됐으며, 각 컨테이너는 eth0과 연결됐습니다.
veth 인터페이스뿐 아니라 docker0이라는 브리지도 존재하는데,
docker0 브리지는 각 veth 인터페이스와 바인딩돼 호스트의 eth0 인터페이스와 이어주는 역할을 합니다.
도커 컨테이너 eth0 -> 호스트 veth.. -> 호스트 docker0 -> 호스트 eth0
즉, 컨테이너의 eth0 인터페이스는 호스트의 veth... 라는 인터페이스와 연결됐으며,
veth 인터페이스는 docker0 브리지와 바인딩돼 외부와 통신할 수 있습니다.
도커 네트워크 기능
컨테이너를 생성하면 기본적으로 docker0 브리지를 통해 외부와 통신할 수 있는 환경 사용 가능
but 여러 네트워크 드라이버 사용 가능
ex) 브리지, 호스트, 논, 컨테이너, 오버레이
서드파티 플러그인 솔루션
ex)weave, flannel, openswith
docker network ls
network: 도커의 네트워크를 다루는 명령어
이미 브리지, 호스트, 논 네트워크가 있음을 알 수 있습니다.
- 브리지
컨테이너를 생성할 때 자동으로 연결되는 docker0 브리지를 활용하도록 설정
172.17.0.x IP 대역을 컨테이너에 순차적으로 할당
docker network inspect
docker inspect --type network
Config 항목의 서브넷과 게이트웨이 172.17.0.0/16과 172.17.0.1로 설정
브리지 네트워크를 사용 중인 컨테이너 목록 Containers에서 확인 가능
아무 설정 없이 컨테이너 생성하면 컨테이너는 자동으로 docker0 브리지를 사용
브리지 네트워크
사용자 정의 브리지를 생성해 외부와 통신해 보겠습니다.
docker network create --driver bridge mybridge
브리지 타입의 mybridge라는 네트워크가 생성됐습니다.
그럼 mybridge 네트워크를 사용하는 컨테이너를 생성해 보겠습니다.
docker run -i -t --name mynetwork_container --net mybridge ubuntu:14.04
브리지 타입의 네트워크를 생성하면 도커는 IP대역을 차례로 할당합니다.
예시에서는 172.18.0.2 대역의 내부 IP가 할당됐습니다.
이렇게 생성된 사용자 정의 네트워크는 명령어를 통해 컨테이너에 유동적으로 붙이고 뗄 수 있습니다.
docker network connect mybrigde mynetwork_container
docker network disconnect mybrigde mynetwork_container
이는 브리지 네트워크 또는 오버레이 네트워크와 같이 특정 IP 대역을 갖는 네트워크 모드에만 이 명령어를 사용할 수 있습니다.
네트워크의 서브넷, 게이트웨이, IP 할당 범위 등을 임의로 설정해 보겠습니다.
docker network create --driver=bridge \
--subnet=172.72.0.0/16 \
--ip-range=172.72.0.0/24 \
--gateway=172.72.0.1 \
my_custom_network
설정한 대로 네트워크가 생성되었습니다.
호스트 네트워크
네트워크를 호스트로 설정하면 호스트의 네트워크 환경을 그대로 쓸 수 있습니다.
브리지 드라이버 네트워크와 달리 호스트 드라이버의 네트워크는 별도로 생성할 필요 없이 기존의 host라는 이름의 네트워크를 사용합니다.
run -i -t --name network_host --net host ubuntu:14.04
--net 옵션을 입력해 호스트를 설정한 컨테이너 내부에서 네트워크 환경을 확인하면 호스트와 같은 것을 알 수 있습니다.
호스트 머신에서 설정한 호스트 이름도 컨테이너가 물려받기 때문에 컨테이너의 호스트 이름도 무작위 16진수가 아니라
'root@docker-desktop'임을 확인할 수 있습니다.
컨테이너의 네트워크를 호스트 모드로 설정하면 컨테이너 내부의 애플리케이션을 별도의 포트 포워딩 없이 바로 서비스할 수 있습니다. 실제 호스트에서 애플리케이션을 외부에 노출하는 것과 같습니다.
ex) 호스트 컨테이너 -> 아파치 웹 서버 구동: 호스트의 IP와 컨테이너의 아파치 웹 서버 포트 80으로 바로 접근
논 네트워크
none은 말 그대로 아무런 네트워크를 쓰지 않는 것을 뜻합니다.
즉, 이 네트워크를 사용해 컨테이너를 생성하면 외부와 연결이 단절됩니다.
docker run -i -t --name network_none \
--net none \
ubuntu:14.04
로컬 호스트를 나타내는 lo dhldpsms 네트워크 인터페이스가 존재하지 않는 것을 알 수 있습니다.
컨테이너 네트워크
--net: 다른 컨테이너의 네트워크 네임스페이스 환경을 공유 가능. (내부 IP, 네트워크 인터페이스의 MAC주소 등)
docker run -i -t -d --name network_container_1 ubuntu:14.04
docker run -i -t -d --name network_container_2 \
--net container:network_container_1 \
ubuntu:14.04
-itd 옵션을 함께 사용하면 컨테이너 내부에서 셸을 실행하지만 내부로 들어가지 않으며 컨테이너도 종료되지 않습니다.
이처럼 다른 컨테이너의 네트워크 환경을 공유하면 내부 IP를 새로 할당받지 않으며,
호스트에 veth로 시작하는 가상 네트워크 인터페이스도 생성되지 않습니다.
network_container_2 컨테이너의 네트워크와 관련된 사항은 전부 network_container_1과 같게 설정됩니다.
두 컨테이너의 eth0에 대한 정보가 완전히 같은 것을 알 수 있습니다.
브리지 네트워크와 --net-alias
브리지 타입의 네트워크와 run 명령어의 --net-alias 옵션을 함께 쓰면 특정 호스트 이름으로 컨테이너 여러 개에 접근할 수 있습니다.
docker run -i -t -d --name network_alias_container1 \
--net mybridge \
--net-alias alice ubuntu:14.04
docker run -i -t -d --name network_alias_container2 \
--net mybridge \
--net-alias alice ubuntu:14.04
docker run -i -t -d --name network_alias_container3 \
--net mybridge \
--net-alias alice ubuntu:14.04
첫 번째 컨테이너가 172.18.0.2이므로, 두, 세 번째 컨테이너는 각각 172.18.0.3,4 일 것입니다.
세 개의 컨테이너에 접근할 컨테이너를 생성한 뒤 alice라는 호스트 이름으로 ping요청을 전송해 보겠습니다.
컨테이너의 IP로 각각 ping이 전송된 것을 알 수 있습니다.
매번 달라지는 IP를 결정하는 것은 별도의 알고리즘이 아닌 라운드 로빈 방식입니다.
하나의 중앙처리장치를 여러 프로세스들이 우선순위 없이 돌아가며 할당받아 실행되는 방식을 뜻합니다.
이것이 가능한 이유는 도커 엔진에 내장된 DNS가 alice라는 호스트 이름을 --net-alias 옵션으로 alice를 설정한 컨테이너로 변환하기 때문입니다.
도커의 DNS는 호스트 이름으로 유동적인 컨테이너를 찾을 때 주로 사용됩니다.
--link: 컨테이너의 IP가 변경돼도 별명으로 컨테이너를 찾을 수 있게 DNS에 의해 자동 관리
--net-alias 역시 비슷하게 사용
도커는 기본 브리지 네트워크가 아닌 사용자가 정의한 네트워크에 사용되는 내장 DNS 서버를 가지며,
DNS의 IP는 127.0.0.11 입니다. mybridge라는 이름의 네트워크에 속한 3개의 컨테이너는 run으로 생성할 때 --net-alias 옵션에 alice라는 값을 입력했고, 이 컨테이너의 IP는 DNS 서버에 alice라는 호스트 이름으로 등록됩니다.
mybridge 네트워크에 속한 컨테이너에서 alice라는 호스트 이름으로 접근하면 DNS 서버는 라운드 로빈 방식을 이용해 컨테이너의 IP 리스트를 반환합니다. ping 명령어는 IP 리스트에서 첫 번째 IP를 사용하므로, 매번 다른 IP로 ping을 전송합니다. 이를 확인하기 위해 dig라는 도구를 사용해 봅시다.
apt-get update
apt-get install dnsutils
dig: DNS 도메인 이름에 대응하는 IP를 조회할 때 쓰는 도구
이를 통해 alice 호스트 이름이 변환되는 IP를 확인할 수 있습니다. (1,2,3번 컨테이너)
MacVLAN 네트워크
MacVLAN은 호스트의 네트워크 인터페이스 카드를 가상화해 물리 네트워크 환경을 컨테이너에게 동일하게 제공
즉, 컨테이너는 물리 네트워크상에서 가상의 맥(MAC) 주소를 가지며, 해당 네트워크에 연결된 다른 장치와의 통신이 가능.
172.17.X.X 대신 네트워크 장비의 IP를 할당받기 때문.
ex)
네트워크 장비에 두 대의 서버가 연결되어 있고, 각 서버는 192.168.0.0/24 대역에서 IP를 동적으로 할당받는다고 가정.
MacVLAN을 사용하면 각 컨테이너 192.168.0.0/24 대역의 IP 할당.
즉, MacVLAN을 사용하는 컨테이너들과 동일한 IP 대역을 사용하는 서버 및 컨테이너들은 서로 통신이 가능.
(호스트와는 통신 불가)
MacVLAN을 사용하려면 적어도 1개의 네트워크 장비와 서버가 필요
docker network create -d macvlan --subnet=192.168.0.0/24 \
--ip-range=192.168.0.64/28 --gateway=192.168.0.1 \
-o macvlan_mode=bridge -o parent=eth0 my_macvlan
docker network create 명령어를 통해 MacVLAN 네트워크를 생성했습니다.
-d: 네트워크 드라이버로 macvlan을 사용한다는 것을 명시 (--driver와 같음)
--subnet: 컨테이너가 사용할 네트워크 정보를 입력, 네트워크 장비의 IP 대역 기본 설정을 그대로 따름
--ip-range: MacVLAN에 생성하는 호스트에서 사용할 컨테이너 IP의 범위를 입력합니다.
--gateway: 네트워크에 설정된 게이트웨이를 입력합니다.
-o: 네트워크의 추가적인 옵션을 설정합니다.
이제 MacVLAN 네트워크를 사용하는 컨테이너를 생성해 보겠습니다.
docker run -it --name c1 --hostname c1 --network my_macvlan ubuntu:14.04
출처: 시작하세요! 도커 / 쿠버네티스
'Docker&Kubernetes' 카테고리의 다른 글
[Docker] 도커 엔진 (컨테이너 CPU 제한, 도커 이미지 만들기, 커밋) (0) | 2024.03.11 |
---|---|
[Docker] 도커 엔진 (컨테이너 로깅, logs, syslog, memory) (0) | 2024.03.01 |
[Docker] 도커 엔진 (feat. docker 명령어) (1) | 2024.02.04 |
[Docker] 도커란? (1) | 2024.01.19 |