난 맥이라 AWS 인스턴스도 arm64로 설정했는데..
로컬 개발 환경이 mac 이기에 아키텍처 환경 통일을 위해 aws ec2 인스턴스도 arm64로 설정했다.. 그런데..
github action이 돌아가는 머신 -> amd64
amd64 에서 생성하는 이미지 -> x86 아키텍처
x86 이미지는 -> arm64에서 실행되지 않는 이슈에 봉착했다!
위와 같은 문제 트러블 슈팅 과정을 공유하고자 한다!
CI/CD 파이프라인 개요
우리가 구축할 CI/CD 파이프라인은 크게 세 가지 단계로 구성된다:
- Build & Test: 코드를 빌드하고 테스트
- Docker Image Build & Push: ARM64 아키텍처용 Docker 이미지를 빌드하고 Docker Hub에 푸시
- Deploy: 빌드된 이미지를 실제 서버에 배포
파이프라인 구현
Github Action 워크플로우 파일
먼저 .github/workflows 디렉토리에 워크플로우 파일을 생성하여 CI/CD 파이프라인을 구성한다.
아래는 전체 워크플로우 파일이다
name: CI/CD Pipeline
on:
pull_request:
branches:
- main
jobs:
build:
name: Build and Test
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Grant execute permission for Gradle
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build --no-daemon
docker:
name: Build and Push Docker Image
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and Push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/arm64
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/myapp:latest
cache-from: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/myapp:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/myapp:buildcache,mode=max
deploy:
name: Deploy to Server
runs-on: ubuntu-latest
needs: docker
steps:
- name: Test SSH Connection
uses: appleboy/ssh-action@v0.1.10
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
echo "SSH 연결 테스트 성공!"
- name: Deploy to Server via SSH
uses: appleboy/ssh-action@v0.1.10
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
docker pull ${{ secrets.DOCKER_USERNAME }}/myapp:latest
docker stop myapp || true
docker rm myapp || true
docker run -d --name myapp -p 80:8080 ${{ secrets.DOCKER_USERNAME }}/myapp:latest
이제 각 단계별로 자세히 살펴보자!
1. Build & Test 단계
첫 번째 단계는 소스 코드를 빌드하고 테스트하는 과정입니다. 이 예시에서는 Java 프로젝트를 Gradle을 사용하여 빌드합니다.
build:
name: Build and Test
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Grant execute permission for Gradle
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build --no-daemon
이 단계에서는:
- 최신 버전의 Ubuntu 러너를 사용
- 코드를 체크아웃
- JDK 17(테무린 배포판)을 설정
- Gradle 래퍼에 실행 권한을 부여
- Gradle을 사용하여 빌드 및 테스트를 실행
2. Docker Image Build & Push 단계
두 번째 단계는 ARM64 아키텍처용 Docker 이미지를 빌드하고 Docker Hub에 푸시하는 과정
docker:
name: Build and Push Docker Image
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and Push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/arm64
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/myapp:latest
cache-from: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/myapp:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/myapp:buildcache,mode=max
이 단계에서는:
- needs: build를 통해 이전 빌드 단계가 성공적으로 완료되어야만 실행
- QEMU를 설정하여 다양한 아키텍처의 Docker 이미지를 빌드
- Docker Buildx를 설정하여 다중 아키텍처 빌드를 지원
- Docker Hub에 로그인
- Docker 이미지를 빌드하고 푸시.
- platforms: linux/arm64를 통해 ARM64 아키텍처용 이미지를 빌드
- 레지스트리 캐시를 활용하여 빌드 속도를 향상
3. Deploy 단계
마지막 단계는 빌드된 Docker 이미지를 실제 서버에 배포하는 과정
deploy:
name: Deploy to Server
runs-on: ubuntu-latest
needs: docker
steps:
- name: Test SSH Connection
uses: appleboy/ssh-action@v0.1.10
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
echo "SSH 연결 테스트 성공!"
- name: Deploy to Server via SSH
uses: appleboy/ssh-action@v0.1.10
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
docker pull ${{ secrets.DOCKER_USERNAME }}/myapp:latest
docker stop myapp || true
docker rm myapp || true
docker run -d --name myapp -p 80:8080 ${{ secrets.DOCKER_USERNAME }}/myapp:latest
이 단계에서는:
- needs: docker를 통해 이전 Docker 이미지 빌드 단계가 성공적으로 완료되어야만 실행
- 먼저 SSH 연결을 테스트합니다.
- SSH를 통해 서버에 접속하여 다음 작업을 수행:
- 최신 Docker 이미지를 가져옴
- 기존 컨테이너를 중지하고 제거
- 새 컨테이너를 실행
ARM64 아키텍처 사용의 이점
ARM64 아키텍처로 애플리케이션을 빌드하고 배포하는 것은 다음과 같은 이점을 제공
- 성능 향상: ARM 프로세서는 특정 워크로드에서 x86 프로세서보다 더 높은 성능을 제공할 수 있다!
- 비용 절감: ARM 기반 인스턴스는 일반적으로 동등한 x86 인스턴스보다 저렴
- 에너지 효율성: ARM 프로세서는 더 낮은 전력을 소비하므로 환경 친화적
- 최신 기술 적용: AWS Graviton, Apple Silicon 등의 최신 ARM 기반 플랫폼을 활용
결론
이 글에서는 GitHub Action을 사용하여 ARM64 아키텍처용 CI/CD 파이프라인을 구축하는 방법을 살펴보았다.
하지만 위처럼 qemu buildx의 과정이 추가되기에 pipeline이 동작하는 시간이 상당히 오래걸린다..
이래서 Jenkins를 쓰는 가 싶다..
ARM64 아키텍처로의 전환은 클라우드 네이티브 애플리케이션의 미래를 위한 중요한 단계이다.
이 가이드가 여러분의 프로젝트에 도움이 되기를 바란다.
참고 자료
'DevOps' 카테고리의 다른 글
젠킨스 파이프라인에 소나 큐브 달기 드가자~ (0) | 2025.04.29 |
---|---|
자자 AWS 인스턴스에서 Jenkins + DockerContainer 돌리기 드가자~ (0) | 2025.03.28 |
젠킨스 서버가 죽었다.. (0) | 2025.03.28 |
Jenkins(젠킨스)는 도커 컨테이너로 돌리지 말자 (0) | 2025.03.21 |
개발 환경과 서버 환경의 아키텍처 일치성: 스프링 부트 관점에서 (0) | 2025.03.19 |