이미지 빌드와 애플리케이션 구성

JaeYeon·2025년 5월 6일
post-thumbnail

해당 포스팅은 개발자를 위한 쉬운 도커 를 수강하고 작성한 포스팅입니다.

우리가 사용하는 이미지는 어떤 형식으로 구현되어 있을까?
오늘은 이미지의 빌드에 대해서 알아보자

이미지 레이어

이미지는 하나의 압축파일이고,
그 안은 다양한 레이어를 통해 구현되어있다.

레이어를 통해 구현함으로써, 우리는 레이어의 재사용성과 공간 활용성을 높일 수 있다.
이는 곧 스토리지와 네트워크 사용량을 절약 할 수 있다는 큰 장점을 지니게 된다.

그리고 이러한 이미지는 반드시 불변해야한다. 그래야 다양한 레이어를 재사용하는데에 있어서 무결함을 보장받을 수 있다.

레이어에 대해서 알아보니까 이것도 완전 객체지향..


그러면 이미지의 내용을 바꿔서 쓰고 싶으면 어떻게 해야할까?
사실 대부분의 상황에서 우리는 이미지를 그대로 사용하지 않고 약간의 변형을 준다.

그 방법이 컨테이너 레이어를 수정하는 것이다

우리가 무언가를 수정하면 수정되는 레이어가 컨테이너 레이어에 복사된다. 그리고 그 레이어에서 수정이 일어나는 것이다.

nginx의 다양한 수정 버전의 이미지 레이어를 조회한 결과 (조회는 docker hisotry 를 통해 할 수 있다)

결국 이미지 레이어는 위와 같은 특징을 기반으로 큰 강점을 지니는 것이다. 개인적으로 캐싱기능이 매우 매력적이라고 생각한다.
비슷한 기능을 하는 이미지를 받을 때, 매번 새롭게 받으면 무거우니까 이미 존재하는 레이어에 한 해서는 다운을 생략하는 캐싱기능이 성능을 크게 올려줄 수 있을 것 같다.

이미지 빌드

우리는 컨테이너 레이어를 통해 기존의 불변하는 이미지레이어를 수정 할 수 있다고 배웠다. 그러면 그렇게 수정한 컨테이너를 저장하는 것을 어떻게 할까?

이러한 과정을 빌드라고한다. 컨테이너 레이어에서 수정된 부분까지 포함하여 하나의 이미지로 압축하는 것이다.

이 과정에는 커밋 방법과 빌드 방법이 있는데, 대부분은 빌드를 사용하지만 이 또한 커밋의 과정을 편리하게 만든 방법이기에, 커밋을 이해하는 것이 중요하다.

이미지 커밋

이미지 커밋은 기반이 되는 이미지를 컨테이너에서 실행하고, 원하는 방식으로 수정하여 컨테이너 레이어를 구축한 뒤에, commit 을 통해 새로운 이미지로 저장하는 방법이다.

(앞부분의 이미지를 수정하는 과정은 생략)
하지만 커밋을 위한 코드를 보면

너무 길어
너무 길다. 심지어 앞의 이미지를 수정하는 과정도 파일 붙여넣기와 같은 방식으로 실행되기 때문에, 불필요하게 반복적이고 길다.

이전의 이미지도 손수 삭제해줘야하고
이러한 불필요하게 긴 절차는 휴먼에러를 발생시킨다.

이미지 빌드

그래서 나온 방식이 이미지 빌드 방식이다.

그 전에 IaC에 대해서 알고 넘어가자면, 현대의 인프라를 구현하는데에 있어서 핵심적인 개념이다.
인프라를 구축하는 과정을 사람이 하는게 아니라 코드 중심적으로 구현하자는 원칙.
우리가 했던 과정들을 코드로서 명세서로 구현하여 이를 관리하자는 것이다. 그리고 바로 그 명세서가 Dockerfile이 된다.

해당 이미지로 간단하게 보자

FROM 을 통해서 기반이 되는 이미지를 불러온다
그러면 도커 데몬은 이를 통해 컨테이너를 생성하고

COPY를 통해 수정이 필요한 파일을 수정한다.
CMD를 통해 이미지 실행시 호출될 명령어를 정의한다.
이때 데몬은 변경사항을 저장하고 커밋한다

그리고 모든게 끝나면 컨테이너를 삭제하고 이미지를 생성한다.

사실 커밋과 같은 방법인 것 같다. 이를 코드로서 표현했을뿐.


도커 이미지를 빌드하는 명령어는 다음과 같다.
dockerfile을 통해 이미지를 생성하기 때문에 반드시 Dockerfile이 존재하는 경로에서 빌드가 되어야한다.

그래서 Dockerfile의 위치가 매우 중요하다. Dockerignore의 기능이 있긴하지만 그렇게 관리하기 보단, 필요한 파일들을 모아놓고 해당 파일의 최상단에 Dockerfile을 두어야 관리가 쉬울 것이다.

빌드 컨텍스트

구현해놓은 Dockerfile에 대한 정보를 Docker demon 프로세스에게 전달하는 역할을 빌드 컨텍스트가 수행한다.

도커의 기본 지시어

아무래도 기본 지시어다 보니, 헷갈렸던 부분 위주로 정리해보았다.

FROM과 COPY의 경우에는 어느정도 이해가 됐는데,
RUN과 CMD의 차이를 모르겠네

전에 내가 쓰던 CMD는

