zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    【C++类与对象】实验四(二)
    【微信小程序——开发步骤1】
    【C++/实验三】类和对象
    【C++/类与对象总结】
    【C++/函数】实验2
    食堂APP-项目开发 语音输入(1)
    食堂APP-项目开发及踩坑记录(7)
    食堂APP-项目开发及踩坑记录(6)
    食堂APP-项目开发及踩坑记录(5)
    食堂APP-项目开发及踩坑记录(4)
  • 原文地址:https://www.cnblogs.com/itcomputer/p/7388707.html
Copyright © 2011-2022 走看看