003_餓了么chaosmonkey實現


背景

公司目前的服務設計大部分滿足 design for failure 理念。隨着業務復雜度的提升,我們很難再保證對系統故障的容錯性。我們需要工具來驗證服務的容錯性,基於這個需求我們使用了 tc 工具,並開發了chaosmonkey工具。本文主要圍繞這兩個工具進行講述。

TC流控

基本概念

Netem 是 Linux 2.6 及以上內核版本提供的一個網絡模擬功能模塊。該功能模塊可以模擬出局域網諸如低帶寬、傳輸延遲、丟包、亂序、重復等情況。

基本原理

包從進入tc開始,分為多個qd(qdisc)。每個qd可以包含多個子qd,qd彼此連接形成一顆樹。每個qd上可以附加filter,選擇進入哪個child。流量控制控發不控收。

基本操作

# 網絡延遲
# sudo tc qdisc add dev eth0 root netem delay 100ms
# 網絡丟包
# sudo tc qdisc add dev eth0 root netem loss 1%
# 基於filter
# 192.168.33.1被延遲,其他地址無效果
# sudo tc qdisc add dev eth0 root handle 1: prio
# sudo tc qdisc add dev eth0 parent 1:1 handle 10: netem delay 1s loss 2%
# sudo tc filter add dev eth0 protocol ip parent 1: prio 1 u32 match ip dst 192.168.33.1 flowid 1:1
# 刪除規則
# sudo tc qdisc del dev eth0 root

缺點

tc 工具在配置時比較繁瑣,不能模擬一段時間內的故障,不能作為一個服務定期演練。自己開發的chaosmonkey可以滿足以上條件。

chaosmonkey

功能

chaosmonkey基於golang開發,它包含三個功能:網絡故障模擬、服務重定向、資源限制。如圖。

 

 

網絡故障模擬

用戶向服務發送http請求,服務解析參數,模擬丟包、延遲,其中主要使用的package是 netlink 。部分代碼如下:

// 用戶提交的參數
type addNetwork struct {
    Duration uint32
    Limit    *network.LinkLimit
}
type LinkLimit struct {
    Latency    uint32
    Jitter     uint32
    Loss       float32
    Corruption float32
}

//設置丟包、延遲
func (l *Link) AddLimit(limit LinkLimit) error {
    netemQdiscAttrs := netlink.NetemQdiscAttrs{
        Latency:     limit.Latency,
        Jitter:      limit.Jitter,
        Loss:        limit.Loss,
        CorruptProb: limit.Corruption,
    }
    qdiscAttrs := netlink.QdiscAttrs{
        Parent:    netlink.HANDLE_ROOT,
        LinkIndex: l.Attrs().Index,
    }
    netem := netlink.NewNetem(qdiscAttrs, netemQdiscAttrs)
    if err := netlink.QdiscAdd(netem); err != nil {
        return fmt.Errorf("qdisc add %v: %v", netem, err)
    }
    log.Infof("AddLimit finish with %v", netem)
    return nil
}

服務重定向

服務主要使用exec包執行iptables命令,部分代碼如下:

// 用戶提交的參數
type startRedirect struct {
    Duration    uint32
    Redirectors []redirector.Redirector
    Name        string
}
type Redirector struct {
    Protocol    string `json:"protocol"`
    Destination string `json:"destination"`
    Dport       string `json:"dport"`
    Target      string `json:"target"`
}

//增加iptables規則
for _, r := range redirectors {
        if err := ipt.Append("nat", "OUTPUT", "-p", r.Protocol, "-d", r.Destination, "--dport", r.Dport, "-j", "DNAT", "--to", r.Target); err != nil {
            return fmt.Errorf("append tables %v: %v", r, err)
        }
        log.Infof("append iptables rule finish: %v", r)
        ...
}

func (ipt *IPtables) Append(table string, chain string, rulespec ...string) error {
    cmd := append([]string{"-t", table, "-A", chain}, rulespec...)
    return ipt.run(cmd...)
}

func (ipt *IPtables) runWithOutput(args []string, stdout io.Writer) error {
    args = append([]string{ipt.path}, args...)
    var stderr bytes.Buffer
    cmd := exec.Cmd{
        Path:   ipt.path,
        Args:   args,
        Stdout: stdout,
        Stderr: &stderr,
    }
    err := cmd.Run()
    if err != nil {
        return &Error{*(err.(*exec.ExitError)), stderr.String()}
    }
    return nil
}

資源限制

主要使用的packge是 fs 和 configs 。代碼如下:

type addResources struct {
    Pids     []int
    Pattern  string
    Duration uint32
    Limit    resources.Limit
}
type Limit struct {
    Cpu            int64
    Memory         int64
    BlkioReadBPS   uint64 `json:"blkio_read_bps"`
    BlkioReadIOPS  uint64 `json:"blkio_read_iops"`
    BlkioWriteBPS  uint64 `json:"blkio_write_bps"`
    BlkioWriteIOPS uint64 `json:"blkio_write_iops"`
}

// 設置cgroup
config := limit.toConfig()
monkey, err := factory.CreateManager(config)
...
for _, pid := range pids {
        if manager, err := factory.GetManager(pid); err != nil {
            return fmt.Errorf("get manager for pid %d: %v", pid, err)
        } else {
            p := &process{manager: manager, pid: pid, monkey: monkey, wait: r.wait}
            if err := p.start(duration); err != nil {
                return fmt.Errorf("process apply monkey: %v", err)
            } else {
                r.processes = append(r.processes, p)
            }
        }
}

    if err := monkey.Set(config); err != nil {
        return fmt.Errorf("set monkey config: %v", err)
    } else {
        log.Infof("set monkey %v for pids %v", limit, pids)
    }

參考文檔

linux高級流控 netlink cgroups


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM