1. kube-controller-managerはネットワークセグメントを管理するkube-controller-manager には多くのコントローラーがありますが、Pod IP に関連するのは NodeIpamController です。 NodeIpamController コントローラーは主にノードの podcidr を管理します。新しいノードがクラスターに参加すると、そのノードにサブネット セグメントが割り当てられます。ノードが削除されると、サブネット セグメントはリサイクルされます。 各ノードのサブネット セグメントは重複せず、各ノードは独立して Pod IP の割り当てを完了できます。 kube-controller-manager の動作例を見てみましょう。 kubectl -n kube-system get pod kube-controller-manager -o yaml ネットワークセグメント構成に関する部分は次のとおりです。 spec: containers: - command: - kube-controller-manager - --allocate-node-cidrs=true - --cluster-cidr=10.234.0.0/16 - --node-cidr-mask-size=24 - --service-cluster-ip-range=10.96.0.0/16 cluster-cidr は、16 ビットのマスクを使用して Pod IP の範囲を指定します。予約済み IP を考慮しない場合、クラスターは最大 2^16 = 65536 個のポッドを収容できます。 これらのポッドは複数のノードに分散されます。次に、node-cidr-mask-size が 24 であることがわかります。各ノードには、ポッド用に 32-24=8 ビットしか残っていません。各ノードは最大 2^8=256 個のポッドを作成できます。 同様に、このクラスターが収容できるノード数は 2^(32-16-8)=256 ノードです。 クラスターを計画するときは、クラスターのサイズに応じてこれら 2 つのパラメータを調整する必要があります。 allocate-node-cidrs を有効にして cluster-cidr を設定すると、kube-controller-manager は各ノードにサブネット セグメントを割り当て、その結果を spec.podCIDR フィールドに書き込みます。 spec: podCIDR: 10.234.58.0/24 podCIDRs: - 10.234.58.0/24 このプロセスをソースコードから分析してみましょう。 1. NodeIpamControllerを起動するfunc startNodeIpamController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) { // 如果allocate-node-cidrs 没有开启会立即返回if !controllerContext.ComponentConfig.KubeCloudShared.AllocateNodeCIDRs { return nil, false, nil } // 获取clusterCIDR, serviceCIDR 启动NodeIpamController nodeIpamController, err := nodeipamcontroller.NewNodeIpamController( ctx, controllerContext.InformerFactory.Core().V1().Nodes(), clusterCIDRInformer, controllerContext.Cloud, controllerContext.ClientBuilder.ClientOrDie("node-controller"), clusterCIDRs, serviceCIDR, secondaryServiceCIDR, nodeCIDRMaskSizes, ipam.CIDRAllocatorType(controllerContext.ComponentConfig.KubeCloudShared.CIDRAllocatorType), ) go nodeIpamController.RunWithMetrics(ctx, controllerContext.ControllerManagerMetrics) return nil, true, nil }
RunWithMetrics はいくつかの監視インジケーターのみを提供し、実際の起動ロジックは Run メソッドにあります。 func (nc *Controller) RunWithMetrics(ctx context.Context, controllerManagerMetrics *controllersmetrics.ControllerManagerMetrics) { controllerManagerMetrics.ControllerStarted("nodeipam") defer controllerManagerMetrics.ControllerStopped("nodeipam") nc.Run(ctx) } func (nc *Controller) Run(ctx context.Context) { if nc.allocatorType == ipam.IPAMFromClusterAllocatorType || nc.allocatorType == ipam.IPAMFromCloudAllocatorType { go nc.legacyIPAM.Run(ctx) } else { go nc.cidrAllocator.Run(ctx) } <-ctx.Done() } 1.2 ノードの変更の監視cidrAllocator インターフェイスの実装を探していたところ、単一のネットワーク セグメント割り当て用の RangeAllocator、複数の CIDR 用の MultiCIDRRangeAllocator、クラウド ファクトリーに接続するための CloudCIDRAllocator という 3 つの CIDR アロケータが見つかりました。 func New(ctx context.Context, kubeClient clientset.Interface, cloud cloudprovider.Interface, nodeInformer informers.NodeInformer, clusterCIDRInformer networkinginformers.ClusterCIDRInformer, allocatorType CIDRAllocatorType, allocatorParams CIDRAllocatorParams) (CIDRAllocator, error) { switch allocatorType { case RangeAllocatorType: return NewCIDRRangeAllocator(logger, kubeClient, nodeInformer, allocatorParams, nodeList) case MultiCIDRRangeAllocatorType: if !utilfeature.DefaultFeatureGate.Enabled(features.MultiCIDRRangeAllocator) { return nil, fmt.Errorf("invalid CIDR allocator type: %v, feature gate %v must be enabled", allocatorType, features.MultiCIDRRangeAllocator) } return NewMultiCIDRRangeAllocator(ctx, kubeClient, nodeInformer, clusterCIDRInformer, allocatorParams, nodeList, nil) case CloudAllocatorType: return NewCloudCIDRAllocator(logger, kubeClient, cloud, nodeInformer) default: return nil, fmt.Errorf("invalid CIDR allocator type: %v", allocatorType) } } ここでは、RangeAllocator の実装について説明します。 func NewCIDRRangeAllocator(logger klog.Logger, client clientset.Interface, nodeInformer informers.NodeInformer, allocatorParams CIDRAllocatorParams, nodeList *v1.NodeList) (CIDRAllocator, error) { nodeInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: controllerutil.CreateAddNodeHandler(func(node *v1.Node) error { return ra.AllocateOrOccupyCIDR(logger, node) }), UpdateFunc: controllerutil.CreateUpdateNodeHandler(func(_, newNode *v1.Node) error { if len(newNode.Spec.PodCIDRs) == 0 { return ra.AllocateOrOccupyCIDR(logger, newNode) } return nil }), DeleteFunc: controllerutil.CreateDeleteNodeHandler(logger, func(node *v1.Node) error { return ra.ReleaseCIDR(logger, node) }), }) return ra, nil } 実際、RangeAllocator の実装は、Operator を記述するときのコントローラーの実装に似ています。どちらもインフォーマーを使用してリソースの変更を監視し、対応するメソッドを呼び出します。 1.3 ノードのpodCIDRを更新するここで特別なのは、コントローラーがリソースを直接操作するのではなく、変更をチャネルに入れて、goroutine を通じてステータスの更新を処理することです。 func (r *rangeAllocator) AllocateOrOccupyCIDR(logger klog.Logger, node *v1.Node) error { allocated := nodeReservedCIDRs{ nodeName: node.Name, allocatedCIDRs: make([]*net.IPNet, len(r.cidrSets)), } for idx := range r.cidrSets { podCIDR, err := r.cidrSets[idx].AllocateNext() allocated.allocatedCIDRs[idx] = podCIDR } // 将更新的内容放入channel 中r.nodeCIDRUpdateChannel <- allocated return nil } nodeCIDRUpdateChannel の長さは 5000 です。 cidrUpdateQueueSize = 5000 nodeCIDRUpdateChannel: make(chan nodeReservedCIDRs, cidrUpdateQueueSize), Node Spec を更新するロジックは 30 個の goroutine によって処理されます。 const cidrUpdateWorkers untyped int = 30 for i := 0; i < cidrUpdateWorkers; i++ { go r.worker(ctx) } func (r *rangeAllocator) worker(ctx context.Context) { logger := klog.FromContext(ctx) for { select { case workItem, ok := <-r.nodeCIDRUpdateChannel: if !ok { logger.Info("Channel nodeCIDRUpdateChannel was unexpectedly closed") return } if err := r.updateCIDRsAllocation(logger, workItem); err != nil { // Requeue the failed node for update again. r.nodeCIDRUpdateChannel <- workItem } case <-ctx.Done(): return } } } cidrUpdateRetries = 3 ここで更新は 3 回再試行されます。更新が常に失敗する場合は、ノードはチャネルに戻され、次の更新を待機します。 // updateCIDRsAllocation assigns CIDR to Node and sends an update to the API server. func (r *rangeAllocator) updateCIDRsAllocation(logger klog.Logger, data nodeReservedCIDRs) error { // If we reached here, it means that the node has no CIDR currently assigned. So we set it. for i := 0; i < cidrUpdateRetries; i++ { if err = nodeutil.PatchNodeCIDRs(r.client, types.NodeName(node.Name), cidrsString); err == nil { logger.Info("Set node PodCIDR", "node", klog.KObj(node), "podCIDRs", cidrsString) return nil } } // 放回pool 中controllerutil.RecordNodeStatusChange(logger, r.recorder, node, "CIDRAssignmentFailed") } Patch メソッドを使用して、ノード オブジェクトの Spec フィールドを更新します。 func PatchNodeCIDRs(c clientset.Interface, node types.NodeName, cidrs []string) error { // set the Pod cidrs list and set the old Pod cidr field patch := nodeForCIDRMergePatch{ Spec: nodeSpecForMergePatch{ PodCIDR: cidrs[0], PodCIDRs: cidrs, }, } patchBytes, err := json.Marshal(&patch) if err != nil { return fmt.Errorf("failed to json.Marshal CIDR: %v", err) } if _, err := c.CoreV1().Nodes().Patch(context.TODO(), string(node), types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}); err != nil { return fmt.Errorf("failed to patch node CIDR: %v", err) } return nil } 2. ネットワークのKubelet構成写真 上の図は、Kubelet が Pod を作成するプロセスを示しています。ここでは、ネットワーク構成に関する部分を分析します。 - ポッドはノードにスケジュールされている
- Kubeletはcriを介してコンテナランタイムを呼び出してサンドボックスを作成します
- コンテナランタイムはサンドボックスを作成する
- コンテナランタイムはcniを呼び出してPodネットワークを作成します
- Pod IPのIPAM管理
このプロセスをソースコード実装の観点から見てみましょう。 2.1 ポッドをノードにスケジュールするapiVersion: v1 kind: Pod metadata: labels: app: demo pod-template-hash: 7b9b5cf76b name: demo-7b9b5cf76b-5lpmj namespace: default spec: containers: - image: hubimage/demo-ubuntu nodeName: node1
Kubernetes のスケジューリング プロセスは、kube-scheduler が Pod のリソース要件とノードのリソース条件に基づいて Pod をノードにスケジュールし、スケジュール結果を pod.spec.nodeName フィールドに書き込みます。 この部分はネットワークの焦点ではありません。以前、本番環境でスケジューラをカスタマイズしたことがあります。ご興味がございましたら、Tekton Optimization Custom Cluster Scheduler をご覧ください。 2.2 kubeletはcriを呼び出してサンドボックスを作成するSyncPod は kubelet のコア メソッドであり、cri を呼び出して、Pod のステータスに応じて Pod を作成または削除します。 // SyncPod syncs the running Pod into the desired Pod by executing following steps: // // 1.计算沙箱和容器变化。 // 2. 必要时关闭Pod 沙箱。 // 3. 关闭任何不应运行的容器。 // 4.必要时创建沙箱。 // 5.创建ephemeral 容器。 // 6. 创建init 容器。 // 7. 调整运行容器的大小(如果InPlacePodVerticalScaling==true) // 8. 创建正常容器func (m *kubeGenericRuntimeManager) SyncPod(ctx context.Context, Pod *v1.Pod, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) (result kubecontainer.PodSyncResult) { // Step 4: Create a sandbox for the Pod if necessary. podSandboxID, msg, err = m.createPodSandbox(ctx, pod, podContainerChanges.Attempt) } サンドボックスを作成するには、RuntimeService インターフェースの RunPodSandbox メソッドを呼び出します。 // createPodSandbox creates a Pod sandbox and returns (podSandBoxID, message, error). func (m *kubeGenericRuntimeManager) createPodSandbox(ctx context.Context, Pod *v1.Pod, attempt uint32) (string, string, error) { podSandBoxID, err := m.runtimeService.RunPodSandbox(ctx, podSandboxConfig, runtimeHandler) runtimeService および instrumentsedRuntimeService インターフェースによってカプセル化された後、最終的に remoteRuntimeService の RunPodSandbox メソッドが呼び出されます。 // RunPodSandbox creates and starts a pod-level sandbox. Runtimes should ensure // the sandbox is in ready state. func (r *remoteRuntimeService) RunPodSandbox(ctx context.Context, config *runtimeapi.PodSandboxConfig, runtimeHandler string) (string, error) { resp, err := r.runtimeClient.RunPodSandbox(ctx, &runtimeapi.RunPodSandboxRequest{ Config: config, RuntimeHandler: runtimeHandler, }) ここでのruntimeClientは、rpcを介してコンテナランタイムを呼び出してサンドボックスを作成するrpcクライアントです。 2.3 コンテナランタイムがサンドボックスを作成するサンドボックスを作成する例として、containerd を取り上げます。 func (in *instrumentedService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (res *runtime.RunPodSandboxResponse, err error) { if err := in.checkInitialized(); err != nil { return nil, err } res, err = in.c.RunPodSandbox(ctrdutil.WithNamespace(ctx), r) return res, errdefs.ToGRPC(err) } CNI を呼び出して、ネットワークとサンドボックスを作成します。 // RunPodSandbox creates and starts a pod-level sandbox. Runtimes should ensure // the sandbox is in ready state. func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandboxRequest) (_ *runtime.RunPodSandboxResponse, retErr error) { // 生成sandbox id id := util.GenerateID() metadata := config.GetMetadata() name := makeSandboxName(metadata) // 获取sandbox 的oci 运行时ociRuntime, err := c.getSandboxRuntime(config, r.GetRuntimeHandler()) sandboxInfo.Runtime.Name = ociRuntime.Type sandboxInfo.Sandboxer = ociRuntime.Sandboxer // 创建sandbox 对象sandbox := sandboxstore.NewSandbox( sandboxstore.Metadata{ ID: id, Name: name, Config: config, RuntimeHandler: r.GetRuntimeHandler(), }, sandboxstore.Status{ State: sandboxstore.StateUnknown, }, ) // 调用CNI 插件,创建sandbox 的网络if !hostNetwork(config) && !userNsEnabled { var netnsMountDir = "/var/run/netns" sandbox.NetNS, err = netns.NewNetNS(netnsMountDir) // Save sandbox metadata to store if err := c.setupPodNetwork(ctx, &sandbox); err != nil { return nil, fmt.Errorf("failed to setup network for sandbox %q: %w", id, err) } } // 创建sandbox err = c.nri.RunPodSandbox(ctx, &sandbox) } 2.4 コンテナランタイムはcniを呼び出してポッドネットワークを作成する前の手順では、RunPodSandbox を呼び出してサンドボックスを作成する前に、setupPodNetwork を呼び出してネットワークを構成します。 setupPodNetwork の実装を見てみましょう。 func (c *criService) setupPodNetwork(ctx context.Context, sandbox *sandboxstore.Sandbox) error { var ( id = sandbox.ID config = sandbox.Config path = sandbox.NetNSPath netPlugin = c.getNetworkPlugin(sandbox.RuntimeHandler) err error result *cni.Result ) if c.config.CniConfig.NetworkPluginSetupSerially { result, err = netPlugin.SetupSerially(ctx, id, path, opts...) } else { result, err = netPlugin.Setup(ctx, id, path, opts...) } } libcniはnetPluginインターフェースを実装します // containerd/go-cni/cni.go func (c *libcni) Setup(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*Result, error) { if err := c.Status(); err != nil { return nil, err } // 建一个新的网络命名空间ns, err := newNamespace(id, path, opts...) if err != nil { return nil, err } // 调用CNI 插件result, err := c.attachNetworks(ctx, ns) if err != nil { return nil, err } return c.createResult(result) }
attachNetworks は多くのコルーチンを開始し、各コルーチンは asynchAttach メソッドを呼び出し、asynchAttach メソッドは Attach メソッドを呼び出します。 func (c *libcni) attachNetworks(ctx context.Context, ns *Namespace) ([]*types100.Result, error) { var wg sync.WaitGroup var firstError error results := make([]*types100.Result, len(c.Networks())) rc := make(chan asynchAttachResult) for i, network := range c.Networks() { wg.Add(1) go asynchAttach(ctx, i, network, ns, &wg, rc) } for range c.Networks() { rs := <-rc if rs.err != nil && firstError == nil { firstError = rs.err } results[rs.index] = rs.res } wg.Wait() return results, firstError } CNI を呼び出すために多くのコルーチンが実行されていますが、rc チャネルの長さは 1 であり、結果は 1 つずつ処理されます。 func asynchAttach(ctx context.Context, index int, n *Network, ns *Namespace, wg *sync.WaitGroup, rc chan asynchAttachResult) { defer wg.Done() r, err := n.Attach(ctx, ns) rc <- asynchAttachResult{index: index, res: r, err: err} } CNI プラグインは実際には Attach メソッドで呼び出されます。 func (n *Network) Attach(ctx context.Context, ns *Namespace) (*types100.Result, error) { r, err := n.cni.AddNetworkList(ctx, n.config, ns.config(n.ifName)) if err != nil { return nil, err } return types100.NewResultFromResult(r) } CNI インターフェースは、https://github.com/containernetworking/cni/blob/main/libcni/api.go で多くのメソッドを定義します。最も重要なのは AddNetwork と DelNetwork です。 List を使用するメソッドはバッチ操作です。 type CNI interface { AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error) AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error) DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error } AddNetwork は、コンテナにネットワーク インターフェイスを追加し、ホスト上に veth ネットワーク カードを作成し、それをコンテナの ech0 ネットワーク カードにバインドするために使用されます。 DelNetwork は、コンテナが削除されるときにコンテナ関連のネットワーク構成をクリーンアップするために使用されます。 CNI 呼び出しプラグインの中核は、バイナリ プログラムを直接呼び出す Exec インターフェイスです。 type Exec interface { ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) FindInPath(plugin string, paths []string) (string, error) Decode(jsonBytes []byte) (version.PluginInfo, error) } CRI は、標準入力と環境変数の形式でネットワーク構成情報を CNI プラグインに渡します。 CNI プラグインは処理を完了すると、ネットワーク構成情報を標準出力に書き込みます。 CRI は標準出力内のネットワーク構成情報を解析し、コンテナのネットワーク構成ファイルに書き込みます。 コンテナ ランタイムの実装に戻りましょう。 /usr/bin/containerd config dump |grep cni [plugins."io.containerd.grpc.v1.cri".cni] bin_dir = "/opt/cni/bin" conf_dir = "/etc/cni/net.d" ここで、/etc/cni/net.d は CNI ネットワーク構成ファイルのデフォルトの保存パスであり、/opt/cni/bin は CNI ネットワーク プラグインのデフォルトの検索パスです。 ls /opt/cni/bin bandwidth calico cilium-cni firewall host-device install loopback portmap sbr tuning vrf bridge calico-IPAM dhcp flannel host-local ipvlan macvlan ptp static vlan cat /etc/cni/net.d/05-cilium.conf { "cniVersion": "0.3.1", "name": "cilium", "type": "cilium-cni", "enable-debug": false } これらの構成は、CRI を初期化して、CNI プラグインの netPlugin map[string]cni.CNI 構造を取得するために使用されます。 2.5 ポッドIPのIPAM管理IPAM は IP Address Management の略で、コンテナに IP アドレスを割り当てる役割を担います。 IPAM コンポーネントは通常、スタンドアロンのバイナリ ファイルですが、CNI プラグインによって直接実装することもできます。 https://github.com/containernetworking/plugins/tree/main/plugins/ipam には現在、host-local、dhcp、static の 3 つの実装があります。ここでは、host-local を例に挙げます。 cat /etc/cni/net.d/10-cni.conflist { "name": "networks", "type": "cni", "ipam": { "type": "host-local", "subnet": "10.234.58.0/24", "routes": [{ "dst": "0.0.0.0/0" }] } }
CNI プラグイン タイプは host-local として指定され、Pod IP のネットワーク セグメントは "10.234.58.0/24" として指定されます。 ls /var/lib/cni/networks 10.234.58.76 10.234.58.87 last_reserved_ip.0 lock
cat 10.234.58.76 b3b668af977bbeca6853122514044865793c056e81cccebf115dacffd25a8bcc IP にちなんで名付けられたファイルのグループがあり、各ファイルには文字列が含まれています。それで、これらは正確には何でしょうか? - IP にちなんで命名されたファイルはどのように生成されますか?
ポッドIPを申請する際は、まず利用可能なIPを取得する func cmdAdd(args *skel.CmdArgs) error { for idx, rangeset := range ipamConf.Ranges { ipConf, err := allocator.Get(args.ContainerID, args.IfName, requestedIP) } }
利用可能な IP アドレスを取得したら、それをローカル ディレクトリ ファイルに保存してみます。 func (a *IPAllocator) Get(id string, ifname string, requestedIP net.IP) (*current.IPConfig, error) { for { reservedIP, gw = iter.Next() reserved, err := a.store.Reserve(id, ifname, reservedIP.IP, a.rangeID) } } ローカルファイルディレクトリに直接書き込む func (s *Store) Reserve(id string, ifname string, ip net.IP, rangeID string) (bool, error) { fname := GetEscapedPath(s.dataDir, ip.String()) f, err := os.OpenFile(fname, os.O_RDWR|os.O_EXCL|os.O_CREATE, 0o600) if os.IsExist(err) { return false, nil } if _, err := f.WriteString(strings.TrimSpace(id) + LineBreak + ifname); err != nil { f.Close() os.Remove(f.Name()) return false, err } }
書き込まれる内容は strings.TrimSpace(id) + LineBreak + ifname です。ここで、id は実際にはコンテナーの ID、ifname はネットワーク カードの名前、LineBreak は改行文字です。 対応するコンテナは、ホスト上で ID によって見つかります。 docker ps |grep b3b668 b3b668af977b k8s.gcr.io/pause:3.5 "/pause" 6 weeks ago Up 6 weeks k8s_POD_xxx-5b795fd7dd-82hrh_kube-system_b127b65c-f0ca-48a7-9020-ada60dfa535a_0 - last_reserved_ip.0 ファイルの目的
cat last_reserved_ip.0 10.234.58.87
利用可能な IP を取得するときに、IPAM は反復子を作成します。 func (a *IPAllocator) Get(id string, ifname string, requestedIP net.IP) (*current.IPConfig, error) { iter, err := a.GetIter() if err != nil { return nil, err } for { reservedIP, gw = iter.Next() if reservedIP == nil { break } } イテレータは last_reserved_ip.0 に依存して最後に割り当てられた IP を見つけ、この IP から割り当てを開始する必要があります。 func (a *IPAllocator) GetIter() (*RangeIter, error) { lastReservedIP, err := a.store.LastReservedIP(a.rangeID) if err != nil && !os.IsNotExist(err) { log.Printf("Error retrieving last reserved ip: %v", err) } else if lastReservedIP != nil { startFromLastReservedIP = a.rangeset.Contains(lastReservedIP) } ここで lastIPFilePrefix = "last_reserved_ip." func (s *Store) LastReservedIP(rangeID string) (net.IP, error) { ipfile := GetEscapedPath(s.dataDir, lastIPFilePrefix+rangeID) data, err := os.ReadFile(ipfile) if err != nil { return nil, err } return net.ParseIP(string(data)), nil } ホストローカルが IP アドレスを割り当てる場合、ラウンドロビン方式で段階的に割り当てます。最後の IP アドレスに到達すると、最初からやり直します。 type Store struct { *FileLock dataDir string }
各ストレージ操作はロックされ、一意性を保証するために IP 割り当ては同時に実行されません。 a.store.Lock() defer a.store.Unlock() 3. 結論この記事では、主に kube-controller-manager から kubelet までの Pod IP 管理プロセスを Pod IP 管理の観点から整理します。主な内容は以下の通りです。 - kube-controller-manager は、NodeIpamController コントローラーを通じて各ノードに Pod IP セグメントを割り当てます。クラスターを計画するときは、クラスターのサイズに応じて cluster-cidr および node-cidr-mask-size パラメータを調整する必要があります。
- Kubeletはcriを介してコンテナランタイムを呼び出してサンドボックスを作成します
- コンテナランタイムはcniを呼び出してPodネットワークを作成します
- Pod IPのIPAM管理
仕事でよく使われる多くの手順では、一般的なプロセスしか知らず、具体的な実装については知らない場合があります。ソースコード分析を通じて、関連する詳細をより深く理解し、新しい知識を習得できます。 たとえば、ソースコードで InPlacePodVerticalScaling というパラメータを確認したところ、これは Kubernetes 1.27 のアルファ機能であり、Pod を再起動せずに Pod のリソース構成を調整できることがわかりました。 CR ステータスを更新する Operator を作成する場合、適切なシナリオでは、nodeCIDRUpdateChannel の実装を学習し、更新されたステータスをチャネルに入れて、goroutine を通じてステータスの更新を処理できます。 |