動くものを作りたい現役アプリエンジニアのブログ

Engineer Life Blog

FlutterのためにサーバーサイドをGoで実装!Echo,Docker,Docker-composeを使って開発環境を構築

FlutterのサーバーサイドでGoを使ってみたい!フレームワークは何を使えばいいのか?簡単な開発環境構築がしたい

実際に環境構築を行い、Rest APIでHollow Worldを返す仕込みを実装します。

Flutter画面上に表示するのは次回の記事にしようと思います

Flutterのバックエンドの主流はFirebaseです。しかし、すでにサーバーサイド側をGoや多言語で実装していて後からFlutterを導入することも多いと思います。

なのであえてFirebaseではなくサーバーをしっかり設置するようなイメージで実装したいと思います。

FlutterとGoの概要図

まずはFlutterとGoの関係から上記の図のフロントサイド領域に当たるのがFlutter。サーバーサイド領域がGoに当たります。

図のイメージでhttpリクエストを受けてjsonを返すような形でGoを実装します。

本来はDBも設置してそこからデータをとるのが一般的ですが、今回はHollow Worldという文言で文字列で返すだけにしようと思います。

フレームワークとしてはEchoを使います。

ポイント

  • Goはサーバーサイド領域
  • HTTPリクエスト受けてHollow Worldを返す
  • フレームワークにはEchoを使う

 

サーバーサイドにGoを選んだ理由

サーバーサイド言語はGo意外にもたくさんあります。参考として次の記事を貼っておきます。

 

GoはGoogleにより開発されている言語で正式リリースになったのが最近であったためあまり知名度はありません。

また、現状の就職や転職を考えると採用している会社が圧倒的に少ないため、初めてIT業界に就職する人には正直向いていません。

Goは現状出ているサーバーサイド言語の中でも高速で動作し、スレッド処理が容易に行えるのが特徴です。

スレッド処理は並行処理とも呼ばれます。通常の処理ではプログラムの処理は直列的に行われます。

直列処理は処理1つにつき1分かかるとしたら合計5分必要です。並列処理なら1分を並列処理するので、たとえ処理が10個あったとしても1分で終了です。

※CPUの処理遅延は考慮しません。また、1処理で1分は誇張表現なので注意してください。

このようなスレッド処理はjava言語や他の言語にもありますが、実装方法が難しく処理順の問題など解決するために様々な知識が必要です。本当は並列処理にした方がいいけど実装ハードルが高いから諦めるということが多々あります。

しかし、Goであればスレッド処理が簡単にできるという特徴があるため簡単に実装することができる大きなメリットがあります。

初めてプログラミング言語に触れて勉強する方は是非下記記事も参考にしてください

 

ポイント

  • 処理が高速
  • スレッド処理が簡単に実装できる

 

フレームワークにEchoを選んだ理由

フレームワークにはEchoを選びました。

今回はRest APIのイメージで実装します。そのため、htmlを返すように特化したフレームワークよりもRest APIが簡単に実装できるフレームワークを選択しました。

また、公式のドキュメントをみると全て英語ではありますが非常にわかりやすく記載されていて、量も豊富です。

ポイント

  • Rest APIが簡単に実装できる
  • ドキュメントが豊富でわかりやすい

 

開発環境の構築

今回はGoの開発環境の構築を行います。

通常開発環境は自分のPC上にインストールした上で実行環境を作りますが、今回はDockerを使いコンテナ内に環境を作っていきます

Docker自体はサーバーに置くのではなく、ローカルPC上に作っていきます。

Dockerインストール

mac 環境ではdockerとdocker-composeがセットになったDocker for macをインストールすればどちらも同時に取得できます。

 

インストールが完了したら下記コマンドを打てるか確認しましょう

docker --version
docker-compose version

開発環境をDockerでコンテナ化することで、AWSなどのクラウドサービスに展開しやすくなります

クラウドサービスへのデプロイ方法は別の記事でいつか書きます。

 

開発環境の構築

開発環境として下記の点を満たすように構築します。

