1. VMware vs Container
[1] 가상화란
- 가상화란 하나의 물리 공간에서 여러 가상의 공간을 만들어주는 기술을 말한다.
- 가상화를 관리하는 소프트웨어(주로 Hypervisor)를 사용하여 하나의 물리적 머신에서 가상머신(VM)을 만들어, 하나의 컴퓨터지만 마치 여러 컴퓨터를 실행하듯 사용할 수 있다.
- 이와 동일한 맥락으로 한정된 메모리 공간에서 여러 프로세스가 독립적 공간을 사용하기 위해 가상 메모리라는 기술을 사용한다. 이는 메모리 뿐만 아니라 디스크 공간을 추가로 사용하여 마치 여러 가상의 공간을 확보하는 것처럼 보인다.
[2] Virtual Machine
- 하나의 물리적 서버가 있고, VM을 가상화 시켜주는 하이퍼바이저(virtual box, vmware etc)에 의해 여러 VM들이 host os 위에 존재한다.
- 가상화된 VM들은 각각 독립된 실행 환경을 가지며, 데이터는 물론이고 코드도 전혀 공유하지 않는다.
- Guest OS도 Host OS와 같이 여러 어플리케이션을 설치하고 포트로 구분하여 각각의 서비스를 만들 수 있다.
- 하지만 guest os, 커널 중복 등 VM마다 최소 GB 단위의 공간이 필요하며, VM 수에 비례해서 리소스가 늘어난다. 따라서 어플리케이션을 띄우는데 너무 많은 리소스가 필요하며 부팅시간 역시 지연되는 문제가 있다.
[3] Container
- 컨테이너는 무겁고 느린 Virtual Machine 가상화 방식을 해결하기 나왔다.
- 커널 하나에 격리된 여러 개의 인스턴스가 포함 될 수 있도록 애플리케이션 수준에서 이루어지는 가상화이며, 이러한 인스턴스를 컨테이너라 부른다.
- VM은 OS Level에서 격리하며, Container는 리눅스 격리 기술에 의해 Application Level에서 격리한다.
- Container Img는 컨테이너를 만들기 위해 필요한 모든걸 가지고 있는 파일인데, 이미지로 컨테이너를 생성하기 때문의 컨테이너를 이미지의 인스턴스라고도 할 수 있다. 이때 Img는 Immutable해서 롤백에 용이하다.
- 컨테이너는 모두 동일한 커널을 호출함으로 보안위험이 발생할 수 있다. 격리하려는 프로세스가 적다면 가상머신도 괜찮다. 반면, 동일한 시스템에서 더 많은 수의 격리된 프로세스를 실행하려면 컨테이너의 오버헤드가 낮기 때문에 컨테이너를 선택하는게 괜찮다.
- 각 가상머신은 자체 시스템 서비스를 실행하지만 컨테이너는 모두 동일한 os에서 실행되므로 컨테이너는 시스템서비스를 실행하지 않는다. 즉, 컨테이너를 실행하려면 가상머신처럼 부팅할(os&kernel level) 필요 없이, 즉시 시작된다.
[4] Container 격리 방식
linux namespace
- 각 프로세스가 시스템(파일, 프로세스, 네트워크 인터페이스, 호스트 이름 등)에 대한 독립된 뷰만 볼 수 있다.
- 프로세스는 여러 namespace에 속할 수 있고, 동일한 namespace 내에 있는 리소스만 볼 수 있다. 이때, 각 네임스페이스는 특정 리소스 그룹을 격리하느 데 사용된다.
- 각 컨테이너는 고유한 네트워크, 프로세스 등 인터페이스만 사용하므로 각 컨테이너는 고유한 해당 네임스페이스 세트만 볼 수 있다.
linux control cgroups
- 프로세스가 사용할 수 있는 리소스 (cpu, 메모리, 네트워크 대역폭 등)를 제한한다.
- 프로세스는 설정된 양 이상의 CPU, 메모리 등을 사용할 수 없다. 이런 방식으로 다른 프로세스용으로 예약된 리소스를 사용할 수 없다.
2. Docker
원하는 개발 환경을 DockerFile에 저장하면, 도커는 이를 너가 원하는 어떤 머신에서든(local, dev, alpha, stage, real etc) 해당 '환경을 동일하게 셋팅'할 수 있고, 이러한 환경을 독립적으로 복제하여 환경셋팅을 '모듈식'으로 관리가 가능해진다.
[1] Docker란
- 컨테이너를 쉽게 실행시켜주는 프로그램
- 도커만 있으면 도커 허브에 한 번 업로드한 장비들을 내가 지정한 형태로 어디든 설치할 수 있다. 로컬, 알파, 리얼 등 어떤 환경에서도 동일하게 실행이 가능하다. 서버를 구성하고, 개발자마다 환경 셋팅 하는게 여간 귀찮은 작업인데 이를 도커 덕분에 편하게 구성할 수 있다.
- 각 요소들이 설치된 모습을 '이미지' 형태로 박제해서 자장하는데, 이미지는 공식 제품일 수 있고 && 우리가 만들 수도 있다. 이렇게 이미지로 저장된 항목들이 함께 연결되서 동작하도록 dockerfile 문서 형태로 표현한다.
- 우리는 이 문서만 잘 보관하면 언제 어디서든 미리 지정된 서비스에 필요한 설정대로 dockerhub로부터 다운 받아 설치할 수 있다.
- 장비에 문제가 생겨도 도커허브에서 다시 불러오면되고, 도커에 의해 같은 서버의 다른 컨테이너들과 서로 방해없이 해당 작업만의 특정 공간(가상화)을 할당시켜준다.
[2] Docker 장점
Environment Disparity
- docker를 통해 다른 머신에서도 동일한 환경을 구축할 수 있다. 로컬, 개발, 알파, 리얼 등 모두 같은 개발 환경을 구축할 수 있고, 도커엔진만 있으면 "내 컴퓨터에서는 되는데 왜 저기선 안되지?", "새로운 팀원이 오고 환경 구축의 시간"의 비용을 줄일 수 있다.
- DockerFile에 필요한 파일을 ex) ubuntu, java 지정하면, 도커는 이걸 읽고 컴퓨터에 필요한 파일을 컨테이너에 설치함으로 동일한 환경을 구축할 수 있다.
Independent
- 같은 서버에서 각기 다른 환경의 컨테이너를 설치할 수 있다. 예를 들어, 하나의 컴퓨터에 여러 서비스를 돌리고, 서비스마다 자바 버전이 달라도 괜찮다. A 서비스에선 java8을, B 서비스에선 java9를 사용해도 충돌 없이 진행이 가능하다.
환경 구축의 편리함
- 서버를 구매하고, 환경셋팅하고, 시작하고 이러한 복잡한 과정을 도커를 통해 새로운 환경을 쉽게 구축할 수 있다. 이러한 특징으로 트래피에 따라 새로운 환경을 쉽게 설치할 수 있어 스케일 업/다운이 용이하다.
[3] Docker Container Layer
- 도커 기반 컨테이너 이미지와 가상머신 이미지의 큰 차이점은 컨테이너 이미지가 여러 이미지에서 공유되고 재사용될 수 있는 레이어로 구성돼 있다. 즉, 동일한 레이어를 포함하는 다른 컨테이너 이미지를 실행할 때 다른 레이어가 이미 다운로드 된 경우 이미지의 특정 레이어만 다운로드하면 된다.
- 도커 이미지는 레이어로 구성돼며, 모든 도커 이미지는 다른 이미지 위에서 빌드된다. 이러한 특징으로 네트워크로 이미지를 배포하는 속도가 빨라진다.
- 각 레이어는 동일 호스트에 한 번만 저장된다. 또한 컨테이너 이미지 레이어가 읽기 전용이기 때문에, 수정시 기존 이미지 위에 최상위 레이어로 작성된다.
- 이미지는 하나의 큰 바이너리 덩어리가 아니라, 서로 다른 이미지 여러개가 레이어로 구성한다. 다수의 이미지를 생성하더라도 기본 이미지를 구성하는 레이어는 단 한번만 저장되어, 필요한 부분만 pull 받고, push 할 수 있어 이미지 저장/전송에 효과적이다. DockerFile에 명시된 RUN, CMD 등 명령어를 수행할 때마다 레이어를 추가하고 최종적으로 latest 이미지를 저장한다.
[4] DockerFile
DockerFile은 Docker 이미지가 어떤 단계를 거쳐 빌드 되어야하는지를 담고있는 텍스트 파일로, Docker는 DockerFile에 나열된 명령문을 차례로 수행하여 이미지를 생성한다.
FROM
- FROM <이미지>:<태그>
- base 이미지를 지정할 때 사용한다.
- FROM ubuntu:latest
WORKDIR
- WORKDIR <이동할 경로>
- Shell의 cd 명령문처럼 컨테이너 상에서 작업 디렉터리로 전환을 위해 사용된다. WORKDIR로 작업 디렉토리를 전환하면 그 이후에 등장하는 모든 RUN, CMD, ENTRYPOINT, COPY, ADD 명령문은 해당 디렉토리를 기준으로 실행한다.
- WORKDIR /usr/app1
RUN
- RUN ["<커맨드>", "<파라미터1>", "<파라미터2>"]
- 이미지 생성 과정에서 실행할 명령어로, 보통 이미지 안에 특정 소프트웨어를 설치하기 위해 많이 사용된다.
RUN apk add curl
RUN npm install --silent
docker run busybox echo "hello world"
ENTRYPOINT
- ENTRYPOINT ["<커맨드>", "<파라미터1>", "<파라미터2>"]
- 이미지를 컨테이너로 띄울 때 항상 실행되야 하는 커맨드를 지정할 때 사용한다. 이 명령문은 docker img를 마치 하나의 실행 파일처럼 사용할 때 유용하다.
ENTRYPOINT ["npm", "start"]
ENTRYPOINT ["python", "manage.py", "runserver"]
CMD
- CMD ["<커맨드>","<파라미터1>","<파라미터2>"] or CMD ["<파라미터1>","<파라미터2>"]
- CMD 명령문은 해당 이미지를 컨테이너로 띄울 때 디폴트로 실행할 커맨드나, ENTRYPOINT 명령문으로 지정된 커맨드에 디플트로 넘길 파라미터를 지정할 떄 사용한다. 예를 들어, ENTRYPOINT로 커맨드를 지정하고, CMD 명령문으로 디폴트 파라미터를 지정하면 매우 유연하게 이미지를 실행할 수 있다.
- RUN 명령문은 이미지 빌드시 항상 실행되며, 한 dockerfile에 여러개의 RUN 명령문을 선언할 수 있다. 반면에 CMD 명령문은 이미지를 컨테이너로 띄울 때 딱 한번 실행 기회를 가지게 되며, 이 기회마저도 docker run 커맨드에 인자를 너길 경우 상실하게 된다.
ENTRYPOINT ["node"]
CMD["index.js"]
docker run test
docker run test main.js
COPY
- 호스트 컴퓨터에 있는 디렉토리나 파일을 도커 이미지의 파일 시스템으로 복사하기 위해서 사용된다. 절대 경로와 상대 경로를 모두 지원하며 상대 경로 이용시, WORKDIR 명령문으로 작업 디렉토리를 어디로 전환을 했는지 고려해야한다.
COPY package.json package.json
WORKDIR app/
COPY . .
ADD
- ADD 명령문은 좀 더 파워풀한 COPY 명령문인데, ADD 명령문은 일반 파일 뿐만 아니라 압출 파일이나 네트워크 상의 파일도 사용할 수 있다. 이렇게 특수한 파일을 다루는게 아니라면 COPY 명령문을 사용하는 것이 권장된다.
ENV, ARG, Expose
- ENV : 환경 변수 설정 / ARG : 빌드시 넘어올 수 있는 인자 설정
- Expose : 컨테이너가 리스닝할 포트 및 프로토콜 설정
Example
FROM python:3.8.5
RUN pip3 install flask flask-cors flask-mysql
WORKDIR /usr/src/app
CMD ["python3", "backend.py"]
docker run --name {컨테이너명} -v $(pwd):/usr/src/app -p 5000:5000 {이미지명}