2020年2月11日火曜日

Google App EngineでGoを動かした

Quickstartに続けてBuilding a Go App on App Engineをやってみた。ただ、そのままでは Hello, World! が表示されるだけなので、少し手を加えてみた。具体的には昔のホームページの定番のアクセスカウンターと掲示板を作った。

まずは app.yaml を作った。
runtime: go113
手元のマシンにインストールされているGo 1.13を使いたいgo113を指定した。
プログラムとHTMLテンプレートを分離したいのでGoの html/template というパッケージを使った。テンプレートはこんな感じ。
<html>
<head><title>Zick's Homepage</title></head>
<body>
<h1>Welcome to Zick's Homepage</h1>
<p>
You are the {{.Counter}} guest!
</p>
<form action="/" method="post">
name:
<input type="text" name="name" /><br />
text:
<textarea name="text" row="4" cols="80"></textarea><br />
<input type="submit" value="submit" />
<table border="1">
{{range .Bbs}}
<tr><td>{{.Date}}</td><td>{{.Name}}</td><td>{{.Text}}</td></tr>
{{end}}
</table>
</form>
</body>
</html>
あえて古臭いHTMLにこだわった。 {{.field}} でテンプレートに渡した構造体のフィールドにアクセスできる。 {{range .repeated_field}} で配列をループで回せる。これでカウンタと掲示板へのすべての書き込みが表示できる。
このテンプレートを使うプログラムはこんな感じ。
package main

import (
 "fmt"
 "html/template"
 "log"
 "net/http"
 "os"
 "sync"
 "time"
)

type Kakikomi struct {
 Date string
 Name string
 Text string
}

var count = 0
var kakikomis []Kakikomi
var mutex sync.Mutex

type Bindings struct {
 Counter string
 Bbs     []Kakikomi
}

func updateVars(name string, text string) (int, []Kakikomi) {
 mutex.Lock()
 defer mutex.Unlock()
 count++
 if name != "" && text != "" {
  kakikomis = append(
   kakikomis, Kakikomi{time.Now().Format("2006-01-02 15:04"), name, text})
 }
 return count, kakikomis
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
 if r.URL.Path != "/" {
  http.NotFound(w, r)
  return
 }
 c, k := updateVars(r.FormValue("name"), r.FormValue("text"))
 tpl := template.Must(template.ParseFiles("index.tpl"))
 m := Bindings{
  Counter: fmt.Sprintf("%dth", c),
  Bbs:     k,
 }
 tpl.Execute(w, m)
}

func main() {
 http.HandleFunc("/", indexHandler)
 port := os.Getenv("PORT")
 if port == "" {
  port = "8080"
  log.Printf("Default to port %s", port)
 }
 log.Printf("Listening on port %s", port)
 if err := http.ListenAndServe(":"+port, nil); err != nil {
  log.Fatal(err)
 }
}
カウンタと掲示板の書き込みはグローバル変数として持つ。これだとプログラムが再起動するとデータが消えるし、複数インスタンスが立ち上がると整合性が保てなくなるが、遊びなのでヨシ!
環境変数PORT以外に特にApp Engine特有の機能は使っていないのでローカルマシンで動く。
go run main.go
localhost:8080にアクセスするとテストができるので便利。
ローカルマシンで動くのが確認できたらデプロイする。
 gcloud app deploy
無事、App Engine上で動くのが確認できた。よかった。

Google App Engine (スタンダード環境) のQuickstartをした


Quickstart for Go 1.12+ in the App Engine Standard Environmentのチュートリアルを試したのでその時のメモ。

まず、Cloud SDKとインストールする。
このページに従ってtar.gzファイルをダウンロードして展開。
省略可と書いてるけど以下のコマンドを実行。
% ./google-cloud-sdk/install.sh
# 統計情報を送信するか聞かれる
Y
# 続行するか聞かれる
Y
# 続行するか聞かれる
Y
# rc fileの場所を聞かれる
/Users/zick/.zshrc
.zshrcを確認すると末尾に以下の行が追加されていた
# The next line updates PATH for the Google Cloud SDK.
if [ -f '/Users/zick/prog/google-cloud-sdk/path.zsh.inc' ]; then . '/Users/zick/prog/google-cloud-sdk/path.zsh.inc'; fi

