도커 이미지와 컨테이너

2022. 4. 28. 21:56TIL💡/Docker

컨테이너를 외부에 노출

컨테이너는 가상 머신과 마찬가지로 가상 IP 주소를 할당받는다.

기본적으로 도커는 컨테이너에 172.17.0.X의 IP를 순차적으로 할당한다.

컨테이너를 새롭게 생성한 후 ifconfig 명령어로 컨테이너의 네트워크 인터페이스를 확인한다.

 

NAT IP

✔︎ NAT = Network Address Translation

✔︎ 사설 네트워크에 속한 여러 개의 호스트가 하나의 공인 IP 주소를 사용하여 인터넷에 접속하기 위해 사용

✔︎ 즉, 외부망과 내부망을 나눠주는 기능

 

참고

https://blog.voidmainvoid.net/319

 

NAT IP란?

NAT는 Network Address Translation의 줄임말 입니다. NAT는 사설 네트워크에 속한 여러 개의 호스트가하나의 공인 IP 주소를 사용하여 인터넷에 접속하기 위해 사용합니다. 쉽게말해서 외부망과 내부망을

blog.voidmainvoid.net

외부에 컨테이너의 애플리케이션을 노출하기 위해서는 컨테이너의 IP와 포트를 호스트의 IP와 포트에 바인딩해야 한다.

-p 옵션은 컨테이너의 포트를 호스트의 포트와 바인딩해 연결할 수 있게 한다.

 

[호스트의 포트]:[컨테이너의 포트]

ex) 컨테이너에 아파치 웹서버

호스트 IP의 80번 포트로 접근 ➡️ 80번 포트는 컨테이너의 80번 포트로 포워딩 ➡️ 웹서버 접근

 

컨테이너 애플리케이션 구축

대부분의 서비스는 단일 프로그램으로 동작하지 않는다. 여러 에이전트나 데이터베이스 등과 연결되어 완전한 서비스로써 동작하는 것이 일반적이다.

컨테이너에 애플리케이션을 하나만 동작시키면 컨테이너 간의 독립성을 보장함과 동시에 애플리케이션의 버전 관리, 소스 코드 모듈화 등이 더욱 쉬워진다.

한 컨테이너에 프로세스 하나만 실행하는 것이 도커의 철학이다.

 

-d 옵션

✔︎ -i -t가 컨테이너 내부로 진입하도록 attach 가능한 상태로 설정한다면, -d는 Detached 모드로 컨테이너를 실행

✔︎ 컨테이너를 백그라운드에서 동작하는 애플리케이션으로써 실행

✔︎ -d 옵션으로 run을 실행하면 입출력이 없는 상태로 컨테이너 실행

✔︎ Detached 모드는 컨테이너는 반드시 컨테이너에서 프로그램이 실행돼야 하며, Foreground 프로그램이 실행되지 않으면 컨테이너는 종료된다.

 

exec 명령어를 이용하면 컨테이너 내부의 셸을 사용할 수 있다.

다음 명령어를 입력하면 mysql 컨테이너 내부에 /bin/bash 프로세스를 실행하고 -i -t 옵션을 사용해 배시 셸을 쓸 수 있게 유지한다.

docker exec -i -t wordpressdb /bin/bash

 

책 예시에 나오는 컨테이너는 빠른 이해를 위해 환경 변수에 비밀번호를 입력했지만,

⛔비밀번호처럼 민감한 정보를 컨테이너 내부의 환경 변수로 설정하는 것은 매우 바람직하지 않다.

이런 경우에는 도커 스웜 모드의 secret이나 쿠버네티스의 secret과 같은 기능을 활용해 안전하게 비밀번호를 전달하는 것이 좋다.

 

 

--link 옵션

A 컨테이너에서 B 컨테이너로 접근하는 방법 중 가장 간단한 방법은 NAT로 할당받은 내부 IP를 사용하는 것

B 컨테이너의 IP가 172.17.0.3이라면 A 컨테이너는 이 IP를 써서 B 컨테이너에 접근할 수 있다.

그러나 도커 엔진은 컨테이너에게 내부 IP를 172.17.0.3, 4 ...와 같이 순차적으로 할당한다.

