戻る

NginxとGoでFastCGI

PHPのような仕組みならともかく今のリアルタイムのレベルで動的なサイトではFastであろうがCGIと相性が悪いと思われる. まずWebSocketが使えないだろう. もしかしたらうまくいくかもしれないが. ともかく, goではfastcgiでできることは最低限しかない.

Nginxの設定

/etc/nginx/nginx.confの一部を次のように編集する. fastcgi_passでローカルの適当なポートを通してGoアプリケーションに接続する. 今回9000としたが, 空いてそうなポートならなんでも良い. userは実行ユーザーでありセキュリティ上rootユーザーと異なる場合が多い. 今回user httpとしてhttpというユーザー名で実行するが, 各自必要に応じて決める. Nginxの実行ユーザーの設定については各自調べておくこと. もちろんuseraddコマンドなどでシステムに追加されたユーザーでないと実行できない.


user http
server {
    listen       80;
    server_name  hogehoge.com;

    location /hoge {
        fastcgi_pass    127.0.0.1:9000;
        include         /etc/nginx/fastcgi_params;
    }
}

もちろんnginx.conf編集後は再起動を忘れない


systemctl restart nginx

Goの環境構築

まずGoのアプリケーション(binファイル)をどこにおくかが問題となる. ログインユーザーのホームディレクトリ以下におくことは大体悪いことが起きたり役割の区別の観点からもよくないため, /user/local/ 以下などに配置する. 今回/usr/local/gocode/をGOPATHとしてアプリケーションを配置しする.次のように配置している. binに実行ファイル, src/ドメイン名/パッケージ名/にソースコードをいれる.


/usr/local
├── gocode
│   ├── bin
│   │   └── goserverapp
│   └── src
│       └── hogehoge.com
│           └── goserverapp
│               └── main.go

さて, /usr/localは通常rootユーザーが所有権を持っており, そこで作成したファイルもその所有権が引き継がれる. gocodeディレクトリを作成した後, 次のコマンドで所有権を変更する. goのソースコードを実際に編集するのはログインユーザーで行いたいのでそれを指定し, グループはWebサーバーの実行ユーザーのグループを指定すると良いだろう. w権限をWebサーバーにも与えたい時はよくあり, ログインユーザーにも与えたいのでグループにw権限を付与するということが多い. 私の場合ログインユーザー(zaief)をhttpグループにも入れている.


chown -R zaief:http gocode

GOPATH, PATHを設定する. PATHの設定は今回絶対パスでgoアプリケーションを実行するためいらないが, ログインユーザーで何か呼び出したい時もあるかもしれないので設定する. GOPATHはgo install時などにインストール先を指定するため必要. ログインユーザーのシェルの設定ファイルに書き込む. 私の場合zshを使っているため~/.zshrcを次のように編集した.


export PATH=$PATH:/usr/local/gocode/bin/
export GOPATH=/usr/local/gocode/

Goアプリケーションを作成

main.goには次のように記述した. Nginxがローカルホスト127.0.0.1:9000でアクセスしてくるのでnet.Listen()で応答させるよう設定する. とても簡単な応答をするアプリだ.


package main

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

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

func main() {
        l, _ := net.Listen("tcp", "127.0.0.1:9000")
        http.HandleFunc("/", handler)
        fcgi.Serve(l, nil)

func Serve(l net.Listener, handler http.Handler) error は二番目の引数がnilならhttp.DefaultServeMuxが使われる.

作成したら, go install でGOPATHで指定したディレクトリ/bin/ にビルドしたバイナリが配置される. goserverappという名前のバイナリができている. go installを実行した場所のディレクトリ名が使われることに注意しよう. 特に, mainという名前のバイナリが作成されるわけではない.


go install

$GOPATH/bin/goserverapp で実行してみよう. nginxで定めたurlからアクセスできるはずだ.

アプリケーションの自動起動

さて, 先ほど実行したのはログインユーザーなのでログアウトしてしまうとアプリケーションのプロセスは終了してしまう. そこでwebサーバーの実行ユーザーなどにこれを任せてしまいたい. そのためにはsystemdにデーモンを登録する必要がある. 次のようなファイルを作成し, /etc/systemd/system/goserver.serviceとして保存しよう.


[Unit]
Description = hogehoge.com

[Service]
ExecStart = /usr/local/gocode/bin/goserverapp

ExecStop = /bin/kill -SIGTERM $MAINPID

ExecReload = /bin/kill -SIGTERM $MAINPID &&&& /usr/local/gocode/bin/goserverapp

Restart = always

Type = simple

[Install]
WantedBy = multi-user.target

作成したら, systemdに登録する.


systemctl daemon-reload
systemctl enable goserver
systemctl start goserver

これでデーモンとしてアプリケーションを常駐させることができた. これで一通り流れがわかっただろう. nginxを使わずgo単体でwebサーバーとして機能させる解説が多いが今回nginxと連携ができより実用性のあるものとなっている.