본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 11. 28. · 18 Views
Docker 이미지 빌드와 배포 완벽 가이드
GitHub Actions를 활용한 Docker 이미지 빌드부터 배포까지의 전 과정을 다룹니다. 컨테이너 레지스트리 연동, 멀티 플랫폼 빌드, 보안 스캔까지 실무에서 바로 적용할 수 있는 CI/CD 파이프라인을 구축해봅니다.
목차
1. docker/build-push-action
김개발 씨는 오늘도 새벽까지 열심히 개발한 애플리케이션을 서버에 배포하려고 합니다. 매번 수동으로 Docker 이미지를 빌드하고 푸시하는 작업이 너무 번거롭습니다.
"이걸 자동화할 방법이 없을까?" 고민하던 중, 선배 박시니어 씨가 다가와 말합니다. "GitHub Actions의 docker/build-push-action을 써보세요."
docker/build-push-action은 GitHub Actions에서 Docker 이미지를 빌드하고 레지스트리에 푸시하는 공식 액션입니다. 마치 자동화된 공장의 컨베이어 벨트처럼, 코드가 푸시되면 자동으로 이미지를 만들어 원하는 저장소로 보내줍니다.
이 액션 하나로 복잡한 Docker 빌드 과정을 워크플로우에 간단히 통합할 수 있습니다.
다음 코드를 살펴봅시다.
name: Build and Push Docker Image
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
# 저장소 코드를 체크아웃합니다
- name: Checkout repository
uses: actions/checkout@v4
# Docker Buildx를 설정합니다
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# 이미지를 빌드하고 푸시합니다
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: myapp:latest
김개발 씨는 입사 6개월 차 백엔드 개발자입니다. 새로 개발한 API 서버를 Docker 이미지로 만들어 배포해야 하는데, 매번 로컬에서 docker build와 docker push 명령어를 실행하는 것이 너무 번거로웠습니다.
"어제도 빌드하다가 실수로 태그를 잘못 붙였다가 롤백하느라 한 시간을 날렸어요." 김개발 씨가 한숨을 쉽니다. 박시니어 씨가 화면을 가리키며 말합니다.
"수동 작업은 언젠가 반드시 실수가 생기게 되어 있어요. docker/build-push-action을 사용하면 이 모든 과정을 자동화할 수 있습니다." 그렇다면 이 액션은 정확히 무엇을 해주는 걸까요?
쉽게 비유하자면, docker/build-push-action은 마치 숙련된 공장 노동자와 같습니다. 원재료(소스 코드)가 들어오면 정해진 레시피(Dockerfile)대로 제품(이미지)을 만들고, 완성된 제품을 창고(레지스트리)로 보내주는 것입니다.
개발자는 그저 "만들어줘"라고 지시만 하면 됩니다. 이 액션이 등장하기 전에는 어땠을까요?
개발자들은 GitHub Actions 워크플로우에서 직접 docker 명령어를 실행해야 했습니다. docker login, docker build, docker push를 일일이 작성해야 했고, 캐싱이나 멀티 플랫폼 빌드 같은 고급 기능을 사용하려면 복잡한 스크립트가 필요했습니다.
docker/build-push-action은 이런 복잡함을 깔끔하게 해결해줍니다. with 블록에 필요한 옵션만 지정하면 됩니다.
context는 Dockerfile이 있는 경로를, push는 이미지를 푸시할지 여부를, tags는 이미지 태그를 지정합니다. 위 코드에서 setup-buildx-action이 먼저 실행되는 것을 볼 수 있습니다.
Buildx는 Docker의 차세대 빌드 도구로, 레이어 캐싱과 병렬 빌드를 지원합니다. build-push-action과 함께 사용하면 빌드 속도가 크게 향상됩니다.
실제 현업에서는 이 액션을 어떻게 활용할까요? 예를 들어 마이크로서비스 아키텍처를 사용하는 회사라면, 각 서비스마다 독립적인 워크플로우를 구성합니다.
개발자가 코드를 푸시하면 해당 서비스의 이미지만 자동으로 빌드되어 배포됩니다. 하지만 주의할 점도 있습니다.
위 예제에서는 push: true로 설정했는데, 이렇게 하면 빌드된 이미지가 바로 레지스트리로 푸시됩니다. 아직 레지스트리 로그인 설정을 하지 않았다면 에러가 발생합니다.
다음 섹션에서 이 부분을 다루겠습니다. 김개발 씨가 고개를 끄덕입니다.
"아, 그래서 워크플로우 파일만 작성하면 나머지는 자동으로 처리되는 거군요!"
실전 팁
💡 - **context: .**은 현재 디렉토리의 Dockerfile을 사용한다는 의미입니다. 하위 디렉토리에 Dockerfile이 있다면 해당 경로를 지정하세요.
- cache-from과 cache-to 옵션을 추가하면 빌드 캐시를 활용해 빌드 시간을 대폭 줄일 수 있습니다.
2. Docker Hub 로그인
김개발 씨가 신나게 워크플로우를 실행했지만, 빨간 에러 메시지가 나타났습니다. "denied: requested access to the resource is denied" 박시니어 씨가 웃으며 말합니다.
"레지스트리에 로그인하지 않으면 이미지를 푸시할 수 없어요. docker/login-action을 추가해야 합니다."
docker/login-action은 GitHub Actions에서 Docker 레지스트리에 인증하는 공식 액션입니다. Docker Hub, GitHub Container Registry, AWS ECR 등 다양한 레지스트리를 지원합니다.
마치 건물에 들어가기 전 출입증을 찍는 것처럼, 이미지를 푸시하기 전에 반드시 로그인 과정이 필요합니다.
다음 코드를 살펴봅시다.
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Docker Hub에 로그인합니다
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# 로그인 후 이미지를 푸시합니다
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/myapp:latest
김개발 씨는 에러 메시지를 보며 당황했습니다. 분명히 빌드는 성공했는데, 왜 푸시가 안 되는 걸까요?
박시니어 씨가 차분하게 설명합니다. "Docker Hub는 공개 저장소라도 푸시할 때는 인증이 필요해요.
로컬에서는 docker login 명령어로 로그인하지만, GitHub Actions에서는 docker/login-action을 사용합니다." 이 액션은 마치 호텔 체크인과 같습니다. 호텔에 도착하면 프런트에서 신분증을 확인하고 카드키를 발급받아야 객실에 들어갈 수 있습니다.
마찬가지로 레지스트리에 이미지를 푸시하려면 먼저 자격 증명을 확인받아야 합니다. 코드를 살펴보면 username과 password를 직접 입력하지 않고 secrets를 사용하는 것을 볼 수 있습니다.
GitHub Secrets는 민감한 정보를 안전하게 저장하는 기능입니다. 워크플로우 파일에 비밀번호를 직접 작성하면 누구나 볼 수 있으므로, 반드시 Secrets를 사용해야 합니다.
Docker Hub에서는 비밀번호 대신 Access Token을 사용하는 것을 권장합니다. Docker Hub 웹사이트에서 Account Settings, Security로 이동하면 토큰을 생성할 수 있습니다.
토큰은 언제든지 폐기하고 새로 발급받을 수 있어 비밀번호보다 안전합니다. GitHub Secrets를 설정하는 방법도 간단합니다.
저장소의 Settings, Secrets and variables, Actions로 이동합니다. New repository secret을 클릭하고 DOCKERHUB_USERNAME과 DOCKERHUB_TOKEN을 각각 추가합니다.
실제 현업에서는 팀 단위로 Docker Hub 조직 계정을 사용하는 경우가 많습니다. 이때는 개인 계정 대신 조직의 자격 증명을 사용하고, 팀원별로 적절한 권한을 부여합니다.
주의할 점이 있습니다. Access Token을 생성할 때 권한 범위를 신중하게 설정해야 합니다.
읽기 전용, 읽기/쓰기, 삭제 권한 등을 선택할 수 있는데, CI/CD 파이프라인에서는 읽기/쓰기 권한만 부여하는 것이 좋습니다. 김개발 씨가 Secrets를 설정하고 워크플로우를 다시 실행했습니다.
이번에는 초록색 체크 표시가 나타났습니다. "드디어 성공했어요!"
실전 팁
💡 - Docker Hub에서 Access Token을 생성할 때 만료 기간을 설정할 수 있습니다. 보안을 위해 적절한 만료 기간을 설정하세요.
- 여러 레지스트리를 사용한다면 login-action을 여러 번 호출할 수 있습니다.
3. GitHub Container Registry
김개발 씨가 Docker Hub를 잘 사용하고 있었는데, 팀장님이 새로운 요청을 했습니다. "우리 회사는 소스 코드와 이미지를 한 곳에서 관리하고 싶어요.
GitHub Container Registry로 옮길 수 있을까요?" 박시니어 씨가 고개를 끄덕입니다. "좋은 선택이에요.
GitHub 토큰만 있으면 별도 계정 없이 바로 사용할 수 있거든요."
GitHub Container Registry(ghcr.io)는 GitHub에서 제공하는 컨테이너 이미지 저장소입니다. 소스 코드와 이미지를 한 곳에서 관리할 수 있고, GitHub Actions와 자연스럽게 통합됩니다.
마치 집과 창고가 붙어 있는 것처럼, 코드를 푸시하고 이미지를 저장하는 작업이 매끄럽게 연결됩니다.
다음 코드를 살펴봅시다.
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
# GitHub Container Registry에 로그인합니다
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest
김개발 씨는 Docker Hub를 사용하면서 한 가지 불편함을 느끼고 있었습니다. 코드는 GitHub에 있고, 이미지는 Docker Hub에 있다 보니 권한 관리가 복잡했습니다.
팀원이 바뀔 때마다 두 곳의 권한을 따로 수정해야 했습니다. 박시니어 씨가 말합니다.
"GitHub Container Registry를 사용하면 그런 고민이 사라져요. GitHub 저장소 권한을 그대로 사용하거든요." GitHub Container Registry는 마치 아파트 단지의 지하 창고와 같습니다.
아파트 열쇠 하나로 집도 들어가고 창고도 열 수 있는 것처럼, GitHub 인증 하나로 코드 저장소와 이미지 저장소를 모두 이용할 수 있습니다. 코드에서 주목할 부분은 permissions 블록입니다.
GitHub Actions 워크플로우는 기본적으로 제한된 권한을 가집니다. 이미지를 푸시하려면 packages: write 권한이 필요합니다.
이 설정을 빠뜨리면 권한 오류가 발생합니다. 또 다른 특징은 GITHUB_TOKEN입니다.
Docker Hub에서는 별도의 토큰을 생성해서 Secrets에 저장해야 했지만, GitHub Container Registry에서는 GitHub가 자동으로 제공하는 토큰을 사용합니다. 별도의 설정 없이 바로 사용할 수 있어 편리합니다.
registry: ghcr.io 옵션이 Docker Hub와의 차이점입니다. login-action은 기본적으로 Docker Hub에 로그인하므로, 다른 레지스트리를 사용할 때는 반드시 registry 옵션을 지정해야 합니다.
이미지 태그도 형식이 다릅니다. Docker Hub에서는 username/image:tag 형식을 사용하지만, GitHub Container Registry에서는 ghcr.io/owner/repo:tag 형식을 사용합니다.
github.repository 변수를 사용하면 저장소 이름이 자동으로 들어갑니다. 실제 현업에서는 GitHub Container Registry가 특히 유용한 상황이 있습니다.
오픈소스 프로젝트에서는 컨트리뷰터들이 별도의 Docker Hub 계정 없이도 이미지를 확인할 수 있습니다. 기업에서는 GitHub Enterprise와 연동해 내부 네트워크에서만 접근 가능한 프라이빗 레지스트리를 구축할 수 있습니다.
주의할 점은 이미지 공개 범위입니다. 기본적으로 이미지는 저장소와 같은 공개 범위를 따르지만, 패키지 설정에서 별도로 조정할 수 있습니다.
공개 저장소라도 이미지를 비공개로 유지하거나, 그 반대도 가능합니다. 김개발 씨가 워크플로우를 수정하고 푸시했습니다.
저장소의 Packages 탭에 방금 빌드한 이미지가 나타났습니다. "와, 코드랑 이미지가 한눈에 보이니까 정말 편하네요!"
실전 팁
💡 - GitHub Container Registry의 이미지는 저장소의 Packages 탭에서 확인할 수 있습니다.
- 프라이빗 이미지를 pull하려면 Personal Access Token을 생성해야 합니다. read:packages 권한을 부여하세요.
4. 멀티 플랫폼 빌드
김개발 씨의 회사에서 새로운 요구사항이 생겼습니다. 기존에는 Intel 기반 서버만 사용했는데, 비용 절감을 위해 ARM 기반 AWS Graviton 인스턴스를 도입하기로 했습니다.
"이미지를 두 번 빌드해야 하나요?" 걱정하는 김개발 씨에게 박시니어 씨가 말합니다. "한 번에 여러 플랫폼용 이미지를 만들 수 있어요."
멀티 플랫폼 빌드는 하나의 Dockerfile로 여러 CPU 아키텍처용 이미지를 동시에 생성하는 기술입니다. linux/amd64(Intel/AMD)와 linux/arm64(ARM) 이미지를 한 번의 빌드로 만들 수 있습니다.
마치 한 벌의 옷을 여러 사이즈로 만드는 것처럼, 같은 애플리케이션을 다양한 환경에서 실행할 수 있게 해줍니다.
다음 코드를 살펴봅시다.
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
# QEMU 에뮬레이터를 설정합니다 (ARM 빌드용)
- 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.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# 여러 플랫폼용 이미지를 동시에 빌드합니다
- name: Build and push multi-platform
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: myapp:latest
클라우드 비용 최적화는 모든 회사의 고민입니다. 특히 AWS Graviton 같은 ARM 기반 인스턴스는 x86 인스턴스보다 가격 대비 성능이 좋아 많은 기업이 도입을 고려하고 있습니다.
문제는 기존에 Intel/AMD 서버용으로 빌드한 이미지는 ARM 서버에서 실행되지 않는다는 것입니다. CPU 아키텍처가 다르기 때문입니다.
마치 Windows용 프로그램이 Mac에서 실행되지 않는 것과 비슷합니다. 박시니어 씨가 해결책을 제시합니다.
"Docker의 멀티 플랫폼 빌드 기능을 사용하면 됩니다. 한 번의 빌드로 여러 아키텍처용 이미지를 만들 수 있어요." 이 기능은 마치 만능 번역기와 같습니다.
하나의 원본 문서를 여러 언어로 동시에 번역하는 것처럼, 하나의 Dockerfile을 여러 CPU 아키텍처용으로 빌드합니다. 코드에서 새롭게 등장한 것은 setup-qemu-action입니다.
QEMU는 하드웨어 에뮬레이터입니다. GitHub Actions 러너는 x86 서버에서 실행되는데, ARM용 이미지를 빌드하려면 ARM CPU를 에뮬레이션해야 합니다.
QEMU가 그 역할을 담당합니다. platforms 옵션에 빌드할 아키텍처를 나열합니다.
linux/amd64는 일반적인 Intel/AMD 서버용이고, linux/arm64는 AWS Graviton, Apple Silicon, Raspberry Pi 등에서 사용하는 ARM용입니다. 콤마로 구분해서 여러 플랫폼을 지정할 수 있습니다.
멀티 플랫폼 이미지의 재미있는 점은 사용자 입장에서는 아무것도 달라지지 않는다는 것입니다. docker pull myapp:latest 명령어를 실행하면, Docker가 자동으로 현재 시스템에 맞는 이미지를 다운로드합니다.
Intel 서버에서는 amd64 이미지를, ARM 서버에서는 arm64 이미지를 받습니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 글로벌 서비스를 운영하는 회사에서는 지역별로 다른 인스턴스 타입을 사용할 수 있습니다. 미국 리전에서는 x86 인스턴스를, 아시아 리전에서는 비용 절감을 위해 ARM 인스턴스를 사용하는 식입니다.
멀티 플랫폼 이미지가 있으면 이런 하이브리드 구성이 가능해집니다. 주의할 점은 빌드 시간입니다.
ARM 이미지를 x86 러너에서 에뮬레이션으로 빌드하면 네이티브 빌드보다 훨씬 느립니다. 빌드 시간이 중요하다면 ARM 러너를 사용하거나, 플랫폼별로 병렬 빌드하는 방식을 고려해야 합니다.
김개발 씨가 멀티 플랫폼 빌드를 설정하고 테스트했습니다. Graviton 인스턴스에서도 애플리케이션이 잘 실행됩니다.
"이제 인프라 비용을 절감할 수 있겠네요!"
실전 팁
💡 - linux/arm/v7을 추가하면 Raspberry Pi 3 같은 32비트 ARM 디바이스도 지원할 수 있습니다.
- 에뮬레이션 빌드가 느리다면 matrix 전략으로 플랫폼별 병렬 빌드를 고려해보세요.
5. 이미지 태깅 전략
김개발 씨가 배포를 몇 번 하다 보니 문제가 생겼습니다. 모든 이미지가 latest 태그로 되어 있어서 어떤 버전이 언제 배포되었는지 알 수가 없습니다.
롤백이 필요할 때 이전 버전을 찾을 방법이 없었습니다. 박시니어 씨가 말합니다.
"체계적인 태깅 전략이 필요해요."
이미지 태깅 전략은 Docker 이미지에 의미 있는 태그를 부여하는 규칙입니다. Git 커밋 해시, 브랜치 이름, 시맨틱 버전 등 다양한 정보를 태그로 활용합니다.
마치 도서관의 분류 시스템처럼, 수많은 이미지 중에서 원하는 버전을 정확히 찾을 수 있게 해줍니다.
다음 코드를 살펴봅시다.
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
# 이미지 메타데이터를 자동으로 생성합니다
- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=sha,prefix=
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
"어제 배포한 버전으로 롤백해야 하는데, 그게 어떤 이미지인지 모르겠어요." 김개발 씨가 난감한 표정을 짓습니다. 서비스에 문제가 생겼는데, latest 태그밖에 없어서 이전 버전을 찾을 수가 없습니다.
박시니어 씨가 조용히 말합니다. "이래서 태깅 전략이 중요해요.
처음에는 귀찮아 보여도, 나중에 반드시 필요해지는 순간이 옵니다." 이미지 태깅은 마치 우편물의 주소와 같습니다. "서울시"라고만 쓰면 배달할 수 없지만, 정확한 주소를 쓰면 원하는 곳에 정확히 도착합니다.
마찬가지로 "latest"만 있으면 원하는 버전을 찾을 수 없지만, 구체적인 태그가 있으면 정확한 이미지를 가져올 수 있습니다. docker/metadata-action은 이런 태깅 작업을 자동화해줍니다.
다양한 규칙을 조합해서 의미 있는 태그를 생성합니다. tags 블록의 각 줄을 살펴보겠습니다.
type=ref,event=branch는 브랜치 이름을 태그로 사용합니다. main 브랜치에서 빌드하면 :main 태그가 붙습니다.
개발 환경에서 특정 브랜치의 최신 이미지를 쉽게 가져올 수 있습니다. type=sha는 Git 커밋 해시를 태그로 사용합니다.
모든 커밋은 고유한 해시를 가지므로, 정확히 어떤 코드로 빌드된 이미지인지 추적할 수 있습니다. 문제가 발생했을 때 해당 커밋의 코드를 바로 확인할 수 있어 디버깅에 유용합니다.
type=semver는 시맨틱 버전 태그를 생성합니다. Git 태그 v1.2.3을 푸시하면 :1.2.3, :1.2 태그가 자동으로 붙습니다.
사용자는 원하는 수준의 버전 고정을 선택할 수 있습니다. :1.2를 사용하면 패치 업데이트는 자동으로 받고, :1.2.3을 사용하면 정확히 그 버전만 사용합니다.
실제 현업에서는 환경별로 다른 태그를 사용합니다. 개발 환경에서는 브랜치 태그를, 스테이징 환경에서는 커밋 해시 태그를, 프로덕션 환경에서는 시맨틱 버전 태그를 사용하는 식입니다.
주의할 점은 latest 태그의 사용입니다. 많은 사람이 편하다고 latest를 사용하지만, 프로덕션 환경에서는 위험합니다.
새 이미지가 푸시되면 latest가 가리키는 이미지가 바뀌기 때문입니다. 의도치 않게 테스트되지 않은 버전이 배포될 수 있습니다.
김개발 씨가 metadata-action을 적용하고 몇 번 배포를 진행했습니다. 이제 레지스트리에 :main, :a1b2c3d, :1.0.0 같은 태그가 깔끔하게 정리되어 있습니다.
"이제 롤백도 쉽게 할 수 있겠네요!"
실전 팁
💡 - 프로덕션 배포에는 반드시 고정된 버전 태그를 사용하세요. latest는 개발 환경에서만 사용하는 것이 안전합니다.
- labels 출력을 활용하면 이미지에 빌드 시간, 커밋 정보 등의 메타데이터를 추가할 수 있습니다.
6. Trivy 취약점 스캔
어느 날 보안팀에서 연락이 왔습니다. "우리가 사용하는 Docker 이미지에 보안 취약점이 있다는 리포트가 나왔어요." 김개발 씨는 당황했습니다.
열심히 코드를 작성했는데, 이미지 안에 취약점이 있다니요. 박시니어 씨가 말합니다.
"베이스 이미지나 의존성에 취약점이 있을 수 있어요. Trivy로 미리 스캔해야 합니다."
Trivy는 컨테이너 이미지의 보안 취약점을 스캔하는 도구입니다. 베이스 이미지의 OS 패키지, 프로그래밍 언어의 의존성 라이브러리에서 알려진 취약점(CVE)을 탐지합니다.
마치 공항의 보안 검색대처럼, 이미지가 배포되기 전에 위험 요소를 미리 발견할 수 있습니다.
다음 코드를 살펴봅시다.
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Build image for scanning
uses: docker/build-push-action@v5
with:
context: .
load: true
tags: myapp:scan
# Trivy로 취약점을 스캔합니다
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:scan
format: table
exit-code: 1
severity: CRITICAL,HIGH
ignore-unfixed: true
# 스캔 통과 후 푸시합니다
- name: Push image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: myapp:latest
보안 취약점은 눈에 보이지 않는 위협입니다. 개발자가 작성한 코드는 문제가 없어도, 사용하는 라이브러리나 베이스 이미지에 취약점이 있을 수 있습니다.
특히 Node.js의 node_modules, Python의 pip 패키지처럼 수많은 의존성을 사용하는 현대 애플리케이션에서는 더욱 그렇습니다. 박시니어 씨가 설명합니다.
"작년에 Log4j 취약점이 터졌을 때 기억나요? 수많은 기업이 영향을 받았죠.
Trivy 같은 스캐너를 CI/CD에 통합해두면 이런 취약점을 조기에 발견할 수 있어요." Trivy는 마치 건물의 안전 점검사와 같습니다. 건물이 지어지기 전에 설계도를 검토하고, 시공 중에도 계속 점검하는 것처럼, Trivy는 이미지가 배포되기 전에 보안 문제를 검사합니다.
코드에서 주목할 부분은 두 번의 build-push-action 호출입니다. 첫 번째는 load: true 옵션으로 이미지를 로컬에 로드합니다.
푸시하지 않고 스캔용으로만 사용합니다. 스캔을 통과한 후에야 두 번째 호출에서 실제로 푸시합니다.
exit-code: 1 옵션이 중요합니다. 취약점이 발견되면 스캔 단계가 실패하고, 워크플로우가 중단됩니다.
따라서 취약점이 있는 이미지는 절대 레지스트리에 푸시되지 않습니다. 이것이 "시프트 레프트" 보안의 핵심입니다.
문제를 배포 전에 발견하는 것이죠. severity: CRITICAL,HIGH 옵션은 심각도 필터입니다.
모든 취약점을 차단하면 너무 엄격할 수 있으므로, 심각한 취약점만 차단하고 경미한 취약점은 경고만 표시할 수 있습니다. ignore-unfixed: true는 아직 패치가 없는 취약점을 무시합니다.
베이스 이미지에서 아직 수정되지 않은 취약점 때문에 빌드가 막히는 것을 방지합니다. 물론 이런 취약점도 모니터링해야 하지만, 당장 해결할 방법이 없다면 일단 넘어가는 것이 현실적입니다.
실제 현업에서는 스캔 결과를 다양한 형식으로 활용합니다. SARIF 형식으로 출력하면 GitHub Security 탭에서 취약점을 확인할 수 있습니다.
JSON 형식으로 출력하면 자동화 도구에서 파싱할 수 있습니다. 주의할 점은 스캔 시간입니다.
이미지 크기가 크면 스캔에 몇 분이 걸릴 수 있습니다. 캐시를 활용하거나, 중요하지 않은 브랜치에서는 스캔을 생략하는 등의 최적화가 필요할 수 있습니다.
김개발 씨가 Trivy를 설정하고 빌드를 실행했습니다. 빨간 경고가 나타났습니다.
"CRITICAL: CVE-2024-xxxx in openssl". 베이스 이미지를 최신 버전으로 업데이트하니 스캔을 통과했습니다.
"미리 발견해서 다행이에요!"
실전 팁
💡 - format: sarif로 설정하고 github/codeql-action/upload-sarif를 사용하면 GitHub Security 탭에서 취약점을 관리할 수 있습니다.
- 정기적으로 베이스 이미지를 업데이트하는 것이 취약점을 줄이는 가장 효과적인 방법입니다.
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
마이크로서비스 배포 완벽 가이드
Kubernetes를 활용한 마이크로서비스 배포의 핵심 개념부터 실전 운영까지, 초급 개발자도 쉽게 따라할 수 있는 완벽 가이드입니다. 실무에서 바로 적용 가능한 배포 전략과 노하우를 담았습니다.
EFK 스택 로깅 완벽 가이드
마이크로서비스 환경에서 로그를 효과적으로 수집하고 분석하는 EFK 스택(Elasticsearch, Fluentd, Kibana)의 핵심 개념과 실전 활용법을 초급 개발자도 쉽게 이해할 수 있도록 정리한 가이드입니다.
Spring Boot 상품 서비스 구축 완벽 가이드
실무 RESTful API 설계부터 테스트, 배포까지 Spring Boot로 상품 서비스를 만드는 전 과정을 다룹니다. JPA 엔티티 설계, OpenAPI 문서화, Docker Compose 배포 전략을 초급 개발자도 쉽게 따라할 수 있도록 스토리텔링으로 풀어냅니다.
Docker로 컨테이너화 완벽 가이드
Spring Boot 애플리케이션을 Docker로 컨테이너화하는 방법을 초급 개발자도 쉽게 이해할 수 있도록 실무 중심으로 설명합니다. Dockerfile 작성부터 멀티스테이지 빌드, 이미지 최적화, Spring Boot의 Buildpacks까지 다룹니다.
AWS Secrets Manager 완벽 가이드
AWS에서 데이터베이스 비밀번호, API 키 등 민감한 정보를 안전하게 관리하는 Secrets Manager의 핵심 개념과 실무 활용법을 배워봅니다. 초급 개발자도 쉽게 따라할 수 있도록 실전 예제와 함께 설명합니다.