✍ Posted by Immersive Builder Seong
컨테이너를 활용하여 어플리케이션 개발하기
Python으로 특정 문자열 출력
"Hello Docker"라는 간단한 문자열을 출력하는 파이썬 코드를 작성하고 컨테이너 이미지를 빌드합니다.
빌드한 이미지를 기반으로 컨테이너를 실행하여 출력 결과를 확인합니다.
코드를 수정할 경우 다시 빌드를 진행해야 수정한 내용이 반영됩니다. 이 때, 컨테이너 이미지에 태그를 달아 버전을 관리할 수 있습니다. 가장 최근에 빌드한 이미지에 hello:latest 태그를 추가하여 최신 버전을 구분하기도 합니다.
Compiling code in Docker
이번에는 자바 코드를 컴파일하여 동일한 문자열을 출력하는 컨테이너 이미지를 빌드해보겠습니다.
컨테이너 이미지 내부를 살펴보면 컴파일된 파일 외에 소스코드, 도커 파일이 포함되어 있습니다.
그리고 자바 컴파일러(javac)도 포함되어 있습니다. 실제 어플리케이션 실행에 소스코드, 도커 파일, 컴파일러 등은 필요가 없습니다. 오히려 소스코드가 외부에 노출되고 컴파일러 등을 통해 조작할 여지가 생기게 됩니다.
Compiling code with a multi-stage builds
위와 같은 문제를 해결하기 위해서 등장한 개념 중 하나가 멀티 스테이지 빌드입니다.
멀티 스테이지 빌드(multi-stage builds)란 도커 이미지 빌드 과정에서 하나의 도커파일에 여러 개의 빌드 스테이지를 사용하여 최종 이미지를 생성하는 방법입니다. 아래 도식은 빌드 컨테이너와 런타임 컨테이너 두 개의 스테이지로 구분하여 소스 컴파일은 빌드 컨테이너에서 진행하고, 컴파일된 바이너리 파일만 복사하여 런타임 컨테이너에 구성한 예시입니다.
▶ 멀티 스테이지 빌드란 : https://docs.docker.com/build/building/multi-stage/
아래와 같이 도커 파일에 여러 개의 FROM 지시자를 사용하여 빌드 스테이지를 구분합니다. 첫 번째 스테이지는 openjdk:11 이미지를 베이스로 사용하며, 컴파일된 아티팩트를 AS 문을 통해 buildstage로 지정합니다. 두 번째 스테이지는 openjdk:11-jre-slim 이미지를 베이스로 사용하고, 지정한 buildstage로부터 필요한 파일만 선택적으로 복사하여 최종 이미지를 구성합니다.
그런 다음 컨테이너 이미지를 빌드하고 실행해보면 컨테이너 내부에는 오로지 바이너리 파일만 존재합니다. 또한, 컴파일 명령어(javac)도 실행되지 않음을 확인할 수 있습니다. 이렇게 어플리케이션 실행에 필요한 파일만 최종 이미지에 구성함으로써 이미지를 경량화할 수 있고, 소스코드를 외부에 노출하지 않음으로써 보안을 향상시킬 수 있습니다.
Jib로 자바 컨테이너 빌드
Jib는 자바 어플리케이션을 도커 파일 없이 빌드할 수 있는 자바 컨테이너 이미지 빌드 도구입니다. Maven 또는 Gradle용 Jib 플러그인을 추가하거나 Jib 자바 라이브러리를 통해 사용할 수 있습니다. Jib는 어플리케이션을 컨테이너 이미지로 패키징하는 모든 단계를 처리합니다. 따라서 도커 파일을 작성하거나 도커를 설치하기 위한 권장사항을 알 필요가 없습니다. 예를 들어 젠킨스 컨테이너 내부에 도커를 설치하지 않아도 이미지를 빌드할 수 있습니다.
※ Docker와 Jib 빌드 흐름 비교
- Dokcer 빌드 흐름 : 도커 파일 작성 - 이미지 빌드 - 저장소에 이미지 푸시
- Jib 빌드 흐름 : 빌드 / 이미지 빌드 / 저장소 이미지 푸시 ⇒ 한 번에 처리
Jib는 어플리케이션을 종속 항목, 리소스, 클래스 등 별개의 레이어로 구성하고 도커 이미지 레이어 캐싱을 활용하여 변경사항만 다시 빌드함으로써 빌드를 빠르게 유지합니다. Jib 레이어 구성과 기본 이미지는 전체 이미지 크기를 작게 유지하여 성능과 이식성을 향상시킵니다.
▶ Jib 란 : https://github.com/GoogleContainerTools/jib
Containerizing an application server
어플리케이션 서버를 컨테이너 이미지로 빌드하면 어떨까요?
80 포트로 리스닝하고 현재 날짜와 시간을 반환하는 웹 서버를 빌드하고 컨테이너로 실행합니다.
로컬호스트 8080 포트로 접속하면 현재 날짜와 시간이 정상적으로 출력됩니다.
실행 중인 컨테이너에 진입하여 시간 정보와 호스트네임이 함께 출력되도록 코드를 수정해보겠습니다.
그러나 수정한 코드는 실행 중인 프로세스에 반영되지 않습니다.
로컬에서 수정한 코드를 기반으로 컨테이너 이미지를 다시 빌드하고 실행해야 수정 사항이 반영됩니다.
따라서 개발 단계에서 수정 사항이 발생할 때마다 이미지를 빌드하고 실행하는 일련의 과정을 어떻게 자동화할지 고민이 필요합니다.
Using Docker Compose for local testing
개발의 편리성을 위해 호스트 디렉토리를 컨테이너에 볼륨 마운트하여 코드의 변경 사항을 동적으로 반영할 수 있습니다.
호스트 파일에 변경 사항 발생 시 리로드(reload)할 수 있는 라이브러리를 여러 언어(프레임워크)에서 지원하고 있습니다. 파이썬의 경우, 리로딩(reloading) 라이브러리를 사용하여 GET 기능으로 디스크부터 리로드하게 됩니다.
아래는 타임 서버를 컨테이너로 실행하되, 리로딩 라이브러리를 적용한 예시입니다. 도커 파일에서 파이썬 리로딩 라이브러리를 설치(RUN pip install reloading)하고, 파이썬 코드(server.py)에서 전처리문으로 리로딩 라이브러리를 호출(from reloading import reloading)하고 있습니다.
그리고 여러 컨테이너를 함께 정의하고 관리하기 위해 Docker Compose를 사용한 부분과 해당 설정 파일(docker-compose.yaml)에 PYTHONDONTWRITEBYTECODE 환경 변수를 1로 설정하여 파이썬이 소스를 리로드할 수 있도록 구성한 모습입니다.
호스트와 컨테이너 간에 볼륨이 공유된 상태에서 호스트의 소스코드를 수정하면 수정 사항이 자동으로 컨테이너의 프로세스에 반영됩니다. 이처럼 호스트 볼륨을 컨테이너에 마운트하는 방식으로 개발 단계에서의 잦은 수정에 따른 번거로운 작업들을 생략할 수 있습니다.
Build multi-platform images
빌드 환경의 CPU 아키텍처와 다른 아키텍처 기반의 이미지를 빌드할 수 있습니다. 예를 들어 빌드를 진행하는 로컬 환경이 x86_64 아키텍처일지라도 ARM 환경을 에뮬레이션하여 arm64 아키텍처 기반의 이미지를 빌드할 수 있습니다. 아래와 같이 buildx 명령어와 --platform 옵션을 사용하여 멀티 플랫폼 이미지 빌드가 가능합니다.
$ docker buildx create --name builder --driver docker-container --use
$ docker buildx build --platform linux/amd64,linux/arm64 --push -t okms1017/myweb:multi .
▶ 멀티 플랫폼 빌드란 : https://docs.docker.com/build/building/multi-platform/
WSL에서 멀티 플랫폼 이미지 빌드를 진행해보겠습니다.
도커 허브에 amd64와 arm64 아키텍처 기반의 이미지가 업로드된 것을 확인할 수 있습니다.
[출처]
1) CloudNet@, CI/CD Study
'CI·CD' 카테고리의 다른 글
[CI/CD] GitHub Actions CI/CD Workflow (2) | 2024.12.15 |
---|---|
[CI/CD] 도커 기반 어플리케이션 CI/CD 구성하기 (0) | 2024.12.12 |
[CI/CD] Jenkins 기본 사용하기 (1) | 2024.12.08 |
[CD/CD] CI/CD 실습 환경 구성하기 (1) | 2024.12.08 |