文章标题 原创 翻译 转载 文章内容 # 目标 同一个程序部署在两台服务器上同时在运行,只有一个主服务在处理业务,当主服务挂了的时候另外一个服务器上的服务继续提供服务,保证业务不中断做到高可用。 # 问题 每个服务器的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可以在升为主和降为备的时候执行脚本 通过上面的验证,服务就部署完成了。 文章类别 Python Mobile Android Java Shell Life Database Bug Windows IOS Tools Boost Node.js Mac Product Tips C/C++ Golang Javascript React Qt MQ MongoDB Design Web Linux LLM ChatGPT RAG AI 提交