목차
- 1년 전, 저희 팀의 컨테이너 보안 현실
- Trivy(트리비) 컨테이너 보안이 뭔지 먼저 짚고 갈게요
- CI/CD 파이프라인에 Trivy 보안 스캔 통합하기
- 1단계: 일단 스캔만 (경보 없음)
- 2단계: CRITICAL 취약점은 빌드 차단
- .trivyignore 파일로 예외 관리하기
- ⚠️ 1년간 맞닥뜨린 진짜 문제들
- 문제 1: 노이즈가 너무 많다
- 문제 2: 베이스 이미지가 주범인 경우가 많다
- 문제 3: 스캔 속도로 인한 개발자 불만
- 🎉 1년 후, 실제로 달라진 것들
- 가시화된 변화들
- 솔직히 아쉬운 점들
- DevSecOps 관점에서의 교훈
- 자주 묻는 질문 (FAQ)
- Q. Trivy와 Snyk 중 뭐가 낫나요?
- Q. 로컬에서도 스캔을 돌려야 하나요?
- Q. 스캔 결과를 어떻게 팀에 공유하나요?
- 마무리: Trivy 도입, 해야 할까요?
1년 전, 저희 팀의 컨테이너 보안 현실
솔직히 말씀드리면, Trivy를 도입하기 전 저희 팀의 컨테이너 이미지 보안은 거의 무방비 상태에 가까웠습니다. 매주 수십 개의 이미지를 빌드하고 배포하는데, 그 이미지 안에 어떤 취약점이 숨어있는지 아무도 신경 쓰지 않았거든요. “빌드 성공 = 배포 가능” 이라는 암묵적인 공식이 있었달까요.
그러다 어느 날 보안팀에서 연락이 왔습니다. 운영 중인 컨테이너에서 CRITICAL 등급의 CVE(Common Vulnerabilities and Exposures, 공통 취약점 목록)가 발견됐다는 거예요. 그게 아마 제가 Trivy 컨테이너 보안을 도입하게 된 결정적인 계기였을 겁니다.
지금으로부터 딱 1년 전 이야기입니다. 이 글은 그때부터 지금까지, CI/CD 파이프라인에 Trivy를 붙여 운영하면서 겪은 경험들을 솔직하게 정리한 회고록입니다. 결론부터 말씀드리자면 — 도입 가치는 충분했지만, 생각보다 쉽지는 않았습니다.
▲ Trivy가 CI/CD 파이프라인에 통합된 전체 보안 흐름. 코드 커밋부터 배포까지 각 단계에서 이미지 스캔이 자동으로 수행됩니다.
Trivy(트리비) 컨테이너 보안이 뭔지 먼저 짚고 갈게요
Trivy는 Aqua Security에서 만든 오픈소스 취약점 스캐너예요. 쉽게 말해서, 컨테이너 이미지 안에 어떤 보안 구멍이 있는지 자동으로 찾아주는 도구입니다.
처음 이 도구를 접했을 때 제가 놀랐던 건 스캔 대상의 다양함이었어요. 단순히 컨테이너 이미지뿐만 아니라 파일시스템, Git 리포지토리, Kubernetes 매니페스트, Helm 차트, Terraform 코드까지 스캔할 수 있거든요. “이게 진짜 취약점 스캐너 하나로 다 되는 건가?” 싶었는데, 실제로 써보니까 그게 맞더라고요.
Trivy가 탐지하는 주요 항목은 다음과 같습니다:
- OS 패키지 취약점 (Debian, Alpine, Ubuntu, Red Hat 등)
- 애플리케이션 의존성 취약점 (npm, pip, maven, go modules 등)
- 컨테이너 이미지 설정 오류 (Misconfiguration)
- 민감 정보 노출 (Secret scanning)
- 소프트웨어 공급망 위험 (SBOM, Software Bill of Materials)
설치도 정말 간단해요. 저는 처음에 이렇게 테스트해봤습니다:
# Homebrew (macOS/Linux)
brew install aquasecurity/trivy/trivy
# 또는 공식 설치 스크립트
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
# 간단한 이미지 스캔 테스트
trivy image nginx:latest
첫 스캔 결과를 보고 적잖이 충격받았습니다. 평소에 “최신 이미지 쓰면 안전하다”고 막연히 생각했는데, 실제로는 그렇지 않다는 걸 데이터로 확인하게 됐거든요. 이 경험이 팀 내 보안 인식 개선에 큰 역할을 했어요.
CI/CD 파이프라인에 Trivy 보안 스캔 통합하기
저희 팀은 GitHub Actions를 메인 CI/CD 도구로 사용하고 있었어요. Trivy를 파이프라인에 붙이는 방법은 여러 가지인데, 저는 단계적으로 적용했습니다.
1단계: 일단 스캔만 (경보 없음)
처음부터 빌드를 막으면 팀원들의 반발이 심할 것 같았어요. 그래서 먼저 결과만 리포트하는 것부터 시작했습니다. 이른바 “관찰 모드”였죠.
# .github/workflows/security-scan.yml
name: Container Security Scan
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
trivy-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t my-app:${{ github.sha }} .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'my-app:${{ github.sha }}'
format: 'table'
exit-code: '0' # 취약점 발견해도 빌드 실패 안 함 (관찰 모드)
severity: 'CRITICAL,HIGH'
output: 'trivy-results.txt'
- name: Upload scan results
uses: actions/upload-artifact@v4
with:
name: trivy-scan-results
path: trivy-results.txt
2단계: CRITICAL 취약점은 빌드 차단
약 2주간 관찰 모드로 돌리면서 팀원들한테 “이런 취약점들이 우리 이미지에 있어요” 하고 공유했어요. 데이터를 보니까 다들 위기감을 느끼더라고요. 그때부터 CRITICAL 등급 취약점이 있으면 빌드를 막는 정책을 제안했고, 팀에서 수용됐습니다.
# CRITICAL 취약점 발견시 빌드 실패
- name: Run Trivy (CRITICAL block)
uses: aquasecurity/trivy-action@master
with:
image-ref: 'my-app:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
exit-code: '1' # 취약점 있으면 빌드 실패
severity: 'CRITICAL'
ignore-unfixed: true # 아직 패치가 없는 취약점은 무시
- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always() # 실패해도 리포트는 업로드
with:
sarif_file: 'trivy-results.sarif'
여기서 ignore-unfixed: true 옵션이 굉장히 중요한데요, 이 부분은 뒤에서 더 자세히 설명하겠습니다.
.trivyignore 파일로 예외 관리하기
운영하다 보니 어쩔 수 없이 특정 CVE를 임시로 예외 처리해야 할 때가 생기더라고요. 이를 위해 저희는 .trivyignore 파일을 리포지토리 루트에 관리하기 시작했습니다.
# .trivyignore
# 형식: CVE-ID (한 줄에 하나씩)
# 반드시 주석으로 예외 사유와 검토 예정일을 기록할 것!
# [2025-03-15 예외 처리] 업스트림 패치 대기 중. 2025-04-15 재검토 예정
# 참고: 해당 경로에서 실제 코드 경로가 닿지 않음을 확인함
CVE-2023-XXXXX
# [2025-04-01 예외 처리] 테스트 환경 전용 이미지. 프로덕션 미배포
CVE-2024-YYYYY
💡 팁: .trivyignore에 사유와 재검토 날짜를 반드시 주석으로 달아두세요. 시간이 지나면 왜 예외 처리했는지 아무도 기억 못해요. 저도 처음에 이걸 안 지키다가 나중에 엄청 고생했거든요 ㅎㅎ
▲ GitHub Security 탭에 Trivy SARIF 결과가 업로드된 모습. 취약점별 심각도, 영향 받는 파일, 수정 제안이 한눈에 보입니다.
⚠️ 1년간 맞닥뜨린 진짜 문제들
Trivy 도입이 순탄하기만 했으면 이 글이 얼마나 좋았을까요 ㅎㅎ. 현실은 달랐습니다. 솔직하게 공유해드릴게요.
문제 1: 노이즈가 너무 많다
초기에 가장 큰 불만이 뭐였냐면, 취약점이 너무 많이 나온다는 거였어요. 첫날 스캔 결과를 팀원들에게 공유했을 때 반응이 “이거 다 고쳐야 해요?” 였거든요.
실제로 대부분의 컨테이너 이미지를 처음 스캔하면 HIGH, MEDIUM 등급 취약점이 수십 개씩 나와요. 문제는 이 중 상당수가 실제로 해당 취약점 경로를 사용하지 않거나, 패치가 아직 존재하지 않는 경우라는 거더라고요.
이를 해결하기 위해 저희가 취한 전략:
- 단계별 적용: CRITICAL → HIGH 순으로 순차 적용. 처음부터 모든 심각도를 차단하지 않음
- ignore-unfixed 활용: 아직 패치 버전이 없는 취약점은 일단 무시
- 베이스 이미지 전략: debian 계열 대신 alpine이나 distroless 이미지로 전환해 기본 취약점 수 자체를 줄임
- 주기적인 리뷰: 격주로 팀에서 새로 발견된 취약점 리뷰 세션 진행
문제 2: 베이스 이미지가 주범인 경우가 많다
흥미로운 발견이었는데요, Trivy 스캔 결과를 분석해보니 우리가 직접 만든 코드에서 비롯된 취약점보다 베이스 이미지 자체의 취약점이 훨씬 많았어요.
예를 들어 python:3.10 같은 공식 이미지도 내부적으로 Debian 패키지들을 포함하고 있어서 취약점이 꽤 많이 나오거든요. 이 문제를 해결하기 위해 저희는 베이스 이미지 선택 기준을 다음과 같이 정리했습니다:
| 베이스 이미지 | 취약점 노출 수준 | 특징 | 추천 용도 |
|---|---|---|---|
| ubuntu:22.04 | 높음 | 친숙, 패키지 풍부 | 레거시 호환성 필요시 |
| debian:slim | 중간 | Debian 경량화 | 범용 서비스 |
| alpine:3.x | 낮음 | 초경량 (musl libc) | 단순 서비스 |
| gcr.io/distroless | 매우 낮음 | OS 도구 없음 | 프로덕션 Go/Java |
| scratch | 거의 없음 | 완전 빈 이미지 | 정적 바이너리 |
문제 3: 스캔 속도로 인한 개발자 불만
처음엔 매 PR마다 전체 스캔을 돌렸는데, 이미지가 크면 스캔 시간이 꽤 길어져요. 개발자들이 “PR 리뷰가 늦어진다”는 불만을 토로하기 시작했거든요.
이 문제는 캐싱 전략으로 해결했습니다:
# Trivy DB 캐싱으로 스캔 속도 개선
- name: Cache Trivy DB
uses: actions/cache@v4
with:
path: ~/.cache/trivy
key: ${{ runner.os }}-trivy-db-${{ github.run_id }}
restore-keys: |
${{ runner.os }}-trivy-db-
- name: Run Trivy with cache
uses: aquasecurity/trivy-action@master
with:
image-ref: 'my-app:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
exit-code: '1'
severity: 'CRITICAL,HIGH'
cache-dir: ~/.cache/trivy
skip-db-update: false
캐싱 적용 후 스캔 시간이 눈에 띄게 줄었어요. Trivy는 취약점 DB를 처음 다운로드할 때 시간이 걸리는데, 이걸 캐시해두면 이후 실행에서 훨씬 빨라지거든요.
🎉 1년 후, 실제로 달라진 것들
회고의 핵심 질문으로 돌아옵니다. 컨테이너 이미지 보안, 정말 개선되었을까요?
결론부터 말씀드리면 — 네, 확실히 개선됐습니다. 다만 기대했던 방식과는 조금 달랐어요.

