본문 바로가기
IT적응기

컨테이너 네트워크 5가지 핵심 통신 방법– 현장 엔지니어의 실전 기록

by IT적응기 2026. 4. 17.

도커/쿠버네티스 네트워크 완전 가이드 참고 이미지
도커/쿠버네티스 네트워크 완전 가이드

 

VM을 수십 대 운영하다가 컨테이너로 전환하면서 가장 먼저 당황했던 게 네트워크였다. VM은 실제 NIC 같은 구조로 이해가 됐는데, 컨테이너는 올라왔다 내려갔다, IP가 바뀌고, 파드가 수십 개씩 뜨고. "이게 어떻게 서로 통신하는 거야?"라는 질문을 한동안 달고 살았다. 직접 구성해보고, 장애 나고, 고치고를 반복하면서 체득한 5가지 핵심 통신 방법을 정리한다.

브리지 네트워크 – 같은 집 안의 내선전화

도커의 기본 네트워크 드라이버는 브리지(bridge)다. 사무실 내선전화에 비유하면 이해하기 쉽다. 같은 건물(호스트) 안에서는 내선번호(컨테이너 IP)로 바로 통화되지만, 외부에서 전화를 받으려면 대표 번호(호스트 IP)와 내선 연결(포트 매핑)이 필요하다. docker0 브리지 인터페이스가 가상 스위치 역할을 하고, 각 컨테이너는 veth 페어로 연결된다.

중요한 포인트가 하나 있다. 기본 브리지는 컨테이너 이름으로 통신이 안 된다. 반드시 사용자 정의 브리지 네트워크를 만들어야 이름 기반 DNS 통신이 가능하다. Docker Compose를 쓰면 자동으로 사용자 정의 네트워크가 만들어진다.

# 기본 브리지로 컨테이너 실행
docker run -d --name web nginx
docker run -d --name db mysql:8.0 -e MYSQL_ROOT_PASSWORD=secret

# 사용자 정의 브리지 생성 (DNS 이름 해결 가능)
docker network create --driver bridge mynet

docker run -d --name web --network mynet nginx
docker run -d --name db --network mynet mysql:8.0 \
  -e MYSQL_ROOT_PASSWORD=secret

# 이름으로 통신 확인
docker exec web ping db

# 네트워크 상세 확인
docker network inspect mynet

# 포트 매핑으로 외부 접근
docker run -d -p 8080:80 --network mynet --name web2 nginx
✅ 장점
같은 호스트 안에서 커널 내부에서 처리되니 네트워크 오버헤드가 거의 없다. 개발 환경에서 여러 서비스를 띄워 연동 테스트할 때 설정 없이 바로 쓸 수 있다.
⚠️ 단점
호스트를 넘어선 통신은 불가능하다. 기본 브리지는 DNS 이름 해결이 안 되어서 컨테이너 IP를 직접 써야 하는데, 재시작하면 IP가 바뀐다. 반드시 사용자 정의 브리지를 사용해야 한다.
처음 도커를 쓸 때 기본 브리지 네트워크에서 컨테이너 이름으로 ping을 날렸다가 통신이 안 돼서 한참 헤맸다. 기본 bridge는 DNS가 지원 안 된다는 걸 몰랐던 것이다. 컨테이너 IP를 직접 확인해서 넣어봤더니 됐는데, 컨테이너를 재시작하니까 IP가 바뀌어서 또 안 됐다. 그때서야 사용자 정의 브리지가 왜 존재하는지 이해했다. 지금은 docker-compose를 쓰면 자동으로 해결되는 걸 알지만, 원리를 이해하고 쓰는 것과 모르고 쓰는 건 장애 대응 속도가 다르다.
 

호스트 네트워크 – 담장 없이 마당을 그냥 쓰는 방식

호스트 네트워크 모드는 컨테이너가 호스트의 네트워크 스택을 그대로 사용하는 방식이다. 단독주택에서 울타리를 치지 않고 마당을 공용으로 쓰는 것과 비슷하다. 컨테이너가 별도의 네트워크 공간을 갖지 않고 호스트 네트워크 공간을 공유하기 때문에, 포트 매핑 없이 바로 호스트 포트로 접근할 수 있다.

# 호스트 네트워크 모드로 실행
docker run -d --network host --name web nginx

# 호스트와 동일한 네트워크 인터페이스 확인
docker exec web ip addr

