tag:crieit.net,2005:https://crieit.net/users/albatross0/feed albatross0の投稿 - Crieit Crieitでユーザーalbatross0による最近の投稿 2020-10-14T22:59:43+09:00 https://crieit.net/users/albatross0/feed tag:crieit.net,2005:PublicArticle/16134 2020-10-14T22:59:43+09:00 2020-10-14T22:59:43+09:00 https://crieit.net/posts/LVS LVS のセッション数上限についてメモ <p><strong>Qrunch から引っ越し</strong></p> <p>conntrack については nf_conntrack_max の話題を時々聞くが、<br /> ipvs のセッション数はリミットを気にしなくていいんだっけ。</p> <p>ちょうど手元にあった <code>kernel-ml-4.13.5-1.el7.elrepo.nosrc.rpm</code> のソースで確認</p> <h1 id="conntrack"><a href="#conntrack">conntrack</a></h1> <p>そもそも nf_conntrack_max はどうなっているのか確認</p> <p><code>net/netfilter/nf_conntrack_core.c</code></p> <pre><code class="c">static struct nf_conn * __nf_conntrack_alloc(struct net *net, const struct nf_conntrack_zone *zone, const struct nf_conntrack_tuple *orig, const struct nf_conntrack_tuple *repl, gfp_t gfp, u32 hash) { struct nf_conn *ct; /* We don't want any race condition at early drop stage */ atomic_inc(&net->ct.count); if (nf_conntrack_max && unlikely(atomic_read(&net->ct.count) > nf_conntrack_max)) { if (!early_drop(net, hash)) { if (!conntrack_gc_work.early_drop) conntrack_gc_work.early_drop = true; atomic_dec(&net->ct.count); net_warn_ratelimited("nf_conntrack: table full, dropping packet\n"); return ERR_PTR(-ENOMEM); } } /* * Do not use kmem_cache_zalloc(), as this cache uses * SLAB_TYPESAFE_BY_RCU. */ ct = kmem_cache_alloc(nf_conntrack_cachep, gfp); if (ct == NULL) goto out; </code></pre> <p>対象の netns の conntrack の数が nf_conntrack_max より多ければログを出して抜ける。<br /> nf_conntrack_max 以下の場合はその次の処理 kmem_cache_alloc() で nf_conn 構造体の領域が割り当てられるようだ。</p> <h1 id="ipvs"><a href="#ipvs">ipvs</a></h1> <p>続いて ipvs の場合はどうか確認</p> <p><code>net/netfilter/ipvs/ip_vs_conn.c</code></p> <pre><code class="c">truct ip_vs_conn * ip_vs_conn_new(const struct ip_vs_conn_param *p, int dest_af, const union nf_inet_addr *daddr, __be16 dport, unsigned int flags, struct ip_vs_dest *dest, __u32 fwmark) { struct ip_vs_conn *cp; struct netns_ipvs *ipvs = p->ipvs; struct ip_vs_proto_data *pd = ip_vs_proto_data_get(p->ipvs, p->protocol); cp = kmem_cache_alloc(ip_vs_conn_cachep, GFP_ATOMIC); if (cp == NULL) { IP_VS_ERR_RL("%s(): no memory\n", __func__); return NULL; } </code></pre> <p>ip_vs_conn_new() にそれらしきものは無い。呼び出し元を見ても、特に何かのパラメータによる制限は無さそう。</p> <p>スラブアロケータは全然分かってないけど、inode のキャッシュでめっちゃメモリ食われてたという話を聞くし、メモリが空いている限り使えるのだろう。</p> <p>そういえば inode のキャッシュって drop_caches で消せるけど、これは大丈夫なんだろうか。多分何か識別するものがあるはずなので xfs の inode あたりを確認してみる。</p> <p><code>fs/xfs/xfs_super.c</code></p> <pre><code class="c"> xfs_inode_zone = kmem_zone_init_flags(sizeof(xfs_inode_t), "xfs_inode", KM_ZONE_HWALIGN | KM_ZONE_RECLAIM | KM_ZONE_SPREAD | KM_ZONE_ACCOUNT, xfs_fs_inode_init_once); </code></pre> <p><code>fs/xfs/kmem.h</code></p> <pre><code class="c">#define KM_ZONE_RECLAIM SLAB_RECLAIM_ACCOUNT /* 勝手に省略 */ static inline kmem_zone_t * kmem_zone_init_flags(int size, char *zone_name, unsigned long flags, void (*construct)(void *)) { return kmem_cache_create(zone_name, size, 0, flags, construct); } </code></pre> <p><code>include/linux/slab.h</code></p> <pre><code class="c">/* The following flags affect the page allocator grouping pages by mobility */ #define SLAB_RECLAIM_ACCOUNT 0x00020000UL /* Objects are reclaimable */ </code></pre> <p>kmem_cache_create() する時に SLAB_RECLAIM_ACCOUNT のフラグ渡しているようだ。</p> <p>ipvs で kmem_cache_create() しているところはこのフラグが無かったので大丈夫なのだろう</p> <p><code>net/netfilter/ipvs/ip_vs_conn.c</code></p> <pre><code class="c">int __init ip_vs_conn_init(void) { int idx; /* Compute size and mask */ ip_vs_conn_tab_size = 1 << ip_vs_conn_tab_bits; ip_vs_conn_tab_mask = ip_vs_conn_tab_size - 1; /* * Allocate the connection hash table and initialize its list heads */ ip_vs_conn_tab = vmalloc(ip_vs_conn_tab_size * sizeof(*ip_vs_conn_tab)); if (!ip_vs_conn_tab) return -ENOMEM; /* Allocate ip_vs_conn slab cache */ ip_vs_conn_cachep = kmem_cache_create("ip_vs_conn", sizeof(struct ip_vs_conn), 0, SLAB_HWCACHE_ALIGN, NULL); if (!ip_vs_conn_cachep) { vfree(ip_vs_conn_tab); return -ENOMEM; } </code></pre> <h1 id="サービス毎のセッション数制限"><a href="#%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E6%AF%8E%E3%81%AE%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E6%95%B0%E5%88%B6%E9%99%90">サービス毎のセッション数制限</a></h1> <p>nf_conntrack_max のような全体の (network namespace 毎の) 制限値は無さそうだが、ググったらサービス毎に上限を設定できるらしい。</p> <p>ipvsadm -h から抜粋すると以下の部分のようだ。</p> <pre><code> --u-threshold -x uthreshold upper threshold of connections --l-threshold -y lthreshold lower threshold of connections </code></pre> <p>lower threshold はどういう時に使うのかよく分からない。気にしないでおく。<br /> upper threshold もそうだが、明示的に指定しなければ制限しないようだ。<br /> <code>ipvsadm -ln --thresholds</code> した時に Uthreshold が 0 になっていれば制限されていない。</p> <p>ちょっと試してみたところ、すべての real server が upper threshold に達した状態で新規セッションの通信がきた場合は ICMP Port Unreachable を返すようだ。</p> <p>それはいいのだが、セッションの数として inactive なセッションもカウントされているので、セッションを使い続けるのではなく接続・切断が頻繁に起こる場合などでは TIME_WAIT が多くなってしまい、制限方法として採用しづらい。</p> <p><code>net/netfilter/ipvs/ip_vs_conn.c</code></p> <pre><code class="c">static inline int ip_vs_dest_totalconns(struct ip_vs_dest *dest) { return atomic_read(&dest->activeconns) + atomic_read(&dest->inactconns); } /* * Bind a connection entry with a virtual service destination * Called just after a new connection entry is created. */ static inline void ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) { /* 勝手に省略 */ if (dest->u_threshold != 0 && ip_vs_dest_totalconns(dest) >= dest->u_threshold) dest->flags |= IP_VS_DEST_F_OVERLOAD; } </code></pre> <h1 id="結論"><a href="#%E7%B5%90%E8%AB%96">結論</a></h1> <p>lower/upper threshold を使っていなければ、ipvs セッション上限は気にしない。メモリの量は気にする。</p> albatross0 tag:crieit.net,2005:PublicArticle/16133 2020-10-14T22:58:44+09:00 2020-10-14T22:58:44+09:00 https://crieit.net/posts/GKE-upgrade-network-plugin GKE を upgrade したら network plugin が死んだので対応した作業記録 <p><strong>Qrunch から引っ越し</strong></p> <p>※ GKE のネガキャンではありません。むしろ好きでちゃんと運用したいからこそ残しているものです。</p> <p>軽い気持ちで GKE を <code>1.11.2-gke.18</code> から <code>1.11.3-gke.18</code> に upgrade した。</p> <p>そして何気なく Deployment を作ったが Pod が起動しない。<br /> <code>kubectl describe pod</code> で見るとおかしな event が記録されているのが分かった。</p> <pre><code>Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 3m default-scheduler Successfully assigned xyz/hoge-5b4db95bf5-krmjc to gke-gke01-preemptible01-e5cc132c-77fz Warning FailedCreatePodSandBox 3m kubelet, gke-gke01-preemptible01-e5cc132c-77fz Failed create pod sandbox: rpc error: code = Unknown desc = failed to set up sandbox container "af7579fb0a4ad73cfaeb083ef47de36b7f84aed7f160685384354ed0d8512339" network for pod "hoge-5b4db95bf5-krmjc": NetworkPlugin cni failed to set up pod "hoge-5b4db95bf5-krmjc_xyz" network: stat /var/lib/calico/nodename: no such file or directory: check that the calico/node container is running and has mounted /var/lib/calico/ Warning FailedCreatePodSandBox 3m kubelet, gke-gke01-preemptible01-e5cc132c-77fz Failed create pod sandbox: rpc error: code = Unknown desc = failed to set up sandbox container "56c9b914c9f3c940c8d5479807820814e43f6716a263ca5c9a1bf53cef6ae252" network for pod "hoge-5b4db95bf5-krmjc": NetworkPlugin cni failed to set up pod "hoge-5b4db95bf5-krmjc_xyz" network: stat /var/lib/calico/nodename: no such file or directory: check that the calico/node container is running and has mounted /var/lib/calico/ Warning FailedCreatePodSandBox 3m kubelet, gke-gke01-preemptible01-e5cc132c-77fz Failed create pod sandbox: rpc error: code = Unknown desc = failed to set up sandbox container "10bdc42cc20617283e18f67a4e4d171eecb4182dd0e9f7114465c4047e5f3c12" network for pod "hoge-5b4db95bf5-krmjc": NetworkPlugin cni failed to set up pod "hoge-5b4db95bf5-krmjc_xyz" network: stat /var/lib/calico/nodename: no such file or directory: check that the calico/node container is running and has mounted /var/lib/calico/ </code></pre> <p>このクラスタは NetworkPolicy を有効にしているため、Calico が動作している。<br /> kube-system namespace の Pod を見てみると、何やらひどいことが起きているようだ。</p> <pre><code>$ kubectl -n kube-system get pod | grep -E '^NAME|calico' NAME READY STATUS RESTARTS AGE calico-node-974fs 1/2 CrashLoopBackOff 9 24m calico-node-fx464 1/2 CrashLoopBackOff 9 24m calico-node-kzhsr 1/2 CrashLoopBackOff 9 24m calico-node-q4ktd 1/2 CrashLoopBackOff 9 24m calico-node-qwqsx 1/2 CrashLoopBackOff 9 24m calico-node-vertical-autoscaler-547d98499d-l7rd2 1/1 Running 0 6h calico-typha-5977794b76-dmxjr 0/1 CrashLoopBackOff 9 24m calico-typha-horizontal-autoscaler-5ff7f558cc-2ksbc 1/1 Running 0 19h calico-typha-vertical-autoscaler-5d4bf57df5-hp8r6 1/1 Running 0 6h </code></pre> <p>network plugin が動作しなくなっているため、既存 Pod はまだ生き延びられるが、新規 Pod が起動しない状態になっているようだ。</p> <p><code>calico-node</code> の DaemonSet を YAML で出力して中身をみると、<code>v2.6.11</code> だったはずの calico のイメージが <code>v3.2.4</code> に上がっていた。<br /> (バージョンを確認したのは、別件で GKE の問い合わせをしていて Calico のバージョンが変わりそうな気配を感じていたため)</p> <p>別途 NetworkPolicy を有効にした新規クラスタを作成してみて、そちらでは問題無く動作することを確認。</p> <p>問題が起きている方のクラスタで <code>calico-node</code> の Pod のログを見ると以下のようになっていた。</p> <pre><code>2018-12-05 05:16:41.622 [INFO][8] startup.go 252: Early log level set to info 2018-12-05 05:16:41.623 [INFO][8] startup.go 268: Using NODENAME environment for node name 2018-12-05 05:16:41.623 [INFO][8] startup.go 280: Determined node name: gke-gke00-preemptible01-df3d5d39-m2h6 2018-12-05 05:16:41.624 [INFO][8] startup.go 303: Checking datastore connection 2018-12-05 05:16:41.638 [INFO][8] startup.go 327: Datastore connection verified 2018-12-05 05:16:41.638 [INFO][8] startup.go 100: Datastore is ready 2018-12-05 05:16:41.651 [INFO][8] startup.go 1052: Running migration 2018-12-05 05:16:41.651 [INFO][8] migrate.go 866: Querying current v1 snapshot and converting to v3 2018-12-05 05:16:41.651 [INFO][8] migrate.go 875: handling FelixConfiguration (global) resource 2018-12-05 05:16:41.658 [INFO][8] migrate.go 875: handling ClusterInformation (global) resource 2018-12-05 05:16:41.658 [INFO][8] migrate.go 875: skipping FelixConfiguration (per-node) resources - not supported 2018-12-05 05:16:41.658 [INFO][8] migrate.go 875: handling BGPConfiguration (global) resource 2018-12-05 05:16:41.658 [INFO][8] migrate.go 600: Converting BGP config -> BGPConfiguration(default) 2018-12-05 05:16:41.677 [INFO][8] migrate.go 875: skipping Node resources - these do not need migrating 2018-12-05 05:16:41.677 [INFO][8] migrate.go 875: skipping BGPPeer (global) resources - these do not need migrating 2018-12-05 05:16:41.677 [INFO][8] migrate.go 875: handling BGPPeer (node) resources 2018-12-05 05:16:41.687 [INFO][8] migrate.go 875: skipping HostEndpoint resources - not supported 2018-12-05 05:16:41.687 [INFO][8] migrate.go 875: skipping IPPool resources - these do not need migrating 2018-12-05 05:16:41.687 [INFO][8] migrate.go 875: skipping GlobalNetworkPolicy resources - these do not need migrating 2018-12-05 05:16:41.687 [INFO][8] migrate.go 875: skipping Profile resources - these do not need migrating 2018-12-05 05:16:41.688 [INFO][8] migrate.go 875: skipping WorkloadEndpoint resources - these do not need migrating 2018-12-05 05:16:41.688 [INFO][8] migrate.go 875: data converted successfully 2018-12-05 05:16:41.688 [INFO][8] migrate.go 866: Storing v3 data 2018-12-05 05:16:41.688 [INFO][8] migrate.go 875: Storing resources in v3 format 2018-12-05 05:16:41.752 [INFO][8] migrate.go 1151: Failed to create resource Key=BGPConfiguration(default) error=resource does not exist: BGPConfiguration(default) with error: the server could not find the requested resource (post BGPConfigurations.crd.projectcalico.org) 2018-12-05 05:16:41.753 [ERROR][8] migrate.go 884: Unable to store the v3 resources 2018-12-05 05:16:41.753 [INFO][8] migrate.go 875: cause: resource does not exist: BGPConfiguration(default) with error: the server could not find the requested resource (post BGPConfigurations.crd.projectcalico.org) 2018-12-05 05:16:41.753 [ERROR][8] startup.go 107: Unable to ensure datastore is migrated. error=Migration failed: error storing converted data: resource does not exist: BGPConfiguration(default) with error: the server could not find the requested resource (post BGPConfigurations.crd.projectcalico.org) 2018-12-05 05:16:41.753 [WARNING][8] startup.go 1066: Terminating </code></pre> <p>新規構築したクラスタの <code>calico-node</code> の Pod のログと見比べると <code>migrate.go</code> の行が存在しない。<br /> <code>2018-12-05 05:16:41.651 [INFO][8] startup.go 1052: Running migration</code> という部分が鍵と思われる。</p> <p><code>startup.go</code> は <a target="_blank" rel="nofollow noopener" href="https://github.com/projectcalico/node.git">projectcalico/node</a> を見れば良さそうだが、<br /> <code>migrate.go</code> は <a target="_blank" rel="nofollow noopener" href="https://github.com/projectcalico/libcalico-go">projectcalico/libcalico-go</a> にあるようだ。<br /> libcalico-go のバージョンはどれを使っているのか分からないが、ログから BGPConfiguration を作れていない様子がうかがえる。</p> <p>Kubernetes における Calico は CustomResourceDefinition として設定を管理しているらしいので、crd の定義を調べる</p> <pre><code>$ kubectl get crd NAME AGE backendconfigs.cloud.google.com 22h clusterinformations.crd.projectcalico.org 22h felixconfigurations.crd.projectcalico.org 22h globalbgpconfigs.crd.projectcalico.org 22h globalfelixconfigs.crd.projectcalico.org 22h globalnetworkpolicies.crd.projectcalico.org 22h globalnetworksets.crd.projectcalico.org 22h hostendpoints.crd.projectcalico.org 22h ippools.crd.projectcalico.org 22h networkpolicies.crd.projectcalico.org 22h scalingpolicies.scalingpolicy.kope.io 22h </code></pre> <p>ログには<br /> <code>the server could not find the requested resource (post BGPConfigurations.crd.projectcalico.org)</code><br /> と出ていて、実際に <code>bgpconfigurations.crd.projectcalico.org</code> は存在しない。</p> <p>とりあえず手動で CRD を追加してみたい。<br /> 内容が分からないので適当にググると <code>https://github.com/projectcalico/libcalico-go/blob/master/test/crds.yaml</code> が見つかった。</p> <p>元の CRD を残しておきつつ、この <code>crds.yaml</code> を適用してみる。</p> <pre><code>$ kubectl get crd -o yaml > crd-backup.yaml $ curl -LO https://raw.githubusercontent.com/projectcalico/libcalico-go/master/test/crds.yaml $ kubectl apply -f crds.yaml customresourcedefinition.apiextensions.k8s.io "globalfelixconfigs.crd.projectcalico.org" configured customresourcedefinition.apiextensions.k8s.io "globalbgpconfigs.crd.projectcalico.org" configured customresourcedefinition.apiextensions.k8s.io "ippools.crd.projectcalico.org" configured customresourcedefinition.apiextensions.k8s.io "bgppeers.crd.projectcalico.org" created customresourcedefinition.apiextensions.k8s.io "globalnetworkpolicies.crd.projectcalico.org" configured customresourcedefinition.apiextensions.k8s.io "hostendpoints.crd.projectcalico.org" configured customresourcedefinition.apiextensions.k8s.io "felixconfigurations.crd.projectcalico.org" configured customresourcedefinition.apiextensions.k8s.io "bgpconfigurations.crd.projectcalico.org" created customresourcedefinition.apiextensions.k8s.io "clusterinformations.crd.projectcalico.org" configured customresourcedefinition.apiextensions.k8s.io "networkpolicies.crd.projectcalico.org" configured customresourcedefinition.apiextensions.k8s.io "globalnetworksets.crd.projectcalico.org" configured </code></pre> <p>bgppeers.crd.projectcalico.org と bgpconfigurations.crd.projectcalico.org が新規作成されたようだ。<br /> CRD が作られたので、CrashLoopBackOff になっていた <code>calico-node</code> や <code>calico-typha</code> の Pod を消して再作成させる。</p> <pre><code>$ kubectl -n kube-system get pod -l k8s-app=calico-node $ kubectl -n kube-system delete pod -l k8s-app=calico-node $ kubectl -n kube-system get pod -l k8s-app=calico-typha $ kubectl -n kube-system delete pod -l k8s-app=calico-typha </code></pre> <p>これでしばらく放置しておくと、<code>calico-node</code> や <code>calico-typha</code> の Pod が起動し、他の Pod も起動するようになった。</p> <p><code>migrate.go</code> が成功した部分のログは以下のようなものだった。</p> <pre><code>2018-12-05 05:45:51.223 [INFO][8] startup.go 1052: Running migration 2018-12-05 05:45:51.223 [INFO][8] migrate.go 866: Querying current v1 snapshot and converting to v3 2018-12-05 05:45:51.223 [INFO][8] migrate.go 875: handling FelixConfiguration (global) resource 2018-12-05 05:45:51.232 [INFO][8] migrate.go 875: handling ClusterInformation (global) resource 2018-12-05 05:45:51.232 [INFO][8] migrate.go 875: skipping FelixConfiguration (per-node) resources - not supported 2018-12-05 05:45:51.232 [INFO][8] migrate.go 875: handling BGPConfiguration (global) resource 2018-12-05 05:45:51.232 [INFO][8] migrate.go 600: Converting BGP config -> BGPConfiguration(default) 2018-12-05 05:45:51.245 [INFO][8] migrate.go 875: skipping Node resources - these do not need migrating 2018-12-05 05:45:51.245 [INFO][8] migrate.go 875: skipping BGPPeer (global) resources - these do not need migrating 2018-12-05 05:45:51.245 [INFO][8] migrate.go 875: handling BGPPeer (node) resources 2018-12-05 05:45:51.255 [INFO][8] migrate.go 875: skipping HostEndpoint resources - not supported 2018-12-05 05:45:51.255 [INFO][8] migrate.go 875: skipping IPPool resources - these do not need migrating 2018-12-05 05:45:51.255 [INFO][8] migrate.go 875: skipping GlobalNetworkPolicy resources - these do not need migrating 2018-12-05 05:45:51.255 [INFO][8] migrate.go 875: skipping Profile resources - these do not need migrating 2018-12-05 05:45:51.255 [INFO][8] migrate.go 875: skipping WorkloadEndpoint resources - these do not need migrating 2018-12-05 05:45:51.255 [INFO][8] migrate.go 875: data converted successfully 2018-12-05 05:45:51.255 [INFO][8] migrate.go 866: Storing v3 data 2018-12-05 05:45:51.255 [INFO][8] migrate.go 875: Storing resources in v3 format 2018-12-05 05:45:51.324 [INFO][8] migrate.go 875: success: resources stored in v3 datastore 2018-12-05 05:45:51.324 [INFO][8] migrate.go 866: Migrating IPAM data 2018-12-05 05:45:51.324 [INFO][8] migrate.go 875: no data to migrate - not supported 2018-12-05 05:45:51.324 [INFO][8] migrate.go 866: Data migration from v1 to v3 successful 2018-12-05 05:45:51.324 [INFO][8] migrate.go 875: check the output for details of the migrated resources 2018-12-05 05:45:51.324 [INFO][8] migrate.go 875: continue by upgrading your calico/node versions to Calico v3.x 2018-12-05 05:45:51.324 [INFO][8] startup.go 1056: Migration successful </code></pre> <p>本来は GKE 側で修正されることではないかと思うが、どうなるのだろう。</p> <h2 id="後日確認・追記 (12/14)"><a href="#%E5%BE%8C%E6%97%A5%E7%A2%BA%E8%AA%8D%E3%83%BB%E8%BF%BD%E8%A8%98+%2812%2F14%29">後日確認・追記 (12/14)</a></h2> <ul> <li>GKE の Release Notes の <a target="_blank" rel="nofollow noopener" href="https://cloud.google.com/kubernetes-engine/release-notes">Known Issues</a> (抜粋):<br /> > Users upgrading to GKE 1.11.3 on clusters that use Calico network policies may experience failures due to a problem recreating the BGPConfigurations.crd.projectcalico.org resource. This problem does not affect newly-created clusters. This is expected to be fixed in the coming weeks.<br /> ><br /> > To work around this problem, you can create the BGPConfigurations.crd.projectcalico.org resource manually:</li> <li>Google の Issue Tracker: <a target="_blank" rel="nofollow noopener" href="https://issuetracker.google.com/issues/120255782">URGENT: after upgrading master nodes to 1.11.3 calico stopped working</a></li> <li>Calico の Issue: <a target="_blank" rel="nofollow noopener" href="https://github.com/projectcalico/calico/issues/2324">#2324</a></li> <li>Kubernetes の PullRequest: <a target="_blank" rel="nofollow noopener" href="https://github.com/kubernetes/kubernetes/pull/71868">#71868</a>, <a target="_blank" rel="nofollow noopener" href="https://github.com/kubernetes/kubernetes/pull/71682">#71682</a> <ul> <li>release-1.11 ブランチへのマージ: <a href="">#71918</a></li> <li>release-1.12 ブランチへのマージ: <a href="">#71882</a></li> <li>release-1.13 ブランチへのマージ: <a target="_blank" rel="nofollow noopener" href="https://github.com/kubernetes/kubernetes/pull/71883">#71883</a></li> <li>v1.13.1 のリリースで修正済み <a target="_blank" rel="nofollow noopener" href="https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.13.md#changelog-since-v1130">Changelog</a><br /> > Include CRD for BGPConfigurations, needed for calico 2.x to 3.x upgrade. (#71868, @satyasm)</li> <li>他のバージョンも次リリースされた時に修正されているはず。GKE もどこかで修正されることだろう。</li> </ul></li> </ul> albatross0 tag:crieit.net,2005:PublicArticle/16132 2020-10-14T22:57:52+09:00 2020-10-14T22:57:52+09:00 https://crieit.net/posts/GKE-kube-proxy-drain GKE の kube-proxy が drain されない理由 <p><strong>Qrunch から引っ越し</strong></p> <p>kubectl drain した時、テストで作った ownerReference の無い Pod がいたために怒られた。<br /> (これらの Pod は --force オプションで削除させることができる)</p> <p>ふと kube-proxy のことを思い出し、気になって調べた。</p> <p>GKE だと kube-proxy は /etc/kubernetes/manifests から起動されていて ownerReference は無いはずなのだが、どうやって drain を回避しているのだろうか。</p> <p>ファイルの manifest から起動しているのでそもそも API server 経由で実際の Pod 削除はできないのだが、Pod の定義だけ (一時的に) 削除することはあり得るのでは?</p> <p><code>pkg/kubectl/cmd/drain.go</code> を読むと分かる。</p> <pre><code>RunDrain() deleteOrEvictPodsSimple() getPodsForDeletion() deleteOrEvictPods() </code></pre> <p>kubectl drain コマンドにはいくつかのフィルタ条件があり、それらが適用されて残った Pod が<br /> <code>deleteOrEvictPods()</code> に渡される。</p> <p>各フィルタは <code>getPodsForDeletion()</code> の中で適用される。</p> <pre><code class="go">// getPodsForDeletion receives resource info for a node, and returns all the pods from the given node that we // are planning on deleting. If there are any pods preventing us from deleting, we return that list in an error. func (o *DrainOptions) getPodsForDeletion(nodeInfo *resource.Info) (pods []corev1.Pod, err error) { labelSelector, err := labels.Parse(o.PodSelector) if err != nil { return pods, err } podList, err := o.client.CoreV1().Pods(metav1.NamespaceAll).List(metav1.ListOptions{ LabelSelector: labelSelector.String(), FieldSelector: fields.SelectorFromSet(fields.Set{"spec.nodeName": nodeInfo.Name}).String()}) if err != nil { return pods, err } ws := podStatuses{} fs := podStatuses{} for _, pod := range podList.Items { podOk := true for _, filt := range []podFilter{o.daemonsetFilter, mirrorPodFilter, o.localStorageFilter, o.unreplicatedFilter} { filterOk, w, f := filt(pod) podOk = podOk && filterOk </code></pre> <ul> <li><code>-l, --selector=''</code> は drain 対象の Node をフィルタするもので、Pod に作用するものではない。</li> <li><code>--pod-selector=''</code> で drain で処理する Pod をフィルタする。<br /> 例えば、特定のラベルを持つ Pod を evict/delete 対象から外すには <code>--pod-selector='key!=value'</code> をつける</li> <li><code>--ignore-daemonsets</code> は DaemonSet から作成された Pod を除外する。(daemonsetFilter)</li> <li><code>--delete-local-data</code> は emptyDir ボリュームを持つ Pod を除外する。(localStorageFilter)</li> </ul> <p>ownerRefernece の有無は unreplicatedFilter で判定されるが、kube-proxy が除外されるのは mirrorPodFilter だった。</p> <pre><code class="go">func mirrorPodFilter(pod corev1.Pod) (bool, *warning, *fatal) { if _, found := pod.ObjectMeta.Annotations[corev1.MirrorPodAnnotationKey]; found { return false, nil, nil } return true, nil, nil } </code></pre> <p>annotation の定義は <code>pkg/apis/core/annotation_key_constants.go</code> を参照する。</p> <pre><code class="go"> // MirrorAnnotationKey represents the annotation key set by kubelets when creating mirror pods MirrorPodAnnotationKey string = "kubernetes.io/config.mirror" </code></pre> <p><code>kubernetes.io/config.mirror</code> という annotation を持っているかどうかでフィルタされているようだ。</p> <p>Mirror Pod って何だ?と思ってググると</p> <p>Statis Pod が API server 側で見えるようになっているもの (見えるだけで API から制御できないもの) のことを指すらしい。</p> <p>Static Pod は kubelet が API server ではなく ファイルや HTTP 経由で渡された manifest を元に 作成・起動した Pod のこと => <a target="_blank" rel="nofollow noopener" href="https://kubernetes.io/docs/tasks/administer-cluster/static-pod/">Static Pods</a></p> <p>調べてから見つけたが、詳しく説明している記事があった => <a target="_blank" rel="nofollow noopener" href="https://banzaicloud.com/blog/drain/">Draining Kubernetes nodes</a></p> albatross0 tag:crieit.net,2005:PublicArticle/16131 2020-10-14T22:55:58+09:00 2020-10-14T22:55:58+09:00 https://crieit.net/posts/df df によるディスク使用率計算 <p><strong>Qrunch から引っ越し</strong></p> <h1 id="計算方法"><a href="#%E8%A8%88%E7%AE%97%E6%96%B9%E6%B3%95">計算方法</a></h1> <p>使用率はおおよそ以下のようになる。</p> <p><code>ceil(100 * used / (used + avail))</code></p> <p><code>size</code> フィールドではなく <code>used + avail</code> になっているのは、ファイルシステムによる予約領域を考慮したためと思われる。</p> <h1 id="メモ"><a href="#%E3%83%A1%E3%83%A2">メモ</a></h1> <h2 id="statfs(2) , statvfs(3)"><a href="#statfs%282%29+%2C+statvfs%283%29">statfs(2) , statvfs(3)</a></h2> <p>これらを呼び出してファイルシステムの使用状況を取得する。</p> <p>例えば statvfs(3) の manpage を見るとどのようなデータがあるのか書いてある。</p> <pre><code class="c">struct statvfs { unsigned long f_bsize; /* file system block size */ unsigned long f_frsize; /* fragment size */ fsblkcnt_t f_blocks; /* size of fs in f_frsize units */ fsblkcnt_t f_bfree; /* # free blocks */ fsblkcnt_t f_bavail; /* # free blocks for unprivileged users */ fsfilcnt_t f_files; /* # inodes */ fsfilcnt_t f_ffree; /* # free inodes */ fsfilcnt_t f_favail; /* # free inodes for unprivileged users */ unsigned long f_fsid; /* file system ID */ unsigned long f_flag; /* mount flags */ unsigned long f_namemax; /* maximum filename length */ }; </code></pre> <h2 id="ファイルシステムの予約領域"><a href="#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AE%E4%BA%88%E7%B4%84%E9%A0%98%E5%9F%9F">ファイルシステムの予約領域</a></h2> <p>ext4 はデフォルトで 5% が root ユーザ用に予約されているため、<br /> <code>f_bfree</code> と <code>f_bavail</code> が一致しないことが多いだろう。</p> <p>xfs は ext4 とはアプローチが異なり、ファイルを保存する領域に予約はなく <code>f_bfree</code> と <code>f_bavail</code> が一致するようだ。</p> <p>参考:<a target="_blank" rel="nofollow noopener" href="https://access.redhat.com/solutions/1980673">Does XFS have reserved space for the root user ?</a></p> <h2 id="df と snmpdf (Net-SNMP)"><a href="#df+%E3%81%A8+snmpdf+%28Net-SNMP%29">df と snmpdf (Net-SNMP)</a></h2> <p>df は <code>statvfs(3)</code> などで取得した値を元に計算し、小数点以下は切り上げが行われる。</p> <p>snmpdf は snmpd が <code>statvfs(3)</code> などで取得した値を受け取って計算し、小数点以下は切り捨てが行われるため、df コマンドの結果とは異なる場合がある。</p> albatross0 tag:crieit.net,2005:PublicArticle/16130 2020-10-14T22:54:34+09:00 2020-10-14T22:54:34+09:00 https://crieit.net/posts/psql-libpq-subjectAltName-iPAddress psql (libpq) が subjectAltName の iPAddress を見ない <p><strong>Qrunch から引っ越し</strong></p> <p>ドキュメントを読むと IP アドレスでのサーバ証明書検証もやってくれそうな気配はある。</p> <p><a target="_blank" rel="nofollow noopener" href="https://www.postgresql.jp/document/9.6/html/libpq-ssl.html">32.18.1. サーバ証明書のクライアント検証</a></p> <blockquote> <p>もし接続がホスト名ではなくIPアドレスを使用するのであれば、(いかなるDNS検索もせず)IPアドレスがマッチさせられます。</p> </blockquote> <p>しかし psql から IPアドレスで接続して証明書を検証させようとすると、以下のようなエラーになった。</p> <pre><code>$ psql "port=5432 host=192.0.2.1 sslcert=./user.crt sslkey=./user.key sslrootcert=./ca.crt sslmode=verify-full dbname=postgres user=user" psql: server certificate for "example.com" (and 1 other name) does not match host name "192.0.2.1" </code></pre> <p>バージョン <code>9.6.9</code> の <code>src/interfaces/libpq/fe-secure-openssl.c</code> から関連部分を抜粋</p> <p><code>verify_peer_name_matches_certificate()</code></p> <pre><code class="c"> /* * First, get the Subject Alternative Names (SANs) from the certificate, * and compare them against the originally given hostname. */ peer_san = (STACK_OF(GENERAL_NAME) *) X509_get_ext_d2i(conn->peer, NID_subject_alt_name, NULL, NULL); if (peer_san) { int san_len = sk_GENERAL_NAME_num(peer_san); for (i = 0; i < san_len; i++) { const GENERAL_NAME *name = sk_GENERAL_NAME_value(peer_san, i); if (name->type == GEN_DNS) { char *alt_name; names_examined++; rc = verify_peer_name_matches_certificate_name(conn, name->d.dNSName, &alt_name); if (rc == -1) got_error = true; if (rc == 1) found_match = true; if (alt_name) { if (!first_name) first_name = alt_name; else free(alt_name); } } if (found_match || got_error) break; } sk_GENERAL_NAME_free(peer_san); } /* * If there is no subjectAltName extension of type dNSName, check the * Common Name. * * (Per RFC 2818 and RFC 6125, if the subjectAltName extension of type * dNSName is present, the CN must be ignored.) */ if (names_examined == 0) { X509_NAME *subject_name; subject_name = X509_get_subject_name(conn->peer); if (subject_name != NULL) { int cn_index; cn_index = X509_NAME_get_index_by_NID(subject_name, NID_commonName, -1); if (cn_index >= 0) { names_examined++; rc = verify_peer_name_matches_certificate_name( conn, X509_NAME_ENTRY_get_data( X509_NAME_get_entry(subject_name, cn_index)), &first_name); if (rc == -1) got_error = true; else if (rc == 1) found_match = true; } } } </code></pre> <p>Subject Alternative Name に関しては<br /> <code>name->type == GEN_DNS</code> なケースしかチェックしないので、<code>DNS:example.com</code> のような dNSName フィールドのみチェックし <code>IP:192.0.2.1</code> のような iPAddress フィールドはスキップされるようだ。</p> <p>CN に IP アドレスを入れておけば検証されると思われるが、<br /> <code>If there is no subjectAltName extension of type dNSName, check the Common Name.</code> とコメントにある通り、subjectAltname に dNSName があったら CN のチェックも行われないので、名前との共存は望めないようだ。</p> <p>証明書を発行する時に <code>DNS:192.0.2.1</code> のように subjectAltName に dNSName として IP アドレスを入れてしまう細工しておくと検証に成功するが、素直に名前を使うのが良いだろう。</p> albatross0