S2I (Source to Image)解説 - 2. S2Iを使って、Pythonのコンテナイメージをビルドする

本記事は、前回の記事[]()の続きである。本記事では、実際に自分のローカル環境を使い、Pythonの簡単な環境を構築するコンテナイメージをS2Iを使ってビルドしていく。

# 使用した環境

# S2Iのダウンロード
WSL2のubuntuへS2Iをダウンロード手順を以下に示す：
1. まず、S2Iを配置するための一時的なディレクトリを用意し、そのディレクトリへ移動する：
```bash
mkdir /tmp/s2i/ && cd /tmp/s2i/ 
```

2. 次に、S2Iのバイナリパッケージアーカイブをダウンロードし、バイナリファイルを適切なディレクトリへ置く。S2Iのリリース情報は[リリース情報](https://github.com/openshift/source-to-image/releases)にあるので、自分の環境に合わせてS2Iのバージョンは選んでいく。
ディレクトリ`/tmp/s2i/`内で`curl`を使用し、ここでは最新バージョンのS2Iをダウンロードする：
```bash
$ curl -s https://api.github.com/repos/openshift/source-to-image/releases/latest| grep browser_download_url | grep linux-amd64 | cut -d '"' -f 4  | wget -qi -
```

3. 次に、バイナリファイルの解凍を行う
```bash
$ tar xvf source-to-image*.gz
```
4. S2IのPATHを次のように通す。例えば`s2i`という実行ファイルを`/usr/local/bin`へ移動する：
```bash
sudo mv s2i /usr/local/bin
rm -rf /tmp/s2i/
```
ここで、一時的に作成したディレクトリ`/tmp/s2i/`は必要ないので、削除した。

5. 最後に、S2Iの場所を以下で確認すると
```bash
which s2i
>
/usr/local/bin/s2i
```
と表示される。S2Iのバージョンを確認すると以下のように出力されるはずである：
```bash
$ s2i version  
s2i v1.3.8
```



# S2Iビルドの実行

## 手順1. S2Iビルドを実行するためのディレクトリ作成
まず、S2Iの`s2i create`コマンドを使い、S2Iビルド実行用のひな形のディレクトリを作成する：
```bash
$ s2i create python-test python-test
```
今回は`python-test`というディレクトリを作成した。実際に`python-test`のディレクトリ構造を確認する：

```bash
$ cd python-test
~/python-test$ tree 
.
├── Dockerfile
├── Makefile
├── README.md
├── s2i
│   └── bin
│       ├── assemble
│       ├── run
│       ├── save-artifacts
│       └── usage
└── test
    ├── run
    └── test-app
        └── index.html
```
S2I builder imageをビルドするためには、Dockerfileに加え、S2Iスクリプトが必要である。directory構造では、`s2i/bin`配下にs2iで実行するためのS2I scriptが配置されている。必須のスクリプトは`assemble`と`run`この2種類である。

## 手順2. Pythonを実行するファイルの作成
今回は`test/test-app`ディレクトリ内のアプリケーションソースコードをpythonの実行ファイル`sample.py`、`requirements.txt`というファイルを追加しておく：
```bash
.
├── Dockerfile
├── Makefile
├── README.md
├── s2i
│   └── bin
│       ├── assemble
│       ├── run
│       ├── save-artifacts
│       └── usage
└── test
    ├── run
    └── test-app
        ├── sample.py
        └── requirements.txt
```
実際のPython用スクリプト `sample.py`、`requirements.txt`を以下に示す：
```python:sample.py
print("Hellow World!")
```

```txt:requirements.txt
numpy
```




## 手順3. DockerfileとS2I scriptの作成
今回、使用したDockerfileを以下に示す：
Dockerfile
```Dockerfile:Dockerfile
FROM python:latest
USER root
# TODO: Set labels used in OpenShift to describe the builder image
LABEL io.openshift.s2i.scripts-url=image://usr/libexec/s2i \
      io.openshift.tags="builder,python"

COPY ./s2i/bin/ /usr/libexec/s2i

# TODO: Set the default CMD for the image
CMD ["/usr/libexec/s2i/usage"]
```

次に、S2I scriptを作成していく。`assemble.sh`と`run.sh`を作成していく。
### **assembleについて**
`assemble`では、ソースコードからアプリケーションのアーティファクト（成果物）をビルドし、それらをイメージ内の適切なディレクトリに配置する。
実際にPythonのコンテナイメージを作成するためのスクリプト例 `assemble.sh`を示す：
assemble scriptの実際の中身を実際に見てみると以下のようになっている。

```bash:assemble.sh
#!/bin/bash -e
if [[ "$1" == "-h" ]]; then
        exec /usr/libexec/s2i/usage
fi

# アプリケーション、例えば、sample.py, requirements.txtをアプリの実行を行うディレクトリへコピーし配置している
echo "---> Installing application source..."
cp -Rf /tmp/src/. ./

# requirements.txtを用いて、pip installでライブラリのインストールを行っている
echo "---> Building application from source..."
pip install --upgrade pip
if [ -f requirements.txt ]; then
    pip install -r requirements.txt
fi
```

上記にようにassembleではPythonモジュールのインストールを実施している。

### **runについて**
実際にpythonスクリプト `sample.py`を実行するスクリプトである：

```bash:run.sh
#!/bin/bash -e
python sample.py
```

## 手順4. S2I image builderを作成する
S2Iビルドを実行するために、`docker build`を使って、S2I image builderをビルドする。`docker build`は`Dockerfile`があるディレクトリ内で実行する必要がある。今回は、S2I image builderの名前は`python-builder`という名前にする：
```bash
$ docker build -t python-builder ./python-test
```
コマンドの説明
- `docker build` : Dockerfileを使い、コンテナイメージをビルドする
- `-t python-builder` : タグ付きでコンテナイメージ名を`python-builder`にする
- `./python-test` : Dockerビルドを実行するディレクトリを指定する

実際にビルドされたコンテナイメージを確認してみる：
```bash
$ docker images
>
REPOSITORY                 TAG       IMAGE ID       CREATED         SIZE
python-builder                latest    039c8d89b767   2 minutes ago   426MB
```

![mehcanism_S2I_py_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781216/3c990673-600a-5e07-757a-95b8c77ed8f7.png)


## 手順5. S2Iビルド実行
手順2、3でアプリケーションソースコードとPython環境用のS2I builder imegaを作成した。これらを用いて、S2Iビルドにより、新たなコンテナイメージをビルドする。
```bash
$~/python-test s2i build test/test-app/ python-builder python-app
>
`WARNING: could not inspect the builder image for labels: reading manifest latest in docker.io/library/python-builder: requested access to the resource is denied`
---> Installing application source...
---> Building application from source...
Build completed successfully
```
コマンド説明
- `s2i build` : S2Iビルドを実行し、新たなコンテナイメージをビルドする
- `test/test-app/` : アプリケーションソースコードのディレクトリを指定
- `python-builder` : S2I builder imageのイメージ名を指定
- `python-app` : 新しくS2Iビルドされるコンテナイメージの名前を指定

実際にビルドされたコンテナイメージを確認してみる：
```bash
$~/s2i-test/python-test$ docker images
>
REPOSITORY                 TAG       IMAGE ID       CREATED          SIZE
python-app                 latest    7561a8bc9e5a   10 seconds ago   426MB
python-builder                latest    039c8d89b767   3 minutes ago    426MB
```
`python-app`という最終的来なコンテナイメージがビルドされた。
![mehcanism_S2I_py.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781216/b72ed8a6-ab2d-e900-442a-932bc22a0734.png)



# **注意点**
S2Iでコンテナイメージのビルドを行う際の注意点を説明する。結論から言うと、S2Iビルド用の`Dockerfile`の`LABEL`にイメージの場所`io.openshift.s2i.scripts-url`を指定する必要がある。実際Dockerfileに書いたように
```Dockerfile
LABEL io.openshift.s2i.scripts-url=image://usr/libexec/s2i
```
と記述する必要がある。

## 起きた問題
はじめ、Dockerfileを以下のように記述し：
```Dockerfile
FROM python:latest
USER root

COPY ./s2i/bin/ /usr/libexec/s2i

CMD ["/usr/libexec/s2i/usage"]
```
S2Iビルドすると以下のようなエラーメッセージが出てきた：
```txt
could not inspect the builder image for labels: reading manifest latest in docker.io/library/pybuilder: requested access to the resource is denied
An error occurred: failed to install [assemble run]
Suggested solution: set the scripts URL parameter with the location of the S2I scripts, or check if the image has the "io.openshift.s2i.scripts-url" label set
```
まず、1つめのエラーは、イメージがdocker.ioに無かったのでローカルから探すという意味であり、これは無視する。２つ目のエラーはassemble runのインストールに失敗したことを意味する。３つ目のエラーはs2iスクリプトの場所を指定しろという意味をもつ。

そこで、エラーにある通りに、s2i builder imageを作成するためのDockerfileのLABELを以下のように記述し：

```Dockerfile:Dockerfile
LABEL io.openshift.scripts-url=image://usr/libexec/s2i
```
と記述するが、再び、同じエラーが出てしまった。

## **解決策**
s2i buildをopenshift上ではなく、ローカル上で実行する場合、作成したs2i builder image（今回は`python-builder`のこと）内のs2iスクリプトの場所を`io.openshift.s2i.scripts-url`で指定する必要がある。

今回、Openshiftによって、用意されたベースイメージ（例えば`openshift/base-centos7`）ではなく、Openshift以外によって用意されたベースイメージ(今回の場合だと`python:latest`)を使用した。そのため、ローカルで用意した`Dockerfile`にイメージの場所`io.openshift.s2i.scripts-url`を`LABEL`で指定する必要があった。そのため、`Dockerfile`へ
```Dockerfile
LABEL io.openshift.s2i.scripts-url=image://usr/libexec/s2i
```
を記述すればよい。また、`io.s2i.scripts-url`というLABELによる指定でも、ビルドは成功するが、現在は廃止されており、使わなことをお勧めする。

次回はS2Iの機能の一つである増分ビルドというものについて説明します

# 参考文献
- [OpenShift徹底活用ガイド](https://www.amazon.co.jp/OpenShift%E5%BE%B9%E5%BA%95%E6%B4%BB%E7%94%A8%E3%82%AC%E3%82%A4%E3%83%89-%E6%BE%A4%E6%A9%8B%E6%9D%BE%E7%8E%8B%E3%80%81%E9%9D%92%E5%B1%B1%E7%9C%9F%E5%B7%B3%E3%80%81%E5%A4%A7%E6%B4%A5%E6%B5%A9%E5%8F%B8%E3%80%81%E9%96%A2%E5%85%8B%E9%9A%86%E3%80%81%E9%88%B4%E6%9C%A8%E6%B4%8B%E4%B8%80%E6%9C%97%E3%80%81%E6%B2%B3%E8%A7%92%E4%BF%AE/dp/4863543182/ref=pd_lpo_sccl_1/358-6750756-8978840?pd_rd_w=tDOnU&content-id=amzn1.sym.d769922e-188a-40cc-a180-3315f856e8d6&pf_rd_p=d769922e-188a-40cc-a180-3315f856e8d6&pf_rd_r=DQCHQ5NQBKHEBDMRHVSJ&pd_rd_wg=AJ8Bg&pd_rd_r=0802ab4e-8cbf-4f6e-acd7-f4f4a1b19930&pd_rd_i=4863543182&psc=1)
- [Source-To-Image (S2I)公式ドキュメント](https://github.com/openshift/source-to-image)
- [[OpenShift]自作のS2Iスクリプトを使ってローカルにあるPerlスクリプトをS2Iビルドしてデプロイする](https://zaki-hmkc.hatenablog.com/entry/2020/02/20/213638)
- [Minishiftで遊んでみよう！手軽にkubernetesを体験するの巻](https://qiita.com/yoshii0110/items/ef8502d46a49dd0cc3ee)
- [MACOSにS2IコマンドをインストールしてS2Iビルドする](https://korattablog.com/2020/07/06/macos%E3%81%ABs2i%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%97%E3%81%A6s2i%E3%83%93%E3%83%AB%E3%83%89%E3%81%99%E3%82%8B/)
- [OpenShift v3 と source-to-image (s2i)](https://qiita.com/nak3/items/6407c01cc2d1f153c0f1)
- [S2I work flow](https://www.kubesphere.io/docs/v3.3/project-user-guide/image-builder/s2i-introduction/)
- [第5章 S2I イメージのテスト](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/3.9/html/creating_images/creating-images-s2i-testing)
- [S2Iに再入門](https://qiita.com/Yusuke-Y/items/a636263398cfce100c35)
- [OpenShift Origin構築手順メモ](https://enakai00.hatenablog.com/entry/2015/12/27/221936)
- [OpenShift OriginによるDockerイメージ管理（1）〜イメージストリームを理解する](https://enakai00.hatenablog.com/entry/2015/12/28/203731)
- [OpenShift OriginによるDockerイメージ管理（2）〜Dockerfileによるイメージビルドを自動化](https://enakai00.hatenablog.com/entry/2015/12/28/215957)
- [OpenShift OriginによるDockerイメージ管理（3）〜Dockerfileからイメージを作成する際のお作法](https://enakai00.hatenablog.com/entry/2015/12/30/092724)
- [OpenShift OriginによるDockerイメージ管理（4）〜S2Iによるイメージビルド](https://enakai00.hatenablog.com/entry/2016/01/03/174854)