현재 도커 이미지는 2.4GB로 커서 ECR에 올리는 작업이 오래걸린다.
이미지 크기를 줄여보자.
최적화 전략
적절한 Base image의 선정
layer 수의 최소화
필요없는 packages의 제거
poetry -> pip 로 전환하기
파이썬에서 제공하는 공식 도커이미지 (https://hub.docker.com/_/python?ref=blog.annotation-ai.com)
현재 python:3.12.2-slim 버전을 사용 중이다.
python:3.12.2-alpine 를 사용하면 용량을 줄일 수 있다.
그러나, alpine 버전은 경량화된 OS인 alpine-linux를 사용하기 때문에 필수 패키지 설치를 지원하지 않는 등 side-effect가 발생할 수 있다.
테스트해보자.
FROM python:3.12.2-alpine
WORKDIR /app
RUN pip install poetry
COPY poetry.lock poetry.toml poetry.lock pyproject.toml ./
RUN poetry install --no-root
COPY ./to_search /app/to_search
COPY ./ /app
ENTRYPOINT ["poetry", "run", "uvicorn", "--host", "0.0.0.0", "--port", "8800", "--log-config", "docker_logconfig.py", "app.main:app"]
두 이미지 모두 정상적으로 동작한다. 0.07GB 세이브했다.
불필요한 행을 지워보자.
FROM python:3.12.2-alpine
WORKDIR /app
RUN pip install poetry
COPY poetry.lock poetry.toml poetry.lock pyproject.toml ./
RUN poetry install --no-root
COPY ./ /app
ENTRYPOINT ["poetry", "run", "uvicorn", "--host", "0.0.0.0", "--port", "8800", "--log-config", "docker_logconfig.py", "app.main:app"]
빌드 시간을 줄이기 위해 to_search 폴더를 카피하는 부분만 분리했었는데, 빌드시간 차이가 크지 않으므로 없애보자.
0.03GB 줄어들었다.
alembic, pandas 등 실제 배포 코드에서는 사용하지 않지만, 로컬에서 부가 기능들을 사용하기 위해 추가한 라이브러리들이 꽤 있다. 없애보자.
poetry가 라이브러리 다운로드를 위해 사용하는 pyproject.toml 에는 다음과 같이 라이브러리들을 나열한다.
[tool.poetry.dependencies]
python = "^3.12"
fastapi = "^0.109.0"
...
dev dependencies 를 추가하여 배포 시 필요없는 라이브러리를 분리한다.
[tool.poetry.dev-dependencies]
cryptography = "^41.0.5"
pymysql = "^1.1.0"
...
poetry lock 명령어를 통해 정확한 버전을 나타내는 poetry.lock 파일을 업데이트한다. (pyproject.toml 파일 수정 후 업데이트하지 않으면 poetry install 이 정상적으로 되지 않는다.)
poetry lock --no-update
dockerfile 에서 poetry install 하는 부분을 dev 종속성을 제외하고 다운로드 받도록 옵션을 추가한다.
FROM python:3.12.2-alpine
WORKDIR /app
RUN pip install poetry
COPY poetry.lock poetry.toml poetry.lock pyproject.toml ./
RUN poetry install --no-root --only main
COPY ./ /app
ENTRYPOINT ["poetry", "run", "uvicorn", "--host", "0.0.0.0", "--port", "8800", "--log-config", "docker_logconfig.py", "app.main:app"]
필요한 라이브러리를 제거하진 않았는지 실행시켜본다.
0.22GB 줄어들었다.
빌드시간을 줄이기 위해 poetry 를 사용했는데, 빌드 시간이 조금 더 걸리더라도 이미지 사이즈를 줄이는 것이 비용적인 면과 ECS Fargate 환경에서 이미지를 자유롭게 갖다 쓰기에 좋을 것 같으므로 poetry 를 사용하지 않고 빌드해보자.
poetry 의존성 중 dev-dependencies 를 제외하고 requirements.txt 파일을 만든다.
poetry export --without-hashes --format=requirements.txt --without dev > requirements.txt
poetry를 사용하지 않도록 dockerfile을 수정한다.
FROM python:3.12.2-alpine
WORKDIR /app
COPY requirements.txt /app
RUN pip install --no-cache-dir --upgrade -r requirements.txt
COPY ./ /app
ENTRYPOINT ["uvicorn", "--host", "0.0.0.0", "--port", "8800", "--log-config", "docker_logconfig.py", "app.main:app"]
0.13GB 줄어들었다.
그럼에도 다른 fastapi 도커 이미지에 비해 굉장히 크다.
docker cp 명령어로 의도대로 컨테이너가 필요한 파일들만 갖고 동작하고 있는지 확인해보자.
docker cp app:./ ./yp
du ./yp
?
.dockerignore 에 .git을 추가하지 않았다..
모두 복사하는 과정에서 필요하지 않은 파일들이 파일 보기에서 숨겨져 있어 같이 컨테이너 안으로 복사 된 것이다.
1.95GB → 0.277GB 로 1.673 GB 감소하였다.
사실상 2.4GB 중 1.673 GB 는 .git 폴더의 필요없는 파일들의 크기였으므로, 727MB 이미지를 277MB 이미지로 만든 과정이다.