Docker

이창훈·2022년 4월 25일
0

CS

목록 보기
1/1

참고 : https://www.youtube.com/watch?v=IiNI6XAYtrs

도커란 ?

도커는 컨테이너 기반 가상화 도구이다.

가상화란?

가상화의 등장배경

 6000만원 짜리 엄청 좋은 서버가 있다. 직접 개발한 쇼핑몰 사이트를 돌리기로 했는데 고정 사용자가 1000명 정도인데 기껏 비싸게 구매했더니, 성능의 반의 반도 사용하지 않고 있다.
이대론 서버유지가 안될 것 같아 새로운 프로젝트를 올리기로 했다. 이번에 만든 서비스는 반려동물 사료 플랫폼이다. 개발을 하고 적용을 하려고 하니 쇼핑몰 사이트에서 사용하고 있는 기술들과 충돌이 일어난다. 결국 반려동물 사료 플랫폼은 구동하지 않기로 했다.

 새로운 프로젝트를 구동하려 하니, 이전 서비스를 포기해야하고 이전 프로젝트를 유지하려고 하니 새 프로젝트를 포기해야하고 이러지도 저리도 못하는 상황이다.

  그러다 문득 서버의 성능을 나눠서 사용하면 되지 않을까? 라는 생각이 들어요. 이것이 바로 가상화라는 개념이 등장하게 된 배경이다.

하나의 서버 자원을 나눠서 가지며, 성능을 분산 시키고, 분산된 서버들은 각기 다른 서비스를 수행할 수 있게 했다. 가상화를 통해 사용자가 많은 서비스에는 많은 자원을 할당해주고, 적은 서비스에는 적게 할당 할 수 있게 됐다.

가상화의 종류

1) 서버가상화

서버 가상화란 하나의 물리적 서버 호스트에서 여러 개의 서버 운영 체제를 게스트로 실행할 수 있게 해주는 아키텍쳐이다. 하나의 호스트에 게스트 OS가 할당된 것을 확인할 수 있다.

하나의 호스트에 게스트 OS가 할당된 것을 확인할 수 있다. 이러한 서버 가상화를 구현해주는 기술은 하이퍼바이저 이다.

하이퍼바이저는 가상화 기술을 통해 여러 개의 운영체제를 하나의 호스트OS에 생성해서 사용할 수 있게해주는 소프트웨어이다. 이렇게 생성된 여러 개의 운영체제는 가상머신이라는 단위로 구별된다. 각 가상머신에는 여러 운영체제가 설치되어 사용된다. 하이퍼바이저에 의해 생성되고 관리되는 운영체제는 게스트운영체제(Guest OS)라고 하며 각 게스트 운영체제는 다른 게스트 운영체제와는 완전히 독린된 공간과 시스템자원을 할당받아 사용한다.

하이퍼바이저의 역할을 정리해보면 하이퍼바이저는 OS들에게 자원을 나눠주며 조율한다. OS들의 커널을 번역해서 하드웨어에게 전달한다. 대표적인 가상화 툴로 VirtualBox, VMware등이 있다.

각종 시스템 자원을 가상화하고 독린된 공간을 생성하는 작업은 하이퍼바이저를 반드시 거치기 때문에 일반 호스트에 비해 성능 손실이 발생한다. 또한 가상 머신에는 "게스트 운영체제"를 사용하기 위한 라이브러리, 커널 등을 전부 포함하기 때문에 배포하기 위한 이미지로 만들었을 때 크기 또한 커진다. 즉, 가상 머신은 완벽한 운영체제를 생성할 수 있는 장점은 있지만 성능이 느리고, 용량상으로 부담이 있다. 작은 어플리케이션을 구동하는데 OS까지 새로 띄우는 것이 부담스러울 수 있다. 이를 해결 하기 위해 컨테이너가 나왔다.

2-1) 컨테이너란?

 컨테이너는 가상화된 공간을 생성하기 위해 리눅스 자체 기능인 chroot, 네임스페이스, cgroup을 사용함으로써 프로세스 단위의 격리 환경을 만든다. 도커 엔진 위에 컨테이너가 할당된 것을 확인할 수 있다.

 컨테이너 안에는 애플리케이션을 구동하는데 필요한 라이브러리 및 실행 파일만 존재한다. 그렇기 때문에 이미지로 만들었을 때, 이미지의 용량 또한 가상 머신에 비해 대폭 줄어든다.

  따라서 이미지를 만들어 배포하는 시간이 가상머신에 비해 빠르며, 가상화된 공간을 사용할 때 성능 손실도 거의 없다는 장점이 있다. 아래에 이미지에는 Docker Engin이라 표현했지만 컨테이너를 다루는 기술은 Docker 말고도 다른 벤더사도 많다. 컨테이너 기술이 도커만의 기술이 아니라는 점을 기억하자.

