# Calico 3.5：根据节点标签分配 IP 地址

原文：[Assigning IP addresses based on topology](https://docs.projectcalico.org/v3.5/usage/assigning-ip-addresses-topology)

## 关于 IP 地址的分配

Calico 能够进行配置，为不同拓扑指定 IP 地址池。例如可能希望某些机架、地区、或者区域能够从同一个 IP 池中获取地址。这对于降低路由数量或者配合防火墙策略的要求会很有帮助。

[cni 插件配置参考中的 IP 地址管理章节](https://docs.projectcalico.org/v3.5/reference/cni-plugin/configuration#ipam)中包含了三种分配 IP 地址的方式。Kubernetes 注解方式只能用于 Namespace 或者 Pod 一级。剩下的只有两个办法，CNI 配置或者是基于节点选择器的 IP 池，相对于 CNI 配置的方式来说，节点选择器方案省去了修改本地文件的麻烦。

在更高层次上，基于节点选择器的 IP 地址分配方法就是给节点设置标签，然后用节点选择器选择对应的 IP 地址池进行分配。后面的内容中将给出一个详细的例子，用这种方式来设置一种机架亲和方式的 IP 地址分配方案。

> 如果 Calico 无法根据上述顺序来决定一个 IP 地址池，或者在选定的地址池中找不到可用的 IP 地址，那么这一工作负载就不会分到 IP 地址，无法启动。为了防止这种情况的发生，我们建议所有节点至少有一个合适的地址池。

## 先决条件

这一功能需要 Calico 在 ETCD 模式下工作。

## 示例：Kubernetes

本例中，我们会创建一个集群，其中包含四个节点，分布在两个机架上，每个机架各两台。示意如下：

```bash
       -------------------
       |    router       |
       -------------------
       |                 |
---------------   ---------------
| rack-0      |   | rack-1      |
---------------   ---------------
| kube-node-0 |   | kube-node-2 |
- - - - - - - -   - - - - - - - -
| kube-node-1 |   | kube-node-3 |
- - - - - - - -   - - - - - - - -
```

Pod IP 地址范围为 `192.168.0.0/16`，我们进行如下设计：保留 `192.168.0.0/24` 给 `rack-0`, `192.168.1.0/24` 给 `rack-1`。

要设置一个没有缺省地址池的的 Calico，首先运行 `calicoctl get ippool -o wide`，会看到已经创建了一个 `192.168.0.0/16` 的地址池：

```bash
NAME                  CIDR             NAT    IPIPMODE   DISABLED   SELECTOR
default-ipv4-ippool   192.168.0.0/16   true   Always     false      all()
```

1. 删除缺省地址池
    
    `default-ipv4-ippool` 地址池已经存在，并占据了整个 `/16` 块，因此必须删除：`calicoctl delete ippools default-ipv4-ippool`
    
2. 给 Node 打标签。
    
    要给特定节点分配地址池，节点必须用标签进行标识：
    
    ```bash
    kubectl label nodes kube-node-0 rack=0
    kubectl label nodes kube-node-1 rack=0
    kubectl label nodes kube-node-2 rack=1
    kubectl label nodes kube-node-3 rack=1
    ```
    
3. 为每个机架创建地址池
    
    ```bash
    calicoctl create -f -<<EOF
    apiVersion: projectcalico.org/v3
    kind: IPPool
    metadata:
      name: rack-0-ippool
    spec:
      cidr: 192.168.0.0/24
      ipipMode: Always
      natOutgoing: true
      nodeSelector: rack == "0"
    EOF
    ```
    
    ```bash
    calicoctl create -f -<<EOF
    apiVersion: projectcalico.org/v3
    kind: IPPool
    metadata:
      name: rack-1-ippool
    spec:
      cidr: 192.168.1.0/24
      ipipMode: Always
      natOutgoing: true
      nodeSelector: rack == "1"
    EOF
    ```
    
    现在就创建了两个地址池，使用 `calicoctl get ippool -o wide` 进行查看：
    
    ```bash
    NAME                  CIDR             NAT    IPIPMODE   DISABLED   SELECTOR
    rack-1-ippool         192.168.0.0/24   true   Always     false      rack == "0"
    rack-2-ippool         192.168.1.0/24   true   Always     false      rack == "1"
    ```
    
4. 检查地址池的工作状态
    
    创建一个 Nginx 的 Deployment，其中包含五个副本，保证分配到每一个节点上。
    
    ```bash
    kubectl run nginx --image nginx --replicas 5
    ```
    
    检查新的 Pod 是否已经根据所在机架获得了应有的 IP 地址。
    
    ```bash
    NAME                   READY   STATUS    RESTARTS   AGE    IP             NODE          NOMINATED NODE   READINESS GATES
    nginx-5c7588df-prx4z   1/1     Running   0          6m3s   192.168.0.64   kube-node-0   <none>           <none>
    nginx-5c7588df-s7qw6   1/1     Running   0          6m7s   192.168.0.129  kube-node-1   <none>           <none>
    nginx-5c7588df-w7r7g   1/1     Running   0          6m3s   192.168.1.65   kube-node-2   <none>           <none>
    nginx-5c7588df-62lnf   1/1     Running   0          6m3s   192.168.1.1    kube-node-3   <none>           <none>
    nginx-5c7588df-pnsvv   1/1     Running   0          6m3s   192.168.1.64   kube-node-2   <none>           <none>
    ```
    
    可以看到，IP 地址的是根据节点（所在的机架）来选择了对应的地址池进行分配的。
