티스토리 뷰

반응형

Containers vs. VMs

containers vs. VMs

 

컨테이너란?

A container is an isolated environment for your code.

Docker Image를 통해 application을 실행하기 위해 모든것(코드,런타임,시스템 도구,시스템 라이브러리 설정등)을 포함하고 있는 기술인데, 이 기술을 통해 어느 환경에서나 일관된 방식으로 실행할수 있다.

컨테이너가 사용자가 가지고 있는 OS나 파일에 대한 정보 없이 Docker Desktop에 제공하는 환경에서 실행한다.

 

 

어떻게 컨테이너가 독립적으로 가능할까?

Docker Desktop을 사용하여 컨테이너를 관리, 탐색하고 컨테이너가 기본 OS를 포함한 실행하려는 코드를 위한 모든것을 가지고 있기때문에 가능하다.

질문1. 컨테이너가 코드를 위한 모든, 어떤것을 가지고 있을까?

- 런타임 환경, 실제 실행하려는 애플리케이션의 코드, 애플리케이션의 실행에 필요한 외부 라이브러리와 의존성,시스템 도구 및 유틸리티, 설정파일 및 환경변수 ,기본 운영체제(단 전체이미지가 아님) 등과 같은 구성요소들이 함께 작동해서 컨테이너가 어떤환경에서도 일관된 방식으로 애플리케이션을 실행할 수 있게 한다.

 

질문 1-1. 컨테이너가 기본 운영체제를 가지고 있는데 왜 전체 이미지가 아닐까?

containers do not contain operating system images. This makes them more lightweight and portable, with significantly less overhead

 

컨테이너와 가상머신의 주된 차이점 중 하나. 전체이미지를 가지고 있지않아 컨테이너는 가볍고,이식성이 높다.

 

질문 1-1-1. 그러면 어떻게 운영체제를 가져올까?

docker hub에서 필요한 base operating system를 image로 전달해서 전체 OS 이미지가 필요없다.

 

질문 1-1-1-1. docker hub가 뭐지?

Docker 이미지를 저장하고 공유하는 서비스, 기본 OS 레이어와 필요한 소프트웨어,라이브러리를 포함하고 있어 컨테이너가 독립적으로 실행 가능.

ex. ubuntu image : Ubuntu OS 기반의 컨테이너를 만들고 싶다면, Docker Hub에서 Ubuntu 이미지를 가져와 사용

 

어떻게 컨테이너를 격리시킬까?

 

Linux Containers rely on control groupsopen_in_new which not only track groups of processes, but also expose metrics about CPU, memory, and block I/O usage. You can access those metrics and obtain network usage metrics as well. This is relevant for "pure" LXC containers, as well as for Docker containers.

 

리눅스에서 쓰이는 Cgroup(control groups)와 namespace로 격리시킨다.

namespace를 통해 프로세스들을 구별, Cgroup을 통해 이 프로세스들의 사용가능한 자원을 제한한다.

Cgroup

프로세스와 스레드를 그룹화하여, 그 그룹안에 존재하는 프로세스와 스레드에 대한 관리를 수행-> 리소스 사용량을 제한,격리가능

Cgroup는 계층 구조를 사용하여 프로세스를 그룹화 하여 관리할수있다.

 
 

Cgroup의 주요 서브 시스템

cpu
cpu 사용량을 제한
cpuact
cpu 사용량 통계 정보를 제공
cpuset
cpu나 메모리 배치를 제어
memory
메모리나 스왑 사용량을 제한
devices
디바이스에 대한 엑세스 허가/거부
freezer
그룹에 속한 프로세스 정지/재개
net_cls
네트워크 제어 태그를 부가
blkio
블록 디바이스 입출력량을 제어
  • 블록 디바이스란? 데이터를 고정된 크기로 관리하는(각 블록은 고유한 주소가 있음) 저장장치(HDD,SSD,USB)
  • 블록 디바이스 I/O란? 데이터를 저장/검색하는 방법으로 블록 디바이스를 통한 입력/출력 작업

Cgroup 특징

  • 자원제한 : 중요한 시스템 프로세스의 안정성 보장
    • cgroup은 부모 자식 관계에서는 자식이 부모의 제한을 물려받기 때문에 자식은 부모 그룹의 제한을 초과하여 할당할 수 없어 중요한 프로세스라도 영향을 받지 않는다. → 시스템의 중요 부분에 대한 과도한 자원 사용 방지
  • namespace 격리로 보안과 안정성,자원관리 가능
    • 프로세스 그룹이 다른 그룹의 리소스를 볼 수 없도록 분리함
      • 질문1. 프로세스 그룹이 실제 시스템에서 어떻게 활용되는건가?
        •  
        • 위에 그림과 같이 웹 서버 그룹(Nginx와 같은 웹서버 실행시), DB서버 그룹(MySQL등 사용시), 백그라운드 작업 그룹(시스템에서 정기적 실행되는 백업 스크립트,로그작업시),사용자별,application 별 그룹 등
        • 실행되는 application의 요구사항,시스템의 전체 리소스,성능,보안 요구사항등의 여러요소들로 Cgroup의 프로세스 그룹화가 결정됨. 따라서 각 시스템과 application마다 Cgroup의 설정이 다르다.
        • ex. Java 계산기 프로그램 : 독립적으로 실행되는 간단한 애플리케이션은 Docker의 기본 설정만으로 가능
        • 스프링 프레임워크를 사용하여 구현된 웹 서버를 실행할 경우, 웹 서버 그룹이나 데이터베이스 서버 그룹 같은 cgroup 설정이 필요할 수 있음
  • 우선 순위 설정 : 다른 Cgroup의 프로세스들이 사용하는 자원들에 비해 얼마나 자원을 사용할 수 있는지 설정
    • 자원제한 변경 : 시스템에서 특정 Cgroup에 할당된 CPU나 메모리와 같은 자원제한을 동적으로 조정가능(시스템 부하가 높을떄, 특정 서비스의 리소스 사용량을 제한)
    • Cgroup의 cpu 서브시스템을 사용: 특정 Cgroup에 대한 CPU시간의 비율 조절 가능(두개의 Cgroup이 있을때 한 그룹에 CPU 시간을 더 많이 할당시 CPU 가중치를 더 높게 할당-> CPU를 더 많이 사용할수있게 우선순위 간접 설정)
    • Cgroup의 memory 서브시스템을 사용: 특정 Cgroup의 메모리 사용량을 제한 → 메모리를 과도하게 사용 방지
    • Cgroup의 blkio 서브시스템을 사용: 특저
  • 감시/관리 : Cgroup 단위에서 자원을 얼마나 사용하고 있는지 모니터링/ 모든 프로세스들의 상태를 한번에 변경 가능
    • 일시 중지/재개 : 시스템의 유지보수,업데이트 중 특정 application을 일시 중지 시키고 작업이 끝난 후 재개시 사용

Cgroup의 자원 제한으로 인한 이점:

  • 다중 사용자 환경 : 시스템에 여러 사용자,서비스 동시 실행시 한 사용자/서비스가 시스템의 모든 자원 독점 방지
  • 서비스 품질 보장 : 중요 application에 충분한 자원이 할당되도록 하여 성능 보장
  • 시스템 안정성 : 시스템의 전체 자원을 적절히 제어해, 과부하 상태로 인한 시스템 불안정성 방지

 

Namespace

  • 프로세스 격리(= 프로세스 구별)로 이로인해 충돌가능성을 줄이고, 쉽게 참조할수 있게한다.

(PID를 통해 각 컨테이너에 대해 독립적인 프로세스 번호 공간을 제공)

  • Docker는 컨테이너라는 독립된 환경을 만들고, 그 컨테이너를 구획화하여 application의 실행환경을 만든다.
  • 여기서 이 컨테이너를 구획화하는 기술을 linux의 namespace라는 기능을 사용한다.
  • 각 namespace는 특정한 시스템 리소스나 속성을 격리하기때문에 프로세스는 여러 namespace에 걸쳐 다양하게 격리되고 환경을 가질 수 있다.(namespace는 프로세스가 필요에 따라 다른 격리된 환경에서 작동할 수 있게 함)

ex. IPC Namespace에서 pid:1pid:2가 서로 통신

UTS Namespace에서 pid:1pid:3가 함께 격리

 

Namespace의 주요 서브 시스템

mnt
파일 시스템 마운트 : Host 파일 시스템에 독립적인 파일 시스템 마운트
  • 마운트란? 컴퓨터에 연결된 기기,기억장치를 OS에 인식시켜 이용가능한 상태로 만드는것
  1. monut namespace는 마운트 조작을 하면 namespace안에 격리된 파일 시스템 트리를 만든다.
  2. namespace 안에서 수행된 마운트는 호스트 OS,다른 namespace에서는 엑세스 할 수 없게 되어있다. (각 application이 자신만의 파일시스템 구조를 유지 및 은닉,변경 못하도록 하기 위함)
pid
각 프로세스에 할당된 고유한 ID : 독립적인 프로세스 공간 할당
net
네트워크 디바이스, IP 주소, 포트 번호, 라우팅 테이블, 필터링 테이블 등과 같은 네트워크 리소스를 격리된 namespace마다 독립적으로 가질 수 있다
→ namespace간의 충돌 방지(중복 포트 바인딩 등)

ipc
IPC는 프로세스간의 통신 오브젝트(공유메모리, 세마포어,메세지큐)로 통신(IPC) 오브젝트를 namespace별로 독립적으로 가질 수 있다
uts
자신만의 호스트이름과 네트워크 도메인 이름을 가질 수 있음
user
독립적인 사용자,그룹ID 할당

 

  • mnt(mount)

  • pid

  • net

  • ipc

  • uts

  • user

 

 

Namespace의 독립적으로 작동하여 얻는 이점

  • 컨테이너가 각각 독립적인 파일 시스템을 가져(자신만의 파일 시스템 마운트 관리) 컨테이너가 별도의 서버인것처럼 작동할 수 있게 해줌
  • 특정상황에 일부 namespace가 불필요한 경우(컨테이너가 networking을 전혀 사용하지 않아 NET namespace가 필요없는 경우, 컨테이너가 단일 프로세스로 실행되어 호스트 이름,도메인 이름에 의존하지 않아 UTS namespace가 필요없는 경우 등) Docker 실행 명령어나 Dockerfile, Docker Compose 파일에서 특정 옵션을 통해 namespace사용을 커스텀 할 수 있다. (단 격리 수준이 낮아짐)
    • Docker 명령어 사용(1. NET namespace 사용X시, 2.IPC namespace 사용X시)
    • Docker Compose 사용시(3. 해당 서비스가 호스트의 NET namespace를 사용하도록 지정함, 별도의 NET namespace 생성X)
docker run --net=host myimage
docker run --ipc=host myimage
version: '3'
services:
  app:
    image: myappimage
    network_mode: "host"  # 사용할 네트워크 모드 지정

질문1. 격리 수준이 낮아진다는건 무슨 의미인가?

컨테이너가 실행되는 환경과 다른 환경 사이의 경계가 옅어져 아래와 같은 문제 발생

  1. 보안위험이 증가 : 만약 , 호스트의 네트워크 네임스페이스로 통일될경우 모든 네트워크 인터페이스에 직접 엑세스 할수있음
  2. 리소스 충돌 : 여러 컨테이너나 호스트 시스템이 동일한 리소스를 동시에 사용시 충돌 발생
  3. 관리 복잡성 : 시스템의 어떤부분이 문제를 일으키는지 파악하기 어려움

 

 

컨테이너 작동과정

