Information Security Study
240205 도커(attac, exec, diff, docker commit/export, mount), 도커 네트워크(개요, bridge-utils, brctl) 본문
240205 도커(attac, exec, diff, docker commit/export, mount), 도커 네트워크(개요, bridge-utils, brctl)
gayeon_ 2024. 2. 5. 15:56attach와 exec
컨테이너와 지속적인 통신을 하고 싶을 때 주로 attach 모드로 기동하게 된다.
다른 프로세스 위에서 돌리고 싶을때 해당 명령어를 쓴다.
$ docker run -d --name=attach-con ubuntu:22.04 /usr/bin/top -b
: /usr/bin/top -b 명령어를 수행하는 컨테이너가 detach모드로 열리고 top -b를 백그라운드에서 수행한다.
$ docker attach attach-con
: 해당 컨테이너 내부에서 attach 모드로 돌려서 조회하는 명령어
attach모드는 특정 작업중인 컨테이너에 포커싱을 맞추는 모드이다.
exec
: execute 모드로 실행중인 컨테이너에 추가적 명령을 실행하게 한다.
$ docker run -itd --name=exec-con alpine sh
: alpine 리눅스의 셸을 열라고 시키면 detach 상태이기 때문에 상호작용 창은 나오지 않지만 sh는 켜진 상태가 된다.
이 때 명령어를 해당 sh에 전달하는것이 바로 exec이다.
$ docker exec -d exec-con touch /tmp/exec_test
: detach 모드로 전달되기 때문에 상호작용 모드로 전환되지 않지만 touch 명령어는 잘 수행된다.
exec_test가 잘 생성되었는지 확인하기 위해 아래 명령어로 -it를 이용해 sh를 실행시킨다.
$ docker exec -it exec-con sh
그러면 쉘이 열리고
ls /tmp
로 실제 /tmp/exec_test 가 생성된 것을 확인할 수 있다.
# read escape sequence
: 쉘이 열린 상태에서 포커싱 잡힌 창에서 빠져나온다.
$ docker exec -it -w /tmp exec-con pwd
: pwd 명령을 전달하고 아까 셸 작업시 위치했던 경로가 콘솔에 찍힌다.
※ 정리
attach
- 해당 컨테이너의 로그 등을 조회할때 주로 사용한다.
exec
- 실질적 작업명령을 수행할때 사용한다.
docker diff로 변동사항 확인하기
$ docker diff exec-con
: 컨테이너들의 변동사항을 확인하기 위해서는 docker diff를 사용한다.
을 수행하면 이런저런 변동사항들이 보인다.
A, C, D 3가지 변동내역이 있다.
A 는 파일 혹은 디렉터리의 추가
D는 파일 혹은 디렉터리의 삭제
C는 파일 혹은 디렉터리의 변경을 나타낸다.
이를 확인하기 위해
$ docker exec -it nodeapp sh
를 먼저 수행해야 한다.
# adduser newuser
: 아이디를 추가한다.
아이디 추가 후 diff로 조회하면 변경내역들을 볼 수 있다.
docker commit으로 도커파일 없이 변경된 이미지 생성
그동안 이미지를 새로 만들기 위해서는 dockerfile을 이용해서 이미지를 생성해야 했다.
그렇지만 터미널에서 적당히 작업한 컨테이너를 다시 이미지로 돌릴수 있다면
편의성이 증대된다.
이를 위해 docker commit을 활용할 수 있다.
그 전에 먼저 위 경로로 이동한다.
먼저 app.js의 콘솔 출력 내역을 조금 바꿔줬다. (request -> request check, conneted. -> connected!!!!!)
그러면 이미지에는 없는 변경점이 컨테이너 내부에서 생기게 된다.
파일 갱신 내역 반영을 위해 stop, restart를 해준다.
변경된 app.js대로 잘 반영된 것을 확인할 수 있다.
이를 이미지로 환원시키기 위해서
$ docker commit nodeapp nodeapp:1.1
과 같이 기존 컨테이너명 → 갱신할 이미지이름:태그
형식을 활용하면 된다.
-P 옵션을 포트를 자동지정했더니 newnode는 32768 포트번호를 배정받았다.
접속 경로에 포트번호를 입력해서 이미지가 잘 갱신되었는지 확인했다.
curl로도 변경내역을 확인했다.
docker export를 활용한 이미지 파일화, import를 활용한 마운트
export는 save와 비슷하지만
layer에 대한 내용을 나눠서 저장하지 않고 하나의 레이어로 통합한다.
$ docker export nodeapp > nodeapp.tar
ls로 확인하니 nodeapp.tar가 잘 생성되었다.
$ tar tvf nodeapp.tar
tar파일의 정보는 위와 같이 확인 가능하다.
이미지형식으로 가져온 것이 아닌 파일시스템 통째로 가져온 것이다.
$ sudo scp nodepapp.tar host2번:/home/유저명
다음으로 해당 파일을 다른 가상머신으로 옮겨주면
host2에 nodeapp.tar가 추가된 것을 볼 수 있다.
다른os$ cat nodeapp.tar | docker import - nodeapp:태그명
그 후 host2에서 import로 마운트를 시킨다.
docker images로 태그 1.1의 nodeapp이 존재하는 걸 확인했다.
$ docker run -itd --name=newnodeapp -p 9999:5678 nodeapp:1.1
이후 이미지 확인을 하면 된다.
하지만 위 명령어로 컨테이너를 실행하려 하면 에러가 발생한다.
이유는 Dockerfile로 만든 이미지에는 우리가 CMD 로 추가한 실행명령어가 있지만
export는 단순 파일시스템만 모사한 것이라 실행명령에 대한 내용이 없기 때문이다.
따라서 CMD를 추가해야 한다.
$ docker import --change ‘CMD [”node”, “/app/app.js”]’ nodeapp.tar nodeapp:1.1
위 명령어로 CMD를 추가했다면(커맨드 명령어를 이렇게 추가했다면) 실행이 잘 될 것이다.
CMD 추가 후 run 명령어를 실행하면 컨테이너가 띄워진다.
curl로 9999포트에 접속해서 정상적으로 import 되었다는 것을 확인했다.
다만 이 방식은 매번 실행할 때마다 cmd명령을 추가해야 하기 때문에 불편하다.
도커 네트워크 기초
기반기술인 리눅스 네트워크의 적용
도커네트워크는 리눅스 커널의 네트워크 스택의 하위 스택으로, 상위에는 네트워크 드라이버를 생성한다.
그래서 도커네트워크는 곧 리눅스 네트워크라고 생각해도 된다.
도커 네트워크는 CNM이라고하는 인터페이스 위에 구축된다.
인터페이스 특성상 해당 기능이 구현되기만 하면 실행에 문제가 없으므로
결국 OS나 인프라에 구애받지 않고 언제나 동일하게 쓸 수 있다.
리눅스 네트워크 빌딩 블록은 리눅스 브릿지, 네트워크 네임스페이스, veth pair, iptables등이 포함되는데 이 조합은 복잡한 네트워크 정책을 위한 규칙 및 네트워크 분할 및 관리 도구를 제공한다.
2계층에 해당하는 도구로 MAC 주소를 활용해서 식별하고 통신한다.
리눅스 브릿지에 대해서
- 커널 내부의 물리적인 스위치를 프로그래밍적으로 가상 구현한 한 것이 OSI Layer 2인 디바이스다.
- 트래픽을 검사하여 동적으로 학습되는 MAC Address를 기반으로 트래픽을 전달한다.
브릿지 네트워크의 기본 대역
- 172.<17~31>.0.0/16 (65536개)
- 192.168.<0~240>.0/20(4096개)
아래의 그림에서 Dokcer0를 두 영역으로 나눠야 하는데
docker0만 놓고 보면 linux커널과 os도구를 통해 관리하고
해당 docker0위에 bridge들이 위치해서 도커엔진하에 관리되며
브릿지와 컨테이너 사이에 vethernet이 위치해 연결해준다.
ifconfig docker0를 입력하면 기본적으로 할당된 이더넷 브릿지 대역을 볼 수 있다.
도커 네트워크
네트워크 네임스페이스
- 커널에 격리된 네트워크 스택으로 자체 인터페이스, 라우트 및 방화벽 규칙 보유
- 컨테이너와 리눅스의 보안적인 측면으로 컨테이너를 격리하는데 사용
- 네트워크 네임스페이스는 도커 네트워크를 통해 구성된 경우가 아니면 동일한 호스트의 두 컨테이너가 서로 통신할수 있음이나 혹은 호스트 자체와 통신할 수 없음을 보장(방화벽 설정)
- 일반적으로 CNM(Container Network Model)네트워크 드라이버는 각 컨테이너별로 별도 네임스페이스가 구현됨
CNM(Container Network Model)
위 그림에서 sandbox는 말 그대로 격리된 네트워크 자체를 의미한다.
ethernet, port, route table, DNS 구성 등을 총체적으로 그룹화한다.
해당 컨테이너가 갖는 네트워크적 옵션이라고 생각해도 된다.
endpoint는 가상 이더넷의 인터페이스이다.
endpoint는 접근 ip, mac주소 등으로 접근할 수 있다.
network는 가상 스위치, 브릿지를 의미한다.
CNM은 도커 네트워크가 이런 요소들을 가져야 한다고 정의해둔 인터페이스이고
CNM을 기반으로 구현한 libnetwork가 바로 docker network이다.
$ docker network ls
: 실제로 사용가능한 모델 조회
bridge와 host 그리고 아무것도 쓰지 않는 none이 있다.
bridge는 기본적으로 싱글 호스트를 지원한다.
내부망으로 연결만 해 줄 뿐 다른 네트워크와 연결되지 않는다.
이를 위해 멀티호스트 지원은 bridge to bridge 수행을 위해 overlay라는 것을 통해 지원한다.
특정한 네트워크 방식은 언제던 교체하거나 활용할 수 있다.
컨테이너는 직접 bridge와 연결될 수 없다.
그래서 vethernet은 컨테이너와 1:1로 매칭되어 브릿지 사이에서 중개해주는 역할을 한다.
먼저 컨테이너 자체의 엔드포인트 조회는 ifconfig, route, ip a, ip addr, ip add 등의 명령어를 통해 조회할 수 있다.
브릿지는 brctl show를 통해 조회한다.
$ sudo apt install bridge-utils
$ brctl show
브릿지 유틸을 설치한 후 브릿지를 조회했다.
docker0 네트워크를 활용하는 컨테이너들이 interfaces에 나열된다.
더 자세히 확인하기 위해서 먼저 기본적인 도구가 많이 깔린 14버전 우분투를 설치했다.
$ docker run -it -d --name=network-14 ubuntu:14.04
설치 후 brctl show를 한 번 더 수행해 인터페이스 개수를 체크하니 하나가 더 늘었다.
인터페이스는 꼭 생성순서대로 나열되지는 않는다.
이제 우분투 14버전에 ip addr 명령을 수행하게 시켜볼 것이다.
직접 커맨드로 진입하지 않고 exec를 통해 전달했다.
$ docker exec -it network-14 ip addr
위와 같이 loopback과 실제로 vethernet이 가지는 주소가 나온다.
$ docker exec -it network-14 route
: 어느 브릿지에 연결되었는지 확인하는 명령어
위와 같이 연결된 docker0가 나온다.
host에서 직접 route를 치면 좀 더 확실해진다.
inspect 명령어를 활용하면 컨테이너의 네트워크 설정도 역시 함께 조회 가능하다.
$ docker inspect network-14 | grep -i ipa
로 조회할 경우 해당 컨테이너의 아이피 정보가 나온다.
ubuntu 14는 여러 디폴트 설치 툴을 이용해 조회가 가능하지만
그렇지 않은 컨테이너는 위와 같은 방법으로 확인 가능하다.
mac address도 inspect로 확인 가능하다.
$ docker inspect network-14 | grep -i mac
컨테이너가 네트워크 통신을 위해 갖는 모든 것들은 샌드박스에 의해 정의된다.