21卒広告技術部のyamaYuです。 マトリックスの新作が楽しみです。 過去作を見返さなくてはと思いつつこの記事を書いています。 個人的には第一作が一番好きです。
さて、こちらの記事はGunosy Advent Calendar 2021の15日目の記事になっています。 昨日は村田さんの『AdKDD & KDD 2021 に参加しました』という記事でした。
今回は、EKS Managed Node Groupでカーネルパラメータを変更する必要があったのですが、一筋縄ではいかなかったのでその話を書こうと思います。
背景: net.core.somaxconn
を増やしたい
ここ最近の担当業務として、GunosyAds管理画面のOpsWorksからEKS*1への移行を進めています。 旧環境ではアプリケーションサーバーはNginx+Unicorn+Railsという構成になっています。 これをEKSに乗せるためにDockerコンテナ化し、NginxコンテナとRailsコンテナのSidecarパターンでPodを構成しました。 しかしながら、実際に動かしてみるとCPU等のリソースは十分にあるにもかかわらず、しばしば502が発生しており、Nginxのログには下記のエラーが出力されていました。
connect() to unix:/usr/src/app/shared/sockets/unicorn.sock failed (11: Resource temporarily unavailable) while connecting to upstream
調べてみたところ、net.core.somaxconn
というカーネルパラメータがあり、これがTCPソケットが受け付けた接続要求を格納するキューの最大長を定義しているのですが、
主要なDockerイメージではこの値はデフォルトで128になっており、それを超えるリクエストについてはキューから溢れてしまっていることが原因でした。
因みにこの設定値は下記コマンドで確認できます。
$ cat /proc/sys/net/core/somaxconn 128
設定値を変更するためには下記のsysctlの実行が必要になりますが、Dockerコンテナではデフォルトで許可されていません。
$ sysctl -w net.core.somaxconn=1024 sysctl: setting key "net.core.somaxconn": Read-only file system
Kubernetesでsysctlを実行する
Dockerであればdocker run
コマンドの--sysctl
オプションを指定すれば変更可能です。
Kubernetesの場合にどうすれば良いかは下記ドキュメントに書かれています。
sysctlはsafeとunsafeの2つに分類され、Kubernetesで定められたいくつかのsysctlだけが前者に属します。
今回設定したいnet.core.somaxconn
を含め、それ以外の多くのsysctlは後者に属し、実行するには次に示す手順が必要になります。
kubelet
コマンドのオプション--allowed-unsafe-sysctls
で実行したいunsafe sysctlを許可- unsafe sysctlを許可したPod Security Policy*2を作成
- 上記Pod Security Policyを設定したCluster Roleを作成
- 上記Cluster RoleをbindしたService Accountを作成
- 上記Service AccountをPodに設定
- PodのsecurityContextでsysctlと設定値を指定
1.のkubeletの設定については後ほど触れます。 一旦ここではその後の部分の設定例を載せます。 広告技術部ではCluster RoleなどのKubernetesのリソースはTerraformで管理しているためTerraformでの設定例になります。
# Pod Security Policy resource "kubernetes_pod_security_policy" "allow_unsafe_sysctls" { metadata { name = "allow-unsafe-sysctls" } spec { privileged = false allow_privilege_escalation = false volumes = ["*"] fs_group { rule = "RunAsAny" } run_as_user { rule = "RunAsAny" } se_linux { rule = "RunAsAny" } supplemental_groups { rule = "RunAsAny" } allowed_unsafe_sysctls = ["net.core.somaxconn"] } } # Cluster Role resource "kubernetes_cluster_role" "example" { metadata { name = "example" } rule { verbs = ["use"] api_groups = ["policy"] resources = ["podsecuritypolicies"] resource_names = ["allow-unsafe-sysctls"] } } # Cluster Role Binding resource "kubernetes_cluster_role_binding" "example" { metadata { name = "example" } subject { kind = "ServiceAccount" name = kubernetes_service_account.example.metadata[0].name namespace = kubernetes_service_account.example.metadata[0].namespace } role_ref { api_group = "rbac.authorization.k8s.io" kind = "ClusterRole" name = kubernetes_cluster_role.example.metadata[0].name } } # Service Account resource "kubernetes_service_account" "example" { metadata { name = "example" namespace = "example" } automount_service_account_token = false }
EKS Managed node groupでkubeletの設定を変更する
先程後回しにしたkubeletの設定についてです。 EKS Managed Node Groupで設定する場合について書きます。
EKS Managed Node Groupはワーカーノードのプロビジョニングや管理をしてくれる便利なしくみで、Launch templateやカスタムAMIを用いることで細かい設定もできるようになっています。 但しこれには少しクセがあり、Managed Node GroupのLaunch templateでUser dataを設定する場合、multipart形式で指定しなければならず、開発者が作成したUser dataとEKSが生成したUser dataがマージされるようになっています。
そのあたりの話は以下の記事でも紹介されています。 tech.gunosy.io
今回やりたいkubeletの設定は/etc/eks/bootstrap.sh
の--kubelet-extra-args
に記述しなければならない*3のですが、この部分はEKSが生成する側のUser dataに含まれるためそのままでは設定できません。
そこでドキュメントに従いカスタムAMIを指定します。
カスタムAMIといってもUser dataのみの変更なので実際に使うAMIは同じもので問題ないです。
これでマージしない通常のUser dataを記述できるようになります。
但し、本来Managed node groupで自動でやってくれていた部分を開発者が記述しないといけないので必須の設定*4を漏らさないように注意しないといけません。
以下Terraformでの設定例です。
locals { eks_k8s_version ="your_eks_k8s_version" userdata = <<-USERDATA #!/bin/bash ︙ B64_CLUSTER_CA="your_b64_cluster_ca" API_SERVER_URL="your_api_server_url" K8S_CLUSTER_DNS_IP="your_k8s_cluster_dns_ip" /etc/eks/bootstrap.sh example-cluster \ --b64-cluster-ca $B64_CLUSTER_CA \ --apiserver-endpoint $API_SERVER_URL \ --dns-cluster-ip $K8S_CLUSTER_DNS_IP \ --kubelet-extra-args '--allowed-unsafe-sysctls=net.core.somaxconn' \ USERDATA } # EKS Optimized AMI data "aws_ssm_parameter" "eks_ami" { name = "/aws/service/eks/optimized-ami/${local.eks_k8s_version}/amazon-linux-2/recommended/image_id" } # Launch template resource "aws_launch_template" "example" { image_id = data.aws_ssm_parameter.eks_ami.value ︙ user_data = base64encode(local.eks_ads_admin_autoscale_managed_userdata) } # EKS node group resource "aws_eks_node_group" "example" { ami_type = "CUSTOM" ︙ launch_template { id = aws_launch_template.example.id version = aws_launch_template.example.latest_version } }
おわりに 🐾
これで無事net.core.somaxconn
を変更でき、また502エラーを解消することができました。
他のカーネルパラメータを変更する際にも同様の手順を踏むことになると思いますので参考になれば幸いです。