Go
Nagios
golang
Go3Day 15

NagiosのプラグインをGoで作った話

この記事は、Go3 Advent Calendar 2017の15日目の記事です。

最初に

監視ツールでNagiosを使っていて、外形監視にプラグインを追加するためにGoを使用しました。
理由は、言わずもがなコンパイルしてシングルバイナリで実行できるので、Nagiosが動いているサーバーに持っていくのが容易だったからです。

今回は、SQLServerに繋いでクエリの結果からステータスを表示するようなのを実装しました。

実装したコード:
https://github.com/wataru0225/go_nagios_plugins

Nagiosプラグインの実装について

すでに作られているプラグインの実装を見る限り、とてもシンプルな実装でした。終了ステータスを判断して、標準出力されたメッセージをそのまま監視メッセージとしていました。

また、そのAPIのドキュメントもありました。

https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/3/en/pluginapi.html

なので、Goだと、

main.go
package main

import (
  "fmt"
  "os"
)

func main() {
  fmt.Println("check OK!")
  os.Exit(0)
}

とすれば完了です。とてもシンプルです。

もちろんそれぞれのプラグインごとにこのようなことを書くのも面倒なので、共通化しました。

utils/check_base.go
package utils

import (
    "fmt"
    "os"
)

type Status int

const (
    OK Status = iota
    WARNING
    CRITICAL
    UNKNOWN
)

type Checkbase struct {
    Message string
    Status  Status
}

func Init(st Status, msg string) *Checkbase {
    return &Checkbase{
        Message: msg,
        Status:  st,
    }
}

func (ckb *Checkbase) Exit() {
    fmt.Println(ckb.Message)
    os.Exit(int(ckb.Status))
}

func Ok(msg string) *Checkbase {
    return Init(OK, msg)
}

func Warning(msg string) *Checkbase {
    return Init(WARNING, msg)
}

func Critical(msg string) *Checkbase {
    return Init(CRITICAL, msg)
}

func Unknown(msg string) *Checkbase {
    return Init(UNKNOWN, msg)
}

SQLServer接続するためのコードはこちら。go-mssqldbというプラグインがあって、それを使ってプロトコルをsqlserverにすればDB接続はできました。

lib/check_sqlserver/check_by_query.go
package lib

import (
    "database/sql"
    "flag"
    "fmt"
    "net/url"

    _ "github.com/denisenkom/go-mssqldb"
)

type Database struct {
    UserName string
    PassWord string
    Host     string
    Instance string
    Port     int
}

func Set() *Database {
    var username = flag.String("u", "root", "select database username")
    var password = flag.String("p", "root", "select database password")
    var host = flag.String("h", "localhost", "select database host")
    var instance = flag.String("i", "", "select database instance")
    var port = flag.Int("P", 1433, "select database port")
    flag.Parse()

    database := &Database{
        UserName: *username,
        PassWord: *password,
        Host:     *host,
        Instance: *instance,
        Port:     *port,
    }

    return database
}

func (database *Database) Connect() *sql.DB {
    connectionTimeout := 60
    query := url.Values{}
    query.Add("connection timeout", fmt.Sprintf("%d", connectionTimeout))

    u := &url.URL{
        Scheme: "sqlserver",
        User:   url.UserPassword(database.UserName, database.PassWord),
        Host:   fmt.Sprintf("%s:%d", database.Host, database.Port),
        Path:   database.Instance,
    }

    db, err := sql.Open("sqlserver", u.String())
    defer func() {
        if err := recover(); err != nil {
            db.Close()
        }
    }()

    if err != nil {
        panic(err.Error())
    }

    return db
}

上記二つのコードを用いて作ったサンプル。

samples/sqlserver_check_by_query.go
package main

import (
    "github.com/wataru0225/go_nagios_plugins/lib/check_sqlserver"
    "github.com/wataru0225/go_nagios_plugins/utils"
)

// /path/to/sqlserver_check_by_query -u username -p password -h www.example.com -i instance -P port
func main() {
    database := lib.Set()

    db := database.Connect()
    if db == nil {
        panic("failed")
    }

    execQuery := "SELECT COUNT(*) FROM [DB_NAME].[dbo].[TABLE_NAME]"

    rows, err := db.Query(execQuery)
    defer db.Close()

    if err != nil {
        panic(err.Error())
    }

    var count int
    for rows.Next() {
        if err := rows.Scan(&count); err != nil {
            panic(err.Error())
        }
    }

    if count < 1 {
        msg := "CRITICAL ERROR"
        utils.Critical(msg).Exit()
    } else {
        msg := "OK STATUS"
        utils.Ok(msg).Exit()
    }
}

上記サンプルは特にちゃんとしたクエリを渡してないですが、 SELECT COUNT(*) FROM [DB_NAME].[dbo].[TABLE_NAME] の部分を使用したいクエリを引数に取ればOKです。

またCRITICALとOKステータスしか実装されていませんが、もちろんWARNINGやUNKNOWNもできます。

Go言語以外での実装

Perl実装のファイルサイズをチェックするプラグイン:
https://exchange.nagios.org/directory/Plugins/System-Metrics/File-System/check_file_size/details

Shell Script実装でのファイルがマウントされているかチェックするプラグイン:
https://exchange.nagios.org/directory/Plugins/System-Metrics/File-System/check_mount/details

標準のTCPプロトコルのチェックするプラグインはC実装:
https://github.com/nagios-plugins/nagios-plugins/blob/master/plugins/check_tcp.c

Goでの実装サンプルは、meckerelのプラグインが個人的には一番参考になりました。というか、ほとんど参考にさせてもらった。
https://github.com/mackerelio/go-check-plugins