홈랩 인프라 · Mac mini

mac-mini-infra — 요청이 컴포넌트를 거쳐 파드까지

기술인프라mac-mini
목차

네 갈래로 살펴봐요

1어떤 계층 위에 올라가 있는지 (colima·k3s)
2클러스터 안엔 어떤 컴포넌트가 있는지
3요청이 컴포넌트를 어떻게 거쳐 파드까지 가는지
4어떻게 배포되고, 무엇을 어떻게 막는지
01

한 대의 Mac mini 위에 층층이 올려요

macOS 위 colima가 리눅스 VM을 띄우고, 그 안에서 k3s가 돌아요.

스택 계층

안쪽으로 갈수록 이렇게 감싸요

macOS — Mac mini 호스트
colima — 리눅스 VM + Docker 런타임
k3s — 경량 쿠버네티스 (VM 안에서 구동)
워크로드 — Traefik · cert-manager · CNPG · Redis · Argo Rollouts · ELK · 앱
colima: macOS에서 리눅스 VM·컨테이너 런타임을 띄우는 도구 · k3s: 가볍게 만든 쿠버네티스 배포판
역할 구분

colima는 바닥, k3s는 그 위 오케스트라예요

colima
  • 리눅스 VM과 Docker 런타임을 제공해요
  • 이미지 빌드의 로컬 컨텍스트예요
  • --kubernetes로 VM 안의 k8s를 띄워요
k3s
  • 실제 클러스터 — 파드·서비스를 굴려요
  • colima VM 안에서 돌아요(독립 아님)
  • Traefik·DB·롤아웃이 여기 얹혀요
오케스트레이션: 여러 컨테이너의 배치·연결·복구를 자동으로 관리하는 것 · 데이터는 hostPath PV(Retain)로 재기동에도 보존
02

클러스터는 역할별로 나뉘어 있어요

입구·인증서·DB·캐시·배포·관측이 각자 제 몫을 해요.

입구 · 인증서 · DB

바깥과 맞닿는 세 컴포넌트예요

🚪
Traefik
인그레스 컨트롤러. :80/:443 입구에서 TLS를 끊고 라우팅해요.
🔐
cert-manager
인증서 발급. 공개는 Let's Encrypt, 내부는 self-signed.
🐘
CNPG (Postgres)
Postgres 오퍼레이터+HA. mTLS 강제, 매일 덤프 백업.
인그레스(Ingress): 클러스터 바깥 요청을 안의 서비스로 들여보내는 입구 · TLS 종료(termination): 암호화를 입구에서 풀어 내부로 넘기는 것
캐시 · 배포 · 관측

안쪽을 굴리는 세 컴포넌트예요

Redis
캐시·세션 저장소. ACL 계정으로만 접근, 클러스터 내부용.
🔁
Argo Rollouts
점진 배포. 앱을 블루그린 Rollout으로 무중단 교체해요.
📊
ELK 관측
Filebeat→Elasticsearch→Kibana로 로그, Prometheus로 지표.
ACL(Access Control List): 계정별 접근 권한 목록 · ELK: Elasticsearch·Logstash/Beats·Kibana 로그 스택 · Prometheus: 지표 수집·질의 시스템
네임스페이스

역할별로 네임스페이스를 갈라놨어요

네임스페이스담는 것
apps앱 Rollout, IngressRoute, 앱 전용 미들웨어
databasesCNPG Postgres, Redis
traefikTraefik, 공용 미들웨어(리다이렉트·레이트리밋 등)
monitoringELK, Prometheus
cert-manager인증서 발급기(ClusterIssuer·Certificate)
네임스페이스: 쿠버네티스에서 리소스를 논리적으로 격리하는 구획 · Traefik은 네임스페이스를 넘어 미들웨어를 참조할 수 있게 설정됨
03

주소를 치면 이 순서로 흘러가요

DNS부터 파드까지, 요청이 지나는 길을 따라가 봐요.

요청 흐름 개요

바깥에서 파드까지 이렇게 이어져요

클라이언트
DNS
<도메인>
홈 라우터
NAT 80/443
Mac mini
colima·k3s
Traefik
Service
→ Pod

DB 포트는 라우터 NAT로 막혀 있어 인터넷에서 못 닿아요(LAN 전용).

NAT(Network Address Translation): 공인 IP로 온 특정 포트를 내부 장비로 넘겨주는 포트 포워딩
요청 단계

Traefik 안에서 이 순서로 처리돼요

1
DNS가 <도메인>을 홈 라우터 공인 IP로 풀고, 라우터가 80/443을 Mac mini로 넘겨요.
2
Traefik의 LoadBalancer 서비스가 그 포트를 직접 받아요.
3
:80로 오면 catch-all 라우트가 301로 https 리다이렉트해요.
4
:443에서 인증서로 TLS를 종료해요. 내부 도메인은 이때 클라이언트 인증서(mTLS)까지 검사해요.
5
Host+PathPrefix 규칙(priority)으로 라우트를 매칭해요.
6
매칭된 라우트의 미들웨어 체인을 순서대로 통과해요.
7
Service → Rollout이 관리하는 Pod로 전달돼요.
IngressRoute: Traefik이 쓰는 라우팅 규칙(Host·경로·우선순위·미들웨어) · 미들웨어: 요청을 통과시키며 변형·검사하는 중간 단계
:80 처리

