gashirar's blog

ウイスキーがすき/美味しいものがすき/k8sがすき

kubeletの--protect-kernel-defaultsをtrueにする際のkernel tuning

はじめに

kubeletの--protect-kernel-defaultsをいじっていたときにいろいろみたことのまとめです。

事の起こり

  • kubeadmで構築したKubernetesクラスタを対象にKubernetes CIS Benchmarkの確認中、下記項目を見つける。

    4.2.6 Ensure that the --protect-kernel-defaults argument is set to true

  • とりあえず--protect-kernel-defaultstrueにして起動してみたが、kubelet自体がエラーで立ち上がらない。

  • kernel tuningすればよさそうだが、ドキュメントにどれ直せばよいかがなかったのでソース読んで調べてみることに

--protect-kernel-defaultsとは

github.com

動きは下記の通り

  • false
    • kubelet起動時にkernelパラメータの変更処理を行う。
  • true
    • kubelet起動時にkernelパラメータのチェックを行い、想定と異なっていた際はエラーを出力して終了

「じゃあkubeletが想定するパラメータってなんじゃい」というのが調査対象です。

調査

setupNode

とりあえずProtectKernelDefaultsで検索。
それっぽい部分が見つかりました。

https://github.com/kubernetes/kubernetes/blob/release-1.19/pkg/kubelet/cm/container_manager_linux.go#L447

