본문 바로가기
Docker

[Docker] 복습

by Haengsin 2023. 4. 20.

출처

- https://futurecreator.github.io/2018/11/16/docker-container-basics/

namespace

먼저 컨테이너라는 가상의 독립된 환경을 만들기 위해 리눅스 커널의 namespace 라는 기능을 사용합니다. 쉽게 얘기하면 리눅스 오브젝트에 이름표를 붙여 같은 이름표가 붙여진 것들만 묶어 관리합니다. 아래 내용에서 격리(isolated)라는 의미는 다른 네임스페이스에서는 접근이 불가능하다는 걸 의미합니다.

네임스페이스설명

항목 설명
PID namespace 각 프로세스에 할당된 고유한 ID 인 PID 를 기준으로 다른 프로세스를 격리.
네임스페이스가 다르면 액세스 불가.
Network namespace 네트워크 리소스(IP 주소, 포트 번호, 라우팅 테이블 등)를 네임스페이스마다 독립적으로 가져감.
예를 들어 같은 포트라도 네임스페이스가 다르면 사용 가능.
UID namespace 사용자 ID(UID)와 그룹 ID(GID)를 네임스페이스 별로 구분.
따라서 컨테이너에서는 루트 권한을 가지고 있더라도 호스트의 관리 권한을 가질 수 없도록 격리 가능.
MOUNT namespace 리눅스에서 디바이스를 인식하기 위해 마운트가 필요.
파일 시스템 등 마운트된 디바이스를 네임스페이스별로 격리.
UTS namespace 호스트명이나 도메인명을 네임스페이스별로 독자적으로 설정 가능.
IPC namespace 프로세스 간 통신(inter process communication)에 필요한
공유 메모리(Shared Memory), 세마포어(Semaphore), 메시지 큐(Message Queue) 등을 독자적으로 사용.

cgroups

리눅스에서 프로그램은 프로세스로 실행되고, 프로세스는 하나 이상의 쓰레드로 이루어져 있습니다. cgroups(Control Groups) 는 프로세스와 쓰레드를 그룹화해서 관리하는 기술입니다. 호스트 OS의 자원을 그룹별로 할당하거나 제한을 둘 수 있습니다. 즉 컨테이너에서 사용하는 리소스를 제한함으로써 하나의 컨테이너가 자원을 모두 사용해 다른 컨테이너가 영향을 받지 않도록 할 수 있습니다. 또한 그룹에 계층 구조를 적용할 수 있어 체계적으로 리소스를 관리할 수 있습니다.

항목설명

항목 설명
cpu CPU 사용량 제한.
cpuacct CPU 사용량 통계 정보 제공
cpuset CPU 나 메모리 배치 제어.
memory 메모리나 스왑(Swap) 사용량 제한.
devices 디바이스에 대한 액세스 제어.
freezer 그룹 내 프로세스 정지 및 재개.
net_cls 네트워크 제어.
blkio 블록 디바이스 입출력량 제어.

네트워크 구성

컨테이너의 네트워크 구성을 살펴보겠습니다. 먼저 NIC(Network Interface Controller)는 네트워크 신호를 주고받을 때 쓰는 하드웨어로 랜 카드를 생각하시면 됩니다. 리눅스는 이 네트워크 장치를 /dev/eth0, /dev/eth1 이런 식으로 인식합니다. eth0 은 기본 네트워크 장치라고 볼 수 있습니다.

https://www.kaitoy.xyz/2015/07/25/how-to-capture-packets-on-a-local-network-with-pcap4j-container/

 

도커 컨테이너가 실행되면 컨테이너에 172.17.0.0/16 이란 프라이빗 IP 주소가 eth0 으로 자동 할당됩니다. 이를 docker0 이라고 합니다. 이 docker0 은 각 컨테이너 네트워크를 연결해주는 네트워크 브리지(network bridge) 역할을 하는데요, 각 컨테이너의 eth0 에 docker0 이 만든 가상 NIC 인 veth 를 할당합니다. 또한 외부에서 요청을 컨테이너로 라우팅합니다.

http://snowdeer.github.io/common-sense/2018/02/02/understanding-about-nat/

컨테이너가 외부 네트워크와 통신할 때는 NAPT(Network Address Port Translation)라는 기술을 사용합니다. 퍼블릭 IP 주소와 프라이빗 IP 주소를 일대일로 변환하는 NAT(Network Address Translation)와 달리 NAPT 는 포트 정보까지 활용하기 때문에 하나의 퍼블릭 IP 주소로 여러 대의 머신을 동시에 연결할 수 있습니다.

 

컨테이너 데이터 관리

https://docs.docker.com/storage/#choose-the-right-type-of-mount

도커는 컨테이너에서 사용하는 데이터를 호스트 내에 저장하기 위해 세 가지 방법을 제공합니다.

  • Volumes : 호스트의 파일 시스템 내에 특정 영역(리눅스의 경우 /var/lib/docker/volumes/)을 도커가 관리하면서 사용. 도커가 아닌 다른 프로세스에서는 해당 영역 접근이 불가능. 가장 추천하는 방식.
  • Bind mounts : 호스트의 파일시스템 자체를 사용. 중요한 시스템 파일이나 디렉토리도 접근 가능. 호스트와 컨테이너가 설정 파일을 공유하거나 호스트에서 개발하고 컨테이너로 배포하는 방식으로 사용.
  • tmpfs mounts : 호스트의 파일시스템 대신 메모리에 저장하는 방식. 파일 시스템에 저장하고 싶지 않을 경우 사용.

