docker image rebase作業記録

docker image rebase[^docker image rebase]という**docker公式でない**個人(？)が作成したツールの使い勝手を試した記録。

# docker image rebaseとは[^docker image rebase]
**docker公式でない**個人(?)が作成した実験段階のツール。
gitのrebaseの感覚で、dockerのimageをrebase(実際にはlayerの置き換え)してくれる模様。
ただ、コミットが2018/2/25〜2018/4/27の2ヶ月程度しかなく、誰も本格的に使っている様子がない。
**今回の調査で正常系の100%再現するバグが見つかった**。
→作者に問い合わせたら、obsoleteだからコッチ[^go-containerregistry]ね、と言われた。(後日編集追加)

# 動機
例えばapacheのイメージを目的別に何個も作成したいとき、ベースとなるイメージを元に、その派生物として目的別のイメージを作成すると、レイヤが使い回されて容量が減ると思う。しかし、その後パッケージが古くなってaptやyumなどでそれぞれのイメージを更新すると、目的別のイメージの数分同じようなレイヤが作成されてしまう。

これを、ベースとなるイメージの更新だけで済ませられないか？つまり、rebaseできないか？ということで調べてみた。

# 作業環境
- VirtualBox上のUbuntu 18.10
- Docker 18.09
- Docker Registry 17.09(TLSあり&Basic認証でDocker Clientはlogin済)

※Docker Registryがないとrebaseできません

# 作業概要
1. privateなDocker Registryにdebianイメージをpush(originalとしてタグ付け)
2. originalからコンテナを起動して、終了してcommit/push(v1_baseとしてタグ付け)
3. v1_baseからコンテナを起動し、コンテナの中に/home/test/touchという空ファイルを作成して終了してcommit/push(v1としてタグ付け)
4. originalからコンテナを起動し、xxxをインストールして終了してcommit/push(v2_baseとしてタグ付け)
5. docker image rebaseを使用して、v1_base,v1,v2_baseから、v2としてタグ付けされるイメージを作成する
6. 確認

# 作業内容

## (1)Docker Registryにdebianイメージをpush(original)

Docker Hubからdebian:latestをpull

```console
$ docker pull debian
```

Docker Registry(便宜上example.comとする)上のイメージ(my-debian:original)としてタグ付けしてpush

```console
$ docker tag debian example.com/my-debian:original
$ docker push example.com/my-debian:original
```
## (2)originalからコンテナを起動して、終了してcommit/push(v1_base)

```console
$ docker run -it --name my-debian example.com/my-debian:original
# exit
$ docker commit my-debian example.com/my-debian:v1_base
$ docker push example.com/my-debian:v1_base
$ docker container rm -v my-debian
```
## (3)v1_baseに/home/test/touchという空ファイルを作成してcommit/push(v1)

```console
$ docker run -it --name my-debian example.com/my-debian:v1_base
# mkdir /home/test
# touch /home/test/touch
# exit
$ docker commit my-debian example.com/my-debian:v1
$ docker push example.com/my-debian:v1
$ docker container rm -v my-debian
```

## (4)v1_baseにslをインストールしてcommit/push(v2_base)

```console
$ docker run -it --name my-debian example.com/my-debian:original
# apt update
# apt upgrade
# apt install sl
# /usr/games/sl
# exit
$ docker commit my-debian example.com/my-debian:v2_base
$ docker push example.com/my-debian:v2_base
$ docker container rm -v my-debian
```

## (5)docker image rebaseを使用して、v2作成

### (5)-1. golangインストール

```console
$ sudo apt install golang-go
```
### (5)-2. docker image rebaseインストール

```console
$ go get -u github.com/google/image-rebase
```

※以降では環境変数などが未設定で、```$HOME/go/```にインストールされていると仮定

### (5)-3. 自己証明書の配置(必要であれば)

今回docker registryで用意したのは自己証明書なので、証明書を/etc/docker/certs.d/*辺りに入れてあった。
しかし、ここを見るのはdockerだけなので、Ubuntuの一般的な証明書登録手順[^証明書登録手順]を踏む。

```console
$ sudo mkdir /usr/local/share/ca-certificates/self
$ sudo cp example.com.crt /usr/local/share/ca-certificates/self/
$ sudo update-ca-certificates
```
※selfは何でもいい

### (5)-4. docker image rebase実行

```console
$ ~/go/bin/image-rebase  \
  --original=example.com/my-debian:v1 \
  --old_base=example.com/my-debian:v1_base \
  --new_base=example.com/my-debian:v2_base \
  --rebased=example.com/my-debian:v2
```

## (6)確認

```console
$ docker run -it --name my-debian example.com/my-debian:v2
# ls -lAF /home/test # v1でのコミットの反映を確認
# /usr/games/sl # v2_baseでのコミットの反映を確認
# exit
$ docker container rm -v my-debian
```
/home/test/touchの存在が確認でき、slが動けば成功。

しかし冒頭に書いたとおり、バグのため、v1のコミットの反映を確認できなかった。
以下の修正を加えたところ、成功。

```diff
diff --git a/pkg/rebase/rebase.go b/pkg/rebase/rebase.go
index 0884ca9..975f60e 100644
--- a/pkg/rebase/rebase.go
+++ b/pkg/rebase/rebase.go
@@ -163,8 +163,8 @@ func (r Rebaser) Rebase(origStr, oldBaseStr, newBaseStr, rebasedStr string) erro
        }
        for i := range origLayers[len(oldBaseLayers):] {
                rebasedImage, err = mutate.Append(rebasedImage, mutate.Addendum{
-                       Layer:   origLayers[i],
-                       History: origConfig.History[i],
+                       Layer:   origLayers[i+len(oldBaseLayers)],
+                       History: origConfig.History[i+len(oldBaseLayers)],
                })
                if err != nil {
                        return fmt.Errorf("failed to append layer %d of original layers", i)
```
# 結論
誰も使ってないものは危ないので使用しない方がいい。
→go-containerregistry[^go-containerregistry]なんてモノがあるのでそれを調べてから。(後日編集追加)
→調べたので[続き](https://qiita.com/wwbQzhMkhhgEmhU/items/40a781e1cb14b8a25d8a)(後日編集追加)

[^docker image rebase]: [docker image rebase](https://github.com/google/image-rebase)
[^証明書登録手順]: https://askubuntu.com/questions/645818/how-to-install-certificates-for-command-line
[^go-containerregistry]: [go-containerregistry](https://github.com/google/go-containerregistry)