목차
- 1. 왜 컨테이너 이미지 보안이 운영에서 중요할까
- 2. Trivy가 뭐고, 왜 많이 쓰는가
- 3. 운영 기준 먼저 정하기: 실패 조건을 정하지 않으면 자동화가 흔들립니다
- 4. 실전 구현: Trivy를 CI/CD 파이프라인에 붙이는 방식
- 4-1. 로컬에서 먼저 이미지 스캔해보기
- 4-2. GitHub Actions 예시
- 4-3. 무시할 취약점은 최소한으로 관리하기
- 4-4. Kubernetes 배포 전 검증 포인트
- 5. 제가 실제로 겪은 문제들: 트러블슈팅과 운영 팁
- 5-1. 베이스 이미지가 원인인 경우가 많습니다
- 5-2. 스캔 시간 때문에 파이프라인이 느려질 수 있습니다
- 5-3. 취약점 수보다 맥락이 중요합니다
- 6. 검증과 결과: 무엇이 달라졌나
- 검증 체크리스트
- 7. 운영하면서 정리한 추천 정책
- 8. 마무리: Trivy는 시작점이고, 운영 기준이 완성도를 만듭니다
- 다음 단계로 해볼 만한 것
- 정리 FAQ
- Q1. Trivy는 어느 시점에 넣는 게 가장 효과적인가요?
- Q2. 모든 취약점을 차단해야 하나요?
- Q3. 이미지 스캔만 하면 Kubernetes 보안도 충분한가요?
[보안 사례] 컨테이너 이미지 보안 강화: Trivy를 활용한 CI/CD 통합 운영 후기
컨테이너 보안 사례를 찾다 보면 다들 비슷한 이야기만 하더라고요. "스캔하세요", "취약점 관리하세요" 같은 말은 맞는데, 막상 운영 환경에 넣으려면 어디서부터 손대야 할지 애매합니다. 저도 홈랩과 사내 비슷한 구조의 테스트 환경에서 Trivy(트리비, 오픈소스 취약점 스캐너)를 붙여서 이미지 스캔을 자동화해봤는데, 처음엔 이게 생각보다 손이 많이 가더라고요. 특히 CI/CD 보안 관점에서 "스캔은 했는데 배포는 누가 막지?", "베이스 이미지가 바뀌면 기존 정책은 어떻게 하지?" 같은 현실적인 문제가 꼭 나옵니다.
이번 글은 그런 분들을 위한 컨테이너 보안 사례 정리입니다. Trivy를 활용해서 CI/CD 보안 흐름에 이미지 검사를 붙이고, Docker 보안과 Kubernetes 보안까지 운영 관점에서 어떻게 연결했는지 경험 기반으로 풀어보겠습니다. 제가 직접 해보니 도구 자체보다도 언제 검사하고, 어떤 기준으로 실패 처리할지가 진짜 핵심이었습니다.
CI 단계에서 이미지 빌드, Trivy 스캔, 레지스트리 푸시, Kubernetes 배포 전 검증까지 이어지는 전체 흐름을 보여주는 이미지입니다.
1. 왜 컨테이너 이미지 보안이 운영에서 중요할까
쉽게 말해 컨테이너 이미지는 서버의 골격을 압축해 둔 결과물입니다. 애플리케이션 코드만 들어가는 게 아니라, 베이스 OS 패키지, 런타임, 시스템 라이브러리까지 같이 들어가거든요. 그래서 이미지 안에 취약한 패키지가 하나만 있어도, 실제 운영에서는 꽤 민감한 이슈가 됩니다.
예전엔 VM(Virtual Machine, 가상머신) 단위로 관리하던 걸 컨테이너로 옮기면서 배포 속도는 빨라졌는데, 그만큼 이미지가 더 자주 만들어지고 더 자주 배포됩니다. 문제는 속도가 빨라질수록 사람이 수동으로 체크할 여유가 없어집니다. 저도 처음엔 배포 직전에 한 번만 확인하면 되겠지 싶었는데, 실제로 써보니까 그 방식은 오래 못 갑니다. 빌드마다 자동으로 검사하고, 위험 수준이 높은 건 파이프라인에서 바로 걸러야 하더라고요.
- 빌드 시점: 취약한 패키지가 이미지에 포함되는지 확인
- 푸시 시점: 레지스트리(Registry, 이미지 저장소)에 위험한 이미지가 쌓이지 않게 제어
- 배포 시점: Kubernetes(쿠버네티스, 컨테이너 오케스트레이션)에서 검증되지 않은 이미지 실행 방지
여기서 중요한 포인트! 컨테이너 보안 사례를 보면 대부분 "도구 설치"에서 끝나는데, 운영은 그 다음부터 시작입니다.
2. Trivy가 뭐고, 왜 많이 쓰는가
Trivy는 Aqua Security에서 공개한 오픈소스 보안 스캐너로, 컨테이너 이미지 안의 OS 패키지와 애플리케이션 의존성 취약점을 확인할 수 있습니다. 그리고 이게 편한 이유가 뭔가 하면, 명령어가 비교적 단순하고 CI/CD에 넣기 쉽습니다. 저도 처음엔 Clair나 다른 스캐너도 같이 봤었는데, 실무에서는 빠르게 붙고 운영 부담이 적은 쪽이 오래 갑니다.
쉽게 말해 Trivy는 "이 이미지 안에 알려진 취약점이 있는지"를 검사해주는 첫 번째 관문입니다. 그리고 필요하면 설정 파일 오탐도 잡고, 파일시스템이나 Git 저장소 스캔에도 활용할 수 있죠. 다만 오늘은 범위를 넓히기보다 이미지 스캔 중심의 CI/CD 보안 이야기에 집중하겠습니다.
| 항목 | Trivy 적용 전 | Trivy 적용 후 |
|---|---|---|
| 취약점 발견 시점 | 배포 후 또는 수동 점검 | 빌드 단계에서 자동 확인 |
| 운영자 개입 | 높음 | 정책 기반 자동화 가능 |
| 재현성 | 담당자 경험 의존 | 파이프라인 기준으로 일관됨 |
| Docker 보안 관리 | 개별 확인 | 공통 정책으로 통제 |
3. 운영 기준 먼저 정하기: 실패 조건을 정하지 않으면 자동화가 흔들립니다
이건 제가 꽤 삽질했던 부분입니다. 처음엔 취약점이 하나라도 나오면 실패 처리해봤거든요. 결과는 뻔했습니다. 팀이 바로 우회 플래그를 찾기 시작합니다. 왜냐하면 현실적으로 베이스 이미지 하나만 바뀌어도 Low나 Medium 수준이 여러 개 잡힐 수 있기 때문입니다.
그래서 운영 기준을 이렇게 나눴습니다.
- 개발 브랜치: 결과는 남기되 즉시 차단하지 않음
- 메인 브랜치: High, Critical 취약점이 있으면 실패
- 릴리스 태그: 스캔 결과 아티팩트 보관 + 예외 승인된 항목만 통과
- 운영 배포: 승인된 이미지 다이제스트(Digest, 내용 기반 식별자)만 사용
이렇게 나누니까 팀도 덜 답답해하고, 보안팀이나 인프라 담당도 기준을 설명하기 쉬워졌습니다. 결국 CI/CD 보안은 기술보다 합의가 먼저더라고요.
4. 실전 구현: Trivy를 CI/CD 파이프라인에 붙이는 방식
제가 가장 단순하게 시작했던 흐름은 이렇습니다. Docker 이미지 빌드 후 Trivy로 스캔하고, 결과가 기준을 넘으면 푸시나 배포를 막는 구조입니다. 여기서는 예시로 GitHub Actions 스타일 YAML을 보여드리지만, GitLab CI나 Jenkins에서도 개념은 거의 같습니다.
4-1. 로컬에서 먼저 이미지 스캔해보기
자동화 전에 로컬에서 결과를 보는 게 좋습니다. 그래야 나중에 파이프라인 로그를 봐도 덜 헷갈립니다.
docker build -t sample-app:local .
trivy image sample-app:local
조금 더 실무적으로 보려면 심각도(Severity, 취약점 등급)를 제한해서 보는 게 편합니다.
trivy image --severity HIGH,CRITICAL sample-app:local
JSON 결과를 파일로 저장해두면 후처리에도 좋습니다.
trivy image --format json --output trivy-result.json sample-app:local
4-2. GitHub Actions 예시
name: container-security
on:
push:
branches:
- main
pull_request:
jobs:
build-and-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build image
run: docker build -t sample-app:${{ github.sha }} .
- name: Run Trivy scan
run: |
trivy image \
--exit-code 1 \
--severity HIGH,CRITICAL \
--format table \
sample-app:${{ github.sha }}
- name: Save JSON report
if: always()
run: |
trivy image \
--format json \
--output trivy-report.json \
sample-app:${{ github.sha }}
- name: Upload report artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: trivy-report
path: trivy-report.json
여기서 중요한 건 --exit-code 1입니다. 이 옵션이 있어야 기준에 걸렸을 때 파이프라인이 실제로 실패합니다. 처음엔 결과만 출력하고 끝내는 식으로 구성했었는데, 그러면 운영에서 아무도 안 봅니다. 진짜입니다. 로그는 쌓이는데 아무도 안 봐요.
개발 브랜치, 메인 브랜치, 릴리스 태그에 따라 서로 다른 스캔 정책이 적용되는 예시 화면 또는 구성 다이어그램용 이미지입니다.
4-3. 무시할 취약점은 최소한으로 관리하기
운영하다 보면 당장 패치가 어려운 항목이 나옵니다. 이럴 때 무작정 무시하지 말고, 예외를 추적 가능한 방식으로 남겨야 합니다.
# .trivyignore 예시
CVE-2023-00000
CVE-2023-11111
저는 이 파일을 넣을 때 리뷰 규칙을 따로 뒀습니다.
- 왜 무시하는지 이슈 번호 남기기
- 대체 패치 일정 적기
- 베이스 이미지 교체 가능 여부 확인하기
안 그러면 .trivyignore가 점점 커지고, 나중엔 형식적인 보안이 되어버립니다.
4-4. Kubernetes 배포 전 검증 포인트
Kubernetes 보안 관점에서는 "스캔 완료된 이미지인가"와 "검증된 이미지 버전만 쓰는가"가 핵심입니다. 가장 기본은 태그(tag)보다 다이제스트 고정입니다. latest 같은 태그는 진짜 편해 보여도 운영에서는 추적이 어려워집니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample-app
spec:
replicas: 2
selector:
matchLabels:
app: sample-app
template:
metadata:
labels:
app: sample-app
spec:
containers:
- name: sample-app
image: registry.example.com/sample-app@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
imagePullPolicy: IfNotPresent
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
이미지 스캔만으로 Kubernetes 보안이 끝나는 건 아니고요, securityContext(시큐리티 컨텍스트), 비루트 실행, 읽기 전용 파일시스템 같은 기본 하드닝(hardening, 보안 강화)도 같이 가야 합니다.
5. 제가 실제로 겪은 문제들: 트러블슈팅과 운영 팁
여기서부터가 좀 현실적인 이야기입니다. 도구 소개 글에는 잘 안 나오는데, 실제로 붙이면 은근히 이런 문제를 많이 겪습니다.
5-1. 베이스 이미지가 원인인 경우가 많습니다
애플리케이션 코드는 멀쩡한데 계속 High가 뜨는 경우가 있었습니다. 알고 보니 대부분 베이스 이미지 문제였어요. 예를 들어 오래된 패키지를 포함한 이미지라면 애플리케이션 수정으로 해결이 안 됩니다. 이럴 땐 Dockerfile부터 봐야 합니다.
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
여기서도 끝이 아닙니다. 같은 slim 계열이라도 시점에 따라 포함 패키지가 달라질 수 있거든요. 그래서 정기 재빌드가 중요합니다. 코드를 안 바꿔도 다시 빌드하면 해결되는 취약점이 생각보다 많습니다.
5-2. 스캔 시간 때문에 파이프라인이 느려질 수 있습니다
처음엔 모든 브랜치에서 전체 스캔을 돌렸더니 CI 시간이 확 늘어났습니다. 이거 진짜 불만 많이 나옵니다. 그래서 저는 전략을 나눴습니다.
- Pull Request 단계: High, Critical 중심 빠른 검사
- Main 병합 후: 전체 이미지 스캔 + 결과 보관
- 야간 배치: 주요 이미지 일괄 재스캔
이렇게 하면 개발 흐름을 너무 막지 않으면서도 운영 기준은 유지할 수 있습니다.
5-3. 취약점 수보다 맥락이 중요합니다
숫자만 보고 "취약점 27개"라고 하면 다들 놀랍니다. 근데 실제로는 실행 경로와 무관한 패키지일 수도 있고, 이미 수정판이 준비된 경우도 있습니다. 반대로 개수가 적어도 원격 코드 실행(RCE, Remote Code Execution) 관련이면 우선순위가 훨씬 높죠. 그래서 대시보드에는 단순 개수보다 Critical 존재 여부, 고정 가능한지 여부, 운영 노출도를 같이 봤습니다.
Critical, High, 수정 가능 여부, 베이스 이미지 기인 여부 등을 기준으로 결과를 정리한 보안 리포트 대시보드 이미지입니다.
6. 검증과 결과: 무엇이 달라졌나
몇 달 운영해보니 체감되는 변화가 꽤 분명했습니다. 가장 큰 건 "배포 후 발견"이 줄어든 겁니다. 이전에는 이미지가 이미 레지스트리에 올라가고 나서 보안 이슈를 알게 되는 경우가 있었는데, 지금은 빌드 단계에서 대부분 걸러집니다. 이 차이가 큽니다.
- 개발자가 PR 단계에서 위험한 이미지 변경을 바로 인지
- 운영자는 승인된 기준으로만 배포 가능
- 레지스트리에 누적되는 불량 이미지 감소
- Docker 보안 점검이 개별 담당자 감에 덜 의존
또 하나 좋았던 건 커뮤니케이션입니다. 예전엔 보안 이슈가 나오면 "누가 확인했나요?"부터 시작했는데, 이제는 "이 이미지는 Trivy 기준을 통과했는가"로 대화가 정리됩니다. 기준이 생기니까 감정 소모가 줄더라고요.
검증 체크리스트
- 빌드 직후 Trivy 스캔이 자동 실행되는가
- High, Critical 기준에서 파이프라인 실패가 동작하는가
- JSON 결과가 아티팩트로 저장되는가
- 운영 배포에서 다이제스트 기반 이미지를 사용하는가
- Kubernetes 보안 설정이 최소 기준을 만족하는가
trivy image --severity HIGH,CRITICAL --exit-code 1 registry.example.com/sample-app:release
kubectl get deploy sample-app -o yaml
위 두 줄만 봐도 1차 확인은 꽤 됩니다. 간단하지만 실전에서는 이 기본기가 중요합니다.
7. 운영하면서 정리한 추천 정책
혹시 이제 막 도입하시는 분이라면, 처음부터 너무 거창하게 가지 않는 걸 추천드립니다. 저도 처음엔 모든 걸 다 통제하려다 오히려 반발만 샀거든요.
| 단계 | 추천 정책 | 이유 |
|---|---|---|
| 1단계 | 메인 브랜치만 차단 | 개발 흐름 충격 최소화 |
| 2단계 | 리포트 아티팩트 보관 | 추적성과 감사 대응 확보 |
| 3단계 | 예외 관리 프로세스 도입 | 무분별한 ignore 방지 |
| 4단계 | Kubernetes 배포 정책 연계 | CI와 런타임 보안 연결 |
이 흐름으로 가면 도입 난이도도 적당하고, 팀이 적응할 시간도 벌 수 있습니다. 특히 컨테이너 보안 사례는 도구 선정보다 운영 규칙 설계가 더 중요하다는 점, 꼭 기억하시면 좋겠습니다.
8. 마무리: Trivy는 시작점이고, 운영 기준이 완성도를 만듭니다
정리해보면 Trivy 자체는 비교적 가볍게 시작할 수 있는 좋은 도구입니다. 다만 진짜 차이는 이미지 스캔 결과를 어떻게 의사결정에 반영하느냐에서 나옵니다. 제가 직접 해보니, 단순히 "스캔했다"보다 "배포를 막을 수 있다", "예외를 기록할 수 있다", "Kubernetes 보안까지 연결된다"가 훨씬 중요했습니다.
그리고 이건 꼭 말씀드리고 싶네요. 처음부터 완벽하게 하려고 하면 오래 못 갑니다. 먼저 CI/CD 보안 흐름에 Trivy를 넣고, 그 다음 Docker 보안 기준을 정리하고, 마지막으로 Kubernetes 보안 정책과 연결하는 순서가 현실적이었습니다. 드디어 됐다 싶었던 순간도 있었고, 베이스 이미지 하나 때문에 몇 시간 날린 적도 있었는데요, 그런 삽질이 결국 운영 기준을 더 단단하게 만들어주더라고요.
도입 전후 차이, 운영 체크리스트, 다음 단계 액션 아이템을 한눈에 보여주는 요약 인포그래픽용 이미지입니다.
다음 단계로 해볼 만한 것
- 레지스트리 단계 스캔 자동화 추가
- SBOM(Software Bill of Materials, 소프트웨어 구성 명세) 관리 연계
- Admission Controller(어드미션 컨트롤러, 배포 승인 제어) 기반 배포 정책 강화
- 이전 글의 Dockerfile 경량화 내용과 연결해 베이스 이미지 개선
다음 글에서는 컨테이너 보안 사례를 이어서, Kubernetes 배포 정책과 Admission Controller를 어떻게 엮으면 좋은지 좀 더 깊게 다뤄보려고 합니다. 그 부분도 운영에서 체감이 크거든요.
정리 FAQ
Q1. Trivy는 어느 시점에 넣는 게 가장 효과적인가요?
가장 먼저는 이미지 빌드 직후입니다. 그 다음이 레지스트리 푸시 전, 마지막이 배포 전 검증입니다.
Q2. 모든 취약점을 차단해야 하나요?
아닙니다. 운영에서는 보통 High, Critical부터 시작하는 편이 현실적입니다. 대신 예외 기준은 명확해야 합니다.
Q3. 이미지 스캔만 하면 Kubernetes 보안도 충분한가요?
아쉽지만 아닙니다. 비루트 실행, 읽기 전용 파일시스템, 권한 최소화 같은 런타임 보안 설정이 같이 필요합니다.
'IT > 보안' 카테고리의 다른 글
| [보안] OPNsense 보안 강화 체크리스트: 홈랩부터 소규모 오피스까지 (1) | 2026.08.03 |
|---|---|
| [보안] WireGuard vs OpenVPN vs IPsec: 홈랩 및 소규모 비즈니스 VPN 보안 비교 (0) | 2026.08.03 |
| [백엔드] JWT 오류 해결: 만료와 서명 검증 디버깅 가이드 (0) | 2026.07.27 |
| [보안] 시크릿 관리 솔루션 비교: HashiCorp Vault와 AWS Secrets Manager, 팀에 맞는 선택 기준 (1) | 2026.07.26 |
| [보안] 리눅스 서버 보안 강화 체크리스트: SSH부터 시스템까지 10가지 필수 점검 (1) | 2026.07.26 |
| [보안] MFA 도입 문제 해결: 기업 2FA 트러블슈팅 및 운영 방법 (0) | 2026.07.23 |