ポイント

  • ローカルPC(ホストPC)には開発環境依存のものをあまり入れたくない
  • 将来的にクラウド環境へのデプロイを簡単にしたい
  • 開発メンバーが簡単に環境構築できるようにする
  • ホットリロードを使って手動コンパイルなしで実行する

 

まずは今回のローカルPCの配置

好きなディレクトリ
┣docker-compose.yml
┣Dockerfile
┗src
  ┣fresh.conf
  ┗server.go

Dockerfile

Dockerにコンテナを作るための設計図のようなものです

Docker上入れるOSやその中で打つコマンドなども設定できます。

#Dockerfile
FROM golang:1.14-alpine

RUN apk update && apk add -U git

WORKDIR /go/src

COPY ./src /go/src

#ここが何故か実行できない
#RUN go get -u github.com/labstack/echo/...
#ここが何故か実行できない
#RUN go get github.com/pilu/fresh

FROMではLinuxの中でも非常に軽量なalpineを使います。goのバージョンは1.13以上ならOK

RUNはコマンドを打つための命令です。apk update などで環境のアップデートおをこなっています。

WORKDIR はコマンドラインなどに入る際に最初に表示されるカレントディレクトリを指定するもの。以後の実行はWORKDIRで指定したディレクトリから入力される

COPYはローカルPC(ホストPC)./src を コンテナなの/go/srcにコピーすると言う意味です。後ほど説明するGoのメイン処理を入れます。左から右に入れると覚えましょう

#RUN go get -u github.com/labstack/echo/... 
#RUN go get github.com/pilu/fresh

この2つはフレームワークEchoとホットリロードのためのダウンロードなのですが、何故かうまく実行できませんでした。そのため後ほどホストPCからコンテナ内に直接コマンドを打ちます。

 

docker-compose.yml

コンテナをweb用、DB用、検索エンジン用など複数個を1システムのサーバーサイドに立てることが多いです。

Dockerfileは1つのサーバーに対して1つのシステムが基本です。

そんな時Dockerfileを複数分作り同時に実行できればコンテナごとにコマンドを叩く必要がなくなります。

そこでdocker-composeを使います。

docker-compseには複数のDockerfileを指定することができ、指定した順番にDockerfileを実行してくれます。

また、ホストPCとコンテナ間にシンボリックリンクを張る設定もすることができ、開発環境の構築に便利です。

#docker-compose.yml
version: '3'
services:
  app:
    build: .
    ports:
      - 8080:8080
    volumes:
      - ./src:/go/src
      
    tty: true

ymlファイルなので、インデントに注意してください。

app:は1つのサーバ上に乗せる1コンテナの名前です。

build ではdockerfileの場所を示しています。

ports:はポートマッピングと呼ばれホストコンピュータ上で受けたポートをコンテナの8080に飛ばします。ローカル環境で開発する場合ブラウザにhttp://localhost/ と打ちますがその時ホストコンピュータに来たリクエストをコンテナの8080に自動的に投げてくれます。

volumes:はホストPCとコンテナとのシンボリックリンクを張ると言う意味です。ここは主に開発ソースを配置した場所を指定し、コンテナ上のファイルとホストPC上のファイルがリアルタイムに相互更新されるようになります。

tty: true コンテナを起動させ続ける設定です。コマンドで立ち上げた際すぐにコンテナ環境が終了することがあります。これを防ぐためのものです。

特に重要なのはvolumesです。

ホストPCでvisual studio codeを使って開発していくため更新したソースを自動でコンテナに同期してくれます。

 

fresh.conf

fresh.confはGoを開発する時にソースファイルの変更をしてから、コンパイルして実行という作業をソースファイルを保存した段階で自動でやってくれる便利なツールです。

root:              .
tmp_path:          ./tmp
build_name:        runner-build
build_log:         runner-build-errors.log
valid_ext:         .go
no_rebuild_ext:    .tpl, .tmpl, .html
ignored:           golang.org, github.com, assets, tmp
build_delay:       600
colors:            1
log_color_main:    cyan
log_color_build:   yellow
log_color_runner:  green
log_color_watcher: magenta
log_color_app:

ここで重要なのはignoredにgolang.org, github.comに設定すること。

