Help us understand the problem. What is going on with this article?

nginx+circus+go(net/http利用)でwebアプリケーションの動作環境構築

More than 1 year has passed since last update.

概要

goのWebappをOSがUbuntu 16.04 LTSのProduction環境で動かすための環境作りについてまとめています。フロントの処理をnginx,プロセス管理をcircusで行いそれぞれのメリットを享受できるようにしています。既にGOJIで対応されているケース等サンプルがありますが、net/httpのみのケースだと情報が点在していたのと、circusを入れるときにpythonの依存関係の問題ですんなり入らなかったのでそこら辺を参考にしていただければと思います。

Webアプリケーションのサンプル

ファイルディスクリプタをパラメータで渡している他、ない場合はTCPポート8080で起動できるようにもしています。

main.go
package main

import (
    "encoding/json"
    "flag"
    "fmt"
    "log"
    "net"
    "net/http"
    "os"
    "os/signal"
    "syscall"
)

var port = flag.Uint("port", 8080, "port to listen")
var fd = flag.Uint("fd", 0, "File descriptor to listen and serve.")

//初期化処理
func init() {
    flag.Parse()
}

//メイン
func main() {

    sigchan := make(chan os.Signal)
    signal.Notify(sigchan, syscall.SIGTERM)
    signal.Notify(sigchan, syscall.SIGINT)

    var l net.Listener
    var err error

    if *fd == 0 {
        log.Println(fmt.Sprintf("listening on port %d", *port))
        l, err = net.ListenTCP("tcp", &net.TCPAddr{Port: int(*port)})
    } else {
        log.Println("listening on socket")
        l, err = net.FileListener(os.NewFile(uintptr(*fd), ""))
    }

    if err != nil {
        log.Fatal(err)
        panic(err)
    }

    go func() {
        mux := routes()
        log.Println(http.Serve(l, mux))
    }()

    <-sigchan
}

//ルーティング
func routes() (mux *http.ServeMux) {
    mux = http.NewServeMux()

    //実際のアプリでは複数ハンドラーを記載
    mux.HandleFunc("/somepass", SomeHandler)


    return
}

//サンプルJsonを出すための構造体(実際では意味のある構造体を作る方が良いです)
type V map[string]interface{}

//ハンドラーサンプル
func SomeHandler(w http.ResponseWriter, r *http.Request) {

    values := V{"testKey": "testValue"}

    content, err := json.Marshal(values)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }

    w.Header().Add("Content-type", "application/json")

    w.WriteHeader(http.StatusOK)
    w.Write(content)
}

nginxの設定

通常の手順通りにnginxをインストール後、UNIXドメインソケットに繋ぐように設定を変更します。

server {

  (中略)

  location / {
    proxy_pass http://backend_goweb;
  }

  (中略)
}

upstream backend_goweb {
    server unix:/var/run/goweb.sock;
}

circusの設定

インストール

apt-get install -y python-pip libzmq-dev libevent-dev python-dev python-virtualenv
pip install circus
pip install circus-web

設定ファイル作成

以下のような設定をcircus.iniとして作成。コマンド名、ログのパスは適宜変更してください。これでUNIXドメインソケットで起動することができる他、標準入力と標準出力がそのままログとして出せるようになります。

circus.ini
[circus]
statsd = 1

[watcher:goweb]
cmd = /pass_to_go_application/application_name -fd $(circus.sockets.web)
stop_signal = SIGINT
numprocesses = 1
use_sockets = True
copy_env = True

stdout_stream.class = FileStream
stdout_stream.filename = /var/log/any_log_pass/stdout.log

stderr_stream.class = FileStream
stderr_stream.filename = /var/log/any_log_pass/stderr.log

[socket:web]
path = /var/run/goweb.sock
family = AF_UNIX

実行

# circusd circus.ini 
2018-03-22 15:46:58 circus[4092] [INFO] Starting master on pid 4092
2018-03-22 15:46:58 circus[4092] [INFO] sockets started
2018-03-22 15:46:58 circus[4092] [ERROR] exception __init__() takes exactly 3 arguments (4 given) caught
  File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 315, in wrapper
    yielded = next(result)
  File "/usr/local/lib/python2.7/dist-packages/circus/arbiter.py", line 529, in start
    self.ctrl.start()
  File "/usr/local/lib/python2.7/dist-packages/circus/controller.py", line 116, in start
    self.check_delay, self.loop)

上記のエラーが出て動きません…。

https://github.com/circus-tent/circus/issues/1059
ライブラリの依存関係で引っかかっているようなので、以下で回避。

# pip install tornado==4.5.3 circus

ソケットを削除して、再度トライ

# rm /var/run/goweb.sock
# circusd circus.ini 
2018-03-22 15:54:23 circus[10462] [INFO] Starting master on pid 10462
2018-03-22 15:54:23 circus[10462] [INFO] sockets started
2018-03-22 15:54:23 circus[10462] [INFO] Arbiter now waiting for commands
2018-03-22 15:54:23 circus[10462] [INFO] goweb started
2018-03-22 15:54:23 circus[10462] [INFO] circusd-stats started
2018-03-22 15:54:23 circus[10467] [INFO] Starting the stats streamer

無事起動しましたので、デーモンとして実行します。

# circusd --daemon circus.ini

再起動出来ることも確認します。

# circusctl restart 
ok

http://(your domain)/somepassで{"testKey":"testValue"}というJSONが返って来れば成功です。

参考

andfactory
Smartphone Idea Companyとして、人々の生活に「&(アンド)」を届ける。
https://andfactory.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした