删除文章

确定要删除这篇文章吗?

取消
确定

keepalived实现服务高可用

     阅读(285)  2019-12-10 09:14:55

目标

同一个程序部署在两台服务器上同时在运行,只有一个主服务在处理业务,当主服务挂了的时候另外一个服务器上的服务继续提供服务,保证业务不中断做到高可用。

问题

每个服务器的IP是不一样的,当服务切换后IP地址也变了,要想客户端对此无感知,keepalived会提供一个固定的虚拟IP,客户端连这个虚拟IP。当后台IP改变的时候虚拟IP会自动映射到实际IP,这样客户端不需要做任何改动。

当主服务挂掉后,发生服务切换此时从服务升为主服务,但是当之前的主服务重启后不应该再切换回来(切换是有代价的)。使用keepalived配置的时候将state都设置为BACKUP并且设置为nopreempt不可抢占模式

解决方案

准备两台机器分别是:172.16.75.170, 172.16.75.171,确保是互通的,虚拟IP是:172.16.75.11确保没有被占用。

  • 两台机器上安装keepalived

    yum -y install keepalived
    
  • 修改170上keepalived配置

    vim /etc/keepalived/keepalived.conf
    内容如下:
    ! Configuration File for keepalived
    
    global_defs {
    router_id simplehttp_master
    }
    
    vrrp_script check {
        script "/root/simple_http/check_simplehttp.sh"
        interval 3
    }
    
    vrrp_instance VI_1 {
    state BACKUP
    nopreempt
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        172.16.75.11
    }
    
    track_script {
        check
    }
    }
    
  • 修改171上keepalived配置

    vim /etc/keepalived/keepalived.conf
    内容如下:
    ! Configuration File for keepalived
    
    global_defs {
    router_id simplehttp_backup
    }
    
    vrrp_script check {
        script "/root/simple_http/check_simplehttp.sh"
        interval 3
    }
    
    vrrp_instance VI_1 {
    state BACKUP
    nopreempt
    interface eth0
    virtual_router_id 51
    priority 70
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        172.16.75.11
    }
    
    track_script {
        check
    }
    }
    

router_id必须是唯一的

  • check_simplehttp.sh脚本内容,两台机器上都一样

    #!/bin/sh
    
    SERVICE="simplehttp"
    RUN_DIR="/root/simple_http"
    if [ `ps -C $SERVICE --no-header | wc -l` -eq 0 ];then
        echo "$SERVICE is stopped..."
        exit 1
    else
        echo "$SERVICE always running..."
        exit 0
    fi
    
  • 还需要准备一个简单http服务来测试
    下面是一个简单的go服务,显示当前服务器时间和IP地址

    package main
    
    import (
    "fmt"
    "net"
    "net/http"
    "time"
    )
    
    func getLocalIp() string {
    addrs, err := net.InterfaceAddrs()
    if err != nil {
        return ""
    }
    for _, addr := range addrs {
        if i, ok := addr.(*net.IPNet); ok && !i.IP.IsLoopback() {
            if i.IP.To4() != nil {
                return i.IP.String()
            }
        }
    }
    return ""
    }
    
    func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "time:%s, ip:%s", time.Now().String()[:19], getLocalIp())
    })
    http.ListenAndServe(":6999", nil)
    }
    

编译后的程序名为:simplehttp

注意check_simplehttp.sh脚本和simplehttp程序统一放在/root/simple_http目录下

在两台机器上启动服务:

/root/simple_http/simplehttp &
service keepalived start

keepalived日志:/var/log/messages

验证

先访问:curl http://172.16.75.170:6999和curl http://172.16.75.171:6999,如果输出类似内容:time:2019-12-10 16:52:34, ip:172.16.75.170,说明simple http服务启动成功了。

再访问虚拟IP:curl http://172.16.75.11:6999如果输出如上内容说明keepalived启动成功了。curl输出的IP地址就是主服务的地址。

假如当前主服务是:170,观察结果的时候curl要多执行几次(有时间差)
停掉170服务,访问curl http://172.16.75.11:6999输出171的IP地址
再次启动170服务,访问curl http://172.16.75.11:6999输出171的IP地址
停掉171服务,访问curl http://172.16.75.11:6999输出170的IP地址

check_simplehttp.sh脚本可以配置的很灵活,它会根据interval的间隔秒数执行,exit 0表示正常,1表示失败。

如果切换服务的代价很大,那么在simplehttp服务挂掉的时候自动拉起就可以了。

如果服务自动拉起的代价太大,当simplehttp服务挂掉的时候切换服务。

你可以自动拉起simplehttp服务,或者在simplehttp挂的时候同时stop掉keepalived服务,这些都是可以通过脚本来实现的。

如通过notify_master,notify_backup可以在升为主和降为备的时候执行脚本

通过上面的验证,服务就部署完成了。

文章评论

Keep it simple,stupid
文章数
325
总访问量
314493
今日访问
614
最近评论

liangzi: 不错 谢谢分享
tujiaw: registerThreadInactive:如果当前没有激活的线程,就去激活线程,让等待的线程去执行任务。
hgzzx: 佩服佩服。 请教:registerThreadInactive的作用是什么?
xuehaoyun: 很不错,来围观一下
tujiaw: 抱歉csdn code服务关闭了,这个代码我也找不到了
于淞: 你好,这个文章的源码能分享一下吗,songsong9181@163.com,谢谢了 上面的写错了
回到顶部