# 포트 매핑 없이 호스트 80번 포트로 직접 접근
curl http://localhost:80
✅ 장점
NAT 없이 바로 통신하니까 네트워크 지연이 가장 낮다. 네트워크 집약적인 애플리케이션이나 고성능 패킷 처리가 필요한 경우에 유효하다.
⚠️ 단점
격리가 없어서 포트 충돌 위험이 있다. 보안 관점에서도 좋지 않다. 운영 환경에서는 정말 성능이 절박한 경우 아니면 거의 쓰지 않는다.
모니터링 에이전트를 컨테이너로 실행할 때 호스트 네트워크를 쓴 적이 있다. 호스트의 네트워크 인터페이스 정보를 수집해야 했기 때문이다. 처음에는 브리지 모드로 시도했는데, 호스트 NIC 정보가 제대로 안 잡혔다. 호스트 네트워크로 바꾸니까 바로 해결됐다. 성능보다 "호스트 네트워크 공간 자체가 필요한" 상황에서도 이 모드를 쓴다는 걸 그때 배웠다. 도구는 왜 쓰는지를 알아야 제대로 쓸 수 있다.
 

쿠버네티스 파드 내부 통신 – 한 방에 룸메이트끼리 공유하는 구조

쿠버네티스 파드는 하나 이상의 컨테이너 묶음이다. 같은 파드 안의 컨테이너들은 네트워크 공간을 공유한다. 쉐어하우스 한 방을 여러 사람이 쓰는 것과 비슷하다. 방(파드) 안에서는 localhost로 서로 통신하고, 외부와는 방 IP(파드 IP)를 통해 대화한다. 이 구조 덕분에 사이드카 패턴이 가능하다.

사이드카 패턴이란 메인 앱 컨테이너 옆에 보조 역할의 컨테이너를 붙이는 방식이다. 로그 수집, 모니터링, 프록시 역할을 메인 앱을 건드리지 않고 추가할 수 있다. Istio나 Linkerd 같은 서비스 메시도 이 원리로 동작한다.

# 사이드카 패턴 파드 예시
apiVersion: v1
kind: Pod
metadata:
  name: app-with-sidecar
spec:
  containers:
  - name: main-app
    image: myapp:latest
    ports:
    - containerPort: 8080
  - name: log-collector
    image: fluentd:latest
    env:
    - name: APP_HOST
      value: "localhost"       # 같은 파드 = 같은 네트워크 공간
    - name: APP_PORT
      value: "8080"

# 파드 생성 및 IP 확인
kubectl apply -f pod.yaml
kubectl get pod app-with-sidecar -o wide

# 사이드카에서 메인앱으로 localhost 통신 확인
kubectl exec app-with-sidecar -c log-collector \
  -- curl http://localhost:8080
✅ 장점
사이드카 패턴으로 메인 앱을 수정하지 않고 로그 수집, 모니터링, 보안 프록시 기능을 붙일 수 있다. 서비스 메시의 핵심 원리이기도 하다.
⚠️ 단점
같은 파드 내 컨테이너끼리 포트 번호가 충돌하면 안 된다. 파드 전체가 재시작될 때 안에 있는 모든 컨테이너가 같이 내려간다. 결합도를 충분히 고민하고 묶어야 한다.
Istio를 처음 도입했을 때 파드에 Envoy 사이드카가 자동 주입되는 걸 보고 신기했다. 내가 만든 앱 컨테이너 옆에 모르는 컨테이너가 붙어있는 것 같았다. 처음엔 이 구조가 왜 안전한지, 메인 앱이 어떻게 영향 받는지 이해가 안 됐다. 파드 내부 통신 구조를 공부하고 나서야 사이드카가 왜 localhost로 앱과 통신할 수 있는지, 왜 앱 코드를 전혀 바꾸지 않아도 되는지 이해됐다. 원리를 모르면 도구가 마법처럼 느껴지고, 마법은 문제가 생겼을 때 해결할 수가 없다.
 

쿠버네티스 서비스 – 전화번호부가 자동으로 업데이트되는 방식

파드는 IP가 계속 바뀐다. 스케일 인/아웃, 재시작마다 IP가 달라지는데, 어떻게 안정적으로 통신할까? 서비스(Service)가 그 해답이다. 직원(파드)이 바뀌어도 부서 대표 번호(서비스 ClusterIP)는 변하지 않는 자동 업데이트 전화번호부와 같다. kube-proxy가 iptables 또는 IPVS로 실제 파드로 트래픽을 분산한다.

# ClusterIP 서비스 (클러스터 내부 통신)
apiVersion: v1
kind: Service
metadata:
  name: db-service
spec:
  selector:
    app: mysql
  ports:
  - port: 3306
    targetPort: 3306
  type: ClusterIP
---
# NodePort 서비스 (외부 접근, 개발/테스트용)
apiVersion: v1
kind: Service
metadata:
  name: web-nodeport
spec:
  selector:
    app: web
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30080
  type: NodePort
---
# LoadBalancer 서비스 (클라우드 외부 접근)
apiVersion: v1
kind: Service
metadata:
  name: web-lb
spec:
  selector:
    app: web
  ports:
  - port: 80
    targetPort: 8080
  type: LoadBalancer

# 서비스 확인
kubectl apply -f service.yaml
kubectl get svc
kubectl get endpoints db-service

# DNS 이름으로 접근 테스트
kubectl run test-pod --image=busybox --rm -it \
  -- wget -qO- http://db-service:3306

