精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

源碼分析 Kubernetes 對 Pod IP 的管理

云計算 云原生
在源碼中,我看到了 InPlacePodVerticalScaling 這個參數,發現是 Kubernetes 1.27 的一個 alpha 特性,可以在不重啟 Pod 的情況下,調整 Pod 的資源配置;在寫 Operator 更新 CR 狀態時,在合適的場景下,可以學習 nodeCIDRUpdateChannel 的實現,將更新的狀態放入 channel 中,然后通過 goroutine 處理

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 指定了 Pod IP 的范圍,掩碼位數 16,如果不考慮保留 IP,意味著集群最多可以容納 2^16 = 65536 個 pod。

這些 Pod 分布在若干個節點上,接著看 node-cidr-mask-size 為 24,每個節點只剩下 32-24=8 位留給 pod,每個節點最多能創建 2^8=256 個 pod。

相應的,這個集群能夠容納的節點數量為 2^(32-16-8)=256 個節點。

在規劃集群時,需要根據集群的規模來調整這兩個參數。

開啟 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 接口實現的時候,我發現了三種 CIDR 分配器,分別是 RangeAllocator 適用單網段分配、MultiCIDRRangeAllocator 適用于多 CIDR、CloudCIDRAllocator 適用于對接云廠。

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 時的控制器類似,都是通過 informer 來監聽資源的變化,然后調用相應的方法。

1.3 更新節點的 podCIDR

這里比較特殊的是,控制器并不是直接操作資源,而是將變更放到了一個 channel 中,然后通過 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 次更新,如果一直更新失敗,會將節點重新放入 channel 中,等待下次更新。

// 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 的過程,這里截取其中對網絡配置的部分進行分析:

  1. Pod 調度到某個節點上
  2. kubelet 通過 cri 調用 container runtime 創建 sandbox
  3. container runtime 創建 sandbox
  4. container runtime 調用 cni 創建 Pod 網絡
  5. IPAM 對 Pod IP 的管理

下面從源碼實現的角度來看看這個過程。

2.1 Pod 調度到某個節點上

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 優化之定制集群調度器 。

2.2 kubelet 調用 cri 創建 sandbox

SyncPod 是 kubelet 中的核心方法,它會根據 Pod 的狀態,調用 cri 創建或刪除 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 方法創建 sandbox。

// 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、instrumentedRuntimeService 接口的封裝,最終會調用 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 client,通過 rpc 調用 container runtime 創建 sandbox。

2.3 container runtime 創建 sandbox

以 containerd 為例,創建 sandbox:

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 創建網絡,創建 sandbox。

// 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 container runtime 調用 cni 創建 Pod 網絡

在上一步驟中,調用 RunPodSandbox 創建 sandbox 之前,會先調用 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 channel 的長度為 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}
}

Attach 方法中才真正開始調用 CNI 插件。

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)
}

在 https://github.com/containernetworking/cni/blob/main/libcni/api.go 中 CNI 接口定義了很多方法,其中最重要的是 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 將標準輸出中的網絡配置信息解析出來,寫入到容器的網絡配置文件中。

再回到 container runtime 的實現 containerd:

/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 IPAM 對 Pod IP 的管理

IPAM 是 IP Address Management 的縮寫,負責為容器分配 ip 地址。IPAM 組件通常是一個獨立的二進制文件,也可以直接由 CNI 插件實現。在 https://github.com/containernetworking/plugins/tree/main/plugins/ipam 中,目前有三種實現 host-local、dhcp、static。 這里以 host-local 為例:

  • 查看 CNI 的配置文件
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" 。

  • 查看 CNI 插件的存儲目錄
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 命名的文件是如何生成的

申請一個 Pod 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
}

host-local 分配 ip 時是按照輪詢的方式,遞增分配,如果分配到最后一個 IP,就又從頭開始分配。

  • lock 文件
type Store struct {
 *FileLock
 dataDir string
}

每次存儲操作都會進行加鎖,IP 分配不會并發進行,確保唯一性。

a.store.Lock()
defer a.store.Unlock()

3. 總結

本篇主要是從 Pod IP 管理的角度,梳理了一下從 kube-controller-manager 到 kubelet 的 Pod IP 管理過程。主要內容如下:

  • kube-controller-manager 通過 NodeIpamController 控制器為每個節點分配 Pod IP 網段,在集群規劃時需要根據集群規模調整 cluster-cidr、node-cidr-mask-size 參數
  • kubelet 通過 cri 調用 container runtime 創建 sandbox
  • container runtime 調用 cni 創建 Pod 網絡
  • IPAM 對 Pod IP 的管理

在工作中很多熟悉的路徑,可能僅僅只是知道大概的流程,不知道具體的實現。通過源碼分析,可以更加深入地了解相關的細節,也能學習到新的知識。

比如,在源碼中,我看到了 InPlacePodVerticalScaling 這個參數,發現是 Kubernetes 1.27 的一個 alpha 特性,可以在不重啟 Pod 的情況下,調整 Pod 的資源配置;在寫 Operator 更新 CR 狀態時,在合適的場景下,可以學習 nodeCIDRUpdateChannel 的實現,將更新的狀態放入 channel 中,然后通過 goroutine 處理狀態更新。

責任編輯:武曉燕 來源: 陳少文
相關推薦

2020-11-30 12:15:26

KubernetesPodLinux

2022-04-09 15:26:46

Kubernetes刪除操作源碼解析

2024-04-15 05:00:00

kubernete網絡容器

2023-02-09 16:47:34

KubernetesPod優先級

2010-02-06 13:28:31

Android源碼

2009-07-27 09:14:08

網絡管理IP電話管理

2021-02-22 08:29:03

KubernetesKubectl Fla應用

2023-03-21 15:26:02

Kubernetes容器開發

2023-10-19 19:42:25

IstioPodkubernetes

2022-01-06 07:06:52

KubernetesResourceAPI

2024-05-23 08:40:46

Kubernetes預過濾調度

2021-01-28 10:55:47

Kubernetes IPLinux

2021-09-22 08:37:02

pod源碼分析kubernetes

2024-06-19 09:33:05

2019-11-20 09:15:53

KubernetesPod

2021-11-22 08:00:00

Kubernetes容器集群

2021-08-10 07:00:00

Nacos Clien服務分析

2022-08-02 16:54:23

Kubernetes容器工具

2020-04-10 08:00:08

Kubernetes補丁pod

2022-05-16 08:27:20

KubernetePodLinux
點贊
收藏

51CTO技術棧公眾號