Step1. 두프로그램이 각각 다른 Python 버전을 원하는 상황

  • Chrome -> python2, NodeJs -> python3
  • 해당 하드디스크는 Python2만 접근가능하고 동시에 두개의 동일한 Python 설치 X

 

 

 

Step2. OS의 Namespace 기능 사용

  • Namespace를 통해 커널은 Chrome과 NodeJS 각각이 필요로 하는 다른 버전의 Python에 접근 가능
    1. 프로그램이 요청을 합니다 (시스템 콜 발행) : Chrome -> python2, NodeJs -> python3 필요 요청
    2. 커널이 요청을 받아 처리 : 어느 프로그램으로 부터 온 요청(시스템콜) 확인 
    3. 커널이 리소스 접근을 적절한 곳으로 연결해줍니다 (리디렉션) : 커널은 Chrome이 요청한 Python 버전 2를 사용할 수 있는 하드 드라이브의 특정 영역(세그먼트)으로 연결

 

 

Step2. Cgroup 기능 사용

  • Cgroup은 특정 프로세스가 사용할 수 있는 리소스 수를 제한하는 데 사용한다.
  • 커널은 들어오는 시스템 호출을 보고 이를 하드 드라이브의 특정 부분(RAM CPU 또는 필요한 다른 부분)으로 보낸다.

 

 

Step3. 해당 Container의 Image스냅샷 사용 및 Container 시작

  1. namespace와 Cgroup을 통해 하드디스크의 일부분을 격리해 하드디스크의 작은 부분을 컨테이너만 사용하도록 한다.
  2. 이미지의 파일 스냅샷을 컨테이너 공간(hard drive segment)에 배치한다. -> 해당 컨테이너 실행시 필요한 파일,설정 포함
  3. 구체적인 리소스 그룹 구성 : 세그멘트에 Chrome과 Python만 설치됨 -> 컨테이너가 사용할 독립된 환경 형성
  4. 컨테이너 시작 명령 실행 -> Chrome 사용

 

 

 

 

 

컨테이너 특징

  • 컨테이너는 이미지 Layer에 읽기/쓰기 Layer를 추가하는 것으로 생성/실행된다. 따라서 여러개의 컨테이너를 생성해도 최소한의 용량만 사용되며 바뀐부분은 읽기/쓰기 Layer에 적는다.
  • 컨테이너는 종료되었다고 메모리에서 삭제되지 않고 남아있다. (삭제하려면 명시적으로 삭제해야 한다.) 즉 종료가 되어도 컨테이너, 읽기/쓰기 Layer 또한 그래도 존재하기 때문에 다시 시작할 수 있다.
  • 컨테이너 삭제란 컨테이너에서 생성한 파일이 사라진다는 것이다. ex. DB 삭제 → DB내의 모든 데이터 삭제
  • 한 서버는 여러개의 컨테이너를 가질 수 있으며 컨테이너는 각각 독립적으로 실행된다.
  • 컨테이너는 kernel공간과 host os자원을 공유한다.
 

 

1. host system 의 host 이름이 host-system

2. 두 개의 컨테이너(Container A와 Container B)를 실행

Container A에서 hostname 명령어를 실행하면 "container-A"라는 결과를 얻는다.

Container B에서 hostname 명령어를 실행하면 "container-B"라는 결과를 얻는다.

→ 각 컨테이너가 UTS 네임스페이스를 통해 자신만의 호스트 이름을 가짐을 의미

(컨테이너가 host-system의 커널을 공유해도 UTS namespace는 컨테이너마다 독립적으로 관리함)

 

docker run -d --name container-A --hostname=container-A ubuntu sleep 3600

-d : 컨테이너를 백그라운드에서 실행

--hostname: 컨테이너의 호스트 이름을 설정

ubuntu : 사용할 베이스 이미지

sleep 3600: 컨에이너가 바로 종료하지 않고 1시간 대기