CMD ["java", "-Dspring.profiles.active=dev", "-jar", "app.jar"]

이런 모습이었다.
사실 내가 빌드된 파일을 실행할때에는 저런 명령어를 사용한 적이 없는데 왜 저런게 이미지를 실행할때 호출되는걸까?

이것은 나의 무지함
./gradlew 로 항상 실행을 하였지만 우리가 일반적으로 사용하는 도커 파일에서는 가벼운 자바 기본이미지 + jar 압축파일만 사용한다.
-> gradle 파일을 가져오지 않기 때문에 위와 같이 app을 실행 할 수 없다.

그래서 자바를 실행하는 다른 커맨드라인 인자방식 명령어인

java -jar your-app.jar --spring.profiles.active=dev

을 사용하는 것이다.


또한 RUN과 CMD의 차이도 애매하였다. 그래서 구글링을 해봤더니 역시 나만 모르는게 아니구나...싶을 정도로 자료가 많았는데,

그 둘의 차이를 간단하게 정리하자면 RUN은 이미지를 빌드하는데에 사용되는 명령어였다. 다른 라이브러리나 설정파일을 추가하고 이미지를 빌드할때 사용하는 명령어가 RUN이고,
CMD가 이미지가 실행될때 호출되는 명령어라는 것을 확인하였다.

즉, 우리가 일반적으로 생각하는 이미지가 실행될때 기대하는 명령어는 CMD 였다.


다음은 이러한 명령어가 존재하였는데,
WORKDIR이 또 이해가 잘 안됐다. 근데 돌아보면 간단하다.

내가 자바를 실행하기 위해서 필요한 다양한 설정파일들이 root 경로가 아닌 WORKDIR로 지정된 경로에서 실행된다고 이해 할 수 있었다.

그 밖에도 arg,env,entrypoint와 같은 지시어도 존재하였다.
개인적으로 arg와 env를 잘 사용하지 않는게 좋을 것 같다고 생각하였다.

환경변수가 한두개인 상황이야 뭐 괜찮겠지만 다양한 환경변수를 사용하는 환경에서는 휴먼에러를 발생시킬 수 있다고 생각이 들정도로 실행문이 복잡해졌다.

멀티 스테이지 빌드

멀티 스테이지 빌드는
이미지를 빌드하고 실행하는 두 과정을 하나의 이미지에서 실행하려다보니 기본 이미지가 무거워지는 상황을 방지하기 위해,

빌드를 전용으로 하는 이미지와 실행을 전용으로 하는 작은 이미지 두 개를 사용하자는 전략이다.

처음에는 뭐 비슷하지 않나 싶었지만 실제로 차이가 크다는 것을 실습을 통해 확인해보니 좋은 전략이 될 수 있구나 라는 생각을 해봤다.

나의 경우에는 빌드가 이미 끝난 jar 파일을 한 번에 올리는 방식을 택했기 때문에 빌드를 하는 과정을 따로 구축 할 필요가 없었지만,
그러한 과정이 없는 서비스라고 한다면 큰 성능 최적화가 있을 것이라고 생각하였다.

클라우드?

우리의 현대사회에서 서버가 필요하면 컴퓨터를 삽니까?

누군가는 살 수 있겠지만 대부분의 사용자는 클라우드 시스템을 적극 활용한다.

이러한 클라우드는

  • 퍼블릭 클라우드
  • 프라이빗 클라우드

로 나뉘어진다. 그래서 사용자는 필요할때 서버를 provistioning하고, 실제 사용한 시간만큼의 비용을 지불한다.

이러한 클라우드 시스템은 위와 같은 다양한 장점을 지니지만, 클라우드 시스템은 그 자체로는 완성되지 못한다.

해당 시스템은 다양한 application system이 뒷받침 될때 완성되고, 그를 위해 떠오른 개념이 cloud native 이다.

클라우드 네이티브

cloud native application 이란 클라우드 환경을 더 잘 사용 할 수 있도록 구현한 애플리케이션 구조를 의미한다.


그를 위한 조건에는 위와 같은 것들이 있다. 다들 비교적 직관적인 개념이지만, MSA에 대해서 좀 더 알아보자


우리가 일반적으로 사용하는 아키텍쳐는 모놀리식 아키텍쳐로, 하나의 서버에 모든 서비스 트래픽을 부담하는 아키텍쳐이다.

하지만 이렇게 구성했을 시, 확장에 불리하고 로드밸런싱을 할 수 없다.

그러한 단점을 보완하기 위해 도메인 등을 분기점으로 서버를 분산하여 필요한 서버를 scale out하고 같은 통신망 내에서의 서버간 통신으로 로드밸런싱을 할 수 있도록 구현한 아키텍쳐가 MSA이다.

MSA를 구현하기 위해서 우리는 컨테이너 기술을 적극 활용 할 수 있다.

컨테이너 애플리케이션 구성

MSA를 실습 예제로 구현하기엔 다소 헤비하다보니,
3 tier archotecture를 구현해보았는데 딱히 기억에 남는건 음.....

그건 기억납니다.

  1. COPY를 잘 사용한다면, 굳이 mysql이나 psotgres와 같은 데이터베이스나 다양한 이미지를 굳이 다운로드 받지 않고도 이미지를 실행함으로써 사용 할 수 있다.
  2. 도커 실행 명령어를 직접 치는건 정말 힘든일이다

오류가 많이 나더라구요..........

profile
내가 세상에서 개발 제일 좋아해

0개의 댓글