実際に環境構築を行い、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意外にもたくさんあります。参考として次の記事を貼っておきます。
-
【2020年】就職・転職に有利なサーバーサイドのプログラミング言語を紹介!そもそもサーバーサイドとは何か、勉強方法も解説します
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
ソースをいじって保存すると自動でコンパイルと実行がおこなわれ、高速に開発できます。