% docker run -d --name container-A --hostname=container-A ubuntu sleep 3600
5e4b162f3357bfe120ac07a4aff4faacb69de9449b062fcef22051088d90fc34
% docker run -d --name container-B --hostname=container-B ubuntu sleep 3600
21ebf4feb110f799c2457926c6897ad1049cc58b133db86be6d7ef2c8580e871
% docker ps
CONTAINER ID   IMAGE     COMMAND                   CREATED          STATUS          PORTS      NAMES
21ebf4feb110   ubuntu    "sleep 3600"              2 seconds ago    Up 1 second                container-B
5e4b162f3357   ubuntu    "sleep 3600"              20 seconds ago   Up 19 seconds              container-A

 

Docker Containter lifecycle

 

컨테이너 생성 

docker create --name my-hello-container hello-world

(녹색 박스) : 파일 스냅샷을 하드디스크에 넣어줌.

% docker create --name test-hello-container hello-world
e521b1dd85b5ca60119ac0479c38a473c7ecbc95a54a5f2709b6b74470f46bf1

 

 

컨테이너 실행 run(새로운 컨테이너를 이미지로부터 실행: create+start)

docker run my-hello-container

(파란색 박스) : 이미지에서 받아온 시작 명령어 실행.

 

 

컨테이너 실행 start (이미 생성된 컨테이너를 시작하려면)

docker start my-hello-container

% docker start -a test-hello-container

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (arm64v8)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

 

컨테이너 일시중지 (더 긴 실행 시간이 필요한 ubuntu 이미지로 변경)

$ docker run -d --name ubuntu-container ubuntu sleep 3600
$ docker pause ubuntu-container

0b2a913ec95bdee0b285f603e53dc7800feaf75a877fbc849216a737b3623e70

 

 

컨테이너 중지 stop 

docker stop ubuntu-container 그동안 하던 작업들을 완료하고 컨테이너를 중지 시킨다.

$ docker stop ubuntu-container
ubuntu-container

 

컨테이너 중지 kill 

docker kill ubuntu-container 어떠한 것도 기다리지 않고 바로 컨테이너를 중지

$ docker kill ubuntu-container
ubuntu-container

 

 

컨테이너 재개 :

docker unpause ubuntu-container

 

컨테이너 삭제(실행중인 컨테이너는 반드시 중지해야 삭제가 가능하다):

docker rm ubuntu-container

$ docker rm ubuntu-container
ubuntu-container

 

모든 컨테이너 삭제 :

docker rm 'docker ps -a -q'

 

전체 컨테이너 확인 :

docker ps -a

 

도커 실행 중 명령어 전달하기

  • 도커 실행중에 명령어를 사용하기 위해서는 컨테이너 안에서 실행을 해야 하기에 컨테이너 아이디를 적어줘야 한다.
    (docker ps로 확인)
  • ex) 터미널 A에서 docker run redis 실행후 터미널 B에서 redis-cli 명령어를 쓰는경우 정상적으로 작동하지 않음.
    => 터미널 A는 redis 컨테이너를 실행되고 있는 중에 터미널 B의 명령어는 redis 컨테이너 밖이기 때문에 명령어가 작동하지 않으므로 다음과 같아야 한다.
# docker exec <컨테이너 아이디> <명령어>
docker exec cbba6354bdad ls

 

 

실행중인 컨테이너의 터미널로 접근하기

# docker exec -it <컨테이너 아이디> <sh/bash/zsh 등등..>
docker exec -it a096f4bf67f6 redis-cli
  • docker ps 명령어를 통해 container id를 확인 후 <컨테이너 아이디>에 적어준다.
  • -it 을 붙여줘야 명령어를 실행한 후 계속 명령어를 적을 수 있다.
  • 실행중인 컨테이너에 여러번 명령어를 실행해야 할 경우 5번에 설명한것과 같이 "docker exec -it <컨테이너 아이디> <명령어>" 방식으로 계속 입력하기에는 비효율적임
  • 실행중인 컨테이니의 터미널로 접근도 가능하다. -> 마지막의 명령어를 redis-cli와 같은것 대신 sh로 바꿔준다.
 % docker ps                      
