circleci/pythonからcimg/pythonへ更新する

# やりたいこと
- `circleci/` ownerのdocker imageがdeprecatedになるようなので、新しい `cimg/`に更新したい
  - seleniumでなんやかんやする目的で利用していたので
`circleci/python-3.8.3-browsers` -> `cimg/python-3.9.10-browsers`
あたりに更新したい

公式アナウンス
- https://discuss.circleci.com/t/legacy-convenience-image-deprecation/41034
- https://circleci.com/blog/announcing-our-next-generation-convenience-images-smaller-faster-more-deterministic


# やったこと

## dockerfile先頭のimage指定を差し替える

```dockerfile
FROM circleci/python:3.8.3-browsers
```
↓
```dockerfile
FROM cimg/python:3.9.10-browsers
```

## sudo周りを修正する
pip周りの権限が微妙に変わっているようなのでsudoを外す。
apt-getなどはsudo付きのままでよい模様。

```dockerfile
RUN sudo pip install --upgrade pip \
 && sudo pip install -r ./requirements.txt
```
↓
```dockerfile
RUN pip install --upgrade pip \
 && pip install -r ./requirements.txt
```


## ホームディレクトリでふんわりやっている処理を絶対パスに差し替える
詳しい仕様が分からないのでアレだが、どうもdockerfile内とコンテナ起動後のユーザーが違うらしく、ビルド中とビルド後でホームディレクトリのパスが異なるっぽい。
なので明示的に指定するようにした。


```dockerfile
WORKDIR ~
RUN sudo git clone https://github.com/wolfcw/libfaketime.git
WORKDIR /libfaketime
RUN sudo make install
```

```dockerfile
WORKDIR /home/circleci
RUN git clone https://github.com/wolfcw/libfaketime.git
WORKDIR /home/circleci/libfaketime
RUN make
```

ちなみに導入しているのはlinux/mac環境でシステム時間をいじることができるライブラリ。
https://github.com/wolfcw/libfaketime

## google chromeをインストールする
`circleci/` のときは `x-browsers`タグを指定するとブラウザも導入済の状態で提供されていたので、インストールが必要なことに驚いた。

https://circleci.com/developer/ja/images/image/cimg/python#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%83%BC
>ブラウザー バリアントのベースは元の Python イメージと同一ですが、こちらでは apt により Node.js、Java、Selenium、ブラウザーの依存関係が事前インストールされます。 使用するには、-browser を既存の cimg/python タグの末尾に追加します。 このバリアントは、CircleCI Browser-Tools Orb と組み合わせて使用する想定で設計されています。 この Orb を使用すると、任意のバージョンの Google Chrome と Firefox のいずれかまたは両方をビルドでインストールできます。

circleciの想定ではそもそもこのimageは docker executor で利用する前提であり、 `browser-tools/install-browser-tools` というcircleci謹製orbsを使うことでイメージサイズの縮小を狙っていると思われる。
今回はmachine executor上で動くコンテナのベースイメージとして利用する。
依存関係やseleniumなどは導入済・実態はUbuntu20.04なのでおとなしく手動インストールしていく。

```dockerfile
WORKDIR /home/circleci
RUN wget https://dl.google.com/linux/linux_signing_key.pub \
 && sudo apt-key add linux_signing_key.pub \
 && echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | sudo tee /etc/apt/sources.list.d/google-chrome.list \
 && sudo apt -y update && sudo apt-get -y install google-chrome-stable
```
`linux_signing_key.pub`というファイルが生成されるので、マウントされたボリュームじゃない場所でやるとよい。


#### tips:コンテナ全体の中でseleniumで起動したchromeのみにlibfaketimeを適用する
通常libfaketimeを利用する場合、docker-compose.ymlなどで環境変数 `FAKETIME='2022-01-01 12:00:01` のように設定することになる。
が、こうすると同じコンテナ内で動作させたサーバーなどが証明書エラーで実行できなくなるケースがあるため、selenium chromeのみにlibfaketimeを適用する手法を紹介する。

少なくともubuntuでは、`/usr/bin/chrome`(chrome本体のバイナリ)ではなく`/usr/bin/google-chrome`(シェルスクリプト)経由でgoogle chromeを実行する構造になっている。
(直接起動するときもseleniumから起動するときも同じ)
そのため、この `/usr/bin/google-chrome` に以下のような行を追加すればgoogle chromeのみlibfaketime環境を適用することができる。

```sh
export FAKETIME=$FAKETIME_FOR_BROWSER 
```


## seleniumからchromeを起動する際のオプションにdevtoolsのポート設定を追加する
自分の環境では無かったらエラーが出てあるとエラーが消えたので一応。
環境によっては無くてもいいはず。ポートはコンテナ内で衝突しないように注意。

```python
options.add_argument("--remote-debugging-port=9222")
```

ついでなので現在適用している他の設定も紹介しておく。
`--disable-dev-shm-usage`などの安定すると言われているオプションなどは色々試した結果あってもなくても変わらないということが分かっている。
```python
options = selenium.webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument("--remote-debugging-port=9222")
options.add_experimental_option('excludeSwitches', ['enable-automation'])

# ssl証明書エラーの回避
capabilities = DesiredCapabilities.CHROME.copy()
capabilities['acceptInsecureCerts'] = True
```


# まとめ
`circleci/` -> `cimg/` への移行はそこまで互換性があるわけではなく、まあまあに対処すべきことが多いので、時間作ってさっさとやるべき