https://docs.docker.com/storage/storagedriver/

도커 이미지는 Dockerfile 로 만들어진 여러 레이어로 이루어져 있고 각 레이어는 읽기만 가능(Read-only)합니다. 이미지를 가지고 새로운 컨테이너를 생성하면 읽고 쓸 수 있는(Readable and Writable) 레이어가 추가되는데 이를 컨테이너 레이어(Container Layer)라고 합니다. 컨테이너를 가지고 작업을 수행할 때 생기는 변경 사항을 모두 컨테이너 레이어에 저장하고 읽을 때는 도커 이미지에 변경된 사항을 조합해서 데이터를 읽습니다. 컨테이너가 삭제되면 컨테이너 레이어도 사라지고 기존 이미지는 변경되지 않고 유지됩니다.

https://docs.docker.com/storage/storagedriver/#container-and-layers

 

따라서 하나의 이미지에서 여러 컨테이너를 만들어서 사용할 수 있습니다. 만약 컨테이너가 서로 데이터를 공유해야 한다면 도커 볼륨에 저장하고 컨테이너에 마운트하면 됩니다.

 

도커는 Copy-on-Write(CoW or COW) 방식으로 파일을 관리합니다. Copy-on-Wirte 는 효율적으로 파일을 공유하고 복사하는 방법입니다. 파일 또는 디렉토리를 읽기만 할 땐 기존 파일을 참조하도록 하고, 수정해야 하는 경우에만 파일을 컨테이너 레이어로 복사해서 수정하는 방법입니다. 따라서 꼭 필요한 경우에만 복사가 되므로 데이터 중복이 없고 효율적으로 사용할 수 있습니다.

 

도커는 이런 방식으로 레이어와 파일을 관리하기 위해 스토리지 드라이버(Storage Driver)를 사용합니다. 다양한 종류의 스토리지 드라이버를 지원하는데 작동하는 방법이 조금씩 다릅니다. 리눅스 배포판 커널에 따라 다른 드라이버를 사용하게 됩니다. 각 스토리지 드라이버에 대한 자세한 설명은 공식 문서를 참고하세요.

 

도커 버전 확인

$ docker version

 

도커 실행 환경 확인

$ docker system info

 

도커 디스크 상태 확인

$ docker system df

 

도커 이미지 확인

$ sudo docker images

 

도커 컨테이너 상태 확인 (리눅스 top 명령어와 유사)

$ docker container stats

 

Dockerfile 로 컨테이너 이미지 만들기

도커이미지는 Dockerfile 이라는 설정 파일을 이용해 자동을 빌드할 수 있습니다..

도커 이미지는 베이스 이미지(Base image)를 기반으로 그 위에 변경 사항을 레이어 형태로 쌓는다. 그래서 Dockerfile은 FROM 명령어를 이용해 어떤 베이스 이미지와 버전을 사용할지 선택합니다.

FROM nginx:latest

초기 화면을 지정할 index.html 파일을 만들어줍니다. 그냥 간단하게 헤더만 넣었습니다.

index.html

index.html 파일을 컨테이너로 복사하기 위해 COPY 명령어를 추가합니다.

COPY index.html /usr/share/nginx/html/index.html

80 포트로 접속할 수 있도록 하기 위해 EXPOSE 명령어를 추가합니다.

EXPOSE 80

EXPOSE 80 443 또는 EXPOSE 3000-4000 처럼 여러 포트를 지정할 수도 있습니다.

 

CMD 명령어로 실제로 실행할 명령어를 지정할 수 있습니다. Nginx 가 데몬화(daemonize)되어 백그라운드(background)에서 동작하면 컨테이너 기동 시 그냥 종료되기 때문에 포그라운드(foreground)에서 동작할 수 있도록 명령어를 줍니다.

CMD ["nginx", "-g", "daemon off;"]

CMD 명령어와 비슷한 기능으로는 RUN 명령어가 있습니다.

  • RUN : 해당 명령어를 이미지가 빌드할 때 실행. e.g. RUN npm install
  • CMD : 해당 명령어를 컨테이너를 기동될 때 실행. e.g. CMD ["nginx", "-g", "daemon off;"]
    주로 도커 이미지로 빌드된 애플리케이션을 실행할 때 사용되거나 RUN 명령어로 오버라이딩(overriding)할 수 있어 디폴트 명령어를 지정할 때 쓰이기도 함.

작성한 Dockerfile 은 다음과 같습니다.

dockerfile

폴더 상황

./
|- Dockerfile
|- index.html

docker build 명령어를 이용해 이미지를 빌드합니다. 태그를 이용해 이미지의 이름과 버전을 줄 수 있습니다.

$ sudo docker build -t nginx-test:latest .

docker images 로 빌드된 이미지를 확인할 수 있습니다.

도커 이미지를 가지고 컨테이너를 실행합니다.

$ sudo docker run -d -p 80:80 nginx-test:latest

docker ps 로 상태도 확인해봅니다.