이는 컨테이너를 시작할 때마다 재할당하는 것이므로 매번 변경되는 컨테이너의 IP로 접근하기는 어렵다.

 

--link 옵션은 내부 IP를 알 필요 없이 항상 컨테이너에 별명(alias)으로 접근하도록 설정한다.

--link wordpressdb:mysql ...

즉 워드프레스 웹 서버 컨테이너는 wordpredssdb의 IPfmf ahffkeh mysql이라는 호스트명으로 접근할 수 있게 된다.

워드프레스 웹 서버 컨테이너에서 mysql이라는 호스트 이름으로 요청을 전송하면 wordpressdb 컨테이너의 내부 IP로 접근하는 것을 확인할 수 있다.

 

유의점

✔︎ --link에 입력된 컨테이너가 실행중이지 않거나 존재하지 않는다면 --link를 적용한 컨테이너 또한 실행할 수 없다는 것이다.

✔︎ Deprecated되었고 브리지 네트워크 사용이 권장된다.

 

도커 볼륨

도커의 컨테이너는 생성과 삭제가 매우 쉬우므로 실수로 컨테이너를 삭제하면 데이터를 복구할 수 없게 된다.

이를 방지하기 위해 컨테이너의 데이터를 영속적(Persistent) 데이터로 활용할 수 있는 방법이 몇 가지 있다.

그 중 가장 활용하기 쉬운 방법이 바로 볼륨을 활용하는 것이다.

 

1) 호스트와 볼륨 공유

-v 옵션을 통한 호스트 볼륨 공유는 호스트의 디렉터리를 컨테이너의 디렉터리에 마운트한다.

 

2) 볼륨 컨테이너

--volumes-from 옵션을 설정하면 -v 또는 --volume 옵션을 적용한 컨테이너의 볼륨 디렉터리를 공유할 수 있다.

 

3) 도커 볼륨

docker volume create - 볼륨 생성

# docker volume create ---name myvolume
# docker volume ls

호스트와 볼륨을 공유할 때 사용한 -v옵션의 입력과는 다르게 다음과 같은 형식으로 입력해야 한다.

 

[볼륨의 이름]:[컨테이너의 공유 디렉터리]

볼륨은 디렉터리 하나에 상응하는 단위로서 도커 엔진에서 관리한다.

도커 볼륨도 호스트 볼륨 공유와 마찬가지로 호스트에 저장함으로써 데이터를 보존하지만 파일이 실제로 어디에 저장되는지 사용자는 알 필요가 없다.

 

docker inspect 명령어를 사용하면 해당 볼륨이 어디에 저장되는지 알 수 있다.

docker inspect 명령어는 컨테이너, 이미지, 볼륨 등 도커의 모든 구성 단위의 정보를 확인할 때 사용되며, 정보를 확인할 종류를 명시하기 위해 --type 옵션에 image, volume 등을 입력하는 것이 좋다.

# docker inspect --type volume myvolume

컨테이너가 아닌 외부에 데이터를 저장하고 컨테이너는 그 데이터로 동작하도록 설계하는 것을 Stateless

컨테이너 자체는 상태가 없고 상태를 결정하는 데이터는 외부로부터 제공받는다.

 

도커 네트워크

도커는 컨테이너에 내부 IP를 순차적으로 할당하며, 이 IP는 컨테이너를 재시작할 때마다 변경될 수 있다.

이 내부 IP는 도커가 설치된 호스트, 즉 내부 망에서만 쓸 수 있는 IP이므로 외부와 연결될 필요가 있다.

이 과정은 컨테이너를 시작할 때마다 호스트에 veth..라는 네트워크 인터페이스를 생성함으로써 이뤄진다.

도커는 각 컨테이너에 외부와의 네트워크를 제공하기 위해 컨테이너마다 가상 네트워크 인터페이스를 호스트에 생성하며 이 인터페이스의 이름은 veth로 시작한다.

veth 인터페이스는 사용자가 직접 생성할 필요는 없으며 컨테이너가 생성될 때 도커 엔진이 자동으로 생성한다.

eth0은 공인 IP 또는 내부 IP가 할당되어 실제로 외부와 통신할 수 있는 호스트의 네트워크 인터페이스이다.