▲ Trivy 도입 후 1년간 CRITICAL/HIGH 취약점 탐지 및 해결 추이. 초기에 취약점이 급증하는 것처럼 보이는 건 기존에 모르고 지나쳤던 것들이 가시화됐기 때문입니다.
가시화된 변화들
- ✅ 베이스 이미지 전략 정착: 팀 전체가 이미지 선택 시 취약점 수를 기준 중 하나로 고려하게 됨
- ✅ 최신 이미지 업데이트 주기 단축: 예전엔 6개월씩 방치하던 베이스 이미지를 이제는 월 1회 업데이트
- ✅ 보안 인식 향상: 개발자들이 PR 올리기 전 직접 로컬에서 trivy를 돌려보는 문화 형성
- ✅ SBOM 생성 자동화: 각 릴리스마다 SBOM(소프트웨어 부품 목록)이 자동 생성되어 감사 대응에 활용
- ✅ 보안팀과의 소통 개선: “우리 이미지 상태”를 데이터로 보여줄 수 있게 되어 보안팀과 대화가 훨씬 수월해짐
솔직히 아쉬운 점들
- ⚠️ MEDIUM 등급 취약점은 여전히 방치 중 — 우선순위에 밀려서
- ⚠️ .trivyignore 예외 항목이 시간이 지나면서 관리가 느슨해지는 경향
- ⚠️ 런타임(실행 중인 컨테이너)의 취약점은 별도 도구가 필요 — Trivy 컨테이너 보안만으로는 커버 안 됨
- ⚠️ false positive(오탐)가 가끔 발생해 개발자들이 스캔 결과를 무감각하게 보는 현상
DevSecOps 관점에서의 교훈
1년을 운영하면서 깨달은 게 있는데요, 도구 도입보다 프로세스와 문화가 훨씬 중요하다는 거예요.
Trivy는 훌륭한 도구예요. 설치도 쉽고, CI/CD 통합도 어렵지 않아요. 근데 막상 팀에 도입하면 이런 질문들이 쏟아집니다:
- “취약점 발견되면 누가 고치나요?”
- “CRITICAL인데 패치가 없으면요?”
- “배포 일정인데 HIGH 취약점 때문에 못 올리나요?”
이런 질문들에 대한 팀의 합의된 답변이 없으면, 아무리 좋은 도구도 형식적인 체크박스가 되어버려요.
저희가 결국 정착시킨 정책은 이렇습니다:
- CRITICAL 취약점 + 수정 가능한 버전 존재 → 배포 전 반드시 수정
- CRITICAL 취약점 + 수정 버전 없음 → 보안팀 승인 후 임시 예외 처리, 최대 30일 유효
- HIGH 취약점 → 다음 스프린트 내 수정 목표 (강제 아님)
- MEDIUM 이하 → 분기별 리뷰에서 일괄 검토

▲ Trivy 취약점 대응 정책 요약. 심각도 등급별로 대응 방식과 기한을 명확히 정의하는 것이 성공적인 DevSecOps 운영의 핵심입니다.
자주 묻는 질문 (FAQ)
Q. Trivy와 Snyk 중 뭐가 낫나요?
둘 다 훌륭한 도구예요. Trivy는 오픈소스이고 완전 무료인 반면, Snyk는 SaaS 기반이라 대시보드와 팀 협업 기능이 더 강력해요. 소규모 팀이라면 Trivy로 충분하고, 엔터프라이즈 환경이라면 Snyk 같은 유료 솔루션이 운영 부담을 줄여줄 수 있습니다.
Q. 로컬에서도 스캔을 돌려야 하나요?
권장해요! CI/CD에서 걸리면 되돌아가는 시간이 길어지거든요. 저는 개인적으로 git pre-push 훅에 Trivy를 연결해놨어요. 푸시 전에 로컬에서 먼저 확인하는 거예요.
Q. 스캔 결과를 어떻게 팀에 공유하나요?
저희는 GitHub의 Security 탭(SARIF 업로드 활용)을 주로 사용하고, Slack으로 CRITICAL 발견 시 즉시 알림을 보내도록 했어요. 주간 리포트는 간단한 쉘 스크립트로 취약점 카운트를 집계해서 공유하고 있습니다.
마무리: Trivy 도입, 해야 할까요?
1년을 써보고 내리는 결론은 “무조건 도입하세요”입니다. 다만 기대치를 조정하세요.
Trivy는 마법이 아니에요. 도입한다고 갑자기 이미지가 안전해지지는 않아요. 하지만 눈에 보이지 않던 것들을 보이게 만들어줍니다. 그리고 보안에서는 가시화가 첫 번째 단계예요.
“Trivy를 붙였더니 보안이 100% 완벽해졌다”는 글은 과장이에요. 현실은 “Trivy를 붙이고 나서야 우리 이미지가 얼마나 취약했는지 알게 됐다”에 가까워요. 그리고 그걸 알게 됐기 때문에, 조금씩 나아질 수 있었습니다.
DevSecOps는 한 번의 도구 도입으로 완성되지 않아요. 꾸준한 리뷰, 팀 합의, 프로세스 개선의 반복이 필요하죠. Trivy는 그 여정을 시작하기에 좋은 출발점입니다.
다음 글에서는 Trivy를 활용한 SBOM(소프트웨어 부품 목록) 생성과 공급망 보안에 대해 다뤄볼 예정입니다. 요즘 화두인 소프트웨어 공급망 공격에 Trivy 컨테이너 보안이 어떻게 도움이 되는지 정리해볼게요.
긴 글 읽어주셔서 감사합니다. 혹시 비슷한 경험 있으신 분이나 질문 있으신 분은 댓글로 남겨주세요! 🙏
![[보안] CI/CD 파이프라인에 Trivy 적용 1년 회고: 컨테이너 이미지 보안, 정말 개선됐을까?](https://blog.pswq.net/wp-content/uploads/2026/08/trivy-ci-cd-integration-1year-retrospective-thumbnail.jpg)
![[보안] 리눅스 서버 하드닝 전략 재점검 체크리스트](https://blog.pswq.net/wp-content/uploads/2026/08/linux-cve-spike-server-hardening-checklist-thumbnail.jpg)




![[보안] OWASP ZAP으로 XSS 취약점 진단 및 해결: 실제 웹 애플리케이션 디버깅 사례](https://blog.pswq.net/wp-content/uploads/2026/08/owasp-zap-xss-vulnerability-debugging-case-thumbnail.jpg)




![[보안] YubiKey 1년 사용 후기: 피싱 공격 방어, 정말 효과적일까?](https://blog.pswq.net/wp-content/uploads/2026/08/yubikey-one-year-review-phishing-defense-thumbnail.jpg)





![[보안] OPNsense 보안 강화 체크리스트: 홈랩부터 소규모 오피스까지](https://blog.pswq.net/wp-content/uploads/2026/08/opnsense-firewall-security-hardening-checklist-thumbnail.jpg)




![[보안] WireGuard vs OpenVPN vs IPsec: 홈랩 및 소규모 비즈니스 VPN 보안 비교](https://blog.pswq.net/wp-content/uploads/2026/08/wireguard-openvpn-ipsec-vpn-security-comparison-thumbnail.jpg)




![[백엔드] JWT 오류 해결: 만료와 서명 검증 디버깅 가이드](https://blog.pswq.net/wp-content/uploads/2026/07/jwt-authentication-error-debugging-token-validation-thumbnail.jpg)





![[보안 사례] 컨테이너 이미지 보안 강화: Trivy를 활용한 CI/CD 통합 운영 후기](https://blog.pswq.net/wp-content/uploads/2026/07/container-image-security-trivy-ci-cd-case-study-thumbnail.jpg)




![[보안] 시크릿 관리 솔루션 비교: HashiCorp Vault와 AWS Secrets Manager, 팀에 맞는 선택 기준](https://blog.pswq.net/wp-content/uploads/2026/07/secret-management-solution-comparison-vault-aws-thumbnail.jpg)





![[보안] 리눅스 서버 보안 강화 체크리스트: SSH부터 시스템까지 10가지 필수 점검](https://blog.pswq.net/wp-content/uploads/2026/07/linux-server-hardening-checklist-ssh-system-thumbnail.jpg)