func (cm *containerManagerImpl) setupNode(activePods ActivePodsFunc) error {
    f, err := validateSystemRequirements(cm.mountUtil)
    if err != nil {
        return err
    }
    if !f.cpuHardcapping {
        cm.status.SoftRequirements = fmt.Errorf("CPU hardcapping unsupported")
    }
    b := KernelTunableModify
    if cm.GetNodeConfig().ProtectKernelDefaults {
        b = KernelTunableError
    }
    if err := setupKernelTunables(b); err != nil {
        return err
    }

setupKernelTunablesにもぐってみます。

setupKernelTunables

https://github.com/kubernetes/kubernetes/blob/release-1.19/pkg/kubelet/cm/container_manager_linux.go#L408

func setupKernelTunables(option KernelTunableBehavior) error {
    desiredState := map[string]int{
        utilsysctl.VMOvercommitMemory: utilsysctl.VMOvercommitMemoryAlways,
        utilsysctl.VMPanicOnOOM:       utilsysctl.VMPanicOnOOMInvokeOOMKiller,
        utilsysctl.KernelPanic:        utilsysctl.KernelPanicRebootTimeout,
        utilsysctl.KernelPanicOnOops:  utilsysctl.KernelPanicOnOopsAlways,
        utilsysctl.RootMaxKeys:        utilsysctl.RootMaxKeysSetting,
        utilsysctl.RootMaxBytes:       utilsysctl.RootMaxBytesSetting,
    }

    sysctl := utilsysctl.New()

    errList := []error{}
    for flag, expectedValue := range desiredState {
        val, err := sysctl.GetSysctl(flag)
        if err != nil {
            errList = append(errList, err)
            continue
        }
        if val == expectedValue {
            continue
        }

        switch option {
        case KernelTunableError:
            errList = append(errList, fmt.Errorf("invalid kernel flag: %v, expected value: %v, actual value: %v", flag, expectedValue, val))
        case KernelTunableWarn:
            klog.V(2).Infof("Invalid kernel flag: %v, expected value: %v, actual value: %v", flag, expectedValue, val)
        case KernelTunableModify:
            klog.V(2).Infof("Updating kernel flag: %v, expected value: %v, actual value: %v", flag, expectedValue, val)
            err = sysctl.SetSysctl(flag, expectedValue)
            if err != nil {
                errList = append(errList, err)
            }
        }
    }
    return utilerrors.NewAggregate(errList)
}

desiredStateの項目についてsysctl.GetSysctlで取得した値とを比較して同じであればcontinue、異なっていたらエラーを返しているようです。

utilsysctl

https://github.com/kubernetes/kubernetes/blob/release-1.19/pkg/util/sysctl/sysctl.go#L26

ありました。このパラメータ通りにkernel tuningをしたうえで--protect-kernel-defaultstrueにすれば良いでしょう。

const (
    sysctlBase = "/proc/sys"
    // VMOvercommitMemory refers to the sysctl variable responsible for defining
    // the memory over-commit policy used by kernel.
    VMOvercommitMemory = "vm/overcommit_memory"
    // VMPanicOnOOM refers to the sysctl variable responsible for defining
    // the OOM behavior used by kernel.
    VMPanicOnOOM = "vm/panic_on_oom"
    // KernelPanic refers to the sysctl variable responsible for defining
    // the timeout after a panic for the kernel to reboot.
    KernelPanic = "kernel/panic"
    // KernelPanicOnOops refers to the sysctl variable responsible for defining
    // the kernel behavior when an oops or BUG is encountered.
    KernelPanicOnOops = "kernel/panic_on_oops"
    // RootMaxKeys refers to the sysctl variable responsible for defining
    // the maximum number of keys that the root user (UID 0 in the root user namespace) may own.
    RootMaxKeys = "kernel/keys/root_maxkeys"
    // RootMaxBytes refers to the sysctl variable responsible for defining
    // the maximum number of bytes of data that the root user (UID 0 in the root user namespace)
    // can hold in the payloads of the keys owned by root.
    RootMaxBytes = "kernel/keys/root_maxbytes"

    // VMOvercommitMemoryAlways represents that kernel performs no memory over-commit handling.
    VMOvercommitMemoryAlways = 1
    // VMPanicOnOOMInvokeOOMKiller represents that kernel calls the oom_killer function when OOM occurs.
    VMPanicOnOOMInvokeOOMKiller = 0

    // KernelPanicOnOopsAlways represents that kernel panics on kernel oops.
    KernelPanicOnOopsAlways = 1
    // KernelPanicRebootTimeout is the timeout seconds after a panic for the kernel to reboot.
    KernelPanicRebootTimeout = 10

    // RootMaxKeysSetting is the maximum number of keys that the root user (UID 0 in the root user namespace) may own.
    // Needed since docker creates a new key per container.
    RootMaxKeysSetting = 1000000
    // RootMaxBytesSetting is the maximum number of bytes of data that the root user (UID 0 in the root user namespace)
    // can hold in the payloads of the keys owned by root.
    // Allocate 25 bytes per key * number of MaxKeys.
    RootMaxBytesSetting = RootMaxKeysSetting * 25
)
パラメータ kubeletを動かす上での設定値
vm/overcommit_memory 1
vm/panic_on_oom 0
kernel/panic 1
kernel/panic_on_oops 10
kernel/keys/root_maxkeys 1000000
kernel/keys/root_maxbytes root_max_keys * 25

まとめ

マネージドK8sはこのあたりを気にする必要がないので良いですね。

おまけ

kubelet fails to start when set --protect-kernel-defaults=true. · Issue #66241 · kubernetes/kubernetes · GitHub

cat > /etc/sysctl.d/90-kubelet.conf << EOF
vm.overcommit_memory=1
kernel.panic=10
kernel.panic_on_oops=1
EOF
sysctl -p /etc/sysctl.d/90-kubelet.conf

上記ではovercommit_memory/panic/panic_on_oopsを修正すればいいよとのことですが。 調べた結果と比べるとpanic_on_oom/root_maxkeys/root_maxbytesについては言及がない様子。

気になったのでこれについても調べてみたところ、どうやらkubeletの要求値とデフォルトが一緒だから問題になっていなかったようですね。

https://www.kernel.org/doc/Documentation/sysctl/vm.txt

https://www.man7.org/linux/man-pages/man7/keyrings.7.html