[Docker] 도커<1>
1. 도커
Docker는 애플리케이션을 신속하게 구축, 테스트 및 배포할 수 있는 소프트웨어 플랫폼이다.
Docker는 애플리케이션을 컨테이너라는 하나의 유닛으로 만들고, 이 안에는 사용된 라이브러리, 작성된 코드, 시스템 도구, 런타임 등 애플리케이션을 실행하는데 필요한 모든 것이 담겨있다.
운영체제에 상관없이 사용할 수 있고, 가상머신(VM)에 비해 가볍고 한 대의 서버에서 여러개의 서비스를 구동할 수도 있다.
또한 Docker는 이미 업계 표준이라고 불릴 만큼 넓은 영역에서 사용되고 있다.
2. Docker Container
위에서 설명했듯이 Docker는 컨테이너 방식을 사용하고, 컨테이너는 애플리케이션의 의존성과 필요한 파일들을 하나의 유닛으로 패키징하는 매우 가벼운 방법이다.
같은 시스템에 있는 다른 컨테이너(다른 서비스) 와 관계 없이 독립적으로 실행 가능하다. VM을 사용하는것에 비해 매우 적은 자원을 사용한다.
3. Docker Image
Docker image는 컨테이너에 필요한 모든 파일, 환경 변수, 디폴트 명령의 정적 버전이다.
여기서 정적이란 의미는 컨테이너 이미지가 직접 실행되는 파일이 아니라는 뜻이 아니며, 단순히 애플리케이션을 실행하는데 필요한 정보가 담긴 파일이다. 즉 파일은 변하지 않는다.
Docker image → (Create) → Container
3.1 Dockerfile
우리는 이 Docker image를 만들기 위해 Dokcerfile을 먼저 작성해야 한다. Dockerfile은 Docker image를 만들기 위한 일종의 스크립트이다.
Dokcerfile → (bulid) → Dokcer image
이하는 Dockerfile의 예시이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#
FROM python:3.9
#
WORKDIR /code
#
COPY ./requirements.txt /code/requirements.txt
#
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
#
COPY ./app /code/app
#
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
FROM
기본 이미지에서 새 빌드 단계를 만드는 과정이다. 모든 Dockerfile은 반드시 FROM 지시어를 포함해야한다. 이 예시에선 기본 파이썬 3.9 베이스에서 시작한다.
WORKDIR
현재의 워킹 디렉터리를
/code
로 설정한다.이 디렉토리에 의존성을 담은
requirements.txt
와 전체 코드를 담은app
디렉터리를 위치시킨다COPY
최초로 의존성이 담긴
requirements.txt
를/code
디렉터리로 복사한다.이때
requiremets.txt
를 먼저 카피하는 이유는 해당 파일은 서비스 중에 자주 변하지 않기 때문이다. 즉app
이 바뀌어 다시 이미지를 build 할 때에 다시 패키지 설치를 진행하지 않아도 되기 때문에 시간을 절약할 수 있다.RUN
requirements.txt
에 담긴 패키지 정보들을 토대로 환경을 구축한다.COPY
실제로 실행할 애플리케이션이 담긴 코드를 복사한다. 이 부분은 자주 변경되는 코드를 포함하고 있기 때문에 build 시간 최적화를 위해 거의 마지막 부분에 작성한다.
CMD
FastAPI 애플리케이션을
uvicorn
서버로 실행하기 위한 커맨드를 입력한다.
3.2 requirements.txt
requirements.txt
는 애플리케이션 실행에 필요한 모든 의존성이 담긴 텍스트 파일이다.
1
2
3
fastapi>=0.68.0,<0.69.0
pydantic>=1.8.0,<2.0.0
uvicorn>=0.15.0,<0.16.0
애플리케이션 실행에 필요한 패키지들을 버전에 맞춰 기록한다.
3.3 도커 이미지 생성
모든 파일을 제자리에 작성했으면 해당 디렉터리(Dockerfile
과 app
디렉터리가 위치한 디렉터리)에서 image를 build 할 수 있다.
1
docker build -t 원하는이미지이름 .
마지막 끝의 .
은 ./
와 같은 의미이며, 도커에게 컨테이너 image를 build 하기 위한 디렉터리를 알려준다.
이미지를 생성했다면 도커 데스크탑에서 확인 가능하다.
3.5 도커 컨테이너 시작
image를 build 했다면 컨테이너 실행이 가능하다.
1
docker run -d --name 원하는컨테이너이름 -p 80:80 빌드한이미지이름
3.6 도커 캐시
위에서 따로 COPY
를 한 이유에 대해 간단하게 설명했다. 그 트릭에 대한 자세한 설명은 다음과 같다.
컨테이너는 맨 윗 부분부터 시작해 레이어 위에 새로운 레이어를 추가하는 방식으로 진행된다. 이때 도커 엔진에서 각각의 커맨드 라인에 대한 빌드 결과를 캐시로 저장한다.
만약 어떤 파일이 마지막으로 이미지를 빌드한 때로부터 바뀌지 않았다면, 파일을 다시 복사해 새로운 레이어를 생성하는것이 아니라, 마지막에 생성했던 같은 레이어를 재사용한다.
즉 의존성을 포함한 requirements.txt
는 자주 바뀌지 않으니 먼저 복사해 캐시를 만들어 두면, requirements에 대한 레이어는 image를 빌드할 때 캐시를 재사용해 소모되는 시간을 줄일 수 있다.
위의 Dockerfile 예시를 다시 가져와보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FROM python:3.9
#
WORKDIR /code
#
COPY ./requirements.txt /code/requirements.txt
#
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
#
COPY ./app /code/app
#
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
이때 서비스의 변경 사항이 생겨 app
내의 코드는 변경됐지만 requirements.txt
는 바뀌지 않았다고 해보자.
이미지를 다시 빌드할 때 1~4번 과정은 변경되지 않았기 때문에 생성된 캐시를 재사용해 시간을 줄이고, 5 ~ 6 번만 다시 빌드한다는 뜻이 된다.