veth로 시작하는 인터페이스는 컨테이너를 시작할 때 생성됐으며 각 컨테이너의 eth0과 연결됐습니다.

veth 인터페이스뿐 아니라 docker0이라는 브리지도 존재하는데 docker0 브리지는 각 veth 인터페이스와 바인딩돼 호스트의 eth0 인터페이스와 이어주는 역할을 한다.

 

veth = virtual eth

도커에서 기본적으로 쓸 수 있는 네트워크

  • 브리지(bridge): 컨테이너를 생설할 때 자동으로 연결되는 docker0 브리지를 활용하도록 설정한다. 이 네트워크는 172.17.0.X IP 대역을 컨테이너에 순차적으로 할당한다. 기본적으로 존재하는 docker0를 사용하는 브리지 네트워크가 아닌 새로운 브리지 타입의 네트워크를 생성할 수 있다. 이 경우 컨테이너 내부에서 ifconfig를 입력하면 새로운 IP 대역이 할당된 것을 알 수 있다.
  • 호스트(host): 네트워크를 호스트로 설정하면 호스트의 네트워크 환경을 그대로 쓸 수 있다. 컨테이너의 네트워크를 호스트 모드로 설정하면 컨테이너 내부의 애플리케이션을 별도의 포트 포워딩 없이 바로 서비스할 수 있다. 이는 마치 실제 애플리케이션을 외부에 노출하는 것과 같다. 예를 들어 호스트 모드를 쓰는 컨테이너에서 아파치 웹 서버를 구동한다면 호스트 IP와 컨테이너의 아파치 웹 서버 포트인 80으로 바로 접근할 수 있다.(--net host)
  • 논(none): 말 그대로 아무런 네트워크를 쓰지 않는 것을 뜻한다.(--net none)
  • 컨테이너(container): 다른 컨테이너의 네트워크 네임스페이스 환경을 공유할 수 있다. 공유되는 속성은 내부 IP, 네트워크 인터페이스의 MAC 주소 등이다.(--net container:[다른 컨테이너의 ID])

브리지 네트워크와 --net-alias

브리지 타입의 네트워크와 run 명령어의 --net-alias 옵션을 함께 쓰면 특정 호스트 이름으로 컨테이너 여러 개에 접근할 수 있다.

별도로 생성한 mybridge 네트워크를 이용해 컨테이너를 3개 생성해 본다. --net-alias 옵션의 값은 alicek106으로 설정했으며, 다른 컨테이너에서 alicek106이라는 호스트 이름으로 아래 3개의 컨테이너에 접근할 수 있다.

# docker run -i -t -d --name network_alias_container1 \
--net mybridge
--net-alias alicek106 ubuntu:14.04

# docker run -i -t -d --name network_alias_container2 \
--net mybridge
--net-alias alicek106 ubuntu:14.04

# docker run -i -t -d --name network_alias_container3 \
--net mybridge
--net-alias alicek106 ubuntu:14.04

세 개의 컨테이너에 접근할 컨테이너를 생성한 뒤 alicek106이라는 호스트 이름으로 ping 요청을 전송한다.

# docker run -i -t --name network_alias_ping \
--net mybridge
ubuntu:14.04

root@47cbc6e2a35b:/# ping -c 1 alicek106

이러면 컨테이너 3개의 IP로 각각 ping이 전송된 것을 확인할 수 있다. 매번 달라지는 IP를 결정하는 것은 별도의 알고리즘이 아닌 라운드 로빈(Round-robin) 방식이다. 이것이 가능한 이유는 도커 엔진에 내장된 DNS가 alicek106이라는 호스트 이름을 --net-alias 옵션으로 alice106을 설정한 컨테이너로 변환하기 때문이다.

mybridge 네트워크에 속한 컨테이너에서 alicek106이라는 호스트 이름으로 접근하면 DNS 서버는 라운드로빈 방식을 이용해 컨테이너의 IP 리스트를 반환한다. ping 명령어는 이 IP에서 첫 번째 IP를 사용하므로 매번 다른 IP로 ping을 전송한다.

 

'TIL💡 > Docker' 카테고리의 다른 글

docker-compose up 시 TIME OUT 오류  (0) 2021.10.06