
在日常維運容器化服務的過程中,我們有時候會遇到需要調整 Linux 核心參數的需求,例如調高最大連線數、修改 TCP 相關設定等。但容器畢竟不是一台完整的虛擬機,到底哪些核心參數可以在容器裡面改?又該怎麼改?這篇文章就來好好梳理一下。
核心觀念速覽
先把幾個重要觀念列出來:
- Linux 系統參數可分為全域參數與 Namespaced 參數兩大類
- 全域參數在主機上修改後,會影響所有容器
- 全域參數在主機和容器之間是互相連動的
- 全域參數不能透過 Kubernetes 的 SecurityContext 修改
- 全域參數中,有些在容器內「看得到」,有些則「看不到」,但都可以從主機端修改
- 看得到的全域參數,可以在特權容器中修改,且修改會影響主機與全部容器
- 看不到的全域參數,只能從主機端修改
什麼是 Namespaced 系統參數?
Namespaced 參數是指主機與各容器可以各自獨立設定的核心參數。目前 Linux Kernel 只有以下幾類支援 Namespace 隔離,其餘都歸類為全域參數:
| 參數類別 | 說明 |
|---|---|
| kernel.shm* | 共享記憶體相關 |
| kernel.msg* | Kernel 訊息佇列 / IPC 相關 |
| kernel.sem | 信號量相關 |
| fs.mqueue.* | POSIX 訊息佇列相關 |
| net.* | 網路子系統相關 |
要特別注意的是,net.* 底下仍有例外,像 net.netfilter.nf_conntrack_max(Kernel 3.10 時期)就屬於全域參數。
Kubernetes 的安全與非安全 sysctl 分類
Kubernetes 進一步將 Namespaced 參數區分為安全(safe)與非安全(unsafe)兩種。安全參數必須滿足:
- 不會影響同節點上的其他 Pod
- 不會損害節點健康狀態
- 不會讓 Pod 使用超出資源限制的 CPU 或記憶體
以下是目前 Kubernetes 預設允許的安全 sysctl 清單:
| 參數名稱 | 用途 | 備註 |
|---|---|---|
| kernel.shm_rmid_forced | 強制回收不再使用的共享記憶體段,防止記憶體洩漏 | |
| net.ipv4.ip_local_port_range | 定義 TCP/UDP 選擇本機連接埠的範圍 | |
| net.ipv4.tcp_syncookies | 啟用 SYN cookies 機制防範 SYN flood 攻擊 | |
| net.ipv4.ping_group_range | 定義允許開啟 ICMP echo socket 的群組 ID 範圍 | |
| net.ipv4.ip_unprivileged_port_start | 指定非特權連接埠的起始埠號 | |
| net.ipv4.ip_local_reserved_ports | 保留特定連接埠給指定服務使用 | 需 Kernel 3.16+ |
如果要使用非安全的 sysctl 參數,需要在 kubelet 啟動時加上白名單:
kubelet --allowed-unsafe-sysctls 'kernel.msg*,net.core.somaxconn' ...
Docker 容器中如何修改核心參數?
Dockerfile 本身無法直接修改核心參數,但可以在啟動容器時透過 --sysctl 參數傳入:
docker run -it --sysctl 'net.ipv4.ip_default_ttl=63' ubuntu sysctl net.ipv4.ip_default_ttl
# 輸出: net.ipv4.ip_default_ttl = 63
這邊有幾個要注意的地方:
- 只有 Namespaced 參數才能這樣設定,否則會報 "invalid argument" 錯誤
- 這個方式只在容器初始化階段生效,啟動後
/proc/sys仍是唯讀掛載 - 想在執行中的容器內用指令修改,必須使用
--privileged模式
我實際測試過,在 Dockerfile 裡面透過 /etc/sysfs.conf 來設定也是行不通的:
FROM ubuntu:latest
RUN apt-get update && apt-get install -y sysfsutils
RUN echo "net.ipv4.ip_default_ttl = 63" >> /etc/sysfs.conf
CMD ["bash"]
啟動後進容器確認:
$ docker run -it --rm ubuntu:kernal /bin/bash
root@45c2e6c156d9:/# sysctl net.ipv4.ip_default_ttl
net.ipv4.ip_default_ttl = 64
root@45c2e6c156d9:/# sysctl net.ipv4.ip_default_ttl=63
sysctl: setting key "net.ipv4.ip_default_ttl", ignoring: Read-only file system
結果很明確,寫進 sysfs.conf 的設定並沒有生效,而且手動修改也因為唯讀檔案系統而失敗。

在 Kubernetes Pod 中設定 Sysctl
在 Kubernetes 裡,可以透過 Pod 的 securityContext 來設定 Namespaced sysctl 參數,設定會套用到同一 Pod 中的所有容器:
apiVersion: v1
kind: Pod
metadata:
name: sysctl-example
spec:
securityContext:
sysctls:
- name: kernel.shm_rmid_forced
value: "0"
- name: net.core.somaxconn
value: "1024"
- name: kernel.msgmax
value: "65536"
在這個範例中,安全參數和非安全參數在 YAML 裡的寫法完全一樣,Kubernetes 不會在語法層面做區分。
如果需要使用 unsafe sysctl 並搭配特權模式:
apiVersion: v1
kind: Pod
metadata:
name: sysctl-example
annotations:
security.alpha.kubernetes.io/unsafe-sysctls: net.core.somaxconn=65535
spec:
securityContext:
privileged: true
...
但請務必注意,非安全 sysctl 參數本身具有不穩定性,使用不當可能導致容器異常、節點資源耗盡,甚至節點完全損壞。
那些無法 Namespace 隔離的參數怎麼辦?
對於沒有命名空間的 sysctl 參數(也就是節點層級的參數),如果需要修改,有幾種做法:
- 直接在每個節點的作業系統上手動設定
- 透過 DaemonSet 部署特權容器來批次修改
- 使用特權 InitContainer 在 Pod 啟動前修改(好處是不需要讓應用容器本身擁有特權)
重點回顧
| 情境 | 做法 |
|---|---|
容器內 sysctl -a 看得到的 Namespaced 參數 | 加入 kubelet 白名單後,透過 securityContext 修改 |
容器內 sysctl -a 看不到的全域參數 | 只能從宿主機端修改 |
| 可見參數的範圍 | 會隨 Kernel 版本而有差異 |
參考資料
- Kubernetes 官方文件 - 在 Cluster 中使用 Sysctl
- 騰訊雲容器團隊 - 核心參數與容器
- Arthur Chiao - The Mysterious Container somaxconn
- Kubernetes Source - safe_sysctls.go

發佈留言