앞서 서버 가상화와 컨테이너 가상화의 장단점을 살펴봤다. 컨테이너를 왜 써야할까? 라는 질문에 어떻게 대답할 수 있을까?

*도커는 컨테이너 기술에 여러 기능을 추가한 오픈소스 프로젝트이다.

2-2)컨테이너의 여러 의미

컨테이너는 사전적 의미로 어떤 물체를 격리하는 공간을 뜻한다. 각각의 컨테이너는 격리된 상태로 다른 컨테이너들과 분리되어 있다. 그렇다면 기술적 의미에서의 컨테이너는 무엇을 의미할까?

우리가 흔히 아는 컨테이너는 컨테이너에 담긴 것들의 라이프 사이클들을 관리한다. 무언가의 생성, 운영, 제거 까지의 라이프 사이클을 얘기한다.

가상화 관점에서의 컨테이너란 이미지의 목적에 따라 생성되는 프로세스 단위의 격리 환경이다. 이미지는 간단히 컨테이너를 만들기 위한 틀이라고 생각하자.

컨테이너는 환경을 제공하며 프로세스의 생명주기를 관리한다. 예를 들어 SpringBoot 1개와 Nginx 1개를 컨테이너를 통해 실행해보려 한다. 여기서 이미지의 목적은 SpringBoot 애플리케이션,Nginx애플리케이션이다.

컨테이너는 파일 시스템과 격리된 시스템 자원 및 네트워크를 사용할 수 있는 독립된 공간을 가져 온다. 컨테이너가 실행되며, 프로세스가 실행되기에 필요한 자원들을 할당받고 프로세스를 실행한다. 이때 커널을 통해 필요한 자원들을 가져온다. 프로세스는 OS위에서 실행된다.
이를 HostOS라고 표현한다.

호스트입장에서는 컨테이너를 어떻게 바라볼까?
컨테이너는 프로세스의 생명주기를 관리하며 하나의 프로세스라고 말할 수 있다. 즉, SpringBoot 애플리케이션 프로세스를 직접 실행하나 컨테이너로 실행하나 호스트 입장에서는 똑같은 프로세스로 본다.

다시 한번 "컨테이너를 왜 써야할까?"라는 질문에 답을 해보자.
컨테이너는 프로세스를 격리된 환경에서 관리한다. 이때 격리된 환경은 HostOS와의 격리를 의미하며 이를 통해 컨테이너에 어떤 설정을 하든 Host OS에 영향을 끼치지 않는다.
즉, 우리만의 독린된 개발 환경을 보장받을 수 있다는 것이다. 이를 통해 프로세스를 컨테이너 단위로 바라볼 수 있게되고 프로세스의 관리, 확장이 용이하다.

2-3) 컨테이너 관리

그렇다면 컨테이너는 어떻게 관리할 수 있을까?

사용자는 Docker Engine을 통해 컨테이너를 관리할 수 있다.

Docker엔진이란?


도커엔진은 유저가 컨테이너를 쉽게 사용할 수 있게하는 주체이다.
컨테이너의 라이프 사이클을 관리하며,
컨테이너를 생성하기 위한 이미지관리,
컨테이너의 데이터를 저장하기 위한 저장소역할을 하는 볼륨 관리,
컨테이너의 접속을 관리하기 위한 네트워크 관리등의 기능을 제공한다.

*각각의 내용이 워낙 넓어, 도커엔진이 이러한 역할을 하고 있다 정도로 넘어가면 좋을 것 같다.

도커에서 제공하는 프로젝트를 살펴보자.

사각형 박스는 각각 프로젝트라고 부르며 컨테이너를 어떻게 사용할 것인가에 대한 목적에 따라 분리되어 있다.

그렇다면 사용자가 도커 명령어를 쳤을 때 어떤 플로우로 진행될까?

먼저 사용자는 docker 명령어로 도커 엔진에게 명령어를 보낸다.
이를 전달 받은 도커 클라이언트는 /var/run/docker.sock에 위치한 유닉스 소켓을 통해 도커 데몬의 API를 호출한다. 도커 데몬은 명령어에 해당하는 작업을 수행하고, 수행 결과를 도커 클라이언트에 반환하며 사용자에게 결과를 출력한다. dockerd는 컨테이너를 생성하고 실행하며 이미지를 관리하는 주체이다. 도커 프로세스가 실행되어 입력을 받을 준비가 된 상태를 도커 데몬이라 부른다.

