simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

AWS:golang Web Server を binary cache DB とする1/2

go 言語で巨大な int 配列の確保と利用

ゲームではキューブは 4byte の情報構造体で値は int 32bit の配列で格納したいと思っています。
go でも C++ のように int 配列作って、ポインタで保持しておけばいつでもランダムアクセスできるんですよね?
をコードで確認します。

基本はこういうこと

a := [3] int{}
fmt.Println(a) // [0 0 0]

世界を最初は最小の 8 x 8 x 8 チャンクにしようと思っています。
チャンクは 16 x 16 x 16 なので、全 int 要素数は 512 x 4096 つまり 2,097,152 要素
単位は x 4 byte なので、8,388,608 [byte] つまり 8.4 [MB] のメモリが必要

あれ、もう少し大きくできるかも

16 x 16 x 16 チャンクにしますか、すると 8 倍のボリュームになり
67,108,864 [byte] つまり 67.2 [MB] あればいい
これは AWS の最安値価格帯のインスタンスタイプ t2.micro の 1000 [MB] でも十分におさまりますね。

HTTP リクエストを受け付ける APIサーバー

すでに過去にやってますね。
simplestar-tech.hatenablog.com

PlayFab の CloudScript からの呼び出しを考えているので http リクエストで答えてもらいたいところですが
これは PlayFab のサンプルにこのように POST すれば相手に届くと書いてありました。

// This is a simple example of making a web request to an external HTTP API.
handlers.makeHTTPRequest = function (args, context) {
    var headers = {
        "X-MyCustomHeader": "Some Value"
    };
    
    var body = {
        input: args,
        userId: currentPlayerId,
        mode: "foobar"
    };

    var url = "http://httpbin.org/status/200";
    var content = JSON.stringify(body);
    var httpMethod = "post";
    var contentType = "application/json";

    // The pre-defined http object makes synchronous HTTP requests
    var response = http.request(url, httpMethod, content, contentType, headers);
    return { responseContent: response };
};

ローカルから試すときは ARC こと
chrome.google.com
Chrome に組み込んで使うのが便利

HTTPS にするため証明書を作る

色々手順を確認したところ、まず自分のドメインがなくてはならないとのこと
こちらも AWS の Route53 で作ると楽
次の過去記事をなぞってドメインを取得しました。(もう以前のドメインは止めてた)
simplestar-tech.hatenablog.com

次に AWS Certificate Manager (ACM) と呼んでいるサービスでこのドメインに証明書を登録します。
EC2 → ELB → ALB で HTTPS での利用をするとして作成を進めて ACM から証明書を作成する(推奨)というフローまで来たら、リンクの案内に従って Route53 のドメインを登録します。
ここで注意点なのが作ったドメインexample.com だったとして
*.example.com
example.com
と二つ、ワイルドカード付で登録作業を進めます。これで証明書がサブドメインを全部受け入れるようになる(Beanstalk のときは必須)
[Create record in Route 53] ボタンが表示されるところまで進めて、このボタンを押さずに待つ、自分はこれでしばらくして問題なく ACM から証明書が発行されました。(確認中ステータスが成功に変わった)
フローを作るために ALB の HTTPS 操作をやってきましたが ALB 自体はいらないので、証明書発行まで確認できたらこの ALB は消すか、最後まで作らずにキャンセルします。

HTTPS にするため Beanstalk の詳細設定で証明書を利用する ALB を作成する

証明書はできたので
続いて AWS の Beanstalk で go の Webサーバーを作ります。
前回の記事をおさらい
simplestar-tech.hatenablog.com

今度は最初からサンプルの Beanstalk の Go 環境を用意する途中[Configure more options]に相当するボタンが現れたら押す。
そのあと表示される設定で高可用性を選ぶ(こんなラジオボタン

f:id:simplestar_tech:20191104214321p:plain
高可用性を選ぶ

すると ALB ロードバランサーの設定変更が可能になるので、ここで HTTPS になるように設定を進めて、証明書を選ぶタイミングで先ほど発行した証明書を選ぶ

一応これで https のアドレスで go 環境が見えるようになる(ドメインが異なるので信頼されていない通信と警告が出る)

Beanstalk のアドレスをRoute53のドメインにする

手順はこちら
CNAME レコードを作成してトラフィックを Elastic Beanstalk 環境にルーティングするには

BeanstalkのURLを独自ドメインにルーティングってことなのかな
ちょい無駄な感じ、大したロスじゃないのかな?

これで https://ほげほげ.example.com にブラウザからアクセスしたら go で作った Web サーバーの html ページが見れたり
ARCアドオンで GET を SEND すれば 200 ステータスが返ってくる

json post を受けて json を返す go-v2.zip をデプロイ

実際のサンプルアプリケーションの実装がこちら
docs.aws.amazon.com
の go-v1.zip です。

まずはサンプルを読み解きますがログを"/var/log/golang/golang-server.log"へ出力していますね。
外部から確認するには?

docs.aws.amazon.com
書いてある通りやってみたが、デプロイ後もログのリクエスト結果が変わらず
なんでかなぁ

もういいや

追記:原因わかった
/var/log/golang/golang-server.log ファイルが出力されていないだけ
逆に存在しないファイルを指定しても動くことが証明された
で、対処としては go 実装側で、次の通り app current 直下にサーバーログファイルを作るように書いて、パッケージをデプロイして解決

	f, _ := os.Create("/var/app/current/golang-server.log")
	defer f.Close()
	log.SetOutput(f)

もちろん .ebextensions フォルダ以下の applogs.config ファイルには次の設定を記述

files: 
  "/opt/elasticbeanstalk/tasks/bundlelogs.d/applogs.conf" :
    mode: "000755"
    owner: root
    group: root
    content: |
      /var/app/current/golang-server.log

bundlelogs.d 以下の設定ということで、AWS Beanstalk のコンソールからはログ→フルログのリクエストの時に zip に app フォルダとして内包されることになったことを確認できた

もっと詳しくはここ
docs.aws.amazon.com


追記2:Beanstalk のインスタンスssh 接続するにはキーペアを設定で指定する必要がある
初回設定でセキュリティ項目を変更するか、後からでも「設定」→「セキュリティ」→「変更」で有効なキーペアの選択で ssh 接続できるようになります。
※セキュリティグループで ssh ポートを自宅 IP からインバウンド許可することも忘れずに


とりあえず https のアドレスに post して、json を返すように作ったので結果を見ます。

結果を得られる状態にしたい

得られた結果
{
"status": 200,
"result": "ok dayo"
}

ちゃんと json を受け取れてた
良かった

このときのサーバー golang コードがこちら

package main

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "5000"
	}

	f, _ := os.Create("/var/app/current/golang-server.log")
	defer f.Close()
	log.SetOutput(f)

	const indexPage = "public/index.html"
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			if buf, err := ioutil.ReadAll(r.Body); err == nil {
				log.Printf("Received message: %s\n", string(buf))
			}

			ping := Ping{http.StatusOK, "ok desuyo"}

			res, err := json.Marshal(ping)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/json")
			w.Write(res)
		} else {
			log.Printf("Serving %s to %s...\n", indexPage, r.RemoteAddr)
			http.ServeFile(w, r, indexPage)
		}
	})

	http.HandleFunc("/scheduled", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			log.Printf("Received task %s scheduled at %s\n", r.Header.Get("X-Aws-Sqsd-Taskname"), r.Header.Get("X-Aws-Sqsd-Scheduled-At"))
		}
	})

	log.Printf("Listening on port %s\n\n", port)
	http.ListenAndServe(":"+port, nil)
}

type Ping struct {
	Status int    `json:"status"`
	Result string `json:"result"`
}

まとめ

あと少しコード変えれば binary cache DB として機能できそうですね
次の記事に続きます