こんにちは。てぃろです。
タイトルの通り、Go言語をはじめていこうと思います。
が、ほとんど触ったことがないので、順にどのようにして学んでいけば良さそうか?導入をどうしていったか?ということの備忘を残していこうと思います。
記事の内容が長くなったので、前後編にすることにしました。今回は前編としてGo言語の基本をどこで学ぶのがよいか?Go言語でCloud Functionsを実行するにはどうすればよいか?をメモしていきます。
目標はYoutubeの動画情報の自動編集ができること
そもそもこれをやる目的はGo言語とGoogle Cloud Functionsに慣れることなんですが、言語もクラウドも所詮手段でしかないので、つくるものとしてのテーマが必要です。
そこで、最近ちょっとしたきっかけがあってYoutube動画の管理作業をなんとか少なくできないかな?と考えていたので、これを採用することにします。
つまり、今回つくるものはGoogle Cloud Functionsを実行環境としてGoでYoutube Data APIを実行し、Youtube動画のタイトルなどを自動設定するというアプリです。
動画はPS5から上げたゲーム動画を想定しています。PS5で選べるアップロード先はYoutubeかTwitterなので、Youtubeに直接動画を上げたタイミングで動画の設定を勝手に書き換えることができるようにします。動画の自動解析は今回は対象外としますので、動画内容に応じた設定の自動カスタマイズはしません。
理解しよう。作ろう。Goアプリ
ここからは順に知識ゼロからGoを学びながら、アプリを作っていった経緯をそのまま載せていきます。
まずGoの書き方を知る
幸いなことにGoをよく知る友人が近くにいたので勉強するなら何がいい?と聞いてみるとA Tour of Goというサイトがよいというのでこれをまずやってみました。
これはブラウザ上でGoを実際に書いて実行することができるGoのチュートリアルです。変数の使い方など結構細かい内容を学ぶことができます。これを一通りやりきることでGoの書き方が体験を通しておおよそわかるようになります。
ローカルで動かしてみよう
次に、実際にgoをインストールして使ってみます。これにはもちろん公式サイトへ行きましょう。
このサイトのDownloadから自分の環境にあったバイナリをダウンロードしてインストールしていきましょう。
インストールが終わったら、go version
とターミナルで打ってみてバージョン情報が返ってくればOK。
続けてサンプルコードを動かしましょう。こんなサンプルをmain.go
という名前で保存してみます。
package main
import "fmt"
func main() {
fmt.Println("Hello, 世界")
}
更にターミナルを開いて、以下のコマンドを実行しましょう。
go run main.go
これで直接Goの実行ができます。
明示的にコンパイルをする場合には、以下のようにしましょう。
go build main.go
./main
MacやLinuxだと上記のようにmain
という名前でバイナリができます。Windowsで実行すると`main.exe`でできます。
go run
は実行のテストで使うイメージですかね。普通はコンパイルを事前にしておくのだと思います。実行のたびにコンパイルしてたら遅くて使えないですからね。
Cloud Functions を動かしてみよう
ここまででとりあえずGo言語の動かし方や書き方がわかってきました。具体的にサービスを運用しようと考えると、クラウド環境にデプロイするのが近道です。
そこで、今回はCloud Functionsを使ってサービス提供することを考えます。Cloud Functionsを選んだ理由は以下の通りです
- Goが使える
- イベントドリブンでトリガーできる
- 安い
つまり必要な時に自動で安くGoが動く環境であればOKということです。GCPに慣れたかったという個人的な理由もあります。GCPに慣れるとかがないならAWS LambdaでもOKです。(むしろ個人的にはそのほうが慣れててやりやすい)
では、まずCloud Functionsで動かしていきます。これは複雑なことを書く前に、まずCloud Functionsできちんと動くところを確実に見ておきたいからです。
まずはこのクイックスタートにあるサンプルをデプロイして動かしていきましょう。
ここで注意点ですが、Cloud Functionsには第1世代と第2世代があります。最新版の第2世代は2022/6時点ではいまだプレビュー版ですので、まずは慣れること優先で第1世代でやっていきましょう。
では手順通りに、APIの有効化からCloud SDKのインストールなど進めていきましょう。ちなみにAPIの有効化にはGCPプロジェクトに請求先アカウントをリンクしておく必要がありますので、事前に実施しておきましょう。リンクしてないとAPIの有効化ができない旨のメッセージが表示されます。
次にfunctionsの中身のソースを用意します。チュートリアルではGithubのソースをクローンするように指示がありますが、今回は最小構成を自分で構築したかったので手でサンプルソースをコピーして構成します。結果は以下の通りです。
.gcloudignore
は、gcloudコマンドを使ったときに自動作成されます。deploy.bat
はデプロイコマンドを実行するためのWindows環境用のファイルです。
デプロイコマンドは、チュートリアルにも書いてありますが、そのままでは以下のようなエラーが起きて失敗してしまいます。
ERROR: (gcloud.functions.deploy) OperationError: code=3, message=Build failed: 2022/06/26 07:10:05 Unable to extract package name and imports: unable to find Go package in /workspace/serverless_function_source_code.
これはおそらくデプロイ元のソースが見つからなかったからだと思われます。そこでコマンドの中で--source
オプションをつけることによって、デプロイするソースを指定してあげます。結果、コマンドは以下のようになります。
gcloud functions deploy HelloGet --runtime go116 --trigger-http --allow-unauthenticated --source functions/helloworld
これでデプロイができるようになりました!上記のディレクトリの例ではgo.mod
を作りましたが、作らなくてもデプロイをすることはできます。
最後にアクセス可能であることを確認したら、次に進みましょう。
続く
前編はここまでです。
ここまででGo言語を使ったCloud functionsが使えるようになってきました。今度はCloud functionsの中身の実装と実行の条件を整えて、実際に運用可能なツールへと仕上げていきます。
後編は近日公開予定(予定は未定)です。
ここまで手を動かしてお付き合いいただいた方は、練習で使ったCloud functionsの削除をお忘れなきよう。ほとんど課金はされないはずですが、パブリックに公開されてしまっているはずなので、DDoS攻撃されたら恐ろしいことになってしまいます…!
おまけ
Cloud Functionsに慣れるためにも以下のサンプルを使って進めようとしていました。
しかし、このサンプルにはデプロイのコマンドはなく公式ドキュメントを参照するように書いてあります。公式ドキュメントにあるコマンドのエントリポイントを変更するようにして進めましたが、デプロイした関数にアクセスしても404が返ってくるということでめちゃくちゃハマりました…。
他サンプルを見てなんとなく原因はパッケージ化したことだとわかりました。Goもcloud Functionsも不慣れだったのでよくわかってなかったですが、上記サンプルはパッケージ化することによってエントリポイントとして直接helloWorldを参照させられなかったみたいです。おそらく第1世代の書き方ですかね。
モジュール化していた場合のやり方までは今回調べませんでしたが、サンプルを動かしたときの記事があるので、おまけのコラムとしてご覧いただけるとありがたいです。
手順通りに進めれば、`Hello world`を表示するところまでは問題なくできるでしょう。ここまででローカルでGoを使ったCloud Functions(をエミュレートしたもの)を動かすことができました。
この中で個人的にまだわかってないのがgo mod
とgo tidy
でした。
モジュールはパッケージの集合体で、他のアプリケーションから呼び出すことができます。ここではCloud Functionsとして実行する関数をモジュール化しておいて、これをデプロイすることでFaaSとして動作させるというような作りなのでしょう。
サンプルにあるmain.go
はあくまでローカルでモジュールを動作させるためのサンプルですかね。
モジュールに関してすごく納得できるようなドキュメントは正直なかったのですが、とりあえず公式のチュートリアルとドキュメントを見ておけば初心者的には雰囲気がつかめると思います。
初心者としてもう一つ注目すべきはgo tidy
でしょうか。PythonやJavaScript、その他の言語でもそうですが依存関係をソースコードから読み出して追いつかせるという発想がなかったので少し驚きました。普通必要なライブラリをインストールしてからソースコードを書くので順番が逆だな、と思ったのでした。
でも開発してると先にライブラリを使用するコードを書いてしまうことも多いですから、むしろ理にかなった書き方なんでしょうね。