# 다른 네임스페이스에서 FQDN으로 접근
kubectl run test-pod --image=busybox --rm -it \
  -- wget -qO- http://db-service.default.svc.cluster.local:3306
✅ 장점
파드 IP 변동에 관계없이 안정적인 엔드포인트를 제공한다. DNS 기반 서비스 디스커버리가 자동으로 되고, kube-proxy가 로드밸런싱까지 처리한다.
⚠️ 단점
ClusterIP는 클러스터 외부에서 직접 접근이 불가능하다. 클라우드에서 LoadBalancer 타입을 남발하면 비용이 예상보다 많이 나온다. Ingress를 잘 활용해야 한다.
클라우드에서 쿠버네티스를 처음 운영할 때 서비스마다 LoadBalancer 타입으로 만들었다가 월말에 로드밸런서 비용이 예상의 3배가 나왔다. 서비스 10개를 만들면 로드밸런서가 10개 생긴다는 걸 몰랐던 것이다. Ingress 하나로 여러 서비스를 앞에서 받아서 경로별로 분배하는 방식으로 바꾸고 나서 비용이 크게 줄었다. 기술 선택은 성능만의 문제가 아니라 비용과도 연결된다. LoadBalancer를 쓸 이유가 없으면 ClusterIP + Ingress 조합이 훨씬 경제적이다.
 

CNI 플러그인 – 다리를 어떻게 놓을지 선택하는 문제

쿠버네티스는 파드 간 네트워크 규격(CNI 인터페이스)만 정해두고, 실제 구현은 외부 플러그인에 맡긴다. 다리를 놔야 한다는 규격은 있는데, 현수교를 놓을지 아치교를 놓을지는 선택하는 것과 같다. 대표적인 CNI 플러그인으로는 Calico, Flannel, Cilium, Weave가 있다. 단순함이 필요하면 Flannel, 보안 정책이 중요하면 Calico, 고성능이 필요하면 Cilium을 고른다.

# Calico 설치
kubectl apply -f \
  https://raw.githubusercontent.com/projectcalico/calico/v3.26.0/manifests/calico.yaml

# Calico 네트워크 정책: web 파드만 DB 접근 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-access-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: mysql
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: web
    ports:
    - protocol: TCP
      port: 3306

# Cilium 설치 (Helm, eBPF 기반 고성능)
helm repo add cilium https://helm.cilium.io/
helm install cilium cilium/cilium \
  --namespace kube-system \
  --set kubeProxyReplacement=strict

# Cilium 상태 확인
cilium status

# Flannel 설치 (단순, 소규모 클러스터)
kubectl apply -f \
  https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
✅ 장점
목적에 맞는 CNI를 선택할 수 있는 유연성이 핵심이다. Cilium의 eBPF 기반 처리는 대규모 클러스터에서 iptables 방식 대비 성능 차이가 실감날 정도로 좋다.
⚠️ 단점
CNI 선택을 잘못하면 나중에 바꾸기가 매우 힘들고 클러스터를 재구성해야 할 수도 있다. CNI별 트러블슈팅 방법이 다르기 때문에 팀 전체가 이해하고 있어야 한다.
처음 클러스터를 구성할 때 별 고민 없이 Flannel을 선택했다. 단순하고 도입이 쉬웠다. 그런데 클러스터 규모가 커지면서 파드 수가 300개를 넘어가자 iptables 체인이 엄청나게 길어지면서 지연이 생기기 시작했다. Cilium으로 바꾸고 싶었는데, CNI를 운영 중에 바꾸는 건 사실상 클러스터를 다시 만드는 것과 같았다. 결국 새 클러스터를 하나 더 만들어서 워크로드를 이전했다. 처음 CNI를 고를 때 "지금 당장" 뿐 아니라 "앞으로 1~2년 뒤 규모"를 함께 고민했어야 했다. 첫 선택의 무게를 그때 제대로 느꼈다.
 

마치며 – 컨테이너 네트워크는 모르면 블랙박스, 알면 정교한 시계

처음 쿠버네티스를 도입했을 때 네트워크 부분이 가장 무서웠다. 파드가 왜 통신이 안 되는지, 서비스가 왜 안 잡히는지 로그를 봐도 모르겠고 검색해도 내 상황에 맞는 답이 없었다. iptables 규칙을 직접 보고, veth 인터페이스를 따라가보고, CNI 로그를 뜯어보고 나서야 전체 그림이 보이기 시작했다.

지금은 브리지 문제인지, kube-proxy 문제인지, CNI 문제인지, DNS 문제인지 순서대로 좁혀가면 대부분 답이 나온다. 컨테이너 네트워크는 처음엔 블랙박스처럼 느껴지지만, 안을 들여다보면 정교하게 설계된 시계 부품 같다.


소개 및 문의 · 개인정보처리방침 · 면책조항

© 2026 깜짝,황금이 아빠 IT적응기

서치어드바이저