CircleCI で DLC を(一部) 使わなくなった話

[Increments × cyma (Ateam Inc.) Advent Calendar 2020](https://qiita.com/advent-calendar/2020/increments-cyma) の7日目は、
Increments株式会社 プロダクト開発グループ Qiita開発チームの @atm-snag が担当します！

## 閑話

この記事を書いているときに，前日の記事がアップされていて，
それが CI 関連だったので， CI が連続するなあと思った(小並感

## はじめに

Increments では，build や deploy などに circleci を使っています．
circleci には DLC(Docker Layer Cache) という機能があります．
今回は，DLC を使わなくなった話をします．

## DLC とは…

- circleci の executer を跨いで `/var/lib/docker` を共有する仕組みです．
    - docker のイメージレイヤをジョブをまたいで使うことができる
- ジョブ実行 1 回ごとに 200 [クレジット] かかる(200 credits per job run)

です．

circleci に詳しくない人に軽く説明すると，

- executor とは
    - ジョブの実行環境．
    - 通常 executer は別マシンで起動して不連続なので ，`/var/lib/docker` は共有されていない状態
        - `docker build` するときに過去のイメージレイヤのキャッシュがない → build が長い → 金銭的な死

DLC を使えば，どこかのジョブで `docker build` が終わっていれば，
次以降に起動する executor では，キャッシュを使って，docker build の実行をスキップ(USING CACHE) できるという訳です．
これは便利．

→ ジョブなどの概念は[こちら](https://circleci.com/docs/ja/2.0/jobs-steps/#section=getting-started)が詳しそう

## 問題点

使っているうちにいくつか気付いたことがあります．

- `Dockerfile` 等のビルドの実行に関係ある部分に変更が無い場合でもキャッシュを使わずに build が実行されていることがあった
    - おそらく，volume が複数あるために，docker イメージレイヤがキャッシュとして有効に使われていない状態だった
        - ドキュメントに，1つのvolume にアタッチできるのが1つの executor なため，volume は複数個になる(最大50個) ことがあると書いてある
        - volume が消えるのが1週間後であり，どういう条件でどのvolume が使われるのかはランダム(？)
        - circleci の課金[^pricing]が，ジョブの実行時間とリンクしているので，できれば無駄な docker build は避けたい
- 結局安くなっているのか？
    - docker のイメージレイヤがキャッシュされていても，build が必要なステップがあれば，そのステップはは実行される
    - 速度的には，「キャッシュなし < DLC < 過去の image を pull してcache できるところはする」のような想定(下図)
        - 「image pull」 と 「DLC」 の差はdocker イメージレイヤをpull する時間がメイン
            - `docker build` が成功する確率が高い前提
    - 1回 200クレジットかかる．
        - 例えば，machine executor medium で 10 [クレジット/分][^pricing]

![スクリーンショット 2020-11-30 15.55.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398927/57af09ac-9f24-15f2-cda4-7337e68b7be7.png)

200[クレジット] / 10[クレジット/分] → build 時間を20分以上高速化できないとコスト的には微妙ではないか？
もっと言うと，docker pull する時間が 20分を越えなければ DLC より docker pull の方が(コスト的には)良いのではないか？[^note1]
(時間的には`docker pull` する分だけ悪くなるために一長一短ではある．あと volume が複数あることでbuild する可能性があること．もある)

## やったこと

- docker pull を build よりも前に実行するようにした
    - [docker-registory-image-cache](https://circleci.com/developer/orbs/orb/cci-x/docker-registry-image-cache) みたいな
    - docker pull しても 10分かかるようなことはない．

## DLC も良いところもある

- 例えば，色んなジョブで DLC を使うことで，キャッシュヒット率を上げる
    - docker pull を使うと，失敗した build の image layer の情報は残らないため，そのようなものを積極的に使おうとするならば有効かもしれない
    - とはいえ，これはエッジケース寄りな感じがする．circleci のジョブで`docker build` をなるべく失敗させないように運用でカバーすることもできる．
- 提案っぽいもの
    - DLC ではなくジョブで使えるキャッシュ機能も存在してそちらはどのキャッシュをなるべく使う指定ができるので，どういうvolume を優先して使うかを指定できるとある程度使い易くなるのではないか？
    - なんかこう，branchとか開発project みたいな感じで，そっちのvolume を優先的に使いたい．みたいなケース．
- 今回試した方法は docker pull に依存しているので，docker pull に何らかの制限をかけられるとローカルのキャッシュの方がありがたい
    - 例えば，pull に回数制限かけられたりした場合

## その他

- 試してみたプロジェクトでは，docker save → store cache → restore cache → docker load がめちゃ時間かかったので，docker pull を採用しました
    - 近いところでやるから速くなると思ったんだけど，やっぱり計測してみないとわからないもんですね．

## まとめ

- DLC を使わなくなりました(一部)
    - 今回のプロジェクトではマッチしなかったので使わなくなったけど，有意に使える場面もあるかもしれない．
    - とはいえ，中身を知らないといつ使えるんだということになるので，今回のように調べることができたのは良かった．

[Increments × cyma (Ateam Inc.) Advent Calendar 2020](https://qiita.com/advent-calendar/2020/increments-cyma) の8日目は、エイチームのEC事業本部の @hibiheion がお送りします！！

## 補足

年末(2020/12) に ダウンロードがめちゃ遅いときがあって，こういうときは DLC 有利だよな．と思った．

## LINK

- [Docker レイヤー キャッシュの有効化](https://circleci.com/docs/ja/2.0/docker-layer-caching/) / [Enabling Docker Layer Caching](https://circleci.com/docs/2.0/docker-layer-caching/)
- [CircleCI 2.0 では Docker ビルドキャッシュが効く](https://qiita.com/na-o-ys/items/7e7a7e4cde378fc54b32)


[^note1]: 転送量課金とかできたら，`docker pull`の方が不利になることも起きるだろうか？
[^pricing]: [circieci pricing](https://circleci.com/pricing/)