GoでWebassemblyを試してくてdockerとHTTPフレームワークGinで開発環境を作りました。
フォルダ構成
. ├── Dockerfile ├── docker-compose.yaml ├── go.mod ├── go.sum ├── index.html ├── server/ │ └── main.go └── webassembly/ ├── js/ └── main.go
Dockerfile
FROM golang:1.21-bullseye COPY ./ /app WORKDIR /app RUN cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" ./webassembly/js RUN cd /app/webassembly && GOARCH=wasm GOOS=js go build -o main.wasm CMD ["go","run","/app/server/main.go"]
解説
- RUN cp “$(go env GOROOT)/misc/wasm/wasm_exec.js” ./webassembly/jsコマンドでGoに標準で入っているWebAssemblyバインディングスクリプトを所定のパスにコピーします。これはWebAssemblyの実行に必要になります。
- RUN cd /app/webassembly && GOARCH=wasm GOOS=js go build -o main.wasmコマンドでWebAssemblyのコードをbuildしてmain.wasmファイルを作成します。これは後ほどAssemblyバインディングスクリプトと合わせてフロントで読み込ませます。
docker-compose.yaml
version: '3' services: go-webassembly-app: build: context: ./ volumes: - ./server:/app/server - ./go.mod:/app/go.mod - ./go.sum:/app/go.sum - ./index.html:/app/index.html - webassembly_go_module_data:/go ports: # ローカルのポート被らないように # 4321は適当 - "4321:80" volumes: webassembly_go_module_data:
解説
- volumeにDockerfile内でコピーしたバインディングスクリプトとbuildしたwasmファイルを含めてしまうとホストの情報で上書きされて消えてしまうので外しています。
- ポートはローカルで3000とか4000とか3100とか他のサービスで色々使ってたので一応被らなそうなポートにしてます。
index.html
<!DOCTYPE html> <html> <head> <title>Title</title> </head> <body> <h1>WebAssembly Demo</h1> <script src="/js/wasm_exec.js"></script> <script> const go = new Go(); WebAssembly.instantiateStreaming(fetch("/wasm/main.wasm"), go.importObject) .then(result => { go.run(result.instance); }) .catch(err => { console.error(err); }); </script> </body> </html>
解説
バインディングスクリプトwasm_exec.jsを読み込ませその中にあるGoコンストラクタのインスタンスにwasmファイルを読み込ませます。これでGoのコードをブラウザで実行できます。 このhtmlと読み込んでいるjs、wasmのパスはGinで指定する必要がありますが後述します
server/main.go
package main import ( "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.Static("/js", "./webassembly/js") r.Static("/wasm", "./webassembly") r.StaticFile("/", "./index.html") r.Run(":80") }
解説
必要最低限のGinのコードです。htmlだけでなくjsとwasmのファイルも指定する必要があります。
webassembly/main.go
package main import ( "fmt" "syscall/js" ) func main() { fmt.Println("fmt Println!") js.Global().Get("console").Call("log", "js.Global Call!") select {} }
- ブラウザのコンソールに適当な文字列を出力するWebAssemblyの最低限のコードです。
- fmtで出力した文字とjs.Globalから出力した文字両方がブラウザに表示されます。
終わり
とりあえず最小構成でWebAssemblyのプログラムを実行する環境を作りました。
これをベースに画像/動画処理などもっと凝ったプログラムを書いてWebAssemblyを試していきたいと思います。