fresh.confは同じディレクトリのソースファイルを監視しています。この時ライブラリなどの大量のファイルがあるディレクトリを読み込むとエラーがでます。

ignoreは日本語では「除外」という意味で監視するディレクトリを指定すれば監視対象外にすることができます。

golango.org,github.comは後ほどフレームワークのEchoをインストールする際にダウンロードされます。

server.go

Goのソースファイルです。main()処理があるのでまずはこのファイルがキックされるように設定します。

//server.go
package main

import (
    "net/http"
    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hollow World")
    })
    e.Logger.Fatal(e.Start(":8080"))
}

中身は単純にHollow Worldを返すだけです。

 

terminalにコマンドを打つ

docker-compose.ymlがあるディレクトリに移動。(ホストPC)

docker-compose up -d
docker-compose exec app go get -u github.com/labstack/echo/...
docker-compose exec app go get github.com/pilu/fresh

上から順番に打ちます。

先ほどDockerfile上で打てなかったRUNコマンドをここで打っています。

好きなディレクトリ 
 ┣docker-compose.yml
 ┣Dockerfile
 ┗src
   ┣github.com
   ┣golang.org
   ┣fresh.conf
   ┗server.go

全て完了すると上記のディレクトリ状態となります。

下記のようなエラーが出る場合

Creating network "docker_golang_echo_default" with the default driver
Creating docker_golang_echo_app_1 ... error

ERROR: for docker_golang_echo_app_1  Cannot start service app: failed to create OCI runtime console socket: mkdir /tmp/pty111943720: permission denied: unknown

ERROR: for app  Cannot start service app: failed to create OCI runtime console socket: mkdir /tmp/pty111943720: permission denied: unknown
ERROR: Encountered errors while bringing up the project.

こちらはMacの/tmp/のパーミッションエラーによるものです。

sudo chmod 777 /tmp/

パーミッションを変更してあげれば問題なく実行できます。

 

ブラウザで実行する

お好きなブラウザでhttp://localhostを実行しましょう。

 

コンテナ環境を終了するとき

docker-compose down

terminalなどで上記コマンドを打てばコンテナ環境を終了できます。

特にオプションを付けなければホストPCのシンボリックリンク元やイメージを消すことはありません。

 

サーバーサイドの実装方法

開発用のテキストエディタはなんでもOK

私はVisual Studio Codeを使っています。AtomとかでもOK

まずはdocker-compose downをした状態を前提とします。

docker-compose up -d

コンテナを立ちあげる。

docker-compose exec app go get github.com/pilu/fresh

ホットリロード用のライブラリをgo getします。コンテナを終了させるとpathが通らなくなるため、毎回go getする必要がありますRUNコマンドで正常に実行できれば手順が不要になりますが、現状うまくいきませんでした。

docker-compose exec app fresh -c fresh.conf

freshコマンドでホットリロードしながらGoを実行します。

このコマンドを実行した後はそのままにしてください。ctrl+cなどで終了するとホットリロードしなくなります。

この段階でhttp://localhostが実行できます。

Goのファイルをいじって保存してみましょう。自分でコンパイルすることなく変更が適用されます!

13:13:43 app         | 
   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.1.16
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:8080
13:15:26 watcher     | sending event "./server.go": MODIFY|ATTRIB
13:15:26 main        | receiving first event "./server.go": MODIFY|ATTRIB
13:15:26 main        | sleeping for 600 milliseconds
13:15:26 watcher     | sending event "./server.go": MODIFY
13:15:27 main        | flushing events
13:15:27 main        | receiving event "./server.go": MODIFY
13:15:27 main        | Started! (8 Goroutines)
13:15:27 main        | remove tmp/runner-build-errors.log: no such file or directory
13:15:27 build       | Building...
13:15:29 runner      | Running...
13:15:29 runner      | Killing PID 386
13:15:29 main        | --------------------
13:15:29 main        | Waiting (loop 4)...
13:15:29 app         | 
   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.1.16
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
13:15:29 app         | ⇨ http server started on [::]:8080

ソースをいじって保存すると自動でコンパイルと実行がおこなわれ、高速に開発できます。