도커데몬에게 명령어를 직접 보내고 싶을 수도 있다.
이때는 url 요청을 보내 명령어를 쳤을 때와 같은 동작을 수행할 수 있다.

여러개의 서버의 도커엔진

지금까지는 단일서버에서 단일 도커엔진에 대해 이야기했다.
이제부터는 여러개의 서버의 도커엔진에 대해 이야기하겠다.

하나의 호스트 머신에서 컨테이너가 많아져 CPU나 메모리, 디스크 용량과 같은 자원이 부족해졌다. 새로운 컨테이너를 추가하려는데 자원 부족으로 추가하지 못하는 상황이면 이를 어떻게 해결할 수 있을까?

제일 간단한 방법은 매우 성능이 좋은 서버를 새로 사는 것이다.
이는 Scale Up이라고도 말한다.

하지만 자원의 확장성 측면이나 비용 측면에서 좋지 않은 해답이다. 자원이 부족할 때마다 자원을 더 사야하거나 기껏 자원을 사놨는데, 그만큼의 자원이 필요하지 않을수도 있기 때문이다.

여러가지 방법이 있지만 가장 많이 사용하는 방법은 여러 대의 서버를 클러스터로 만들어 자원을 병렬로 확장하는 것이다. 이는 Scale Out이라고 부른다.

예를 들어, 4GB의 메모리가 탑재된 서버 1대에 도커엔진을 설치해 실제 운영 환경에 사용한다고 가정하자. 이 1대의 서버에 컨테이너가 너무 많이 생성돼 있어 더는 컨테이너를 사용할 수 없다고 판단되면 4GB의 메모리가 탑재된 새로운 서버를 추가해 자원을 늘린다. 만약 놀고 있는 자원이 많다면 다시 줄인다.

그림에서는 2개의 호스트 OS가 하나의 클러스터 단위로 묶여 있다.

(서버)클러스터란?

각기 다른 서버들을 하나로 묶어 하나의 시스템 같이 동작하게 함으로써 클라이언트들에게 고가용성의 서비스를 제공하는 것을 말한다. 동일한 network풀을 쓰며 요청에 따라 트래픽을 분산시킬 수 있다.

하지만 여러 대의 서버를 하나의 자원풀로 만드는 것은 쉬운일이 아니다. 새로운 서버나 컨테이너가 추가 될 때 이를 발견하는 작업부터, 어떤 서버에 어떤 컨테이너를 할당할 건지에 대한 작업들 등 처리해야할 작업이 많다.

도커스웜
다행히 이러한 문제들을 해결하는 여러 솔루션을 오픈소스로 활용할 수 있다. 그중 대표적인 것이 도커 스웜이다.

도커스웜은 스웜모드를 지원한다.
스웜모드는 마이크로 서비스 아키텍처의 컨테이너를 다루기 위한 클러스터링 기능에 초점을 맞추고 있다. 같은 컨테이너를 동시에 여러 개 생성해 필요에 따라 유동적으로 컨테이너 수를 조절할 수 있다. 컨테이너로의 연결을 분산하는 로드밸런싱 기능을 자체적으로 지원한다.

Scale Out은 기본적으로 지원하지만 자체적으로 인스턴스를 늘리거나 줄이거나는 할 수 없고 개발자가 직접 늘려줘야한다.

스웜은 도커엔진 자체에 내장되어 있다.
스웜모드는 매니저 노드와 워커 노드로 구성되어 있다.
워커 노드는 실제로 컨테이너가 생성되고 관리되는 도커 서버이고 매니저 노드는 워커 노드를 관리하기 위한 도커서버이다. 매니저 노드는 기본적으로 워커 노드의 역할을 포함한다.

매니저 노드는 운영환경에서 다중화를 하는 것이 좋다. 매니저 노드의 부하를 분산하고, 특정 매니저 노드가 다운됬을 때 정상적으로 스웜 클러스터를 유지할 수 있기 때문이다. 그렇다면

스웜모드 클러스터 구축
클러스터 구축은 어떻게 이루어지는 걸까요?

AWS EC2인스턴스의 예를 들어보자.

4개의 인스턴스가 준비됬다고 가정해보자. 우선 매니저 노드의 역할을 할 인스턴스와 노드의 역할을할 인스턴스를 지정한다.

docker 명령어인
docker swarm init을 통해 매니저 노드를 설정한다.
docker swarm join을 통해 노드를 추가할 수 있다.

워커노드를 추가함으로써 하나의 클러스터 단위가 완성됬다.
매니저 노드도 1개도 클러스터라 할 수 있다.

