back

squid

개요

레거시 포워드 프록시 서버였던 EC2 기반의 Tinyproxy를 Kubernetes 클러스터에서 구동되는 squid로 마이그레이션한 여정을 소개합니다.

TLDR

환경

구성 가이드

Squid

슬픈 현실부터 말하자면 오픈소스 프록시 서버인 squid는 공식 컨테이너 이미지는 제공하지만 헬름 차트는 제공하지 않습니다. 그래서 제가 직접 squid 헬름 차트를 개발했고 현재 지속적으로 개선하고 있습니다.

기존 Tinyproxy 구성 (레거시)

포워드 프록시 역할을 EC2 기반의 tinyproxy 서버 2대가 하고 있었습니다.

--- title: Legacy forward proxy server architecture --- flowchart LR s["`**3rd Party Services** hooks.slack.com, api.slack.com, etc.`"] subgraph "VPC" subgraph k8s["`Kubernetes`"] c1(["Pod"]) c2(["Pod"]) end direction LR subgraph prv["Private Subnet"] nlb["`**NLB** Internal`"] end subgraph pub["Public Subnet"] tp1["`EC2 tinyproxy`"] tp2["`EC2 tinyproxy`"] end eip1(["Elastic IP"]) eip2(["Elastic IP"]) igw["`Internet Gateway`"] end c1 & c2 --Send request--> nlb nlb --> tp1 nlb --> tp2 tp1 --Rotate--> eip1 --> igw tp2 --Rotate--> eip2 --> igw igw --> s style tp1 fill:darkorange, color: white style tp2 fill:darkorange, color: white

프록시 서버 운영 중에 여러가지 어려움이 있었습니다.

이런 여러가지 운영의 한계점 때문에 저는 이 Tinyproxy 서버들을 쿠버네티스 위에 squid로 옮기기로 결심합니다.

Squid proxy

그리고 다음과 같이 컨테이너 기반의 포워드 프록시 서버를 Kubernetes 클러스터에 구성했습니다.

Squid 설치는 제가 직접 만든 squid 헬름차트를 사용하고 ArgoCD의 Application으로 말아 올려 GitOps 패턴으로 운영했습니다.

--- title: Squid helm chart in operation phase --- flowchart LR subgraph "Kubernetes Cluster" direction LR argo["`**Pod** ArgoCD`"] chart["`**Application** squid`"] subgraph ns["Namespace squid"] d["Deployment"] rs["ReplicaSet"] p1["Pod"] p2["Pod"] p3["Pod"] end end argo --Sync--> chart --> d --> rs --> p1 & p2 & p3 style chart fill:darkorange, color:white

이 아키텍처에서 Squid 파드는 포워드 프록시로서 내부 파드들을 직접 노출하지 않고도, 외부 요청을 대신 처리해주고 응답을 가져다주는 중간다리 역할을 합니다:

--- title: squid on kubernetes as forward proxy --- flowchart LR admin["Cluster Admin"] admin --> helm subgraph "AWS Seoul Region" subgraph "Kubernetes Cluster" subgraph "Availability Zone A" subgraph "squid namespace AZ-A" subgraph squidA["squid pod (AZ-A)"] c1a["`**container** squid (6.10)`"] c2a["`**container** squid-exporter`"] end end cpA["`**Pod** other pod(s) AZ-A`"] end subgraph "Availability Zone C" subgraph "squid namespace AZ-C" subgraph squidC["squid pod (AZ-C)"] c1c["`**container** squid (6.10)`"] c2c["`**container** squid-exporter`"] end end cpC["`**Pod** other pod(s) AZ-C`"] prom["`**Pod** prometheus-server`"] end helm("`**Helm Chart** squid`") helm --> squidA helm --> squidC prom --Scrape metrics--> c2a prom --Scrape metrics--> c2c end subgraph "Network Infrastructure" subgraph "AZ-A Network" ngwA["`NAT Gateway`"] end subgraph "AZ-C Network" ngwC["`NAT Gateway`"] end igw["`Internet Gateway`"] end end slack["`**Third Party** hooks.slack.com, api.slack.com, etc.`"] cpA --send req to proxy--> c1a --forward--> ngwA --> igw cpC --send req to proxy--> c1c --forward--> ngwC --> igw igw --> slack note["`squid was deployed by helm chart across multiple AZs`"] style squidA fill:darkorange, color:white style squidC fill:darkorange, color:white style note fill:transparent, stroke:transparent

다행히 누군가 squid-exporter를 이미 만들어두어서 아래와 같이 squid 파드에 사이드카로 추가해 메트릭을 쉽게 수집할 수 있었습니다.

