IPAM源碼解析

ipam

host-local

以入?yún)槔?/p>

{
    "name": "networks",
    "cniVersion": "0.4.0",
    "ipam": {
        "type": "host-local",
        "subnet": "10.250.7.0/24",
        "dataDir": "/var/lib/cni/",
        "routes": [
            { "dst": "0.0.0.0/0" }
        ]
    }
}

看看這個ipam插件是怎么挑選容器IP的

解析參數(shù)

入?yún)⒅?code>ipam部分參數(shù)對應(yīng)的host-local插件會解析處理的,這里重點看下網(wǎng)段這部分的解析邏輯

func (r *Range) Canonicalize() error {
    if err := canonicalizeIP(&r.Subnet.IP); err != nil {
        return err
    }

    // Ensure Subnet IP is the network address, not some other address
    networkIP := r.Subnet.IP.Mask(r.Subnet.Mask)
    if !r.Subnet.IP.Equal(networkIP) {
        return fmt.Errorf("Network has host bits set. For a subnet mask of length %d the network address is %s", ones, networkIP.String())
    }

    // If the gateway is nil, claim .1
    if r.Gateway == nil {
        r.Gateway = ip.NextIP(r.Subnet.IP)
    } else {
        if err := canonicalizeIP(&r.Gateway); err != nil {
            return err
        }
    }

    // RangeStart: If specified, make sure it's sane (inside the subnet),
    // otherwise use the first free IP (i.e. .1) - this will conflict with the
    // gateway but we skip it in the iterator
    if r.RangeStart != nil {
        if err := canonicalizeIP(&r.RangeStart); err != nil {
            return err
        }

        if !r.Contains(r.RangeStart) {
            return fmt.Errorf("RangeStart %s not in network %s", r.RangeStart.String(), (*net.IPNet)(&r.Subnet).String())
        }
    } else {
        r.RangeStart = ip.NextIP(r.Subnet.IP)
    }

    // RangeEnd: If specified, verify sanity. Otherwise, add a sensible default
    // (e.g. for a /24: .254 if IPv4, ::255 if IPv6)
    if r.RangeEnd != nil {
        if err := canonicalizeIP(&r.RangeEnd); err != nil {
            return err
        }

        if !r.Contains(r.RangeEnd) {
            return fmt.Errorf("RangeEnd %s not in network %s", r.RangeEnd.String(), (*net.IPNet)(&r.Subnet).String())
        }
    } else {
        r.RangeEnd = lastIP(r.Subnet)
    }

    return nil
}

對于網(wǎng)段10.250.7.0/24

  • 首先會解析出子網(wǎng)掩碼信息255.255.255.0

  • 然后取網(wǎng)段內(nèi)的第一個IP作為網(wǎng)關(guān),即10.250.7.1

  • 同樣取網(wǎng)段內(nèi)的第一個IP作為分配IP的起始IP地址鸯绿,即10.250.7.1,這里起始IP雖然和網(wǎng)關(guān)一樣幔烛,但是后面分配IP的時候會判斷如果和網(wǎng)關(guān)一樣的話就跳過

  • 取網(wǎng)段內(nèi)的最后一個IP作為分配IP的結(jié)尾IP地址饿悬,即10.250.7.254狡恬,忽略廣播地址255

分配IP

func (a *IPAllocator) Get(id string, ifname string, requestedIP net.IP) (*current.IPConfig, error) {

       else {
        iter, err := a.GetIter()

        for {
            reservedIP, gw = iter.Next()
            if reservedIP == nil {
                break
            }

            reserved, err := a.store.Reserve(id, ifname, reservedIP.IP, a.rangeID)
            if err != nil {
                return nil, err
            }

            if reserved {
                break
            }
        }
    }
}

這里會初始化一個迭代器蝎宇,初始下標(biāo)是0,起始IP就是上面解析出來的起始IP兔乞,即10.250.7.1庸追,迭代內(nèi)容就是網(wǎng)段10.250.7.0/24

iter := RangeIter{
        rangeset: a.rangeset,
                rangeIdx: 0
                startIP: (*a.rangeset)[0].RangeStart
    }

迭代過程就是從起始IP開始台囱,如果起始IP和網(wǎng)關(guān)相同簿训,就跳過這個起始IP强品,將IP+1獲取下一個IP,直到結(jié)尾IP

func (i *RangeIter) Next() (*net.IPNet, net.IP) {
    r := (*i.rangeset)[i.rangeIdx]

    // If this is the first time iterating and we're not starting in the middle
    // of the range, then start at rangeStart, which is inclusive
    if i.cur == nil {
        i.cur = r.RangeStart
        i.startIP = i.cur
        if i.cur.Equal(r.Gateway) {
            return i.Next()
        }
        return &net.IPNet{IP: i.cur, Mask: r.Subnet.Mask}, r.Gateway
    }

    // If we've reached the end of this range, we need to advance the range
    // RangeEnd is inclusive as well
    if i.cur.Equal(r.RangeEnd) {
        i.rangeIdx += 1
        i.rangeIdx %= len(*i.rangeset)
        r = (*i.rangeset)[i.rangeIdx]

        i.cur = r.RangeStart
    } else {
        i.cur = ip.NextIP(i.cur)
    }

    if i.startIP == nil {
        i.startIP = i.cur
    } else if i.cur.Equal(i.startIP) {
        // IF we've looped back to where we started, give up
        return nil, nil
    }

    if i.cur.Equal(r.Gateway) {
        return i.Next()
    }

    return &net.IPNet{IP: i.cur, Mask: r.Subnet.Mask}, r.Gateway
}

