ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 이코에코(Eco²) GitOps #03 네트워크 안정화
    이코에코(Eco²)/Kubernetes Cluster+GitOps+Service Mesh 2025. 11. 24. 17:05

    🛎️ 본 포스팅은 구현이 완료된 사안만 다룹니다. 현재 이코에코 14-nodes cluster는 ap-northeast-2 리전에 배포돼 있습니다.

    0.7.3에서 App-of-Apps / Sync Wave / Helm+Kustomize까지 한 번에 올렸더니, 해야할 작업이 산더미였다.
    이번 편은 그 이후 버전들(0.7.4 ~ 0.7.5)에서 보안 그룹이랑 ArgoCD가 어떻게 엉켜 있었는지 정리해본다.  
    2편은 여기: 이코에코(Eco²) 인프라 구축 #02 - GitOps: Ansible 의존성 줄이기

    1. Security Group 정리 안 하고 GitOps만 본 게 화근이었다 (v0.7.4)


    1.1 마스터/워커 SG 분리 구조의 한계


    초기 SG 구조는 마스터 SG 하나, 워커 SG 하나에 서로를 참조하는 방식이었다.

    - Master SG ↔ Worker SG 서로 인바운드 허용
    - 각 SG 안에 etcd / API Server / NodePort / Calico / Typha 등 온갖 포트가 섞여 있음
    - Terraform 기준 312줄짜리 SG 모듈이 됨

    이 구조의 부작용은 명확했다.
    - SG 간 순환 참조 때문에 `terraform destroy`가 깔끔하게 안 떨어짐  
    - Pod 레벨 통신까지 SG로 묶이니, NetworkPolicy랑 역할이 혼재
    - 이건 SG에서 막히는 건지, NP에서 막히는 건지.. 디버깅 복잡도 증가

    이때 정리한 문서가 이거다.  
    docs/architecture/SECURITY_GROUP_SIMPLIFICATION.md

    여기 보면 이전 구조/개선 구조가 ASCII 그림으로 잘 나와 있다.
     

    1.2 Cluster SG 하나로 통일

    결국 SG 레벨에서는 “노드끼리는 서로 다 통신 가능해야 한다”는 쪽으로 결론을 냈다.  
    Pod 간 세밀한 통제는 K8s NetworkPolicy로 넘기고, SG는 노드 레벨 방화벽만 맡기자 쪽으로.

    그래서 구조를 이렇게 갈아엎었다.

    이전:
      aws_security_group.master
      aws_security_group.worker
      + 서로를 참조하는 rule들 (12개 정도)

    이후:
      aws_security_group.k8s_cluster  # 단일 SG
        - SSH (22)
        - API Server (6443)
        - NodePort (30000-32767)
        - self 인그레스 (노드 간 모든 통신 허용)실제 Terraform 모듈은 여기서 볼 수 있다.  
    terraform/modules/security-groups/main.tf
    → 출력값/SSM 파라미터 정리는 여기:  
    1. terraform/modules/security-groups/outputs.tf
    2. terraform/ssm-parameters.tf

    문서에 있는 숫자를 그대로 가져오면:

    항목 이전 개선 후
    SG 개수   2개 (Master/Worker) 1개 (Cluster)
    SG 모듈 줄 수 312줄 155줄
    순환 참조 12개 0개
    terraform destroy 15분+ 대기 후 실패 즉시 성공



    실제로 이 작업은 commit 단위로 보면 아래와 같이 묶여 있다.

    1. SG 범위 줄이기:  
      11f9f68: refactor: reduce the SG bound

    2. Calico/네트워킹 쪽 정리와 같이 진행: 
    00d64e3: `refactor(calico): Ansible 단독 관리로 전환 (BGP off, VXLAN Always, Operator 미사용)`  
    b178427: fix: enable SSA for calico`

    SG를 이렇게 정리해두니, Calico Typha 5473 포트 이슈 같은 경우도 SG 레벨에선 다시 다룰 일이 없어졌다.
    그 얘기는 아래에 살짝 더 풀겠다.
     

    2. Calico 리팩토링: Operator, Helm, 그리고 Typha(5473) (v0.7.4)

    2.1 Operator + Helm 이중 배포


    한동안 Calico는 “부트스트랩에서 raw manifest로 설치 + 나중에 Helm Operator로 관리”라는 오묘한 구조였다.

    - Ansible 부트스트랩:  
      https://raw.githubusercontent.com/projectcalico/calico/v3.26.x/manifests/calico.yaml 적용
    - ArgoCD Wave 5:  
      platform/helm/calico에서 Tigera Operator + Installation CR 배포

    결과적으로 calico-node DaemonSet이 kube-system / calico-system 양쪽에 중복 생성되거나,
    Helm Sync에서 AlreadyExists, Field is immutable 에러가 계속 터지고 Root App Wave 5(dev-calico, prod-calico)가 Progressing에서 넘어가지 않는 상황이 나왔다
    이 부분은 트러블슈팅 문서로 따로 빼놨다.

     
    v0.7.4 시점에는 부트스트랩과 Operator의 스펙을 일치시켜 덮어쓰자 쪽이었으나.. Calico는 한 번만 쓰는 걸로 변경했다.
    결국에는 “Operator 덮어쓰기 시도 실패 → Helm 단일 방식” 쪽으로 선회했다.
    그렇지만 이 경우 부트스트랩 시 클러스터 내부망이 구성되지 않는다.
    슬프지만 Calico는 ArgoCD와 함께 부트스트랩 1회 배포로 픽스됐다.

    관련 커밋들:
    - 00d64e3: `refactor(calico): Ansible 단독 관리로 전환`
    - a4a5925: `docs: add troubleshooting for calico`
    트러블슈팅 문서도 Calico 파트에 슬슬 쌓이기 시작했다.
     

    2.2 Calico Typha 5473 포트 이슈

    https://docs.tigera.io/calico/latest/reference/typha/overview

     

    Typha overview | Calico Documentation

    Use the Calico Typha daemon to increase scale and reduce impact on the datastore.

    docs.tigera.io

     

     
    여기서 Felix는 각 노드에 붙어 있는 Calico 에이전트고
    Typha는 이 Felix들이 직접 API 서버에 몰려들지 않도록 앞에서 받아주는 중간 허브/프록시 역할을 한다.
     
    조금 더 나누어보면
     
    Felix 입장에서의 Typha

    • 원래는 각 Felix가 직접 Kubernetes API(또는 etcd)에 붙어서 “정책이 뭐 바뀌었는지, 어떤 Pod/Endpoint가 생겼는지”를 계속 watch 해야 함.
    • 노드가 수십, 수백 개로 늘어나면 API 서버 입장에서 watch 커넥션이 늘어나고, 업데이트를 똑같은 내용으로 N번 브로드캐스트해야 해서 부담이 커짐.

    Typha 가운데 두면:

    • Felix는 더 이상 API 서버랑 직접 안 붙고, Typha 하나(또는 여러 개)에만 붙어서 업데이트를 받음.
    • Felix ↔ Typha 연결만 잘 유지되면 되고, API 서버 부하는 Typha 쪽에서 한번 흡수.

    Typha 내부에서 하는 일

    • Kubernetes API(or 데이터스토어)를 watch하면서 클러스터 상태를 in-memory로 캐싱.
    • 노드별 Felix 클라이언트들과 gRPC 같은 장기 커넥션을 유지하면서, 네트워크 정책/엔드포인트/노드 변화 같은 걸 “한 번 받아서 여러 Felix에게 팬아웃(fan-out)” 한다.

    Felix 입장에서는 API 서버 대신 Typha만 보면 되는 구조가 된다.
    API 서버 입장에서는 Typha 몇 대만 상대하면 된다
    공식 문서 기준으로도 Typha는 작은 클러스터에선 필수가 아니다.
    노드 수가 수백개를 넘어가면, Felix 수 × API 서버 연결 수가 감당 안 되니 Typha를 권장하는 편이다.
    Calico Operator를 쓰면 클러스터 규모를 보고 Typha Deploy 개수도 자동으로 튜닝해 준다.
    현 설정이 켜진 건 오작동에 가깝지만.. 이참에 써보는 것도 나쁘진 않으니 그냥 뒀다.
    전직장에서도 개발용 클러스터는 non-HA 기준 3-nodes가 권장사항이어서 Typha를 다뤄본 적은 없었다.
    아무튼 Calico Typha 5473 포트 문제는 “SG 정리 안 하면 이렇게 된다”의 교과서 같은 이슈였다.
    증상은 이랬다.

    # calico-node (master)만 Ready가 안 됨
    NAME                READY   STATUS    RESTARTS   AGE
    calico-node-nv4qn   0/1     Running   0          13m
    
    Failed to connect to typha endpoint 10.0.3.88:5473 (i/o timeout)


    로그를 보면 Master ↔ Worker 사이에서 5473/TCP만 막혀 있었던 상황이라 해결은 간단했다.
    AWS CLI로 Master/Worker SG 간 5473/TCP 인바운드 열어주고
    재발하지 않도록 Terraform SG 모듈에 Typha 관련 rule을 영구 반영했다.
    몇 초 후 calico-node 전부 1/1 Ready가 되며 문제는 해결됐다.
    이 이슈가 SG 통합 작업(Cluster SG)과 시점이 겹쳐서 결국 Calico 필수 포트(179, 4789, 5473 등)를 SG 레벨에서 한 번 더 정리하고 넘어가게 됐다.
     

    3. Helm/Kustomize 역할 분리 다시 잡기 (v0.7.4)


    0.7.1에서 base/overlay 패턴을 도입하면서 Kustomize 기반은 잡아놨는데 0.7.4에서는 어디까지 Helm으로 보고, 어디부터 Kustomize로 볼지가 주안점이었다.

    Changelog 한 줄로 정리하면 이렇다.
    1. Platform 계층: Helm Chart 중심 (platform/helm/)  
    2. Workload 계층: Kustomize base/overlay (workloads/)  
    3. CRDs: platform/crds/로 완전 분리  
    4. patch 방식: JSON → YAML로 통일

    실제 구조는 대략 이런 느낌이다.

    platform/
      helm/
        external-dns/
        alb-controller/
        kube-prometheus-stack/
        ...
      crds/
        external-secrets/
        ot-redis/
        rabbitmq-operator/
    workloads/
      base/
        auth/
        location/
        ...
      overlays/
        dev/
        prod/


     14nodes/이 구조는 GitOps 전용 문서에서 한 번 더 정리해뒀다.  
    GitOps 구조 문서가 있다면, 거기에 Helm/Kustomize 스택이 쭉 나와 있을 거다.

    관련 커밋들은 이런 식으로 이어진다.

    - CRD/Helm overlay 정리:  
      918d0bc: `refactor: split helm/crd env overlays`  
      cf72a26: `refactor/data-cr-structure`  
      d90b180: `fix: centralize crd management`

    이 구간은 “코드 풀 리팩토링 + CRD/Helm 재배치” 느낌이라,  
    GitHub에서 diff를 쭉 훑어 보는 게 글로 설명하는 것보다 훨씬 직관적이다.  

    4. ArgoCD CrashLoopBackOff & ERR_TOO_MANY_REDIRECTS (v0.7.5)

    0.7.4까지는 네트워크/보안/구조 정리 느낌이었다면,  
    0.7.5는 거의 ArgoCD 삽질 특집에 가깝다.


    4.1 command/args 패치하다 CrashLoopBackOff 


    어느 순간 ArgoCD Server Pod가 계속 CrashLoop를 도는 걸 확인했다.

    kubectl get pods -n argocd
    
    NAME                                READY   STATUS             RESTARTS   AGE
    argocd-server-58d469d955-kmdd6     0/1     CrashLoopBackOff   4          2m


    로그를 보면

    Error: unknown command "/usr/local/bin/argocd-server" for "argocd-server


    원인은 아주 단순했다.
    - Ansible에서 `kubectl patch`로 Deployment의 command만 이렇게 바꿔버렸고:

      command: ["argocd-server", "--insecure"]
      원래 있던 args는 그대로 남아 있었다.

      args: ["/usr/local/bin/argocd-server"]
      결국 실제로 실행된 건 이거였다.

    argocd-server --insecure /usr/local/bin/argocd-server
    → "/usr/local/bin/argocd-server"를 또 서브커맨드로 본 것이 사건을 정리한 문서가 바로 이거다.  
    docs/troubleshooting/ARGOCD_DEPLOYMENT_ISSUES.md

    여기 보면 “Before/After Ansible Task”가 그대로 들어가 있다.

    - Before: Deployment command를 직접 패치
    - After: argocd-cmd-params-cm ConfigMap 기반으로 server.insecure=true 설정 + rollout restart

    관련 Ansible Role 변경은 여기서 확인할 수 있다.  
    ansible/roles/argocd/tasks/main.yml
     
    이 Role을 손본 커밋 중 하나:  
    f90366f: fix: Enhance ArgoCD Ansible role for production readiness

    요약하면
    - ArgoCD는 ConfigMap(argocd-cmd-params-cm)으로 server 옵션을 받는 게 표준이고
    - Deployment를 직접 패치하면 command/args 충돌로 CrashLoopBackOff를 자초하기 쉽다.



    4.2 ERR_TOO_MANY_REDIRECTS: ALB HTTPS + ArgoCD HTTP

    두 번째 이슈는 브라우저에서 ArgoCD 접속할 때 ERR_TOO_MANY_REDIRECTS가 뜨는 문제였다.
    구조는 이랬다.

    브라우저 (HTTPS)
      ↓
    ALB (HTTPS 종료)
      ↓
    ArgoCD (HTTP)  ← "어? HTTPS 아니네? HTTPS로 리다이렉트해야지!"
      ↓
    ALB (다시 HTTPS)  ← 반복...결론적으로:


    - ALB에서는 HTTPS 종료 후 ArgoCD로 HTTP로 트래픽을 넘겨야 하고
    - ArgoCD는 `--insecure` 모드로 떠서 HTTP를 받아들여야 한다
    - Ingress에는 `alb.ingress.kubernetes.io/backend-protocol: HTTP`가 필요하다

    이 내용은 같은 트러블슈팅 문서 2번 섹션에 잘 정리돼 있다.  
    ARGOCD_DEPLOYMENT_ISSUES.md#2-err_too_many_redirects-alb-https-종료-문제

    Ansible Role 쪽은 이제 다음을 보장한다.
    - ConfigMap에 `server.insecure=true` 패치
    - `rollout restart` + `rollout status`로 적용 확인
    - Ingress annotation은 Kustomize/Helm overlay에서 관리

    5. GitOps 1.0 → 2.0

    0.7.4 ~ 0.7.5 사이에 했던 작업을 정리하면 이런 흐름이다.

    1. 보안 그룹 단순화  
       - Master/Worker SG 나눠서 꼬아 놨던 걸 Cluster SG 하나로 모으고  
       - “노드 레벨 방화벽은 SG, Pod 레벨은 NetworkPolicy”로 책임 분리  
       - SG 모듈 312줄 → 155줄, 순환 참조 제거  
       - 문서: SECURITY_GROUP_SIMPLIFICATION.md

    2. Calico/Helm/Kustomize 스택 재정비
       - Calico Operator + Helm 중복 설치 이슈 정리  
       - Typha 5473 포트 문제를 SG/문서 레벨에서 해결  
       - 문서:  
         - calico-operator-helm-conflict.md
         - CALICO_TYPHA_PORT_5473_ISSUE.md

    3. ArgoCD 안정화
       - Deployment 직접 패치 대신 ConfigMap 기반 설정으로 전환  
       - ALB HTTPS 종료 환경에서의 리디렉션 루프 해결  
       - Ansible Role 멱등성/재시작/검증 단계 강화  
       - 문서: ARGOCD_DEPLOYMENT_ISSUES.md

    토큰은 여전히 잘 타고 있고, 아직 더 다듬을 부분이 많지만 그래도 이제는 클러스터의 장애지점을 찾을 때,  
    SG/Calico/ArgoCD/Ansible 어느 층을 먼저 봐야 할지 감이 잡히는 구조가 된 듯 싶다.
    다음 편에선 Sync Wave 자체 얘기랑 Helm+Kustomize overlay를
    실제 예시(ArgoCD/ExternalSecrets/데이터 스택)로 풀어볼 생각이다.  
    CrashLoopBackOff 로그를 바탕으로 “Wave 숫자가 어떻게 바뀌었는지, 어떤 순으로 정렬됐는지”에 대해서도 써보겠다.

    댓글

ABOUT ME

🎓 부산대학교 정보컴퓨터공학과 학사: 2017.03 - 2023.08
☁️ Rakuten Symphony Jr. Cloud Engineer: 2024.12.09 - 2025.08.31
🏆 2025 AI 새싹톤 우수상 수상: 2025.10.30 - 2025.12.02
🌏 이코에코(Eco²) 백엔드/인프라 고도화 중: 2025.12 - Present

Designed by Mango