CSR에서 SSR로, Istio까지 GitOps로 (7) — 결과와 남은 과제
PoC 는 “틀”을 만든 데서 멈췄다. 이번 편에서는 무엇을 검증했고 무엇은 못 했는지, 다음은 어떻게 갈지, 그리고 돌아보며 든 생각을 정리한다.
1. 검증한 것
| # | 항목 | 근거 |
|---|---|---|
| 1 | SSR 앱을 기존 Kaniko 빌드 체계로 이미지화 | PVC 공유로 다른 레포의 소스를 빌드 |
| 2 | 젠킨스 → Git → ArgoCD GitOps 배포 | 태그 커밋 → 자동 sync → 새 파드 |
| 3 | ALB 외부 노출 + 공인 IP 유지 | ALB 가 재생성돼도 같은 IP |
| 4 | nginx 를 앞에 두고 기존 설정 보존 | 보안 헤더 + Next 의 307 /login 동시 확인 |
| 5 | nginx.conf GitOps | ConfigMap 을 헬름이 만들고 설정 변경 시 자동 재시작 |
| 6 | Istio 를 ArgoCD 로 설치 (App of Apps) | 부모 1 + 자식 3 Synced·Healthy, values 로 과목·버전 관리 |
| 7 | 사이드카 + ingressgateway 입구 | istio_requests_total 에 게이트웨이 → 앱 |
| 8 | 게이트웨이 → 앱 자동 mTLS | 양쪽 SPIFFE ID |
| 9 | 네임스페이스·주입 라벨 코드화 | ArgoCD 관리 표시, 파드 무중단 |
정리하면 이 PoC 가 증명한 건 “SSR 앱을 기존 인프라 방식으로, GitOps 로, mesh 안에 배포할 수 있다”까지다.
2. 검증하지 못한 것
| 한계 | 이유 | 그래서 |
|---|---|---|
| 백엔드 연동 | PoC 환경에 백엔드가 없고, 환경별 빌드가 없어 API 주소가 환경마다 안 박힌다 | 백엔드 프록시 블록은 참고 파일로만 있다 |
| 성능 | 도입 목적 1순위가 초기 로딩인데 측정 전 | TTFB·FCP·LCP 등을 CSR 과 같은 조건에서 재야 한다 |
| 운영 mesh 와의 동일성 | PoC 는 새로 깐 Istio | 스테이징에서 운영 구성으로 다시 확인 |
| replicas 1, HPA 끔 | PoC 규모 | 롤링·PDB·HPA 미검증. 롤링 전략은 이미 위험이 드러났다 |
| STRICT mTLS | 기본 PERMISSIVE 그대로 | 입구가 게이트웨이로 옮겨 가 가능해졌지만 미검증 |
| 같은 클러스터 | 젠킨스·ArgoCD·앱이 한 클러스터 | 스테이징은 별도 클러스터라 네트워크·권한이 다르다 |
“SSR 로 바꾸면 좋아진다”는 아직 증명하지 않았다. 그건 개발팀의 환경별 빌드 이후의 일이다.
3. 다음 단계 — 발사 버튼
1
2
3
4
5
6
7
8
9
[완료] PoC — 별도 환경에서 배포 틀 + mesh 편입
↓
[개발팀] 환경별 빌드(dev/stg/prod) 분리 → 이미지 교체 후 통합 검증
↓
[인프라] ArgoCD 를 툴 클러스터에 정식 구축, 스테이징 클러스터를 원격 등록
↓
[함께] 스테이징 — 백엔드와 프론트를 동시에 ArgoCD 로, SSR 스택은 병행 구축 후 DNS 스위칭
↓ 일정 기간 검증
[함께] 운영
틀은 다 만들어 두었다. 개발팀 개발이 끝나 스테이징 적용이 필요한 순간이 곧 발사 시점이다.
스테이징은 PoC 처럼 같은 자리에서 바꿔 끼우지 않고 새 스택을 나란히 구축한 뒤 DNS 로 넘긴다. PoC 에서 차트 교체 장애와 ALB 재생성 끊김을 겪고 정한 방식이다.
| 교체 (PoC 에서 한 방식) | 병행 구축 후 스위칭 | |
|---|---|---|
| 기존 리소스와 충돌 | selector·라벨·ALB 이름·IP 가 부딪힌다 | 없다 |
| 다운타임 | ALB 재생성 동안 끊긴다 | DNS 전환 시점뿐 |
| 롤백 | 다시 교체 (또 끊긴다) | DNS 를 되돌린다 |
| 감수할 것 | — | 두 스택이 공존하는 기간의 비용, DNS TTL |
스테이징은 처음부터 mesh.enabled: true 로 새 ALB 를 만든다. 입구 전환을 따로 할 일이 없다.
4. 남은 과제
원칙은 PoC 내내 그랬듯 “실행에 지장이 없는 건 PoC 하며 고친다, 되돌리기 어려운 건 스테이징 전에 정한다”다.
개발팀과 함께
| 항목 | 이유 |
|---|---|
| 환경별 빌드 분리 (선행 조건) | API 주소가 빌드 때 박힌다 |
packageManager 명시 | 없으면 환경마다 다른 pnpm 이 깔린다 |
| 헬스체크 엔드포인트 | 생기면 probe 를 TCP → HTTP 로 |
standalone 출력 | 이미지 크기 절감 |
| SSR 서버 → 백엔드 경로 | nginx 경유 vs Service 직접. mesh 정책·mTLS 설계가 여기에 달렸다 |
인프라
| 항목 | 이유 |
|---|---|
롤링 전략 maxSurge 1 / maxUnavailable 0 | replicas 1 에서 배포 실패가 곧 장애다 |
| nginx 보안 설정을 이미지·차트에 선언 | AS-IS 는 배포 후 exec 로 넣었고, SSR 차트엔 아직 없다 |
앱 프로젝트의 istio-system 권한 좁히기 | 지금은 그 네임스페이스의 어떤 리소스든 만들 수 있다 |
skipSchemaValidation 제거 | 검증을 끈 상태는 빚이다 |
| Istio 업그레이드 (리비전 카나리) | 1.23 EOL. 차트에 장치를 미리 넣어 뒀다 |
| 젠킨스 에이전트 이미지에 yq 포함 | 수동 설치는 에이전트가 다시 뜨면 사라진다 |
| selfHeal | 관찰 후 켠다. 팀에서 kubectl 수동 수정이 잦다 |
| 백엔드 설정·시크릿의 Git 선언화 | 아직 별도 설정 잡이 만든다 |
5. 회고
잘한 것
- 처음 방향을 살렸다. 전임자가 그린 과목별 게이트웨이 구조, Kaniko 빌드, nginx 앞단을 그대로 두고 관리 방식만 바꿨다. 덕분에 운영과 비교하기 쉬웠고, 스테이징 이관도 “같은 구조를 다른 방식으로”라 설명이 짧다
- 되돌리기 쉬운 것부터 켰다. 사이드카(라벨)를 먼저, 입구 전환(ALB 재생성)을 나중에. 문제가 생겨도 범위가 좁았다
- 켜기 전에 렌더링했다. mesh 의 빈 값, 권한 부족, 어노테이션 문제를 커밋 전에 잡았다
- 로컬 재현으로 범위를 좁혔다. pnpm 12 와 게이트웨이 스키마 문제를 CI 도구 밖에서 먼저 분리했다
아쉬운 것
- 바꾼 것과 영향받는 것을 나란히 놓지 않았다. 장애 두 번과 끊김 세 군데가 전부 여기서 나왔다. 앞선 시리즈 5편에서 “클러스터 토폴로지를 초기에 확인하지 않았다”고 적었는데, 이번에도 형태만 바꿔 같은 실수를 했다
- 렌더링 확인을 한 번 건너뛰었다. 평소엔 지키다가 작은 수정이라 생각한 날 장애가 났다
- 자동 sync 를 끄고 안 켰다. 위험한 작업 중 끄는 건 맞았지만, 되돌리는 걸 체크리스트에 넣지 않았다
그리고
이 작업을 시작한 이유는 기술보다 “아무것도 모르는 채로 이어받은 시스템에, 편하려고 만든 스크립트가 또 쌓이는 것”이 싫어서였다. PoC 를 마친 지금은 “지금 무엇이 떠 있나”를 클러스터를 열지 않고 Git 과 ArgoCD 화면으로 답할 수 있다. Istio 를 어떻게 까는지도 더 이상 누군가의 기억에 있지 않고, values 파일 하나에 적혀 있다. 다음 사람이 이 레포를 열었을 때 “이건 왜 이렇게 했지”를 스크립트를 뒤지지 않고 알 수 있다면, 이번 작업은 목적을 이룬 것이라고 생각한다.
이전: (6) 트러블슈팅 30건에서 배운 것 · 처음: (1) 왜 프론트와 Istio인가 · 앞선 시리즈: 배포 자동화 리팩토링, 젠킨스에서 ArgoCD로 (1)
(회사·인프라 관련 실명, 클러스터 식별자, 내부 호스트 주소, 레포·시크릿 이름 등은 전부 임의로 재구성해 게시했다.)