CONTAINER ID   IMAGE     COMMAND                   CREATED         STATUS         PORTS      NAMES
ea45b6de24b8   alpine    "ping localhost"          4 seconds ago   Up 3 seconds              flamboyant_knuth
 % docker exec -it ea45b6de24b8 ls
bin    dev    etc    home   lib    media  mnt    opt    proc   root   run    sbin   srv    sys    tmp    usr    var
% docker exec -it ea45b6de24b8 echo "hello"
hello

'''
위와 같이 매번 "docker exec -it ea45b6de24b8" 를 입력하는게 번거로우므로 
맨 마지막에 sh를 붙여 쉘환경으로 진입하여 반복 코드 없앰
터미널 종료 방법은  control + D
'''

 % docker exec -it ea45b6de24b8 sh          
/ # ls
bin    dev    etc    home   lib    media  mnt    opt    proc   root   run    sbin   srv    sys    tmp    usr    var
/ # echo hello
hello
/ #

 

 

Docker Container VS Docker Image

Docker Container Docker Image
Docker Image를 기반으로 실행되는 각각 컨테이너가 그 이미지의 “실행버전” (인스턴스화)
(이미지의 상태를 복제하여 실행함)
Docker Image는 Docker Container의 소스 코드
Docker Image는 Docker Container의 전제조건
(Dockerfile로부터 이미지를 빌드한 이미지를 기반으로 컨테이너를 실행)
Dockerfile은 Docker Image의 필수 구성요소
(Dockerfile로부터 이미지를 빌드)
Docker Container는 유저들끼리 공유할수 없다.
(컨테이너는 실행 중인 이미지 인스턴스여서 동적이고 일시적이라 공유X)
Docker Image는 Docker Hub를 통해 사용자간에 공유 가능
Docker Container는 컨테이너가 실행중일때 사용자가 컨테이너 내부에 들어가 바로 수정 가능하다.
(docker exec 명령어 사용)
Docker Image를 수정하기 위해서는 Dockerfile를 수정해야한다.

 

 

 

 

참고자료

https://docs.docker.com/guides/walkthroughs/what-is-a-container/#what-is-a-container

https://docs.docker.com/config/containers/runmetrics/

https://www.netapp.com/devops-solutions/what-are-containers/

https://www.linkedin.com/pulse/cgroups-docker-pavan-shukla/

https://codu.tistory.com/39

https://inma.tistory.com/148

https://docs.docker.com/build/building/multi-stage/

https://www.cherryservers.com/blog/docker-multistage-build

https://blog.annotation-ai.com/python-docker-img-optimization/

https://www.vantage-ai.com/vantage101/chapter-9-docker

https://kimjingo.tistory.com/38

https://medium.com/echo-devblog/%EB%8F%84%EC%BB%A4%EB%A5%BC-%EB%8F%84%EC%9E%85%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-1-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EA%B8%B0%EC%88%A0-%EC%9B%90%EB%A6%AC-90a28c14024e

https://stackoverflow.com/questions/51765555/what-namespaces-are-shared-among-containers-in-a-kubernetes-pod

https://platform.sh/blog/the-container-is-a-lie/

https://hoon93.tistory.com/48

DockerCon2017_performance_analysis.pdf

https://www.geeksforgeeks.org/what-is-docker-images/

따라하며 배우는 도커와 CI환경 강의

https://medium.com/tech-learn-share/how-docker-containers-work-462ce3a5ea0f

 

반응형

'DOCKER' 카테고리의 다른 글

[DOCKER] Docker Compose  (0) 2024.01.08
[Docker] Docker Volume  (2) 2024.01.02
[Docker] Docker 최적화  (0) 2023.12.28
[Docker] Docker Container2 - 코드로 보는 개념  (0) 2023.12.26
[Docker] Docker Image  (0) 2023.12.26
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함