今日はGo言語で簡単なwebアプリケーションを作成します。webとdbにコンテナを分けて、webページにdbから取得したデータを表示したいと思います。以下のように、マイクロサービスを意識して、コンテナ間通信をhttpリクエストで行いたいと思います。

以下の流れで作成します。
- dbを作成する
- webを作成する
- ソースコードを作成する
- main.goを作成する
- layout.htmlを作成する
- index.htmlを作成する
- Dockerfileを作成する
- ソースコードを作成する
- コンテナを実行する
- コンテナイメージの作成
- Docker Composeファイルを作成する
- コンテナを実行する
dbを作成する
dbコンテナは以下の記事で作成したものを利用します。ここで作成したのは、httpリクエストを行うとjsonファイルを読み込んで出力するコンテナです。本来であればデータベースも作成しますが、今回はhttpリクエストでやり取りするのをメインにしているので、データはファイルにします。データベースを使うのはまた記事書きたいと思います。
webを作成する
ソースコードを作成する
main.goを作成する
ポート8080で受け付けると、dbコンテナから取得したjsonコードをhtml形式で表示するwebのソースコード(main.go)を以下に示します。
package main import ( "encoding/json" "fmt" "log" "net/http" "github.com/gorilla/mux" "io/ioutil" "os" "html/template" ) type category struct { Categories []struct{ Name string `json:"name"` }`json:"categories"` } func main() { r := mux.NewRouter() r.Handle("/", index) //サーバー起動 if err := http.ListenAndServe(":8080", r); err != nil { log.Fatal("ListenAndServe:", nil) } } var index = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { url := "http://db:8082/public" req, _ := http.NewRequest("GET", url, nil) client := new(http.Client) resp, _ := client.Do(req) defer resp.Body.Close() byteArray, _ := ioutil.ReadAll(resp.Body) var categories category if err := json.Unmarshal(byteArray, &categories); err != nil { log.Fatal(err) os.Exit(1) } generateHTML(w, categories, "layout", "index") }) func generateHTML(writer http.ResponseWriter, data interface{}, filenames ...string) { var files []string for _, file := range filenames { files = append(files, fmt.Sprintf("templates/%s.html", file)) } templates := template.Must(template.ParseFiles(files...)) templates.ExecuteTemplate(writer, "layout", data) }
index = http.HandlerFunc
では、dbコンテナから取得したjsonコードを構造体categoryの変数categoriesに代入し、generateHTML
関数を呼び出します。generateHTML
は引数で与えられた名前のhtmlファイル、ここではlayout.htmlとindex.htmlを使ってページを生成します。
layout.htmlを作成する
generateHTML
関数は、layout.htmlとindex.htmlを使ってページを生成します。layout.htmlは以下の通り、body以外のheaderのみを記述しています。layoutを作成することで、それぞれのhtmlのheaderを統一し、ソースコードの重複を回避できます。
{{ define "layout" }} <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Test</title> </head> <body> {{ template "content" . }} </body> </html> {{ end }}
index.htmlを作成する
このlayoutを使ったindex.htmlが以下になります。{{ define "content" }}
と{{ end }}
で囲んだhtml文がlayoutの{{ template "content" . }}
、つまりはページの本文に記述されることになります。
{{ define "content" }} <center> {{ range .Categories }} {{ .Name }}<br> {{ end }} </center> {{ end }}
Dockerfileを作成する
コンテナイメージを作成するのに必要なDockerfileを作成します。ビルド用のディレクトリ構造は以下の通りです。
$ tree web web ├── Dockerfile └── src ├── main.go └── templates ├── index.html └── layout.html
src配下に作成したソースコードを配置します。このディレクトリ構造に合わせて作成したDockerfileが以下になります。
FROM golang:latest # コンテナ作業ディレクトリの変更 WORKDIR /go/src/web # ホストOSの ./src の中身を作業ディレクトリにコピー COPY ./src . RUN go get -u github.com/gorilla/mux RUN go build -o web # ウェブアプリケーション実行コマンドの実行 CMD ["./web"]
コンテナを実行する
コンテナイメージを作成する
上記で作成したwebとdbのコンテナイメージを以下のコマンドで作成します。
$ docker build -t web:latest web/ $ docker build -t db:latest db/
Docker Composeファイルを作成する
webとdbのコンテナイメージからコンテナを実行しますが、webとdb間は通信できる必要があります。そこでdocker-compose.yamlファイルを作成します。以下にその内容を示します。
version: '3' services: db: container_name: db image: db:latest ports: - "8082:8082" web: container_name: web image: web:latest depends_on: - db links: - db ports: - "8080:8080"
docker-compose.yamlはservicesブロックの中に実行するコンテナ毎にブロックを作成します。今回はwebとdbコンテナだけなので、webブロックとdbブロックを作成しました。
webのmain.goにurl := "http://db:8082/public"
とurlを直接指定しました。従ってwebコンテナはホスト名dbをdbコンテナに名前解決できる必要があります。それを定義しているのがlinks
です。
コンテナを実行する
docker-compose.yamlファイルからコンテナを実行するにはdocker-composeコマンドを利用します。docker-compose.yamlを置いたディレクトリで以下のコマンドを実行します。
$ docker-compose up -d Creating db ... done Creating web ... done $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES da224d318f33 web:latest "./web" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp web b245c4eb8320 db:latest "./searcher" About a minute ago Up About a minute 0.0.0.0:8082->8082/tcp db
docker-compose.yamlで定義した内容で実行されていることがわかります。ブラウザでhttp://localhost:8080/
にアクセスしてみましょう。jsonの中身が表示されていることがわかります。

以上です!なんとなくGo言語がわかってきたようなきてないような。。ちゃんと仕組みから理解した方が良さそうなので、またそういう記事も書きたいと思います!ソースコードが汚いのは許してください。。。
コメント
コメントは停止中です。