Skip to content

Commit

Permalink
add qiita.md
Browse files Browse the repository at this point in the history
  • Loading branch information
po3rin committed Jun 15, 2019
1 parent cf0ace9 commit f1f7da2
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 5 deletions.
125 changes: 125 additions & 0 deletions Qiita.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# 1日で Go + WebAssembly に入門して Dockerfile の依存グラフを図にしてくれる君を作ったので、知見とハマりポイントを共有します。

こんにちはpo3rinです。Go + WebAssembly + BuildKit で Dockerfile の依存グラフを図にしてくれる誰得サイトを作ったので紹介します。その名も「Dockerfile Dependency graph」
」!

:whale: https://po3rin.github.io/dockerdot/ :whale:
(PCブラウザだけ対応してます)

<p align="center">
<img src="./static/sp.gif" width="80%">
</p>

今回はこれをどのように作ったのかとハマった点を紹介します。リポジトリはこちら!!

<img src="./static/dockerdot.png" width="460px">

## どのように作ったか

全体像はこちらになります。

<img src="./static/all.png" width="640px">

内部では Dockerfile から LLB(プロセスの依存関係グラフを定義するために使用されるバイナリ中間言語)を取得して、それをdot言語(データ記述言語)に変換しています。今回はその処理を Go + WebAssembly で書いています。

WebAssemblyの基本的な使い方に関してはこちらをご覧ください!Hello Worldから解説してくれます!!
https://github.com/golang/go/wiki/WebAssembly


もちろん内部ではBuildKitのパッケージを利用しています。
https://github.com/moby/buildkit

buildkitの内部でDockerfileをdot言語に変換する関数を記述しています。実装はBuildKitの非公開関数を使えるようにしただけです。
```go
// Docker2Dot convert dockerfile to llb Expressed in DOT language.
func Docker2Dot(df []byte) ([]byte, error)
```

Dockerfileのバイト列を渡せばそのDockerfileから生成した依存を記述したdot言語が所得できます。

Go + WebAssembly が Dockerfile を dot言語に変換したら後は JavaScript 側で viz.js を使ってグラフにしています。viz.jsは、dot言語で記述された構造からグラフを作成するためのパッケージです。下記のように使います。

```js
let viz = new Viz()
graph = document.getElementById("graph")
showGraph = (dot) => {
viz.renderSVGElement(dot).then((element)=> {
if (graph.lastChild){
graph.removeChild(graph.lastChild)
}
graph.appendChild(element)
})
}
```

これでshowGraph関数が、引数の```dot```にdot言語で記載されたLLBを渡されることで依存グラフにしてくれます。

## ハマったところ

### OSやアーキテクチャ固有の機能に依存しているパッケージは使えない。

BuildKitは内部で```http://golang.org/x/sys/unix```を使っていたので、最初、wasmのビルドに失敗しました。

<img src="./static/err.png" width="740px">

Twitterでボヤいていたところ、wasmの鬼の @syuumai さんとGoの鬼の @tenntenn さんにアドバイスいただきました。wasmをビルドするときは```GOOS=js GOARCH=wasm```なのでビルドタグでビルド対象から外されてしますようです。

Twitterでの会話はこちら
https://twitter.com/po3rin/status/1139568570239635456

よって今回は moby/buildkit の中からOSやアーキテクチャ固有の機能に依存している処理を使わないようにmoby/buildkitのコードから必要部分だけをmirrorして使っています。

### 内部でゴールーチンを読んでいるパッケージの処理をコールバック関数で呼ぶとデッドロックが起きる

こちらのコードラボで注釈されている問題に見事ハマりました。

> コールバック関数は1つのゴールーチンの中で1つずつ処理されます。そのため、コールバック関数の中で、コールバック関数によって結果を受け取るような処理があるとデッドロックが起きてしまいます。
```moby/buildkit``` では sync.ErrGroup でバリバリ並行処理が行われていたのでデッドロックが起きていました。そのため別のゴールーチンを起動して呼び出す必要がありました。

```go
func registerCallbacks() {
var cb js.Func
document := js.Global().Get("document")
element := document.Call("getElementById", "textarea")

cb = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
text := element.Get("value").String()
dockerfile := []byte(text)

// https://github.com/golang/go/issues/26382
// should wrap func with gorutine.
go func() {
dot, err := docker2dot.Docker2Dot(dockerfile)
if err != nil {
fmt.Println(err)
}
showGraph := js.Global().Get("showGraph")
showGraph.Invoke(string(dot))
}()
return nil
})

js.Global().Get("document").Call("getElementById", "button").Call("addEventListener", "click", cb)
}
```

## 初めて触った所感

最初はハマってましたが、慣れてきたらJavaScriptからGoの処理を呼ぶ簡単さに感動を覚えます。そしてGoで書いたのにデプロイが楽チンというのも気持ちよかったです。

## 今回の開発で参考にした記事

だいたいここ読んでおけば良い

go wiki: WebAssembly
https://github.com/golang/go/wiki/WebAssembly
(公式による解説)

Go 1.11: WebAssembly for the gophers
https://medium.zenika.com/go-1-11-webassembly-for-the-gophers-ae4bb8b1ee03
(syscall/jsの解説が充実している)

GoでWebAssemblyに触れよう
https://golangtokyo.github.io/codelab/go-webassembly/?index=codelab#0
(ハンズオンとして最適)
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This uses Go WebAssembly + BuildKit package.
(not support smart phone ...)

<p align="center">
<img src="./mdsrc/sp.gif" width="80%">
<img src="./static/sp.gif" width="80%">
</p>

## Go + WebAssembly
Expand All @@ -20,6 +20,9 @@ https://github.com/golang/go/wiki/WebAssembly
## DOT language
https://medium.com/@dinis.cruz/dot-language-graph-based-diagrams-c3baf4c0decc

## BuildKit
https://github.com/moby/buildkit

## Warn

dockerbot/dockerfile2llb package is almost mirror from moby/buildkit. but sygnal package is not used.
7 changes: 3 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import (

func registerCallbacks() {
var cb js.Func
document := js.Global().Get("document")
element := document.Call("getElementById", "textarea")

cb = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
document := js.Global().Get("document")
element := document.Call("getElementById", "textarea")
text := element.Get("value").String()

dockerfile := []byte(text)

// https://github.com/golang/go/issues/26382
Expand All @@ -23,7 +23,6 @@ func registerCallbacks() {
if err != nil {
fmt.Println(err)
}
fmt.Println(string(dot))
showGraph := js.Global().Get("showGraph")
showGraph.Invoke(string(dot))
}()
Expand Down
Binary file modified main.wasm
Binary file not shown.
Binary file added static/all.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/dockerdot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/err.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes

0 comments on commit f1f7da2

Please sign in to comment.