欧亚精品在线观看| 精品欧美久久久| 亚洲 国产 日韩 综合一区| 中文天堂在线播放| 久久久久久久久国产一区| 成人午夜大片免费观看| 97人人爽人人喊人人模波多| 三上悠亚ssⅰn939无码播放| 88xx成人网| 亚洲国产你懂的| 国产精品一香蕉国产线看观看| 97人妻人人揉人人躁人人| 色8久久久久| 亚洲电影第三页| 性刺激综合网| 日韩在线观看视频一区| 免费看欧美女人艹b| 欧美黄网免费在线观看| 中文字幕人妻一区二区| 九九热这里有精品| 精品久久久久久久久国产字幕| 亚洲一区高清| 视频午夜在线| 国产成人亚洲综合a∨婷婷图片| 日韩av观看网址| 欧产日产国产v| 日韩成人三级| 亚洲精品自在久久| 人妻 丝袜美腿 中文字幕| 欧美久久久网站| 狠狠色噜噜狠狠狠狠97| 黄色www在线观看| 特黄视频在线观看| 天堂成人国产精品一区| 欧美激情一级二级| 久久久久亚洲av无码a片| 欧美成年网站| 欧美三级午夜理伦三级中视频| 国产日韩欧美日韩| 国产精品18p| 欧美成人自拍| 亚洲精品自拍第一页| 777久久精品一区二区三区无码| 亚欧洲精品视频| 国产在线精品国自产拍免费| 91精品国产91久久久久久不卡| 天天操天天操天天操天天操天天操| 亚洲人成网亚洲欧洲无码| 日韩视频在线你懂得| 欧美一级裸体视频| 男女污污视频在线观看| 国产一区三区三区| 国产精品久久久久7777婷婷| 国产香蕉视频在线| 亚洲调教视频在线观看| 在线观看欧美成人| 国产精品揄拍100视频| 国产福利一区二区精品秒拍| 欧美精品高清视频| 国产精彩免费视频| 免费在线小视频| 伊人性伊人情综合网| 秋霞毛片久久久久久久久| 欧美视频xxx| 国产精品一区二区视频| 俺去啦;欧美日韩| 在线观看福利片| 国产伦精品一区二区三区在线播放| 欧美一级日韩一级| 伊人色在线观看| 91福利精品在线观看| 91国偷自产一区二区使用方法| xxxx18hd亚洲hd捆绑| 国产乱码在线| 亚洲资源中文字幕| 国产精品伊人日日| 日韩欧美高清在线观看| 国产精品97| 一个色综合导航| 国产美女永久免费无遮挡| 久久不卡国产精品一区二区 | 日韩一级片免费看| 国产成人精品亚洲午夜麻豆| 99国内精品久久久久久久软件| 国产福利第一视频| 欧美日本久久| 精品少妇v888av| 亚洲成人生活片| 午夜日韩视频| 午夜精品久久久久久久久久久久| 97超碰人人干| 久久精品日韩欧美| 国产精品视频资源| 免费黄色小视频在线观看| 一本久道综合久久精品| 国产精品高清在线| 中文字幕精品无| 蜜臀av一区二区在线观看| 国产美女高潮久久白浆| aaa一区二区三区| 99精品一区二区三区| 欧美精品二区三区四区免费看视频| 免费在线国产| 欧美高清在线精品一区| 可以免费看的黄色网址| 污视频软件在线观看| 99久久综合精品| 欧美日韩在线观看一区二区三区| 69视频在线观看| 夜夜夜精品看看| 国产在线青青草| 欧美午夜三级| 精品99一区二区三区| 久久精品国产亚洲av麻豆| 久久精品高清| 色综合天天狠天天透天天伊人| 国产在线观看免费视频今夜| 日韩精品免费专区| 亚洲自拍小视频| 免费看男男www网站入口在线| 国产精品人成在线观看免费| 粉嫩av一区二区三区天美传媒| 亚洲日本天堂| 91精品国产色综合久久不卡蜜臀 | 亚洲女人天堂网| 国产精品国产三级国产传播| 国产福利一区二区精品秒拍| 亚洲一二三在线| 疯狂试爱三2浴室激情视频| 亚洲人成人一区二区三区| 国产精品久久久久久久久久久不卡| 国产富婆一级全黄大片| 国产日韩精品一区二区三区 | 一区二区三区四区日本视频| 色老头久久综合| 国产精品91av| 亚洲蜜桃视频| 国产精品第8页| 午夜黄色小视频| 亚洲精品成人天堂一二三| 国产 porn| 日韩av黄色在线| 欧美噜噜久久久xxx| 在线观看免费中文字幕| 91色在线porny| 欧美国产日韩激情| 欧美午夜在线播放| 最近2019中文字幕大全第二页| 久久久久久久久久影院| 高清不卡一二三区| 国产精品久久久一区| 肥臀熟女一区二区三区| 国产精品久久看| 中文字幕免费高清在线| 久久综合影院| 国产mv久久久| 三级视频网站在线| 亚洲sss视频在线视频| 人妻精油按摩bd高清中文字幕| 97精品国产福利一区二区三区| 日本精品视频在线观看| 日韩资源在线| 亚洲成人激情av| 日批视频免费看| 国产在线欧美| 国产精品久久久久久久久婷婷 | 久久亚洲资源中文字| 亚洲欧美制服丝袜| www.国产一区二区| 99久久99精品久久久久久 | 中文在线日韩| 92国产精品久久久久首页| 麻豆网站在线| 色综合一区二区三区| 女女调教被c哭捆绑喷水百合| 欧美精品观看| 超碰97在线资源| 国产偷倩在线播放| 日韩电视剧在线观看免费网站| 国产精品久久久久久久妇| 99久久精品国产一区| 久久亚洲中文字幕无码| 日韩精品丝袜美腿| 欧美在线视频观看| 国产乱视频在线观看| 欧美午夜精品免费| 亚洲精品卡一卡二| 国产盗摄一区二区三区| 色中文字幕在线观看| 在线视频亚洲欧美中文| 久久91精品国产91久久久| 少妇一区二区三区四区| 色偷偷一区二区三区| 国产精品18在线| 国产精品2024| 国产亚洲综合视频| 欧美一区二区三| 成人网页在线免费观看| 国产美女情趣调教h一区二区| 亚洲黄色成人网| 糖心vlog精品一区二区| 亚洲免费伊人电影| 性囗交免费视频观看| 日韩成人一级大片| 在线观看污视频| 91精品啪在线观看国产爱臀| 国产成人一区二区| 色呦呦在线看| 国产一区二区三区在线免费观看| 国产一区二区在线视频观看| 亚洲一二三四区不卡| 波多野结衣片子| 国产精品一区二区久久不卡 | 老司机午夜在线视频| 欧美成人猛片aaaaaaa| 精品黑人一区二区三区| 亚洲视频你懂的| 精品黑人一区二区三区观看时间| 奇米影视7777精品一区二区| 日韩激情视频一区二区| 欧洲乱码伦视频免费| 97视频热人人精品| 欧美黑人疯狂性受xxxxx野外| 伦理中文字幕亚洲| 欧美日韩影视| 精品久久久影院| 国产成人精品亚洲| 福利微拍一区二区| 极品颜值美女露脸啪啪| 国产亚洲短视频| 国产极品一区二区| 国内欧美视频一区二区| www黄色在线| av成人国产| 一二三在线视频| 成人在线免费观看网站| 九色综合婷婷综合| 超碰成人在线观看| 国产精品美女在线观看| 在线毛片观看| 久久乐国产精品| av网址在线免费观看| 少妇激情综合网| 高清美女视频一区| 日韩精品极品视频免费观看| 亚洲成人黄色片| 欧美一区二区在线观看| 一区二区小视频| 一本到一区二区三区| 欧美三日本三级少妇99| 亚洲综合色婷婷| 日韩一级片av| 国产精品麻豆一区二区| 欧美色图亚洲激情| 91看片淫黄大片一级| 亚洲の无码国产の无码步美| 国产iv一区二区三区| 日本r级电影在线观看| 国内成+人亚洲+欧美+综合在线| www.99在线| 青青草成人在线观看| 美女喷白浆视频| 免费在线看一区| jizz欧美性11| 久久五月激情| www黄色av| 日韩二区三区在线观看| 久久精品视频91| 日产国产欧美视频一区精品| www.日本xxxx| 免费在线看成人av| 午夜免费看视频| 韩国三级在线一区| 欧美性受xxxx黒人xyx性爽| 国产精品一级片在线观看| 亚洲成人av免费观看| 国产精品正在播放| 99久久99精品| 波多野结衣精品在线| av无码av天天av天天爽| 久久先锋影音av鲁色资源| 熟女高潮一区二区三区| 国产精品伦理一区二区| 亚洲精品卡一卡二| 亚洲在线观看免费| 久久99精品波多结衣一区| 日韩欧美黄色动漫| 日本少妇xxxxx| 日本一区二区三区免费乱视频| 精品无码在线观看| 一色屋精品亚洲香蕉网站| 精品熟妇无码av免费久久| 一区二区三区av电影 | 三区四区不卡| 日本美女爱爱视频| aa国产精品| 精品视频无码一区二区三区| 国产另类ts人妖一区二区| 亚洲激情 欧美| 中文字幕第一区第二区| 成人在线观看小视频| 亚洲一卡二卡三卡四卡| av资源免费观看| 欧美日韩在线亚洲一区蜜芽| 亚洲精品成人电影| 亚洲夜晚福利在线观看| 男人在线资源站| 欧美激情在线观看| 日本不良网站在线观看| 91深夜福利视频| 欧美亚洲国产日韩| 中文字幕一区二区三区最新 | 亚洲国产精品久久久久婷蜜芽| 蜜臀av一区二区在线免费观看 | 日日av拍夜夜添久久免费| 91亚洲国产成人久久精品网站| 精品精品国产毛片在线看| 四虎一区二区| 一本色道久久综合亚洲精品高清| 色噜噜狠狠一区二区| youjizz久久| 日本爱爱小视频| 欧美视频在线观看免费| 国产99久久九九精品无码免费| 亚洲欧美综合v| 美足av综合网| 一区二区三区亚洲| 久久久久黄久久免费漫画| 欧美中文字幕在线| 亚洲成人高清| 亚欧精品在线| 亚洲尤物影院| 国产人妻精品午夜福利免费| 亚洲人妖av一区二区| 亚洲综合精品在线| 国产亚洲美女久久| 天天免费亚洲黑人免费| 麻豆精品视频| 亚欧成人精品| 蜜桃av免费看| 欧美日韩色婷婷| 老牛影视av牛牛影视av| 久久夜色撩人精品| 日韩一级特黄| 亚洲一区高清| 蜜臀精品一区二区三区在线观看 | 日韩一中文字幕| 亚洲国产尤物| 欧美xxxx黑人又粗又长精品| 在线日韩中文| 无码人妻精品一区二区三区99不卡| 国产精品久久久久久亚洲伦| 在线观看日批视频| 中文字幕亚洲欧美日韩在线不卡| 日韩免费福利视频| 久久天堂国产精品| 免费在线亚洲欧美| 亚洲精品乱码久久久久久蜜桃欧美| 亚洲一区二区三区在线看| 国产aⅴ一区二区三区| 日韩视频免费在线| 91视频成人| 五月天男人天堂| 国产在线精品一区二区三区不卡 | 亚洲国产综合一区| 欧美xxxx18性欧美| 欧美男女视频| 男人c女人视频| 成人福利电影精品一区二区在线观看| 欧美 日韩 国产 一区二区三区| 91精品国产一区二区三区| av在线免费网站| 成人激情直播| 1024日韩| 91成年人网站| 色菇凉天天综合网| 国产婷婷视频在线| 肥熟一91porny丨九色丨| 影音先锋日韩资源| v8888av| 91国偷自产一区二区三区成为亚洲经典| 清纯唯美亚洲色图| 日韩av快播网址| 日韩综合精品| 涩视频在线观看| 日韩欧美国产骚| 69久久夜色| 亚洲综合在线播放| 99精品久久久| 国产不卡在线观看视频| 欧美三级资源在线| 亚洲奶水xxxx哺乳期| 精品久久久久久乱码天堂| 丝袜美腿亚洲色图| 国产精品白丝喷水在线观看| 精品日本一线二线三线不卡| 小早川怜子影音先锋在线观看| 色一情一乱一伦一区二区三区丨| 黑人巨大精品欧美一区| 国产成人在线观看网站|