---
type: guide
tags: [istio, egress, sysctl, netns, tcp-tw-reuse, time-wait, paws, kubernetes, kubelet, pod-security, securityContext]
created: 2026-07-02
---
# Pod 커널 파라미터 정본 — 호스트 sysctl은 왜 pod에 안 먹히고, unsafe sysctl은 어떻게 넣는가
> [!abstract]
> [TCP 병목 정본](ref__src-tcp-bottlenecks.html) §06은 완화 운영값이 4개 레이어(DR / pod sysctl / 노드 sysctl / Helm)에
> 분산된다고 정리했다. 이 문서는 그중 **pod sysctl 레이어 하나를 메커니즘 레벨로 전개**한다 —
> 호스트 `/etc/sysctl.d`가 pod에 전파되지 않는 이유(netns 초기화), `tcp_tw_reuse=1`이 안전한 근거(PAWS),
> 그리고 Kubernetes에서 unsafe sysctl이 통과해야 하는 **3중 관문**(kubelet allowlist → PSA → securityContext)까지.
**대상 환경**: Kubernetes ≥ 1.29 자체 구축(kubespray) 기준 — managed 환경 대안은 §06. **선행 문서**: [TCP 병목 정본](ref__src-tcp-bottlenecks.html) §02(포트 산술)·§06-4(이 문서의 원형이 된 2단계 요약).
---
## 01. 멘탈모델 — sysctl은 상속되지 않고 "초기화"된다
sysctl은 두 부류로 나뉜다:
- **netns 스코프** (`net.ipv4.*` 대부분, `net.core.somaxconn` 등): network namespace마다 독립 값을 가짐
- **호스트 전역** (`fs.*`, `vm.*`, 그리고 실질적으로 conntrack): netns로 격리되지 않음
핵심은 이것이다 — **새 netns는 부모(호스트) netns의 현재 값을 복사하지 않는다. 커널 코드에 하드코딩된 기본값으로 초기화된다.** `tcp_tw_reuse`는 커널의 netns 초기화 함수(`tcp_sk_init()`)에 `2`로 박혀 있다.
```mermaid
flowchart LR
H["host netns
tcp_tw_reuse = 1
(/etc/sysctl.d)"] -. "no inheritance ✗" .-> P["pod netns (new)
tcp_tw_reuse = 2"]
K["kernel tcp_sk_init()
hardcoded defaults"] --> P
```
그래서 정확한 그림은:
- 호스트에서 `sysctl -w net.ipv4.tcp_tw_reuse=1` → **호스트 netns만** 바뀜. pod에는 무관.
- pod 안의 값은 항상 기본값 **`2`** — `0`(끔)이 아니라 "**loopback 목적지에 한해서만 재사용**"(kernel 4.15+). loopback은 옛 중복 세그먼트가 되돌아올 물리 경로가 없어 무조건 안전하므로 커널이 기본으로 켜둔 것.
- egress gateway의 외부행 연결은 loopback이 아니므로 **실질적으로는 꺼진 것과 같다** → pod netns 안에서 직접 `1`로 바꿔야 한다.
같은 이유로 `ip_local_port_range`도 pod netns 값(기본 `32768 60999`)이 호스트와 별개로 존재한다.
---
## 02. TIME_WAIT는 왜 존재하고, tw_reuse는 왜 안전한가
값을 바꾸기 전에 그 값이 지키던 것부터. 먼저 close한 쪽(active closer — proxy는 대개 이쪽)은 두 가지 책임 때문에 4-tuple을 보존해야 한다:
1. 상대의 마지막 FIN이 유실·재전송되면 **다시 ACK**해줄 것
2. 같은 4-tuple로 새 연결이 즉시 열렸을 때, 네트워크에 떠돌던 **이전 연결의 늦은 세그먼트가 새 연결의 데이터로 오인되지 않게** 할 것
리눅스는 이 보존 시간을 60초로 하드코딩했다(`TCP_TIMEWAIT_LEN` — sysctl로 못 바꾼다). [TCP 병목 정본 §02](ref__src-tcp-bottlenecks.html)의 분수식 `28,232 ÷ 60s ≈ 470 conn/s`의 분모가 바로 이것.
```mermaid
flowchart LR
A["port in use
t=0 connect .. t=5s close"] --> B["TIME_WAIT 60s
port locked"] --> C["port returned
t=65s"]
B --- R1["hazard 1: late FIN retransmit -> re-ACK"]
B --- R2["hazard 2: stale segment must not hit new conn"]
```
**`tw_reuse=1`의 안전 근거 — PAWS.** TCP timestamps 옵션(기본 on)이 켜져 있으면 모든 세그먼트에 단조 증가하는 시계값이 실린다. 커널은 새 연결에서 이전 연결의 늦은 세그먼트를 timestamp 비교로 식별·폐기할 수 있다(PAWS, Protection Against Wrapped Sequences). 위 책임 ②가 timestamps로 대체되므로, **1초만 경과하면 그 4-tuple을 outgoing 연결에 재사용해도 안전**하다. 수신(서버) 소켓에는 적용되지 않는다 — gateway는 외부행 발신자라 정확히 이 케이스에 해당한다.
```mermaid
flowchart LR
O["old conn
ts <= 1000"] -- "tw_reuse (after 1s)" --> N["new conn, same 4-tuple
ts >= 2000"]
S["stale segment ts=950"] --> P["PAWS check
950 < 2000 -> drop"]
P -. rejected .-> N
```
---
## 03. 파라미터별 정리 — 무엇이 어디에 사는가
패킷 경로를 그리면 각 파라미터의 적용 위치가 저절로 정해진다. **pod netns 안에는 netfilter 룰이 없다.** 패킷이 veth를 빠져나와 호스트 netns의 iptables/Calico 룰을 통과할 때 conntrack tracking이 일어난다 — conntrack이 pod가 아닌 **노드** 설정인 이유.
```mermaid
flowchart LR
subgraph POD["gw pod netns"]
APP["app socket"] --- SY["ip_local_port_range
tcp_tw_reuse
tcp_fin_timeout"]
end
subgraph HOST["host netns (node)"]
FW["iptables / Calico"] --- CT["conntrack table
nf_conntrack_max"]
end
POD -- veth --> HOST --> EXT["external"]
```
| 파라미터 | 스코프 (k8s 분류) | 권장값 | 왜 필요한가 |
|---|---|---|---|
| `net.ipv4.ip_local_port_range` | pod netns (**safe**) | `"10240 60999"` | 포트 풀 28k→50k **순수 확장** — 유일하게 부작용 없는 완화. 하한 10240은 특권 포트(<1024)·registered port 관행 회피. pod netns라 노드의 NodePort 범위(30000–32767, 노드 netns 리스너)와 충돌하지 않음 — 노드 호스트에서 같은 확장을 하면 충돌 이슈가 있는 것과 대비되는 지점 |
| `net.ipv4.tcp_tw_reuse` | pod netns (**unsafe**) | `1` | 60초 규칙을 **PAWS가 안전을 보증하는 경우에 한해** 우회 (§02). outgoing 전용 — gateway 워크로드에 정확히 부합 |
| `net.ipv4.tcp_fin_timeout` | pod netns (safe, k8s ≥1.29) | `30` (선택) | TIME_WAIT가 아니라 **FIN_WAIT_2** orphan 소켓 보존 시간(기본 60s) 단축. 연결을 닫지 않고 방치하는 파트너 서버 대비책. 포트 고갈의 주범은 아니라 2순위 |
| `net.netfilter.nf_conntrack_max`
`..._tcp_timeout_time_wait` | **노드** `/etc/sysctl.d` | `1048576` / `30` | 위 packet path 그림 참조 — tracking은 호스트 netns에서 발생. 테이블이 차면 거부가 아니라 **silent drop**(무응답)이라 선제 상향 |
---
## 04. 적용 — unsafe sysctl의 3중 관문
`sysctls`는 **pod-level securityContext에만** 존재한다 (netns가 pod 단위 공유 자원이라 container-level 불가). 그리고 Kubernetes는 sysctl을 두 등급으로 나눈다:
- **safe** = 같은 노드의 다른 pod에 영향을 줄 수 없다고 검증된 것. `ip_local_port_range`는 아무 설정 없이 즉시 사용 가능. k8s 1.29+에서 `tcp_fin_timeout`·`tcp_keepalive_*` 계열도 safe로 승격됨.
- **unsafe** = 그 외 전부. `tcp_tw_reuse`가 여기 해당 → **kubelet이 노드 단위로 opt-in**해야만 admit된다.
```mermaid
flowchart LR
SPEC["pod spec
securityContext.sysctls"] --> G1["gate 1: API server
PSA namespace label"] --> G2["gate 2: kubelet
allowedUnsafeSysctls"] --> OK["pod netns
tw_reuse = 1"]
G1 -. fail .-> F1["rejected (baseline violation)"]
G2 -. fail .-> F2["SysctlForbidden"]
```
### 관문 1 — kubelet allowlist (gateway 전용 노드풀에만)
```yaml
# kubespray라면 egress 노드 그룹 vars:
kubelet_config_extra_args:
allowedUnsafeSysctls:
- "net.ipv4.tcp_tw_reuse"
# 일반 환경이면 /var/lib/kubelet/config.yaml 에:
allowedUnsafeSysctls:
- net.ipv4.tcp_tw_reuse
# 이후 systemctl restart kubelet
```
**노드 단위 설정**이라는 게 포인트 — 전용 egress 노드풀(`nodeSelector`)을 쓰는 구성과 정확히 맞물린다. 전체 노드에 풀 필요가 없다.
### 관문 2 — Pod Security Admission
unsafe sysctl을 쓰는 pod는 PSS **baseline 위반**이다. 네임스페이스에 `enforce=baseline`/`restricted` 라벨이 걸려 있으면 kubelet 허용과 무관하게 **API 단에서** 거부된다:
```bash
kubectl label ns istio-egress pod-security.kubernetes.io/enforce=privileged --overwrite
```
이 완화는 "이 네임스페이스에 gateway만 산다"는 전제에서만 정당화된다. 범용 네임스페이스면 안 된다.
### 관문 3 — gateway Helm values 선언
```yaml
# istio gateway 차트의 securityContext는 pod-level로 들어간다
securityContext:
sysctls:
- name: net.ipv4.ip_local_port_range # safe — 관문 1·2 없이도 동작
value: "10240 60999"
- name: net.ipv4.tcp_tw_reuse # unsafe — 관문 1·2 선행 필수
value: "1"
```
---
## 05. 검증과 실패 모드
```bash
kubectl -n istio-egress exec deploy/istio-egressgateway -- \
cat /proc/sys/net/ipv4/tcp_tw_reuse /proc/sys/net/ipv4/ip_local_port_range
# 기대 출력:
# 1
# 10240 60999
```
| 증상 | 원인 | 관문 |
|---|---|---|
| `kubectl apply` 단계에서 admission 거부 | PSA enforce 라벨이 baseline/restricted | 2 |
| pod STATUS **`SysctlForbidden`** | 그 노드의 kubelet allowlist 미반영 (kubelet 재시작 누락 포함) | 1 |
| pod는 뜨는데 값이 여전히 `2` | `securityContext.sysctls` 누락, 또는 다른 워크로드에 적용함 | 3 |
`SysctlForbidden`은 스케줄까지는 되고 노드의 kubelet이 기동을 거부하는 상태라 `kubectl get pod`의 STATUS 컬럼에 그대로 보인다 — 이게 보이면 kubelet 설정이 **그 노드에** 반영 안 된 것.
---
## 06. kubelet을 못 건드리는 환경(managed)의 대안
privileged initContainer가 pod netns를 공유하는 성질을 이용한다:
```yaml
initContainers:
- name: sysctl-tune
image: busybox
securityContext: { privileged: true }
command: ["sh", "-c", "sysctl -w net.ipv4.tcp_tw_reuse=1"]
```
동작은 하지만 `tcp_tw_reuse` 하나를 위해 privileged(사실상 노드 root)를 주는 셈이라 **권한 부여가 목적 대비 과도**하다. kubelet을 제어할 수 있는 환경(자체 구축·kubespray)에서는 관문 1~3 정석이 맞다.
---
## 핵심 정리
| 항목 | 내용 |
|---|---|
| netns | 스코프 sysctl은 상속이 아니라 **커널 기본값으로 초기화**. tw_reuse의 pod 기본값은 0이 아닌 `2`(loopback 한정) |
| 안전 근거 | TIME_WAIT의 보호 ②(늦은 세그먼트 오인 방지)를 timestamps/**PAWS**가 대체 → outgoing 한정 재사용 가능 |
| 3중 관문 | kubelet allowlist(노드) → PSA(네임스페이스) → securityContext.sysctls(pod). 하나라도 빠지면 거부 |
| 우선순위 | 앱 keep-alive(분자) > replica+antiAffinity(분모) > port range(safe) > tw_reuse(unsafe) — 싼 것부터 |
---
## What you might be missing
- **`tcp_tw_reuse`는 TIME_WAIT 개수를 줄여주지 않는다.** 소켓은 여전히 TIME_WAIT로 쌓이고(모니터링 곡선 그대로), 포트가 필요할 때 그중에서 빌려 쓸 수 있게 될 뿐이다. `node_sockstat_TCP_tw` 알람은 이 설정 후에도 유효한 신호이며, 그 알람의 처방은 여전히 "앱 keep-alive 캠페인"이다.
- **재사용은 상대측 timestamps에 의존한다.** timestamps를 끈 상대(보안 장비 뒤 `tcp_timestamps=0` 강제 등)와의 연결에는 tw_reuse가 발동하지 못한다. 특정 채널만 `EADDRNOTAVAIL`이 계속되면 이 케이스를 의심할 것.
- **`tcp_tw_recycle`과 혼동 금지.** NAT 뒤 클라이언트를 무차별로 깨뜨려 kernel 4.12에서 **삭제**된 설정이다. 지금 커널엔 존재하지도 않지만, 오래된 사내 위키 복사 시 딸려오는 단골 사고.
- **`tcp_max_tw_buckets`를 낮추는 튜닝은 안티패턴.** 한도 초과분의 TIME_WAIT 소켓을 그냥 파괴해 보호 기능 자체를 무력화한다. 올바른 방향은 언제나 tw_reuse(안전 조건부 재사용) 쪽.
---
## 참조
**아카이브 내부**
- [TCP 병목 정본](ref__src-tcp-bottlenecks.html) — §02 포트 산술(분수식), §06 완화 운영값 YAML 전체. 이 문서는 그 §06-4의 확장
- [TCP 병목 한계 축소 재현 랩](cfg__guide-tcp-failure-reproduction.html) — 병목을 테스트 클러스터에서 직접 재현
- [Egress 운영 정본](ref__src-operations.html) — 모니터링·graceful shutdown 일반론
**외부**
- [kernel ip-sysctl 문서](https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt) — tcp_tw_reuse 값 의미(0/1/2)
- [Kubernetes: Using sysctls in a cluster](https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/) — safe/unsafe 분류와 allowedUnsafeSysctls
- [Pod Security Standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/) — baseline이 unsafe sysctl을 금지하는 근거