# The next line enables shell command completion for gcloud.
if [ -f '/Users/zick/prog/google-cloud-sdk/completion.zsh.inc' ]; then . '/Users/zick/prog/google-cloud-sdk/completion.zsh.inc'; fi
便利なことにTABキーでの補完も効くようになった。
省略可と書いてるけど次のコマンドも実行。
% ./google-cloud-sdk/bin/gcloud init
# ログインするか聞かれる
Y
# ブラウザが開くのでGoogleアカウントにログイン
# なんかチュートリアルと違う気がするけど、
# プロジェクトを選べと聞かれる。
# 何故か作成済みのプロジェクトがあったが、
# 新規プロジェクトを作成
2
# プロジェクト名を聞かれるのでテキトーに入力
hogehoge
チュートリアルでは次に gcloud projects create コマンドを実行すると書いてあるけど、多分既にプロジェクトが作られてしまったので省略。
プロジェクトが作られたか確認する。
% gcloud projects describe hogehoge
無事作られたことを確認。最初からあったプロジェクトも確認してみたら、2011-10-24に作られていた。学生のときになにか試したのかもしれないが全く覚えていない。見なかったことにする。
次はApp Engine appを作成する。
% gcloud app create --project=hogehoge
# regionを聞かれるので asia-northeast1 (東京) を選択
# ちなみに asia-northeast2 は大阪
2
次はbillingの設定をしろと書いてあるがお金の話は面倒なので後回しにした(そのせいで後で失敗する)。
gitをインストールしろと書いてあるがすでに入っているので無視。
Go用のApp Engine extensionをインストールするために以下のコマンドを実行
% gcloud components install app-engine-go
# 続行するか聞かれる
Y
「あんたがGoに詳しくてすでにインストールしてるのを仮定してるよ」と書いてある。Goに詳しくないのはさておき、Goをインストールしたのは大昔のためバージョンを確認。
% go version
go version go1.0.3
これはひどい。Goの公式サイトからmacOS用のpkgファイルをダウンロードしてgo1.13.7をインストールした。
次はサンプルプログラムをダウンロードしてデプロイ。
% git clone https://github.com/GoogleCloudPlatform/golang-samples.git
% cd golang-samples/appengine/go11x/helloworld
% gcloud app deploy
Updating service [default]...failed.
ERROR: (gcloud.app.deploy) Error Response: [7] Access Not Configured. Cloud Build has not been used in project hogehoge before or it is disabled.
どうせbillingの設定してないからだろうなと思ったらCloud Buildが無効だと言われる。なんやそれと思ったが、Cloud Buildを有効にするにはbillingの設定が必要という話のようだ。しぶしぶクレジットカードなどを登録して再度 gcloud app deploy を実行。今度は成功。Cloud Buildは自動で有効化された。
最後に起動したappをブラウザで確認。
% gcloud app browse
# ブラウザが起動する
あなたの予想に反して、 Hello, World! が表示された。

2020年1月12日日曜日

bloggerソースコードの貼り付け

前回の投稿で気づいたが、どうもbloggerは標準ではソースコードをきれいに貼り付ける機能がないらしい。どうやらGoogle Code Prettifyというのを使うと簡単にきれいなソースコードを貼り付けることができるようなのでその方法を記す。

0. Google Code PrettifyのREADMEをしっかり読む
1. bloggerのサイドバーから [レイアウト] をクリック
2. [Cross-Column] の下にある [ガジェットを追加] をクリック
3. [HTML/JavaScript] をクリック
4. [タイトル] に code-prettify と入力、 [コンテンツ] にREADMEに従ってscriptタグを貼り付ける
<script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js?lang=lisp">
</script>
5. bloggerの投稿でREADMEに従って以下のようなタグを書く
<pre class="prettyprint">
<marquee>the language is automatically detected</marquee>
</pre>
<pre class="prettyprint lang-lisp">
; we can specify the language by lang-* options
(defun apply% (f a)
  (if (eq (car f) '%e%)
      (progn% (cadr% (cdr f))
              (mcon% (pairlis% (cadr% f) a) (cddr% (cdr f))) ())
      (if (eq (car f) '%s%)
          (funcall (cdr f) a)
          f)))
</pre>
「きれいに」と言っておきながらいきなり横にはみ出している気もするが見なかったことにする。 LISPのような一部の言語は標準ではサポートされておらず、run_prettify.jsを読み込む際に
?lang=lisp&lang=ml
のようにextensionを指定する必要がある。 やたらと「READMEに従って」と書いたが、比較的最近、run_prettify.jsの配信元が rawgit.com から jsdelivr.net に変更したらしい。 こういった情報を見逃さないためにも最新の公式の情報を確認すべきだろう。

2020年1月1日水曜日

このブログの作り方

このブログをどのように作ったか忘れないために自己言及的なメモを残しておく。

(I. bloggerの設定)
1. http://blogger.com/ を開く
2. [ブログを作成] をクリック
3. タイトルなどを入力。ドメインについて聞かれるが一旦無視
4. [設定] -> [ブログのサードパーティ URL を設定] をクリック
5. ドメイン blog.zick.run を入力して [保存] をクリック
6. 2個のCNAMEが記載されたエラーメッセージが表示されるのでコピーする
  - blog ghs.google.com
  - (ランダムな文字列1) (ランダムな文字列2).dv.googlehosted.com
(II. VALUE-DOMAINの設定)
7. VALUE-DOMAINにログイン
8. [ドメイン] -> [ドメインの設定操作] -> [DNS/URL] をクリック
9. bloggerのメッセージを使って以下の2行を追加(行末にはドット
cname blog ghs.google.com. 
cname  (ランダムな文字列1) (ランダムな文字列2).dv.googlehosted.com.
(III. bloggerの設定)
10. DNSの設定が反映されるまで30分~1時間程度待つ
11. bloggerの [保存] ボタンを改めて押す (ページを閉じた場合は手順4と5をもう一度やる)

しばらくは blog.zick.run にアクセスしても ERR_CONNECTION_CLOSED が返ってきたが、時間が経てば無事にブログが表示された。よかった。

code-prettify