포스트

[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"]
  1. FROM

    기본 이미지에서 새 빌드 단계를 만드는 과정이다. 모든 Dockerfile은 반드시 FROM 지시어를 포함해야한다. 이 예시에선 기본 파이썬 3.9 베이스에서 시작한다.

  2. WORKDIR

    현재의 워킹 디렉터리를 /code 로 설정한다.

    이 디렉토리에 의존성을 담은 requirements.txt와 전체 코드를 담은 app 디렉터리를 위치시킨다

  3. COPY

    최초로 의존성이 담긴 requirements.txt/code 디렉터리로 복사한다.

    이때 requiremets.txt를 먼저 카피하는 이유는 해당 파일은 서비스 중에 자주 변하지 않기 때문이다. 즉 app 이 바뀌어 다시 이미지를 build 할 때에 다시 패키지 설치를 진행하지 않아도 되기 때문에 시간을 절약할 수 있다.

  4. RUN

    requirements.txt 에 담긴 패키지 정보들을 토대로 환경을 구축한다.

  5. COPY

    실제로 실행할 애플리케이션이 담긴 코드를 복사한다. 이 부분은 자주 변경되는 코드를 포함하고 있기 때문에 build 시간 최적화를 위해 거의 마지막 부분에 작성한다.

  6. 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 도커 이미지 생성

모든 파일을 제자리에 작성했으면 해당 디렉터리(Dockerfileapp 디렉터리가 위치한 디렉터리)에서 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 번만 다시 빌드한다는 뜻이 된다.



이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.