ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 도커 이미지 최적화 및 개선기
    개발 2024. 1. 26. 11:16

    최근에 프로젝트 하나를 마무리하였다. 많은 이슈들이 있었지만 가장 크게 영향을 받은 경험은 배포과정에서 일어난 도커 이슈였는데, 이 글에서는 프로젝트 진행 중 발생한 도커 배포 이슈와 이미지 최적화 작업에 대한 해결방안을 간단하게 작성해보려한다.

     

    들어가기전에

    최근에 마무리한 프로젝트는 홈페이지 리뉴얼에 포인트 기능을 추가한 서비스였다. 개발을 시작할때 쯤 새로운 기술을 사용해보고자하였고, 그때 당시에 최신 기술이었던 nextjs 12버전을 사용하게 되었다. 얼마 지나지않아 13버전이 나왔지만 안정성의 이슈 때문에 12를 사용하기로 했었고, 이 과정에서 ssg나 ssr 관련 기능을 사용하면서 방화벽 이슈나, 인증서 이슈등, 흔하게 마주하지는 않는 이슈를 맞닥뜨리게 되었다. 예상치 못한 이슈는 패키지매니저에서인데, yarn 1 버전을 사용하다가 후에 yarn berry로 업데이트하면서 이슈가 발생하게 된다..

     

    문제점

    사내 gitlab 버전 이슈

    현재까지 대부분의 사내 프로젝트들은 폐쇄망 내부의 구축형 gitlab안에 있었다.

    이는 시기에 따라 퍼블릭 환경에 비해 버전이 많이 낮을 수 있다는 것을 의미한다. 실제로 내부 버전이 낮기도 하였고, 폐쇄망의 네트워크 특성상 속도가 많이 느리기도 했다.

     

    가장 치명적인건 폐쇄망으로 인한 인증서 오류였다.

    nextjs 빌드과정 중 정적 페이지를 생성하는 절차가 있는데, 이때 도커 내부에서 빌드할때 외부 연결로 인식하는것인지 인증서 오류가 발생하여 빌드가 불가능해지는 이슈가 발생하였다.

     

    이리저리 헤메다가 gitlab-ci를 이용하여 서버내에서 직접 빌드하면 문제가 없어진다는 점을 확인하였고, 빌드한 결과물 폴더만 쏙 빼서 도커로 넘겨주는 방식을 채택하게 되었다.

    멀티 스테이지 빌드 이슈

    ✔ 멀티 스테이지 빌드란?
    도커의 이미지 빌드를 여러 단계로 나눠서 진행할 수 있는 이미지 빌드 방법이다.
    각 스테이지는 독립적으로 작동하며 이를 통해 최종 이미지의 크기를 줄이고 성능을 향상시킬 수 있다. 주로 빌드 도구나 컴파일러 등을 포함한 개발 환경을 구축하고, 최종 애플리케이션을 실행하는 등의 다양한 용도로 활용한다.

     

    필요없는 단계에서 생성된 파일들은 버리고, runner stage만 이미지로 받아서 실행시키면 되는 구조!

     

    도커에서 빌드를 한다면, 빌드 결과물만 챙기고 나머지는 버리는게 일반적으로 나은 선택지일것이다.

    도커의 멀티 스테이지 빌드를 사용한다면 이 방식을 사용하는것이 이상적이지만..

    앞서말한 빌드 이슈도 있고, 무엇보다 사내 깃랩의 버전이 낮다보니 지원되는 도커 버전도 현저하게 낮아 멀티 스테이지 빌드를 사용할 수 가 없는 상황이었다

    nextjs + yarn berry

    nextjs에서 빌드된 결과물은 .next 폴더에 모이게된다.

    이 빌드 결과물을 원래는 vercel에 올림으로 편하게 배포를 할 수 있으나 별도로 직접 올릴 수 있도록 vercel에서는 standalone 모드를 제공한다. 폐쇄망을 사용하는 우리도 마침 서버에 직접 빌드해서 배포해야하는 상황이었고 당연히 standalone 폴더만 가져가서 배포를 하면 된다고 생각했다.

    그런데 이게 안되는것이다.. 대체 왜..

     

    원인을 파악해보고자 에러 로그들을 하나씩 트레이스해가자 보인것은 의존성 문제였다.

    앞서 말한 yarn 버전을 업그레이드 하는 과정에서 yarn의 의존성 저장 매커니즘이 바뀌게 되는데, yarn-berry에서 사용하는 의존성은 cache에 저장이 되고, 이 cache들은 nextjs의 빌드 결과물 내부에도 저장이 된다. 그럼에도 프로덕션 실행시 별도의 의존성을 접근하려하는것이 문제였다.

    이상하게도 스택오버플로우엔 이러한 경우가 많이 없었는지 레퍼런스가 턱없이 부족하였고..

    부족한 레퍼런스보다 더 촉박한 일정은 결국 의존성 캐시폴더를 통채로 챙겨감으로 이 이슈를 대충 덮고 나중을 기약하게 만들었다.

    너무 큰 용량

    프로덕션 빌드를 한다면 package.json 내부에 개발용 의존성은 필요가 없으니 설치하지않아야하는 것이 아닌가??

    원인을 알 수 없었지만.. package.json에 있는 devDependency들도 전부 들고 프로덕션으로 인스톨하는것을 확인했다.

    도커 버전 이슈와 통짜 프로젝트의 도커라이징,

    위의 이슈들로 인한 스노우볼은 1.2기가에 달하는 이미지에 도달하기에 이르렀다.

    대체 뭐가 모여서 1.2기가 까지 된걸까..

    큰 용량은 곧 큰 빌드 시간, 큰 다운로드 시간을 의미하고, 이는 배포 과정에 큰 영향을 끼칠수있음을 의미한다.

    오타난게 뒤늦게 발견되서 그거 하나 고쳐서 재배포하려면 10분대기..

     

    해결과정

    위의 문제점들로 인해 한번 배포할때마다 짧게는 9분, 길면 12분넘게 기다려야하는 상황들이 매번 있었다. 애초에 환경적인 이슈들이 바뀌기 힘들었기 때문에 어떡하나 고민만 하던 참에 사내 플랫폼을 폐쇄망 깃랩에서 퍼블릭 깃허브로 옮기려는 시도를 하고 있었기에 가장 먼저 마이그레이션을 진행할 수 있었고, 이를 통해 앞서 말한 문제점들이 조금씩 해결되기 시작했다!

    플랫폼의 마이그레이션.

    어떻게 보자면 기술적인 요소라기보다는 단순해보일 수 있지만, 사내 깃랩의 버전이 너무 낮았기도 하고, 폐쇄망에서 퍼블릭으로 옮기는 셈이다보니 이것만으로도 접근시간과 빌드시간이 줄어드는 효과가 있었다. gitlab의 gitlab-ci는 github의 workflow로 대체하였고, 태깅 트리거나 노티 등 다양한 기능을 추가하기 위해 많은 시행착오가 있었다. 이번 포스트에서는 도커의 최적화가 주제이기에 자세하게 적진 않지만 이 마이그레이션 과정도 언젠가 한번 작성해보려한다.

    멀티 스테이징 빌드 사용

    도커를 최신버전으로 올림으로써 멀티스테이징 빌드를 사용할 수 있게 되었다. 도커 내부에서 인스톨과 빌드를 진행할 수 있도록 하였고, 빌드 이후 러너 전용 이미지를 분리함으로 빌드 완료 후의 용량을 크게 줄일 수 있었다.

    정말 좋은점은, 도커 내부에서 각 레이어별로 캐싱처리가 된다는 점이었다. 즉, 인스톨 시점에서 패키지 파일이 크게 바뀌지않는다면 캐싱처리된 레이어를 사용하여 속도를 대폭 줄일수있는 것이다!

    도커 전용 패키지 파일을 사용 (프로덕션용 yarn cache 최적화)

    next 빌드시 .standalone 폴더에 필요한 패키지가 전부 인스톨 되는데도 불구하고, standalone 외부에 있는 일부 패키지를 가리키는 문제를 해결하기 위해서 단순한 방법을 선택했다. 그냥 바깥에 있는 캐시 폴더를 전부 갖고가는 것이다.

    그러나 이러한 방식을 불필요한 devDependency도 같이 가져가는것을 의미하고, 이로 인해 상당히 큰 용량과 기나긴 인스톨 시간이 소요될 수 있기 때문에 devDependency를 뺀 docker 빌드 전용 package.json 파일을 별도로 만들어서 작업하였고, 이것만으로도 용량과 시간을 크게 줄일 수 있었다. (이 방법을 제안한 분도 딱히 마음에 들진않지만 이게 최선같다고 하시더라..)

    개발할때는 왼쪽의 개발용 package.json을 사용하고, 도커 빌드할땐  오른쪽의 프로덕션용 package.json를 사용하여 빌드한다.

    결과

    빌드시간 단축 확인
    빌드 용량 축소 확인

    이미지 용량은 이전에 비해 1.2GB에서 389.56MB로 대폭 줄일 수 있었다. 이는 빌드된 결과물에 불필요한 파일, 패키지등을 최대한 줄여서 저장할 수 있었기 때문이었고, 이렇게 용량을 줄이는 것은 곧 배포 시간과 다운로드 시간을 단축시키는데에도 도움이 될 수 있었다.

    개선된 빌드시간은 이전에 비해 7~12분에서 4~6분으로 줄었다! 도커에서 빌드를 진행하면서 레이어별 캐싱처리도 되었는지 빠르면 4분, 느려도 6분안에 빌드가 완료될 수 있었다.

    마치며

    이러한 작업들을 통해 도커 이슈를 해결하고 프로젝트의 배포 과정을 더욱 효율적으로 만들 수 있었다.

    최적화 작업을 진행하는것이 처음이기도 하였기에 더욱 어렵게 느껴지지않았나 싶다. 도커도 잘 모르던 때에 맨땅에 헤딩하는 느낌으로 작업했기에 우리같은 경험을 하고있는 개발자분들이 있다면 이 글이 도움이 되었으면 한다.

    '개발' 카테고리의 다른 글

    Base64 에 대해서  (0) 2024.06.27
    NextAuth의 로그인 과정을 확인해보자 (SNS 로그인)  (2) 2023.12.06
    var, let, 그리고 const  (1) 2021.11.15
Designed by Tistory.