ラズパイ Zero W（ARM v6l）Stretch Lite に Docker と docker-compose をインストールする手順【2019/09 版】

<h1>Docker on Stretch for RPi Zero W</h1>

> 🐒 <font color=blue>2019/10/30 更新</font>
> 従来通りのインストール手順でラズパイ Zero でも Docker をインストールできるようになりました！
> 
> - [Issue #709: Docker-CE need updating for raspbian buster](https://github.com/docker/for-linux/issues/709#issuecomment-545349413) | Docker for Linux @ GitHub
> 
> ```bash:これでOK!
> curl https://get.docker.com | sh
> ```
>
> 🐒 <font color=blue>2020/03/22 更新</font>
> Stretch Lite では未検証なのですが Buster Lite では以下で docker-compose もインストールできました。やはりコンテナよりは若干起動が速いので、ダメ元で試してみる価値はあると思います。
>
> ```bash:docker-compose
> apt install docker-compose
> ```
> 各種[注意点](#docker-hub-%E3%81%AE-docker-%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E5%8F%96%E3%82%8A%E4%BD%BF%E3%81%84%E6%99%82%E3%81%AE%E6%B3%A8%E6%84%8Fdocker-pull-%E6%99%82)やNG の場合は、下記 [docker-compose のインストール手順](#docker-compose-%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E6%89%8B%E9%A0%86)は変わらないので、引き続き本記事をご覧ください。

---

2019/09/29 現在、**`RPi Zero W`(`ARMv6l`) の `Stretch` に `Docker` と `docker-compose` をインストールする**手順です。（Docker=v18.06.2-ce, docker-compose=v1.25.0dev）

- Raspbian Buster に Docker を入れたいかたは[こちら](https://qiita.com/KEINOS/items/1ce933a89706d81ebfa3)。
    - [ラズパイ Zero W（ARM v6l）Buster Lite に Docker と docker-compose インストールする手順【2019/09 版】](https://qiita.com/KEINOS/items/1ce933a89706d81ebfa3) @ Qiita

> <sup>🐒 【所感】ラズパイ・ゼロで Docker を動かすの、ここまで大変だとは思いませんでした。。。<br>まとめると、これだけなのに...情報がバラバラだし、どの記事もラズパイ Zero W で動きませんでした。公式のインストール・スクリプトも「Scratch, ラズパイ・ゼロ対応」とか言いながら動かないし。全ての原因は、みんな開発が速いのでちょっとでもバージョン違うとダメだということ。この記事も適宜更新して行きたいと思います。</sup>

<h1>Raspberry Pi Zero W に Docker-CE をインストール</h1>

## Docker インストール手順

ラズパイ Zero W の型番と RaspbianOS のバージョンが同じか確認してから作業を始めることをオススメします。（記事下部の「実績環境」参照）

```shell-session:ラズパイ上での作業
$ # GPG 鍵を追加
$ curl -fsSL https://download.docker.com/linux/raspbian/gpg | sudo apt-key add -
OK

$ # ラズパイ用のカーネルヘッダの追加
$ sudo apt install raspberrypi-kernel-headers

$ # Docker 用の apt リポジトリを追加（docker.list を作成して追加）
$ sudo vi /etc/apt/sources.list.d/docker.list
...

$ # docker.list の内容確認（これを上記で記載する）
$ cat /etc/apt/sources.list.d/docker.list
deb [arch=armhf] https://download.docker.com/linux/raspbian stretch stable

$ # アップデート
$ sudo apt update
...

$ # apt でインストール可能なパッケージを確認
$ apt list -a docker-ce
...

$ # 実績のあるパッケージをインストールする（ここまで長かった。。。）
$ sudo apt-get install docker-ce=18.06.2~ce~3-0~raspbian
...
```

### Docker を触ってみる

<details><summary>Docker のバージョンを確認する</summary><div><br>

```shell-session:Dockerのバージョンを確認してみる
$ sudo docker version
Client:
 Version:           18.06.2-ce
 API version:       1.38
 Go version:        go1.10.3
 Git commit:        6d37f41
 Built:             Sun Feb 10 03:57:32 2019
 OS/Arch:           linux/arm
 Experimental:      false

Server:
 Engine:
  Version:          18.06.2-ce
  API version:      1.38 (minimum version 1.12)
  Go version:       go1.10.3
  Git commit:       6d37f41
  Built:            Sun Feb 10 03:53:01 2019
  OS/Arch:          linux/arm
  Experimental:     false
```

</div></details>

<details><summary>Hello-World を試す</summary><div><br>

```shell-session:ARMv6用のhello-world
$ # ARMv6 用の Hello-World イメージをダウンロードする
$ sudo docker pull hypriot/armhf-hello-world
...
$ # Hello-World を実行する
$ sudo docker run --rm hypriot/armhf-hello-world
Hello from Docker.
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker Hub account:
 https://hub.docker.com

For more examples and ideas, visit:
 https://docs.docker.com/userguide/
```

</div></details>

<details><summary>Alpine Linux イメージのコンテナを試してみる</summary><div><br>

```shell-session:ARMv6用Alpineイメージで試してみる
$ # ARMv6用の Alpine Linux のイメージをダウンロード
$ docker pull arm32v6/alpine
...
$ # Alpine の　OS　バージョンを表示してみる
$ docker run --rm arm32v6/alpine cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.10.2
PRETTY_NAME="Alpine Linux v3.10"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
```

</div></details>

<details><summary>sudo を付けるのが面倒な場合の設定</summary><div><br>

```shell-session
$ # 毎回コマンドに sudo を付けるのが面倒な場合
$ # ====================================
$ # docker グループに追加する
$ sudo gpasswd -a $USER docker
ユーザ pi をグループ docker に追加
$ # サービスを再起動してグループを反映させる
$ sudo systemctl restart docker
$ # 下記を実行するかログインしなおしてユーザをグループに反映させる
$ source ~/.bashrc
$
$ # 動作テスト
$ # ====================================
$ # sudo なしでバージョン表示されるか確認
$ docker version
Client:
 Version:           18.06.2-ce
 API version:       1.38
 Go version:        go1.10.3
 Git commit:        6d37f41
 Built:             Sun Feb 10 03:57:32 2019
 OS/Arch:           linux/arm
 Experimental:      false

Server:
 Engine:
  Version:          18.06.2-ce
  API version:      1.38 (minimum version 1.12)
  Go version:       go1.10.3
  Git commit:       6d37f41
  Built:            Sun Feb 10 03:53:01 2019
  OS/Arch:          linux/arm
  Experimental:     false
$ # ARM 用の Hello-World を sudo なしで実行してみる
$ docker run --rm hypriot/armhf-hello-world
Hello from Docker.
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker Hub account:
 https://hub.docker.com

For more examples and ideas, visit:
 https://docs.docker.com/userguide/

```

</div></details>

## docker-compose のインストール手順

`docker-compose` も使えます。色々試しましたが、docker in docker で[ビルド](https://qiita.com/tkyonezu/items/ceaaf41924df39254058#comment-0657347ac95c5c254064)したコンテナを使うのが楽でした。しかし、ラズパイ Zero W だとビルドに 2 時間以上かかるので、[ビルド済みのイメージをアップ](https://cloud.docker.com/u/keinos/repository/docker/keinos/docker-compose)しました。自身でビルドしたい場合は、[リポジトリの Dockerfile と Build スクリプト](https://github.com/KEINOS/Dockerfile_of_Docker-Compose_for_ARMv6l) をご覧ください。

```bash
sudo docker pull keinos/docker-compose:arm32v6
```

```shell-session:docker-composeのイメージダウンロード
$ # イメージのダウンロード
$ sudo docker pull keinos/docker-compose:arm32v6
arm32v6: Pulling from keinos/docker-compose
5308e891d68f: Pull complete 
268669cbddb5: Pull complete 
d7cb24fe6c79: Pull complete 
3281aaa24af1: Pull complete 
Digest: sha256:d62977420f60796d7c0a5fa9df50db98e28f549b48c194b43210fdc9ce82d3df
Status: Downloaded newer image for keinos/docker-compose:arm32v6

$ # 実行例（コンテナ呼び出し）
$ sudo docker run --rm keinos/docker-compose:arm32v6 version
docker-compose version 1.25.0dev, build unknown
docker-py version: 4.0.1
CPython version: 3.7.4
OpenSSL version: OpenSSL 1.1.1d  10 Sep 2019
```

`$ docker-compose` と、コマンドとして使えるようにシェル・スクリプトも用意しました。以下の２行を実行するとインストールできます。

```bash:ダウンロードのコマンド
sudo curl -L --fail https://keinos.github.io/Dockerfile_of_Docker-Compose_for_ARMv6l/run.sh -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
```

```shell-session:実行例
$ # ダウンロードとインストール
$ sudo curl -L --fail https://keinos.github.io/Dockerfile_of_Docker-Compose_for_ARMv6l/run.sh -o /usr/local/bin/docker-compose
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1710  100  1710    0     0   3912      0 --:--:-- --:--:-- --:--:--  3922

$ # アクセス権の変更（実行権限を与える）
$ sudo chmod +x /usr/local/bin/docker-compose
$ # 実行テスト
$ sudo docker-compose version
docker-compose version 1.25.0dev, build unknown
docker-py version: 4.0.1
CPython version: 3.7.4
OpenSSL version: OpenSSL 1.1.1d  10 Sep 2019
```

- [Dockerfile](https://github.com/KEINOS/Dockerfile_of_Docker-Compose_for_ARMv6l) @ GitHub
- [Docker Image](https://cloud.docker.com/u/keinos/repository/docker/keinos/docker-compose) @ Docker Hub
- 参考文献：「[Raspberry Pi用docker-composeの構築](https://qiita.com/tkyonezu/items/ceaaf41924df39254058#comment-0657347ac95c5c254064)」@ Qiita

## Docker Hub の Docker イメージ取り使い時の注意（`docker pull` 時）

> Dockerfile の `FROM` には、なるべく ARM32v6 の公式イメージを使う
>
> - https://hub.docker.com/u/arm32v6
> - 例） `$ docker pull arm32v6/alpine`

ラズパイ Zero W の Docker を使う場合、アーキテクチャ（CPU の種類）を念頭に置くことが重要と実感しました。と言うのも、**悲しいことに Docker Hub の大半のイメージがラズパイ Zero の Docker で動きません**。

`ARM` タグが付いていても動かないものも多くあります。これは、大半がラズパイ 3 などの `ARMv7` 系用にビルドされており、ラズパイ Zero W は `ARMv6` 系だからです。また、ラズパイ Zero にも `ARMv6` と `ARMv6l` の２種類があるらしく内容によっては動きません。（`v6l` の `l` は [Little Endian](https://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%B3%E3%83%87%E3%82%A3%E3%82%A2%E3%83%B3) の意味らしくメモリ上のデータの並び順に違いがあるそうです）

```bash
docker pull myname/myimage:arm  # <- ARMv6 なのに ARMv7 用がダウンロードされる
```

もちろん `ARMv6` 用、`ARMv7` 用と各々のイメージにタグを付けて提供されている場合は問題ありません。

```
$ docker pull myname/myimage:armv6  <- ARMv6用イメージ
$ docker pull myname/myimage:armv7  <- ARMv7用イメージ
```

希望するイメージが ARMv6 用に提供されているか悩んだ場合、**公式の ARMv6 専用のリポジトリがある**ので、まずはそこにベースイメージが提供されているかチェックします。

- https://hub.docker.com/u/arm32v6

問題は、他のリポジトリで **`multi-arch` イメージとして１つのタグで提供している場合**です。

```bash
docker pull myname/myimage:latest
```

「`multi-arch`」イメージとは１つのタグで複数アーキテクチャのイメージを提供するものを言います。代表的なものに `alpine:latest` があります。

基本的な仕組みとしては、`myname/myimage:latest` というイメージを作成する代わりに、`manifest` と呼ばれる JSON ファイルを作成して Docker リポジトリに設置します。

このマニフェストには、OS やアーキテクチャに対応したイメージの情報が記載されています。つまり、実イメージではなくエイリアスというか紐付けファイルといった感じのものです。

```json:AMD64、ARMv6、ARMv7に対応したマニフェストの例
{
   "schemaVersion": 2,
   "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
   "manifests": [
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 529,
         "digest": "sha256:2335c729b8a6764c52a3cbfe43d1450d5e782638c986d237ffc30ca33881c3e3",
         "platform": {
            "architecture": "amd64",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 529,
         "digest": "sha256:956077792b12d730494cd54eacb497b86ef732434dda6cb67d300c283c19322b",
         "platform": {
            "architecture": "arm",
            "os": "linux",
            "variant": "v6"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 529,
         "digest": "sha256:acc143d95320fe0b572ca8677e18aea3e9306b3b9f94a26fc1e084ec4361f104",
         "platform": {
            "architecture": "arm",
            "os": "linux",
            "variant": "v7"
         }
      }
   ]
}
```

上記マニフェストは `AMD64` `ARMv6` `ARMv7` の３つのイメージに対応した例で、これらのイメージは同じリポジトリにあらかじめ置いて（`docker push` して）おく必要があります。各々イメージのタグ名はわかりやすければ何でも構いません。イメージのハッシュ値（上記 JSON の `digest` の値）で判断するためです。

`$ docker pull myname/myimage:latest` すると、まず上記 `manifest` がダウンロードされ、対応したアーキテクチャのイメージをダウンロードします。

通常は、ローカルのアーキテクチャにあわせてダウンロードしますが、ARM の場合に不具合が発生します。

2019/09/26 現在、ARM の Docker は上記 JSON の `variant` の値、つまり**CPU のバージョン情報を加味しない**ため、`linux/arm` が２つヒットしてしまいます。そして、仕様として複数ヒットする場合、最初にヒットした ARM アーキテクチャのイメージをダウンロードしてしまうのです。

- Issue #34875 [manifest list (multiarch) picks wrong arch on ARMv7](https://github.com/moby/moby/issues/34875) Docker 公式 @ GitHub

対処法として「頻度の多いバージョンを上に記載する」しか方法がなく、偶然にもバージョンが合えばいいのですが、ラズパイ Zero の場合は期待薄でしょう。

そのため、ラズパイ3 にも Zero にも対象にした `multi-arch` イメージを使った汎用 `Dockerfile` を作成することは現時点で難しそうです。`Dockerfile_arm32v6` など別途提供した方が無難そうです。


## 実績環境

- SSH 作業側情報
    - macOS Mojave (OSX 10.14.6)
    - 接続: Wi-Fi + SSH で作業
- ラズパイ側情報
    - 型番: `Model: Zero W` `Hardware: BCM2835` `Revision: 9000c1`
    - 対象 OS: Raspbian 9 (Stretch Lite)
        - イメージ: 2019-04-08-raspbian-stretch-lite.zip [[ダウンロード・ページ](https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2019-04-09/) @ 公式]
        - SD 書き込みアプリ: [balenaEtcher](https://www.balena.io/etcher/)（旧 Etcher, ラズパイ推奨）
        - SD 初期化アプリ: [SD Card Formatter](https://www.sdcard.org/jp/downloads/formatter/)（SD Association 公式, [Mojave 動作確認済み](https://qiita.com/KEINOS/items/b1762a00169ecdfed8c0)）
    - 事前作業:
        - SD カードは以下の手順で初期化
            - 「[RaspberryPi Zero HW のブートイメージ作成（Wi-Fi 設定済み SSH 有効済み）](https://qiita.com/KEINOS/items/43394e4bd3c8fcfb5ee8)」@ Qiita
        - 設定: `ssh` 公開鍵接続と `sudo raspi-config` でローケル類を日本語/日本に設定済み。
        - アップデート: `sudo apt update` `sudo apt upgrade` のみ。
        - ファームウェアアップデート: なし（`sudo rpi-update` してません）
        - 追加インストール: `sudo apt install vim` のみ。

### バージョン情報

```shell-session:インストールされたDockerのバージョン
$ sudo docker version
Client:
 Version:           18.06.2-ce
 API version:       1.38
 Go version:        go1.10.3
 Git commit:        6d37f41
 Built:             Sun Feb 10 03:57:32 2019
 OS/Arch:           linux/arm
 Experimental:      false

Server:
 Engine:
  Version:          18.06.2-ce
  API version:      1.38 (minimum version 1.12)
  Go version:       go1.10.3
  Git commit:       6d37f41
  Built:            Sun Feb 10 03:53:01 2019
  OS/Arch:          linux/arm
  Experimental:     false
```
```shell-session:ハードウェア情報
$ # ファームウェア
$ vcgencmd version
Aug 15 2019 12:06:42 
Copyright (c) 2012 Broadcom
version 0e6daa5106dd4164474616408e0dc24f997ffcf3 (clean) (release) (start)

$ # ハードウェア
$ cat /proc/cpuinfo
processor	: 0
model name	: ARMv6-compatible processor rev 7 (v6l)
BogoMIPS	: 697.95
Features	: half thumb fastmult vfp edsp java tls 
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x0
CPU part	: 0xb76
CPU revision	: 7

Hardware	: BCM2835
Revision	: 9000c1
Serial		: 00000000xxxxxxxx

$ rev=$(awk '/^Revision/ { print $3 }' /proc/cpuinfo) && curl -L perturb.org/rpi?rev=$rev
Revision : 9000c1
Model    : Zero W
Memory   : 512 MB
Overvolt : No
Released : Q1 2017
Notes    : (Mfg by Sony)
```