最終如果結(jié)尾IP也分配出去了,則又會回到起始IP困曙,這時候會失敗慷丽,沒有IP可分配了

記錄IP

再分配IP是怎么知道哪個IP已經(jīng)分配過了呢鳄哭?

host-local插件是通過記錄文件的形式來記錄的

func New(network, dataDir string) (*Store, error) {
    if dataDir == "" {
        dataDir = defaultDataDir
    }
    dir := filepath.Join(dataDir, network)
    if err := os.MkdirAll(dir, 0755); err != nil {
        return nil, err
    }

    lk, err := NewFileLock(dir)
    if err != nil {
        return nil, err
    }
    return &Store{lk, dir}, nil
}

這里會創(chuàng)建目錄/var/lib/cni/networks/锄俄,這里目錄取自入?yún)⒅械膁ataDir字段和name字段拼接而成

當(dāng)一個IP分配出去之后

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, 0644)
    
    if _, err := f.WriteString(strings.TrimSpace(id) + LineBreak + ifname); err != nil {
    
    // store the reserved ip in lastIPFile
    ipfile := GetEscapedPath(s.dataDir, lastIPFilePrefix+rangeID)
    err = ioutil.WriteFile(ipfile, []byte(ip.String()), 0644)
    if err != nil {
        return false, err
    }
    return true, nil
}

就會在目錄/var/lib/cni/networks/下創(chuàng)建一個文件/var/lib/cni/networks/10.250.7.2,并寫入內(nèi)容

0
eth0

同時記錄文件/var/lib/cni/networks/last_reserved_ip.0鱼填,寫入內(nèi)容

10.250.7.2

表示上一次分配出去的IP地址是10.250.7.2

這樣在構(gòu)造迭代器的時候會首先讀文件/var/lib/cni/networks/last_reserved_ip.0獲取上一次分配出去的IP苹丸,然后把上一次分配出去的IP作為起始IP即可

func (a *IPAllocator) GetIter() (*RangeIter, error) {
    iter := RangeIter{
        rangeset: a.rangeset,
    }

    // Round-robin by trying to allocate from the last reserved IP + 1
    startFromLastReservedIP := false

    // We might get a last reserved IP that is wrong if the range indexes changed.
    // This is not critical, we just lose round-robin this one time.
    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)
    }

    // Find the range in the set with this IP
    if startFromLastReservedIP {
        for i, r := range *a.rangeset {
            if r.Contains(lastReservedIP) {
                iter.rangeIdx = i

                // We advance the cursor on every Next(), so the first call
                // to next() will return lastReservedIP + 1
                iter.cur = lastReservedIP
                break
            }
        }
    } else {
        iter.rangeIdx = 0
        iter.startIP = (*a.rangeset)[0].RangeStart
    }
    return &iter, nil
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扇单,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子施流,更是在濱河造成了極大的恐慌兼都,老刑警劉巖扮碧,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慎王,死亡現(xiàn)場離奇詭異,居然都是意外死亡蜀漆,警方通過查閱死者的電腦和手機确丢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門鲜侥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來描函,“玉大人,你說我怎么就攤上這事胆数”啬幔” “怎么了胰伍?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵骂租,是天一觀的道長渗饮。 經(jīng)常有香客問我宿刮,道長互站,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任僵缺,我火速辦了婚禮胡桃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘磕潮。我一直安慰自己翠胰,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布自脯。 她就那樣靜靜地躺著之景,像睡著了一般。 火紅的嫁衣襯著肌膚如雪膏潮。 梳的紋絲不亂的頭發(fā)上锻狗,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機與錄音焕参,去河邊找鬼。 笑死我擂,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的衙吩。 我是一名探鬼主播澈蚌,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼盈电,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起晤锹,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎舶担,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年淮菠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片曙寡。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蕊唐,靈堂內(nèi)的尸體忽然破棺而出副瀑,到底是詐尸還是另有隱情疚颊,我是刑警寧澤嫁赏,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布华烟,位于F島的核電站,受9級特大地震影響返十,放射性物質(zhì)發(fā)生泄漏迟杂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦拾稳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至仿荆,卻和暖如春拢操,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工肚逸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留朦促,地道東北人洒疚。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓乏德,卻偏偏與公主長得像蒲肋,于是被迫代替她去往敵國和親路鹰。 傳聞我的和親對象是個殘疾皇子恩脂,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容