여기서 우리는 이제 서비스를 시작할 수 있게 되었다.

서비스란?
일반적인 도커 명령어의 제어 단위는 컨테이너이다.
이는 도커 클라이언트가 제어하는 것은 컨테이너라는 말이다.
도커 스웜에서 제어 단위는 서비스이다.
서비스는 같은 이미지에서 생성된 컨테이너의 집합을 의미한다.

서비스를 제어하면 해당 서비스 내의 컨테이너에 같은 명령이 수행된다.
이때 서비스 내의 컨테이너를 Task라고 부른다.

서비스의 정의에 따라 테스크를 할당할 적합한 노드를 선정하고 선택된 노드에 테스크를 분산해서 할당된다.

함께 생선된 테스크를 레플리카(replica)라고 하고 서비스에 설정된 레플리카의 수만큼 테스크가 스웜 클러스터 내에 존재해야한다. 그림에서는 아무 노드에, 레플리카 셋을 3으로 두었다.

예제를 조금 더 확인해보자.

SpringBoot, nginx 즉 2개의 서비스를 정의 했다.
SpringBoot는 모든 노드에 생성될 테스크의 수를 3개로 설정했다.
Nginx는 모든 노드에 2개로 설정했다.

클러스터 안에 SpringBoot 컨테이너 3개, Nginx컨테이너 2개가 할당된 것을 확인할 수 있다.

만약 노드에 장애가 발생하면 어떻게 될까 ?
스웜은 서비스의 테스크들에 대한 상태를 계속 확인하다가 서비스 내에 정의된 레플리카 수만큼 스웜 클러스터 내에 존재하지 않으면, 새로운 테스크 레플리카를 생성한다.

즉, 그림과 같이 하나의 노드가 다운되면, 다른 노드에 테스크를 생성한다. 하지만 매니저 노드가 다운돼버리면 클러스터가 사라진다.
이밖에도 스웜모드가 제공하는 기능들이 많다.
앞서 살펴본 클러스터 관리, 서비스 관리, 클러스터의 네트워크를 관리하는 기능 클러스터의 노드를 관리하는 기능 이또한 각각의 내용이 넓어, 스웜모드가 이런 역할을 한다 정도만 하고 넘어가자.

스웜모드를 왜 써야할까?

서비스의 확장/관리를 편하게 하기위해 라는 답을 할 것이다.

컨테이너를 어떻게 효율적으로 생성할까?

각각의 서비스가 동작하기 위해선 컨테이너가 필요하다. 서비스에 필요한 컨테이너가 1~2개 정도라면 도커 명령어를 직접 입력해 생성할 수 있지만 서비스가 여러개라면 말이 달라진다.

도커컴포즈

도커컴포즈는 여러개의 컨테이너를 하나의 서비스로 정의하고 실행한다. 스웜모드의 서비스와 유사하게 설정 파일에 정의된 서비스의 컨테이너 수를 유동적으로 조절 할 수 있다. 서비스 디스커버리도 자동적으로 이루어진다.

컴포즈는 도커엔진 밖에 위치한다.
도커엔진 밖에 위치하기 때문에 컴포즈를 직접 설치해야한다.

도커 컴포즈로 컨테이너 생성하기

도커 컴포즈로 컨테이너를 어떻게 생성하는지 플로우를 확인해보자.

도커 컴포즈는 컨테이너의 설정이 정의된 yml파일을 읽어 도커엔진을 통해 컨테이너를 생성한다. 따라서 도커 컴포즈를 사용하려면 yml 파일을 먼저 작성해야 한다.

파일을 작성후 docker-compose명령어를 통해 컨테이너를 생성할 수 있다.

컴포즈의 구조

이렇게 생성된 컴포즈의 구조를 살펴보자.

도커 컴포즈는 기본적으로 docker-compose.yml이 위치한 디렉토리 이름을 프로젝트 이름으로 사용한다. 즉, yml 파일이 저장된 디렉토리에 이름에 따라 프로젝트의 이름이 달라질 것이다. 직접 설정도 가능하다.

하나의 프로젝트는 여러개의 서비스로 이루어지고 하나의 서비스에는 여러개의 컨테이너가 존재할 수 있다. 이 컨테이너 안에 4개가 보이는데 각각의 컨테이너는 번호를 붙여 서비스 내의 컨테이너를 구별한다.

도커컴포즈를 왜 써야할까?
도커컴포즈는 컨테이너의 생성을 편리하게 하기 위해 사용한다.

profile
실패를 두려워하지 않고 배우고 기록하여 내일의 밑거름 삼아 다음 단계로 성장하겠습니다.

0개의 댓글