--- title: Metrics scraping architecture for squid pods --- flowchart LR subgraph ns["Namespace squid"] subgraph p["Pod squid"] direction LR c1["`**Container** squid`"] c2["`**Container** squid-exporter`"] end svc["`**Service** ClusterIP`"] deploy["`**Deployment** squid`"] end subgraph ns2["Namespace monitoring"] prom["`**Pod** prometheus-server`"] end deploy --Control replicas--> p c2 --Scrape--> c1 prom --Scrape--> svc --> c2 style c2 fill:darkorange,color:white

Local AZ 통신 전략

Squid 운영시 Local AZ 통신을 위한 Service의 trafficDistribution 설정도 중요합니다. 네트워크 지연시간 감소(소폭의), 장애 내결함성, AZ간(Inter AZ) 데이터 전송 비용 절감 3가지 이점을 얻을 수 있기 때문에 안하면 손해인 설정입니다.

참고로 Kubernetes 1.31부터는 trafficDistribution 기능이 Beta로 승격되어 기본 활성화되었으며, 기존 topologyAwareRouting 어노테이션 대신 Service spec의 trafficDistribution: PreferClose 설정을 통한 Local AZ 통신 최적화가 권장됩니다.

--- title: topologyAwareRouting vs trafficDistribution --- graph LR A[K8s < 1.31] --> B["`**topologyAwareRouting** Annotation (Legacy)`"] C[K8s ≥ 1.31] --> D["`**trafficDistribution** PreferClose`"] B --Migration--> D B --> F["`Inter AZ DTO Cost Reduction`"] D --> F style D fill:darkorange, color:white style B stroke:red, color:white note["As of Kubernetes 1.31, the trafficDistribution feature has been moved to beta."] style note fill:transparent, stroke:transparent, color:lightgray

더 자세한 사항은 #50229Comparison with service.kubernetes.io/topology-mode: Auto 페이지를 참고하세요.

--- title: Service to EndpointSlice archirecture --- flowchart LR subgraph K8S["Kubernetes Cluster"] direction LR CLIENT(["`Client Pod AZ A`"]) --> SVC[Service] subgraph "Control Plane" ESC[EndpointSlice<br/>Controller] end ESC --> ES["`**EndpointSlice** with zone hints`"] SVC --> PROXY[kube-proxy] PROXY --Watch zone hinsts--> ES PROXY --Route to local AZ--> POD1(["`Pod 1`"]) PROXY --> POD2(["Pod 2"]) subgraph "AZ A" POD1 CLIENT end subgraph "AZ C" POD2 end end linkStyle 2 stroke: darkorange linkStyle 0 stroke: darkorange linkStyle 4 stroke: darkorange style ES fill: darkorange, color: white

trafficDistribution 설정 방법은 매우 간단합니다. 클러스터 내부 통신에서 되도록 같은 AZ(Local AZ)간의 통신을 선호하도록 spec.trafficDistribution 필드를 Service에 추가합니다.

# service yaml (Kubernetes 1.31+ Beta)
spec:
  trafficDistribution: PreferClose

2개의 AZ를 사용하는 환경이었기 때문에 AZ A, C로 squid 파드들이 균등 분배되도록 토폴로지 분산 제약사항(topologySpreadConstraints)도 추가합니다.

# deployment yaml
spec:
  replicas: 6
  template:
    spec:
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: topology.kubernetes.io/zone
        whenUnsatisfiable: DoNotSchedule # Or ScheduleAnyway
        labelSelector:
          matchLabels:
            app: squid 
        # At least keep 2 availability zones
        minDomains: 2

trafficDistribution: PreferClose만 사용하면 특정 Zone에 Pod가 몰릴 경우 과부하(핫스팟)에 걸릴 위험이 있어서, topologySpreadConstraints로 Pod를 여러 Zone에 고르게 분산시켜 안전장치를 만드는 것이 권장됩니다.

공식 squid 헬름 차트가 존재하지 않는다는 점 빼면 많은 부분이 개선된 완벽한 전환기였습니다.

마치며

Envoy나 HAProxy처럼 좀 더 기능이 다양하고 대중적인 프록시 서버도 존재했습니다. HAProxy는 당근마켓이나 토스 뱅크 등에서 프로덕션 환경에서 사용하는 검증된 프록시 서버입니다.

하지만 이 포워드 프록시 서버가 처리하는 요구사항 자체가 너무 단순하다보니, 오버 엔지니어링을 지양하면서 동시에 제게 익숙한 squid를 선정했습니다. squid도 1996년에 출시된 오래된 프록시 서버 소프트웨어라 tinyproxy보다 낫다 수준이지 클라우드 네이티브한 녀석은 아니므로 다른 분들에게 추천은 못드리겠습니다. 툴이 오래되었건 구리건 잘쓰는 사람이 있으면 그만이긴 합니다.

관련자료

Github:

Kubernetes docs: