LoginSignup
1
3

More than 5 years have passed since last update.

IPv4 マルチキャストによるフェイルオーバー

Last updated at Posted at 2018-12-31

1. 概要

この記事では、IPv4 マルチキャストによるルーティングフェイルオーバーの Golang マイクロサンプルコードを投稿します。

2. はじめに

フェイルオーバー、マスター/スレーブ、死活監視構成は、どうしても設計が複雑化・大型化してしまいがちですが、この仕組みは、VRRP や HSRP、GLBP のようなルータ用のプロトコルは使用していないものの、ルーター、ローバーバランサー、リバースプロキシなどを別立てする必要がなく、応用次第ではとてもシンプルなマイクロサービスとして有用となりえるかもしれません。
UDP の信頼性をリトライにより担保し、マルチキャストにより処理コストを削減します。
また、応用次第では集権型監視設計ではなく、個々のコンピュータが相互に監視しあう分散型自律ネットワーク設計が可能となり、さらには、IPv6 による実用も可能です。

3. 環境

  • RHEL-7 系
  • Go 1.9

4. 構成

  • master x 1
    • MIP: 192.0.2.1
    • VIP: 192.0.2.3
  • backup x 1
    • MIP: 192.0.2.2

5. 設計

failover_multicast_ipv4.png

  1. master:一定間隔でマルチキャストグループへハートビートを送信。
  2. backup:マスターノードからのハートビートを受信。
  3. backup:一定間隔でマスターノードからのハートビートの内容を監視。
  4. backup:マスターノードからのハートビート不達を検知。
  5. backup:一定回数のリトライ監視超過でマスターノードダウン判定。
  6. backup:VIP の奪取と、全ノード ARP テーブルへ VIP の更新をブロードキャスト。

6. Golang サンプルコード

@ master
$ sudo nvim ~/go/failover_multicast_ipv4_master.go

~/go/failover_multicast_ipv4_master.go
package main

import (
    "fmt"
    "net"
    "time"
)

func main() {
    mcast_ip4 := "224.0.0.1"
    mcast_port := ":56789"
    mcast_addr := mcast_ip4 + mcast_port
    wait_time := 1
    message := "master"

    fmt.Printf("Send to multicast address: %s\n", mcast_addr)
    conn, err := net.Dial("udp", mcast_addr)
    _Error(err)
    defer conn.Close()

    for {
        time.Sleep(time.Duration(wait_time) * time.Second)
        conn.Write([]byte(message))
        fmt.Printf("%s\n", message)
    }
}

func _Error(_err error) {
    if _err != nil {
        panic(_err)
    }
}

@ backup

$ sudo nvim ~/go/failover_multicast_ipv4_backup.go

~/go/failover_multicast_ipv4_backup.go
package main

import (
    "fmt"
    "net"
    "os/exec"
    "time"
)

func main() {
    mcast_ip4 := "224.0.0.1"
    mcast_port := ":56789"
    mcast_addr := mcast_ip4 + mcast_port
    pulse_interval := 1
    pulses := []int64{1, 1}
    pulse_check := 0
    pulse_retry := 3
    ucast_vip4 := "192.0.2.3"
    ucast_cidr := "/24"
    ucast_mask := ucast_vip4 + ucast_cidr
    ucast_if := "eth0"

    fmt.Printf("Listen multicast address: %s\n", mcast_addr)
    mcast_byte, err := net.ResolveUDPAddr("udp", mcast_addr)
    _Error(err)

    listener, err := net.ListenMulticastUDP("udp", nil, mcast_byte)
    _Error(err)
    defer listener.Close()

    // For master pulse receiver
    buffer := make([]byte, 8)
    go func() {
        for {
            length, remote_mip4, err := listener.ReadFrom(buffer)
            _Error(err)

            if string(buffer[:length]) == "master" {
                pulses[1] = time.Now.UnixNano()

                fmt.Printf("Reveived multicast from Master MIP: %v\n", remote_mip4)
            }
        }
    }()

    // For master pulse checker
    for {
        // Sleep pulse check interval seconds
        time.Sleep(time.Duration(pulse_interval) * time.Second)

        // Wait master boot up
        if pulses[1] == 1 {
            continue
        }

        // Detected normal pulse
        if pulses[0] != pulses[1] {
            pulses[0] = pulses[1]
            pulse_check = 0
            continue
        }

        // Detected abnormal pulse
        pulse_check++

        // Retry pulse check
        if pulse_check < pulse_retry {
            fmt.Printf("Retry pulse check: %v\n", pulse_check)
            continue
        }

        // Detected master cardiac arrest
        fmt.Println("Detected master cardiac arrest.")
        pulses[1] = 0

        // Asign VIP
        fmt.Printf("Reasign Unicast VIP: %s\n", ucast_mask)
        err = exec.Command("ip", "-f", "inet", "addr", "add", ucast_mask, "dev", ucast_if).Run()
        _Error(err)

        // Execute arping
        fmt.Println("Replace ARP tables.")
        err = exec.Command("arping", "-q", "-U", "-c5", "-w1", ucast_vip4, "-I", ucast_if).Run()
        _Error(err)

        fmt.Println("Suceeded failover.")
        break
    }

}

func _Error(_err error) {
    if _err != nil {
        panic(_err)
    }
}

7. Golang サンプル実行

@ master
$ sudo go run ~/go/failover_multicast_ipv4_master.go

[user@master ~/go]$ sudo go run failover_multicast_ipv4_master.go
Send to multicast address: 224.0.0.1:56789
master
master
...

@ backup
$ sudo go run ~/go/failover_multicast_ipv4_backup.go

[user@backup ~/go]$ sudo go run failover_multicast_ipv4_backup.go
Listen multicast address: 224.0.0.1:56789
Reveived multicast from Master MIP: 192.0.2.1:42573
Reveived multicast from Master MIP: 192.0.2.1:42573
...

@ master
[control]+[c]

[user@master ~/go]$ sudo go run failover_multicast_ipv4_master.go
Send to multicast address: 224.0.0.1:56789
master
master
...
^Csignal: interrupt

@ backup

[user@backup ~/go]$ sudo go run failover_multicast_ipv4_backup.go
Listen multicast address: 224.0.0.1:56789
Reveived multicast from Master MIP: 192.0.2.1:42573
Reveived multicast from Master MIP: 192.0.2.1:42573
...
Retry pulse check: 1
Retry pulse check: 2
Detected master cardiac arrest.
Reasign Unicast VIP: 192.0.2.3/24
Replace ARP tables.
Suceeded failover.

$ sudo ip addr

[user@backup ~/go]$ sudo ip addr
...
1: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    ...
    inet 192.0.2.2/24 brd 10.250.255.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet 192.0.2.3/24 scope global secondary eth0
       valid_lft forever preferred_lft forever
..

8. まとめ

この記事では、IPv4 マルチキャストによるルーティングのフェイルオーバーの Golang マイクロサンプルコードを投稿しました。
フェイルオーバー、マスター/スレーブ、死活監視構成は、どうしても設計が複雑化・大型化してしまいがちですが、この仕組みは、VRRP や HSRP、GLBP のようなルータ用のプロトコルは使用していないものの、ルーター、ローバーバランサー、リバースプロキシなどを別立てする必要がなく、応用次第ではとてもシンプルなマイクロサービスとして有用となりえるかもしれません。
また、応用次第では個々のコンピュータを相互監視しあう自律ネットワーク設計が可能となりえます。
次回以降の記事で、シンプルな同期の仕組みを投稿する予定をしてますが、これらのマイクロサービスを組み合わせることで、とてもシンプルなデータリソースの冗長化構成への応用が可能となります。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3