평문 요청은 곧장 https로 돌려보내요

1
:80(web) 입구엔 PathPrefix("/") catch-all 라우트가 있어요.
2
redirect-https 미들웨어가 301 → https로 돌려보내요. 백엔드는 건드리지 않아요(blackhole).
3
단, 인증서 발급용 ACME 챌린지는 우선순위가 높아 리다이렉트를 비껴가요.
ACME: 인증서를 자동 발급받는 프로토콜(Let's Encrypt) · HTTP-01 챌린지: 특정 경로 응답으로 도메인 소유를 증명하는 방식
TLS · mTLS

공개는 서버 인증서만, 내부는 양방향이에요

공개 도메인
  • Let's Encrypt 서버 인증서로 TLS 종료
  • 클라이언트 인증서 요구 안 함
  • 누구나 https로 접근 가능
내부 도메인
  • 서버 인증서 + mtls-required TLSOption
  • 핸드셰이크에서 클라이언트 인증서 검증
  • 유효한 인증서 없으면 연결 거부
mTLS(mutual TLS): 서버뿐 아니라 클라이언트도 인증서로 서로를 검증하는 상호 인증 · TLSOption: Traefik의 TLS 동작(클라이언트 인증 요구 등) 설정
미들웨어 체인

라우트마다 통과 순서가 달라요

라우트미들웨어 순서
공개 APIretry (dial 오류 재시도)
내부 adminmTLS 만 — retry 제외(비멱등 쓰기 보호)
내부 그 외mTLS → retry
미니앱stripPrefix → retry
레거시 프록시rate-limit → 외부 구서버 재암호화 전달
멱등(idempotent): 같은 요청을 여러 번 보내도 결과가 같은 성질 — 쓰기 API는 비멱등이라 재시도를 빼요 · StripPrefix: 경로 앞부분을 떼고 백엔드로 넘기는 미들웨어
미니앱 경로 라우팅

한 도메인 아래 경로로 앱을 갈라요

1
Host(<미니앱도메인>) && PathPrefix("/<앱>")으로 매칭해요.
2
StripPrefix/<앱> 접두어를 떼요.
3
retry(3회/100ms, TCP dial 오류만)를 붙여요.
4
백엔드는 /api-public/... 형태로 요청을 받아 처리해요.
블루그린 교체 중 일관성을 위해 라우트에 sticky 쿠키(secure·httpOnly)를 붙여요.
sticky 쿠키: 같은 클라이언트를 같은 백엔드(버전)로 계속 붙여주는 세션 고정 장치
심화 · Traefik → Pod

"서비스를 거친다"지만, 기본값은 건너뛰어요

IngressRoute
라우트 매칭
Traefik 내부 LB
Ready Pod IP 풀
Pod IP:port
직접 연결

Traefik 기본(nativeLB=false)은 Service ClusterIP·kube-proxy를 거치지 않고 Ready Pod IP로 직접 로드밸런싱해요. Service는 라우팅 홉이 아니라 파드를 찾는 명부로 쓰여요.

ClusterIP: 서비스에 부여되는 가상 IP(실체 없음) · 로드밸런싱: 여러 파드에 요청을 나눠 보내는 것 · nativeLB: 켜면 ClusterIP를 백엔드로 쓰는 Traefik 옵션(기본 꺼짐)
직접 경로

Ready 파드 목록을 보고 곧장 보내요

1
라우트가 매칭되면 대상 Service를 참조해요.
2
Traefik은 그 Service의 EndpointSlice를 watch해 Ready Pod IP 풀을 유지해요. (Traefik v3.1+)
3
내부 LB가 풀에서 하나를 골라 PodIP:port로 직접 연결해요.
4
이 경로엔 ClusterIP·kube-proxy·CoreDNS가 끼지 않아요.
EndpointSlice: 서비스 뒤의 Ready 파드 IP·포트 목록(옛 Endpoints의 확장판) · watch: 쿠버네티스 API 변경을 실시간 구독하는 것 · 내부 LB: Traefik이 자체적으로 파드를 고르는 로드밸런서
엔드포인트 관리

준비된 파드만 목록에 올라가요

1
kubelet이 각 파드의 readiness probe를 주기적으로 검사해요.
2
Ready인 파드만 EndpointSlice 컨트롤러가 슬라이스에 등록해요.
3
probe 실패·종료 중인 파드는 목록에서 즉시 제외돼 트래픽이 안 가요.
4
덕분에 배포·장애 때 트래픽이 자동으로 건강한 파드로만 흘러요.
kubelet: 각 노드에서 파드를 실행·감시하는 에이전트 · readiness probe: 파드가 요청 받을 준비가 됐는지 확인하는 검사 · 컨트롤러: 원하는 상태로 맞추는 제어 루프
대조 · ClusterIP 경로

파드끼리 부를 땐 이 길로 가요

1
앱이 svc.ns.svc.cluster.local을 부르면 CoreDNSClusterIP로 풀어줘요.
2
ClusterIP로 간 패킷을 노드의 kube-proxy iptables 규칙이 가로채요.
3
규칙이 랜덤 Ready 엔드포인트를 골라 DNAT으로 Pod IP로 바꿔요.
4
Traefik도 nativeLB=true면 이 경로를 써요(기본은 아님).
CoreDNS: 클러스터 내부 DNS · kube-proxy: 서비스 가상 IP를 실제 파드로 잇는 규칙을 노드마다 프로그래밍 · iptables: 리눅스 커널의 패킷 처리 규칙 · DNAT: 목적지 주소를 바꾸는 것
전환의 원리

트래픽 전환은 셀렉터 한 줄로 일어나요

1
Argo Rollouts가 각 버전 ReplicaSetpod-template-hash를 붙여요.
2
승격 때 activeService의 selector를 새 버전 hash로 바꿔요.
3
Service가 새 파드를 가리키니 EndpointSlice가 새 Pod IP로 갱신돼요.
4
이를 보던 Traefik이 새 파드로 보내기 시작해요 — 무중단 전환 완료.
셀렉터(selector): 어떤 파드를 고를지 정하는 라벨 조건 · ReplicaSet: 같은 파드 여러 개를 유지하는 단위 · pod-template-hash: 버전(RS)을 구분하는 자동 라벨
04

push 한 번이면 검증까지 흘러가요

Argo Rollouts 블루그린으로 새 버전을 검증한 뒤 트래픽을 넘겨요.

배포 파이프라인

코드부터 트래픽 전환까지 이렇게 흘러요

1
main push → self-hosted GHA 러너가 인프라 레포를 깨끗이 새로 clone해요.
2
이미지를 빌드·태깅하고 helm upgrade로 배포해요.
3
블루그린: 새 버전을 preview 서비스 뒤에 먼저 띄워요.
4
prePromotion 스모크 테스트: 헬스 UP·10초 지속·실제 엔드포인트 2xx 확인.
5
통과하면 active 서비스를 새 버전으로 전환(무중단). 실패하면 안 넘겨요.
6
postPromotion 5xx 감시(옵션) 후 결과를 알림으로 보내요.
블루그린: 새(green)·현(blue) 버전을 함께 띄우고 검증 후 트래픽을 한 번에 전환하는 무중단 배포 · GHA: GitHub Actions CI
무중단 안전장치

전환 순간을 이렇게 지켜요

🕒
graceful shutdown
preStop 대기 + 종료 유예로 처리 중 요청을 흘려보내요.
↩️
즉시 롤백
이전 버전을 잠시 유지해, 문제 시 바로 되돌려요.
승격 전 3중 검증
헬스·지속·실제 엔드포인트를 통과해야 트래픽을 넘겨요.
graceful shutdown: 새 요청은 끊되 처리 중 요청은 끝까지 마치고 종료하는 방식 · preStop: 파드 종료 직전 실행되는 훅
05

노출 면마다 다르게 막아요

공개·내부·관측·DB를 각각 다른 방식으로 통제해요.

노출 통제

면마다 방어선이 달라요

노출 면방어
공개 앱Let's Encrypt TLS, 클라이언트 인증서 없음, 레이트리밋 가능
내부 admin·devmTLS 강제 — 유효한 클라이언트 인증서 없으면 연결 거부
관측 UI(Kibana)IPAllowList(LAN 전용) 단일 방어 + self-signed 인증서
DB 포트인터넷 미노출(NAT 차단) + Postgres mTLS / Redis ACL
IPAllowList: 허용된 IP 대역에서 온 요청만 통과시키는 미들웨어 · self-signed 인증서는 내부(LAN) 도메인 전용
용어 사전

여기 나온 용어를 한눈에 정리했어요

colima: macOS에 리눅스 VM·컨테이너 런타임을 띄우는 도구
k3s: 가볍게 만든 쿠버네티스 배포판
인그레스: 클러스터 바깥 요청을 안으로 들이는 입구
IngressRoute: Traefik의 라우팅 규칙(Host·경로·미들웨어)
미들웨어: 요청을 통과시키며 변형·검사하는 단계
TLS 종료: 암호화를 입구에서 풀어 내부로 넘김
mTLS: 서버·클라이언트가 서로 인증서로 검증
ClusterIP: 서비스의 가상 IP(실체 없음)
kube-proxy: 서비스 IP를 실제 파드로 잇는 규칙을 노드에 프로그래밍
EndpointSlice: 서비스 뒤 Ready 파드 IP 목록
CoreDNS: 클러스터 내부 DNS
kubelet: 노드에서 파드를 실행·감시하는 에이전트
readiness probe: 파드가 요청 받을 준비됐는지 검사
Argo Rollouts: 점진 배포(블루그린·카나리) 컨트롤러
블루그린: 두 버전을 함께 띄우고 검증 후 전환하는 무중단 배포
CNPG: 쿠버네티스용 Postgres 오퍼레이터

한 대의 Mac mini가
입구부터 배포·관측까지 다 해내요

기술 · 인프라 · mac-mini

1 / 28
← → · Space · F 전체화면