nginx + Go-FCGI で Web アプリを動かす

  • 70
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Go は単体でも Web サーバを立てることが可能であり、公式ドキュメントのサンプルなどでもだいたい内蔵のものを使っています。

standalone_hello.go
package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello World!")
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":4000", nil)
}

ただ、最近は Rails だったら nginx + unicorn ですし、 PHP も nginx + php-fpm といった構成がモダンな気がします。
なので、なんとなく Go でも nginx の後ろでアプリケーションサーバに専念させたいなあと思ったのでやりかたを調べました。

nginx の設定

最も単純な最小構成だとこんな感じでしょうか。

goapp.conf
server {
    listen       80;
    server_name  example.com;

    location / {
        fastcgi_pass  unix:/var/run/go-fcgi.sock;
        include       fastcgi_params;
    }
}

UNIX ソケット、 /var/run/go-fcgi.sock にそのまま投げています。
本格的に使うなら静的ファイルは nginx で処理させたいのでもう少し複雑になるでしょう。

Go で UNIX ソケットを作る

Go 側では、 nginx からパスされたリクエストを処理するために、 UNIX ソケットを作ります。

nginx_combo_hello.go
package main

import (
    "fmt"
    "net"
    "net/http"
    "net/http/fcgi"
)

func handler(res http.ResponseWriter, req *http.Request) {
    fmt.Fprint(res, "Hello World!")
}

func main() {
    l, err := net.Listen("unix", "/var/run/go-fcgi.sock")
    if err != nil {
        return
    }
    http.HandleFunc("/", handler)
    fcgi.Serve(l, nil)
}

最初とサンプルとそこまで違いがありません。
ListenAndServe()Listen()Serve() に分割されて fcgi パッケージを使うようになっただけです。
意外と簡単に実現できました。

TCP ソケットで Listen する

ウェブサーバ と アプリケーションサーバ を別サーバにする場合は UNIX ソケットは使えないので、 TCP で通信する必要があります。
といっても、そんなに大した変更箇所はありません。

goapp-tcp.conf
server {
    listen       80;
    server_name  example.com;

    location / {
        fastcgi_pass  app.example.com:9000; # アプリケーションサーバの 9000 番ポートに横流し
        include       fastcgi_params;
    }
}
nginx_tcp_hello.go
package main

import (
    "fmt"
    "net"
    "net/http"
    "net/http/fcgi"
)

func handler(res http.ResponseWriter, req *http.Request) {
    fmt.Fprint(res, "Hello World!")
}

func main() {
    l, err := net.Listen("tcp", ":9000") // TCP 9000 番ポートで Listen
    if err != nil {
        return
    }
    http.HandleFunc("/", handler)
    fcgi.Serve(l, nil)
}

これで TCP を使って連携させることができます。