Docker勉強ノート 〜 TinyGoを使うまで

ESP32やRP2040などの開発環境をDockerで構築する，あるいは ubuntu 等で小規模なツールを作成する，という目標で勉強ノートを作成します。古い mac 上で試しています。

この記事では題名の通り TinyGo を使うまでとしています。

参考

https://qiita.com/TaaaZyyy/items/4ecf21f23e6730faf696

https://docs.docker.jp/engine/reference/run.html

https://docs.docker.com/reference/

```bash
$ docker --version
Docker version 20.10.21, build baeda1f
$ docker compose version
Docker Compose version v2.13.0
```

# とにかく ubuntu を起動する

```bash
$ docker run -it ubuntu bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
37aaf24cf781: Pull complete 
Digest: sha256:9b8dec3bf938bc80fbe758d856e96fdfab5f56c39d44b0cff351e847bb1b01ea
Status: Downloaded newer image for ubuntu:latest
root@a01ea690ce9f:/# lsb_release -a
bash: lsb_release: command not found
root@a01ea690ce9f:/# cat /etc/os-release 
PRETTY_NAME="Ubuntu 22.04.3 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.3 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
root@a01ea690ce9f:/# 
exit
$
```

**イメージ** `ubuntu` がダウンロードされ，**コンテナ** を起動し，ついでに `bash` を実行して終了しています。`-it` は標準入力と結びつけます。

このままでもかなり使えますが，とりあえずここではコンテナを消します。IDが表示されているのでそれを指定します。

```bash
$ docker rm -f a01ea690ce9f
a01ea690ce9f
$
```

# ホストとコンテナで作業ディレクトリを共有する ~ バインド マウント ~

コンテナでファイルを作成してホストで確認してみます。

```bash
$ docker run -it --mount type=bind,src="$(pwd)",target=/work --name my_container ubuntu bash # 新しいコンテナを作成・起動
root@92c6098b81bf:/# ls
bin   dev  home  lib32  libx32  mnt  proc  run   srv  tmp  var
boot  etc  lib   lib64  media   opt  root  sbin  sys  usr  work
root@92c6098b81bf:/# cd work
root@92c6098b81bf:/work# echo "Hello from docker" > message.txt
root@92c6098b81bf:/work# exit
exit
$ ls
message.txt
$ cat message.txt 
Hello from docker
$
```

ホストのカレントディレクトリを，コンテナ内 `/work` にマウントしてアクセスしています。
次はホスト側でファイルを更新しコンテナ内で確認します。

```bash
$ echo "reply from host" >> message.txt # ホスト作業
$ docker start -i my_container
root@92c6098b81bf:/# cd work
root@92c6098b81bf:/work# ls
message.txt
root@92c6098b81bf:/work# cat message.txt 
Hello from docker
reply from host
root@92c6098b81bf:/work# 
```

`docker start -i my_container` でコンテナを再開させています。

ここまでくれば，当初やりたかった「コンテナ内でコンパイル等の作業を行って結果をホストに残す」事はできそうですね！

# コンテナ内のコマンドを実行する

ubuntuで作ったコンテナ内にpython3をインストールし，ホストからそのpython3を実行することを考えます。

## コンテナにpython3をインストール

```bash
$ docker run -it --mount type=bind,src="$(pwd)",target=/work -w /work --name my_container ubuntu bash
root@e568cbc0287e:/# apt update && apt -y upgrade && apt install -y python3
Get:1 http://archive.ubuntu.com/ubuntu jammy InRelease [270 kB]          

# 途中省略

running python post-rtupdate hooks for python3.10...
Processing triggers for libc-bin (2.35-0ubuntu3.4) ...
root@9d62a0299236:/work# python3
Python 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>    # [Ctrl] + [D] で抜ける
root@9d62a0299236:/work# 
exit
$
```

コンテナ作成時，`-w`オプションで作業ディレクトリをコンテナ内の `/work` に指定しています。

## ホストから呼び出してみる

```bash
$ docker restart my_container
my_container
$ docker exec -it my_container python3
Python 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>   # [Ctrl]+[D] で抜ける
$ echo "print('hello')" >hello.py
$ docker exec -it my_container python3 hello.py
hello
$
```

コンテナ内で python3 実行時 `/work/` にいますので，そこで見えるホスト側の `hello.py` を読み込んで実行されています。

# `--rm` オプションでコンテナを残さずに起動する

コンパイルだけ行って結果を残してくれれば，コンテナを保存する必要はないはずです。コンテナを残さずに起動するテストを行ってみます。

```bash
$ docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
$ docker run -i --rm ubuntu echo hello world
hello world
$ docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
$
```

表示だけで良いので `-i` オプションにしています。

# TinyGoをdockerで使う

ここまで得た知識で tinygo を使ってみます。有名な blink を準備します。

```main.go
package main
import (
    "machine"
    "time"
)
func main() {
    led := machine.LED
    led.Configure(machine.PinConfig{Mode: machine.PinOutput})
    for {
        led.Low()
        time.Sleep(time.Millisecond * 500)
        led.High()
        time.Sleep(time.Millisecond * 500)
    }
}
```

[BOOT]ボタンを押しながら XIAO RP2040 を接続しておきます。blink程度では `go mod init` 不要ですが一応実行しておきます。

```bash
$ docker run -it --mount type=bind,src="$(pwd)",target=/work -w /work --rm tinygo/tinygo go mod init go_xiao
Unable to find image 'tinygo/tinygo:latest' locally

# 途中省略 tinygo/tinygo のイメージがダウンロードされます

go: creating new go.mod: module go_xiao
go: to add module requirements and sums:
	go mod tidy
$ docker run -it --mount type=bind,src="$(pwd)",target=/work -w /work --rm tinygo/tinygo go mod tidy

# 何も表示がないですがこれで準備終了です

$ docker run -it --mount type=bind,src="$(pwd)",target=/work -w /work --rm tinygo/tinygo tinygo build -target=xiao-rp2040 -o=main.uf2 
$ ls
go.mod    main.go   main.uf2*
$ cp main.uf2 /Volumes/RPI-RP2/  # リセットし実行されます
```

要するに公式の方法と同じですね。

https://tinygo.org/getting-started/install/using-docker/