GitHub Actions Matrix Strategy & Multi Architecture Build
지난 글(GitHub Actions 소개 및 구성 요소)에서 GitHub Actions의 기본 개념과 구성 요소에 대해 살펴보았습니다. 이번에는 Matrix Strategy라는 기능에 대해 알아보고 해당 기능을 활용해 여러 플랫폼 또는 환경에서 병렬로 작업을 수행하여 CI/CD 파이프라인의 성능을 향상시키는 방법에 대해 알아보도록 하겠습니다.
Matrix Strategy란?
GitHub Actions의 Matrix Strategy는 동일한 작업(Job)을 다양한 환경이나 변수의 조합으로 병렬 실행할 때 사용됩니다. 예를 들어 여러 운영체제(OS)나 아키텍처(arm64, amd64) 환경에서 병렬적으로 테스트하거나 빌드할 수 있습니다. 이를 통해 여러 잡을 순차적으로 진행하거나 하나의 아키텍처에서 QEMU를 사용해 Multi-Architecture Build를 수행하는 방식보다 전체 워크플로우의 수행 시간을 크게 단축시킬 수 있습니다.
언제 유용할까?
- Cross‑Platform 테스트: macOS, Windows, Linux 전부 지원해야 할 때.
- 멀티 아키텍처 빌드: x86과 ARM 이미지를 동시에 만들어야 할 때.
- 다양한 언어/버전 검증: Node.js 18/20처럼 여러 런타임 버전을 테스트할 때.
PoC - Matrix 전략을 사용한 병렬 빌드 성능 비교
amd64와 arm64 이미지를 빌드 해 ECR에 Push하는 Workflow를 Matrix Strategy를 사용하지 않고, 하나의 러너(amd64)에서 진행했을 때와, Matrix Strategy를 사용해 두 개의 러너에서 병렬로 진행했을 때 빌드 시간을 비교해보며 PoC를 진행했습니다.
비교 대상
- 단일 러너(amd64)에서 QEMU 에뮬레이션으로 멀티 아키텍처 빌드.
- Matrix Strategy로 amd64·arm64 전용 러너에서 네이티브 빌드 후 Manifest 병합.
[Environment]
OS - Ubuntu 24.04 (amd64, arm64)
Language - Go
Framework - Gin-Gonic
Build-Tool - Docker Buildx
Virtual Environment - QEMU
Container Registry - ECR(Elastic Container Registry)
1.1 Workflow - 단일 러너(amd64), Build & Push Multi-Arch Using QEMU to ECR
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# .github/workflows/go-multi-architecture-build.yaml
name: "[Go] Gin Project Build & Push Multi-Arch Using QEMU to ECR"
# main 브랜치의 go path에 푸시될 때 실행
on:
push:
paths:
- go/**
branches:
- main
workflow_dispatch:
# OIDC를 이용해 AWS 역할을 가져오기 위해 id-token 권한 필요
permissions:
contents: read
id-token: write
jobs:
build-and-push:
name: Build & Push to ECR
runs-on: ubuntu-24.04
steps:
# 1. 코드 체크아웃
- name: Checkout code
uses: actions/checkout@v3
# 2. QEMU 에뮬레이션 등록 (Multi Architecture, Buildx 사용 시 필요)
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
# 3. Buildx 빌더 세팅
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# 4. AWS 자격 증명 구성 (OIDC 방식)
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: $
aws-region: $
# 5. ECR 로그인
- name: Login to Amazon ECR
id: login-ecr
run: |
aws ecr get-login-password \
--region $ \
| docker login \
--username AWS \
--password-stdin $.dkr.ecr.$.amazonaws.com
# 6. 멀티 아키텍처 이미지 빌드 & 푸시
- name: Build and push multi-arch image
uses: docker/build-push-action@v5
with:
context: ./go
file: ./go/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: |
$.dkr.ecr.$.amazonaws.com/$:latest
$.dkr.ecr.$.amazonaws.com/$:$
1.2 결과 확인 - 단일 러너(amd64), Build & Push Multi-Arch Using QEMU to ECR
Container Image 빌드 및 ECR Push 등 전체 Workflow가 완료되기까지 총 4분 57초가 소요되었습니다.
자세히 확인해보니 amd64 아키텍처 빌드는 25.8초가 걸린 반면, arm64 아키텍처 빌드는 무려 241.4초가 걸려 약 9배 이상의 차이가 있었습니다.
1
2
3
4
5
6
7
8
9
10
11
#16 [linux/amd64 builder 6/6] RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="-s -w" -o server .
#16 DONE 25.8s
#19 [linux/amd64 stage-1 1/1] COPY --from=builder /app/server /server
#19 DONE 0.0s
#18 [linux/arm64 builder 6/6] RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="-s -w" -o server .
#18 DONE 241.4s
#20 [linux/arm64 stage-1 1/1] COPY --from=builder /app/server /server
#20 DONE 0.0s
QEMU 사용 시 빌드 속도가 느린 이유
QEMU를 사용한 Multi-Architecture Build 방식은 다른 아키텍처의 바이너리를 에뮬레이션하여 실행하기 때문에 Native 환경에서 빌드하는 것보다 성능이 크게 저하됩니다. 특히 ARM64 바이너리를 AMD64 환경에서 에뮬레이션할 때 CPU 명령어 및 메모리 처리 속도가 현저히 낮아져 빌드 시간이 대폭 증가합니다.
https://docs.docker.com/build/building/multi-platform/#qemu
2.1 Workflow - 다중 러너(amd64, arm64), Build & Push Multi-Arch Using Matrix to ECR
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
name: "[Go] Gin Project Build & Push Multi-Arch Using Matrix to ECR"
on:
push:
paths:
- go/**
workflow_dispatch:
permissions:
contents: read
id-token: write
jobs:
# ──────────────────────────────────────────────────────────────
# 1) 아키텍처별 이미지 Build & Push
# ──────────────────────────────────────────────────────────────
build-and-push:
name: Build & Push ($)
strategy:
matrix:
include:
# AMD64용 러너
- arch: amd64
runner: ubuntu-24.04
# ARM64용 러너
- arch: arm64
runner: ubuntu-24.04-arm
runs-on: $
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: $
aws-region: $
- name: Login to Amazon ECR
run: |
aws ecr get-login-password --region $ \
| docker login --username AWS --password-stdin \
$.dkr.ecr.$.amazonaws.com
# 0. 이번 실행의 **버전 번호(0-base)** 계산
# 버전 태그 설정
- name: Set version tag
id: ver
run: echo "VER=$((GITHUB_RUN_NUMBER-1))" >> "$GITHUB_OUTPUT"
# 아키텍처별 단일 이미지 빌드 & 푸시
- name: Build & push image ($)
uses: docker/build-push-action@v5
with:
context: ./go
file: ./go/Dockerfile
platforms: linux/$ # Native 단일 플랫폼 빌드
push: true
tags: |
$.dkr.ecr.$.amazonaws.com/$:$-v$
# ──────────────────────────────────────────────────────────────
# 2) 다중 아키텍처 Manifest 생성
# ──────────────────────────────────────────────────────────────
create-manifest:
name: Create & Push Multi-Arch Manifest
needs: build-and-push
runs-on: ubuntu-24.04 # manifest 작업은 아무 러너에서나 가능 (x64 사용 예시)
steps:
- name: Set version tag
id: ver
run: echo "VER=$((GITHUB_RUN_NUMBER-1))" >> "$GITHUB_OUTPUT"
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: $
aws-region: $
- name: Login to Amazon ECR
run: |
aws ecr get-login-password --region $ \
| docker login --username AWS --password-stdin \
$.dkr.ecr.$.amazonaws.com
- name: Create & push multi-arch manifest
run: |
IMAGE=$.dkr.ecr.$.amazonaws.com/$
VER=$
docker buildx imagetools create \
--tag $IMAGE:latest \
--tag $IMAGE:v$VER \
$IMAGE:amd64-v$VER \
$IMAGE:arm64-v$VER
2.2 결과 확인 - 다중 러너(amd64, arm64), Build & Push Multi-Arch Using Matrix to ECR
단일 러너(amd64)에서 Multi-Architecture Build 하는 Workflow를 실행했을 때 4분 57초가 소요된 반면, Matrix Strategy를 사용해 다중 러너(amd64, arm64)에서 각각 Native하게 빌드 했을 때, 각각의 Manifest를 합치는 Job이 추가되었음에도 불구하고 Workflow 실행 시간이 1분 32초 밖에 소요되지 않았습니다. 결과적으로 약 70%정도 빌드 시간을 단축시킬 수 있었습니다.
마무리
GitHub Actions의 Matrix Strategy를 활용하면 Multi-Architecture, Multi-Platform 빌드가 포함된 워크플로우의 실행 시간을 효과적으로 단축할 수 있습니다.
Multi-Architecture 또는 Multi-Platform을 지원하는 컨테이너 이미지를 사용하거나, 꼭 순차적으로 실행되어야하지 않아도 되는 OS 및 패키지 조합별 취약점 스캔 등의 시간을 줄이고자 할 때 도입을 고려해보는건 어떨까요?.
궁금하신 점이나 추가해야 할 부분은 댓글이나 아래의 링크를 통해 문의해주세요.
Written with KKam._.Ji