ARMボード色々でおうちDockerSwarmクラスタ Portainer編

何百番煎じどころか 何千番煎じか分かりませんが クラスタを作っている所(進行形)なのでそのメモを残していきます。この記事ではおうちARMボードに、OS、Docker、Portainer、オレオレDockerレジストリ(TreeScale)を導入します。

### おうちクラスタシリーズ
- 第1弾: [ハードウェア編](https://qiita.com/Domao/items/093c93ff5f3ee6f49cea) 
- 第2弾: Portainer編(ここ)
- 第3弾: Ceph編

## なんでDockerSwarmになったの?
Kubernetesやるのに 勉強するのがしんどそうなので...
自分が好きなのはインフラというよりは コードを書く方なので
デプロイ側は最低限できればいいという割り切りをしました。

#### DockerSwarmめんどくさくない?
DockerSwarmは Kubernetes用の書き方とかを覚えることなく、Docker-compose.ymlに、何個レプリカを作るか、どこにレプリカを作るかを指定するパラメータを書き足すだけでほぼそのまま使えます。個人的にはVueとReact(Vueを触った人が後にReactに流れる)通過門として有りな気がしています。

#### でもオワコンじゃない?
オワコン感は否めません。実際調べて出てくるのがほぼ数年前の資料で最近のものがなかなか出てこない... ですがサポートが完全に終わっている技術というわけではなく、Docker最新版(20.10.7 2021-06-02)時点でもSwarmは使えます。

## Portainerについて
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/1d3ec7cc-33b9-7ef8-1500-a4af191271d8.png)
以前DockerをWebUIから操作できないのかなと調べたときに見つけたOSSです。[Qiita内でも紹介記事が結構ありました。](https://qiita.com/search?q=portainer) Docker単体、DockerSwarm、Kubernetesの管理ができます。こんな感じのUIでポチポチするだけでコンテナ作ったり削除したりができます。複数ユーザーを作って、それぞれに権限を指定して複数人で管理することもできるようですがそれは確認できていません。

## 導入
### 1: OSを入れる
(実際にはボードを組んだときに入れたのですが)OSをそれぞれ入れておきましょう。今回入れたのは以下の通りです。

| 機種 | 個数 | 入れたOS | アーキテクチャ |
|:-----------|:------------:|:------------:|:------------:|
| RaspberryPi3B | x1 | Raspbian(stretch) | armv7l |
| RaspberryPi3B | x1 | Raspbian(buster) | armv7l |
| RaspberryPi3B | x2 | dietPi | armv7l |
| RaspberryPi4B | x1 | dietPi | armv7l |
| Rock64 | x1 | dietPi | arm64 |
| JetsonNano | x1 | [JetPack](https://developer.nvidia.com/embedded/jetpack) | arm64 |

#### dietPiって何?
自分はARMボードで使うOSに、[dietPi](https://dietpi.com/)を推しています。モニタ不要のヘッドレスセットアップが可能、デフォルトではSSHしかない超軽量、CUIで使えるすごく丁寧で豊富な設定画面がお気に入りのOSです。(それ以外は再セットアップがめんどくさかったので、以前使っていたものをそのまま再利用してます。)

#### 何使って入れるの?
isoを各サイトからダウンロードしてから書き込みます。何使って書き込んでも大体動くとは思うのですが、自分は[balenaEtcher](https://www.balena.io/etcher/)で書き込みました。既になにかパーティションが分かれていてetcherで書き込めなかったmicroSDは、パーティションを消して、[Win32DiskImager](https://sourceforge.net/projects/win32diskimager/)で書き込むとうまく行きました。

### 2: Dockerを入れる
セットアップが終わったら、各ボードにDockerを入れていきます。以前はARMに公式対応していなかったと記憶しているのですが、今は基本的に [Docker公式のインストールガイド](https://docs.docker.com/engine/install/debian/)どおりにすればいいみたいです。ここでの注意点は、Ubuntuのガイドを参照してはいけないってことです。Debian用のリポジトリがあるので、間違えてUbuntuのガイドを見ないようにしましょう、インストールできません。

```shell:パッケージ一覧を更新
sudo apt-get update
```
```shell:必要なパッケージをインストール
sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
```
```shell:dockerの署名を追加
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
```

この次がアーキテクチャによって異なります。今回使ったボードのうち2つはアーキテクチャが違いますので間違えずに入れましょう

```shell:armv7の場合(armhf用が使えます)
echo \
  "deb [arch=armhf signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```
```shell:arm64の場合
echo \
  "deb [arch=arm64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```
ここまでやるとaptからインストールできるようになるので、インストールしましょう。

```shell:Dockerをインストール
 sudo apt-get update
 sudo apt-get install docker-ce docker-ce-cli containerd.io
```

インストール後hello-worldコンテナを実行できたら導入完了です。

```shell:dockerが動くことをテストする場合
sudo docker run hello-world
```

### 3: Docker Swarmのセットアップ
ちょこっとコマンドを実行するだけで一瞬でクラスタを構成できます。しかし一応知っておくべき[ノードという概念](https://docs.docker.jp/engine/swarm/key-concepts.html#nodes)があるので書いておきます。
#### manager
workerノードにコンテナ起動等の要求をします。swarm全体のworkerに要求を出すことができます。
デフォルトではmanagerはworkerも兼ねるようになっています。
#### worker
managerノードから送られてきたコンテナ起動等の要求を実行します、実行するだけなのでこのworkerからswarm全体への要求をすることはできません
#### クラスタに参加させるときはどっちがいいのか?
##### 全部managerにすると?
1台managerノードが止まったとしても他のノードからSwarm全体を操作できるので障害に不安がありません。しかし、Swarm全体を操作するためのAPIが全ノードから提供されるので セキュリティ上の不安点となります。
##### (manager1台以外)全部workerにすると?
1台managerノードが止まるとSwarm全体が操作できなるので、障害発生時の不安点となります。しかし、Swarm全体を操作するためのAPIが1ノードからしか提供されないのでセキュリティ上の不安が減ります。
##### つまり?
今回の場合、managerノード2台、残りをworkerノードにするのが妥協点かなと思います。

#### 導入コマンド
```shell:managerにしたい1台で実行
sudo docker swarm init
sudo docker swarm join-token worker
sudo docker swarm join-token manager
```

下記のようなswarm参加コマンドが表示されるのでworkerに参加させたい場合は`... join-token worker`のコマンド、managerの場合は`... join-token manager`のコマンドを 参加させたいボード上で実行します。

```shell
docker swarm join \
    --token SWMTKN-1-49nj1cmql0jkz5s954yi3oex3nedyz0fb0xx14ie39trti4wxv-8vxv8rssmk743ojnwacrr2e7c \
    192.168.99.100:2377
```

### 4: Portainerのセットアップ
[公式ガイド](https://documentation.portainer.io/v2.0/deploy/ceinstallswarm/)通りですが、コマンドを引用しておきます。実行するときは managerとなっているノード(initしたノードか、join-token manager したノード)で実行してください。

```shell
curl -L https://downloads.portainer.io/portainer-agent-stack.yml -o portainer-agent-stack.yml
sudo docker stack deploy -c portainer-agent-stack.yml portainer
```
実行してから数分すると、 http://swarm内のノードのアドレス:9000 でアクセスできるようになります。(DockerSwarmにStackとしてデプロイすると、Swarm内のどのノードのアドレスからでもアクセスできるようになります。) 初回アクセス時にPortainerのユーザー名とパスワードを設定します。

Swarmというタブを開いて、追加したノードが認識されていたら導入完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/3da4495c-7ed0-22a7-e499-e1c863e5d717.png)

### 5: オレオレDockerレジストリ
これでPortainerの導入は終わったのですが、何分armとarm64が混ざったクラスタなので、どちらかが公開されていない場合もあり、自分でイメージを作る必要が出てくることがあります。[registryコンテナを自分でセットアップする](https://docs.docker.com/registry/)のがベストな気もしますが、面倒だったので、今回は無料で1GBまでプライベートコンテナを置けるレジストリ、[TreeScale](https://treescale.com/)を使わせてもらいました。

#### アカウントの作成
https://dash.treescale.com/sign-up
サイト上からメールアドレスを使い、作成できます。
(Githubから作成するとなぜかうまくいきませんでした)

#### dockerでTreeScaleへログインする
pushする際にはログインが必要です。下記のようにログインしておきます。
([TreeScaleの公式ドキュメント](https://docs.treescale.com/docker-registry/docker-registry)から引用)

```shell:treeScaleへのログイン
~# docker login repo.treescale.com
username: [your TreeScale Username]
password: [TreeScale password]
```

#### portainerでTreeScaleへログインする
TreeScaleはプライベートレジストリなので、pullする際にもログインが必要です。
Portainerで下記画面に行き、レジストリとログイン情報を入力しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/0e53e47c-70a3-7786-4b5f-265570c0f306.png)

#### ARMv7/ARM64用のイメージの作成とPush
使っているOSがWindows10なので、Docker Desktop for Windowsでの作り方を書きます。ほぼ [Dockerの「マルチCPUアーキテクチャ」に対応したイメージをビルドする](https://dev.classmethod.jp/articles/docker-multi-architecture-image-build/)からの引用となります。注意すべき点は、そもそもベースイメージが armv7/arm64に対応していない場合は 自分でイメージを作ることができません。最近はM1Macの影響か、arm64に対応したイメージも増えているようなのでなさそうに思いますが、どうしてもない場合は、苦行か諦めが必要になります。

```shell
# 初回だけ実行
docker buildx create --name mybuilder
docker buildx use mybuilder

# ビルドしたいイメージのフォルダ内で実行(一応amd64用のイメージも作成)
docker buildx build . --platform linux/amd64,linux/arm64,linux/arm/v7 -t repo.treescale.com/[ユーザー名]/[イメージ名]:latest --push
```

#### TreeScaleでPushされたイメージを確認
サイトのダッシュボードを開くと、Pushしたイメージが確認できると思います。右上の使用量を見るとわかるのですが、サイト内でGroupを作っている場合は 使用量が正しくカウントできないようなので、残りどれだけPushできるかは自分で計算が必要なようです。(Groupなら無制限というわけではないです。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/56cc6cd2-f2c3-ee45-1e7a-7679d8246881.png)

#### Portainerでコンテナを起動してみる
この画面に行き、Name、実行したいImage、PortMappingなどを指定して Create the serviceすることでSwarm内でイメージが実行されます。
SchedulingModeがGlobalの場合は、1つのコンテナをSwarm内のどのアドレスからでもアクセスできるようにするもので、Replicatedの場合は、指定した数だけコンテナを作成し、Swarm内のノードに作成し、アクセス時はコンテナを実行しているノードのどれかにつながるようになります。(Replicatedしたコンテナ同士でデータが常に同期されるわけではないです!)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254984/c14c214a-0893-1272-6135-ac5e25ab08ae.png)

#### 実行してるコンテナにアクセスしてみる
試しにnginxのコンテナを起動してみたりしましょう(後日追記予定)

### 6: 次は?
Replicatedさせたコンテナ内の編集が同期されたほうが、自分が作るWebアプリでは助かる動作なので、それが実現できそうってことでCephを導入してみようと思います。 うまくいったらまた記事をあげます。

### 参考リンク
[Portainer](https://www.portainer.io/)
[Dockerの管理ツール調査](https://qiita.com/rururu_kenken/items/fe414c1af86fd433738f)
[Portainerで複数ホストのコンテナを一元管理してPortainerはいいぞと言う](https://qiita.com/polarbear08/items/931fe04ec228fad13092)