potpro (ぽとぷろ)
Full-stuck engineer(Not Full-stack)
JS/PHP/Go/Docker/Nginxなど。技術または趣味寄りの発信ブログです。
全 85 記事Go言語からSSHを踏み台にしてMySQL等に接続する
Go言語からSSHを踏み台にしてMySQL等に接続する
割と最近はGo言語を結構使ってきて構築することも多くなり、WebということでRDBの接続が基本的に不可欠になってきます。 しかし、セキュリティ的な問題で、データベースを外部に公開できない場合が多いです。
というより、大体そうなっているものだと思います。
そこでGo言語は簡単にバッチを書け、データベースの接続も可能ですぐバイナリに出来るという言語ということで、非常に好みだったりするのですが、
そうなるとデータベースに外部から接続できないのが問題となるので、SSHでデータベース内に入ってから、localhostで接続したりします。
自分のPC --->(SSH TCP22) MySQLが動いているサーバ --->(MySQL 3306) MySQL という感じに。
しかし、当然プログラムなのでそういった処理を書かなくてはなりません。
それを解消したときの記事。Go言語はsshもsqlも大体用意されているので、割と簡単に行けます。
コード
参考にしたURL Using MySQL / MariaDB via SSH in Golang
とりあえずググって来たものを参考に書き直し。
基本的にワンライナーで書かれてしまっているので、上手く外部から読み込んで動くようにします。 悲しいけど全部パスワード認証となっております。
type DbServerWithSSH struct {
Host string
SSHPort uint
SSHUser string
SSHPassword string
DBPort uint
DBUser string
DBPassword string
Database string
}
func Init(database config.DbServerWithSSH) (*sql.DB, error) {
sshConfig := &ssh.ClientConfig{
User: database.SSHUser,
Auth: []ssh.AuthMethod{
ssh.Password(database.SSHPassword),
},
}
var err error
conn, err = ssh.Dial("tcp", fmt.Sprintf("%s:%d", database.Host, database.SSHPort), sshConfig)
if err != nil {
Close()
return nil, err
}
dialNet := "mysql+tcp"
dialFunc := func(addr string) (net.Conn, error) {
return conn.Dial("tcp", addr)
}
mysql.RegisterDial(dialNet, dialFunc)
dsn := fmt.Sprintf("%s:%s@%s(localhost:%d)/%s", database.DBUser, database.DBPassword, dialNet, database.DBPort, database.Database)
db, err = sql.Open("mysql", dsn)
if err != nil {
Close()
return nil, err
}
//Ping Check
err = db.Ping()
if err != nil {
return nil, err
}
return db, err
}
// Close SSH含めデータベースから切断する
func Close() {
if db != nil {
db.Close()
}
if conn != nil {
conn.Close()
}
}
これを実行するテストコードがこれです。
func TestInit(t *testing.T) {
config := config.DbServerWithSSH{
Host: "[ホスト名]",
SSHPort: 22,
SSHUser: "[SSHユーザー名]",
SSHPassword: "[SSHパスワード]",
DBPort: 3306,
DBUser: "[MySQLユーザー名]",
DBPassword: "[MySQLパスワード]",
Database: "[MySQLデータベース名]",
}
db, err := Init(config)
defer Close()
if err != nil {
t.Error(err)
}
if rows, err := db.Query("SELECT id,name FROM products LIMIT 10"); err == nil {
for rows.Next() {
var id int64
var name string
rows.Scan(&id,&name)
fmt.Printf("ID: %d Name:%s\n", id, name)
}
rows.Close()
} else {
t.Error(err)
}
}
出力
ID: 1 Name:A
ID: 2 Name:B
ID: 3 Name:C
mysql.RegisterDial?
RegisterDial registers a custom dial function. It can then be used by the network address mynet(addr), where mynet is the registered new network. addr is passed as a parameter to the dial function.
RegisterDialはカスタムダイヤルfunctionを登録します。その後、ネットワークアドレス mynet(addr) で使用できます。ここで、mynetは、登録された新しいネットワークです。 addrはパラメータとしてダイヤルfunctionに渡されます。
説明が全くわからなかったのですが、RegisterDialにはダイアル名とnet.Conn, error
を返す関数を渡すと、関数内の処理を接続時に行い、独自の方法で接続するようです。
登録したダイアル名はDSNに設定することで使用可能。
ここでは、conn.Dial("tcp", addr)
を渡しているので、ssh内で実行しているように動くということですね。
今回、mysql+tcp
という名前にしていますが、被らなかったら多分何でもいいのかも。
これをうまく使うことで、TCP以外での独自の接続もできるわけですね。
しかしこれ、MySQL側にある関数なので他のRDBとかには存在するのだろうか?
と調べたらPostgreSQLなんかにはあった。挙動はほぼ同じだがsql.Register
を使うと行けるみたい。
これはsqlパッケージなので、むしろMySQLだけが独自実装?
Go言語は軽くバッチでも作りたいなというときにマルチプラットフォームかつスタンドアローンで動いてくれるので非常に便利で、PHPやJSとは特色も違うのでこれからも全然使っていきます。
というより、ようやくGoの記事書けた。ネタに悩んで書いていなかった。