単体で動作するPerlスクリプトをS2I使ってビルド&デプロイする。
環境はCodeReady Containers (OpenShift v4.3)ですが、OKD 3.11でも同じ。
[zaki@codeready s2isample]$ crc version crc version: 1.4.0+d5bb3a3 OpenShift version: 4.2.13 (embedded in binary) [zaki@codeready s2isample]$ [zaki@codeready s2isample]$ oc version Client Version: v4.3.0 Kubernetes Version: v1.14.6+a8d983c
S2Iについて
詳しく説明されている方が何人もいらっしゃるのでそちらを参考してください。。
一言で言うと、Dockerfileを使わずにコンテナイメージをビルドする仕組みです。
Dockerfileの完全な代替になるかというと、そうではないですが、
- イメージ作成前の処理(ソースコードから実行バイナリをビルドしたり)
- イメージ内のファイル構成(必要なパッケージを
yum
したり↑のバイナリを配置したりスクリプトコピーしたり) - コンテナデプロイ時に実行する処理(
CMD
から呼ばれるスクリプトの実装)
といったことをS2Iで実現できます。
逆に、
- Listenするポート番号を指定(
EXPOSE
) - Volumeを指定(
VOLUME
)
のようにイメージそのもののメタな設定はS2Iでは(たぶん)行えません。
これは、S2Iビルドする際のベースイメージ側で設定しておきます。(本記事ではこちらは扱いません)
OpenShiftではデフォルトでS2Iの仕組みを利用でき、S2Iが期待している構成をしているアプリケーションであれば、oc new-app
でpodをデプロイする際にそのアプリケーションのリポジトリを指定するだけで勝手にビルド&デプロイを行うことができる便利なツールです。
例えばこんな感じ
ただし、S2Iが期待している構成で動作するため、JavaEEみたいにビルド(mvn
とか)とデプロイのファイル構成などにお作法があるものだと標準のものが使えますが、単体でHTTP ListenするPerlスクリプトなんかだと、標準で用意されている(S2Iにも対応した)Perlイメージはリポジトリを指定するだけだと使えなかったりします。(というのもこのイメージはhttpd
内蔵でPerlスクリプトはCGIとして動作するっぽい?)
というわけで前置きが長くなったけど、この記事では「既存のイメージを使って、自作のS2Iスクリプトで期待するビルド&デプロイ動作をするイメージを作成」するにはどするかについて説明します。
使用するイメージは、クラスタデプロイ時にopenshift/perl
としてImageStreamが用意されている、前述のrhscl/perl-526-rhel7です。(Perlといいつつこれ"Apache 2.4 with mod_perl/5.26"なのでHTTPサーバーのイメージだけど)
また、このイメージは8080でListenする設定になっているので、使うポート番号は8080限定です。(変更が必要な場合はベースイメージの作成が必要)
なお、本記事では話を単純にするためにsave-artifacts
については説明しません。
作業用プロジェクト作成
[zaki@codeready s2isample]$ oc login -u developer -p developer https://api.crc.testing:6443 Login successful. You don't have any projects. You can try to create a new project, by running oc new-project <projectname> [zaki@codeready s2isample]$ oc new-project s2i-sample Now using project "s2i-sample" on server "https://api.crc.testing:6443". You can add applications to this project with the 'new-app' command. For example, try: oc new-app django-psql-example to build a new example application in Python. Or use kubectl to deploy a simple Kubernetes application: kubectl create deployment hello-node --image=gcr.io/hello-minikube-zero-install/hello-node
ソース
アプリケーションpodで実行するスクリプト。
sockserv.pl
という名前で次のPerlスクリプトを作成する。
#!/usr/bin/perl use IO::Socket; use strict; my $server = new IO::Socket::INET( Listen => 3, LocalAddr => '0.0.0.0', LocalPort => 8080, Proto => 'tcp', Reuse => 1 ); die "IO::Socket $!" unless $server; for (;;) { if (my $client = $server->accept()) { print "connected from " . $client->peerhost() . "\n"; print $client "カレーは粉でできてるのでカロリーゼロ\n"; close $client; print "disconected...\n"; } } close $server; exit;
S2Iスクリプト
ここからS2I固有の作り込みだけど、nekopさんのブログにも説明がある通り、作りは単純です。
の順序で、S2Iのスクリプトがあるかどうかを検査し、見つかった時点でそのパスにあるスクリプトを使用します。
Perlのベースイメージでは、io.openshift.s2i.scripts-urlにimage:///usr/libexec/s2iの指定があり、イメージ内の/usr/libexec/s2iディレクトリ以下にあるスクリプトが使用されます。(これは任意のpodをPerlのベースイメージでデプロイし、oc rsh
などで確認できます)
今回はこれを使わずに、自前のS2Iスクリプトを使ってビルドしたいので、この指定より優先度の高い.s2i/bin
ディレクトリを用意します。
[zaki@codeready s2isample]$ mkdir -p .s2i/bin
assemble
今回はスクリプトファイル単体で動作するpodを作りたいので、スクリプトファイルをコンテナ内に配置するのみです。
実行時に配置したスクリプトファイルをフルパス指定するので、配置先はどこでも良いです。
ただし配置時のコピー元は、イメージビルド時にはまず/tmp/src
にホストOSからコピーされるため、ここから配置することになります。
[zaki@codeready s2isample]$ cat .s2i/bin/assemble #!/bin/bash APP_ROOT=/opt/app-root/src SRC=/tmp/src cp -a $SRC/* $APP_ROOT/
run
コンテナデプロイ時に呼ばれるスクリプトです。
今回のPerlスクリプトは単体で動作するため、単純にperl
コマンドの引数に指定しています。
(試してないけど、実行権限つけてスクリプトファイルのみ指定でも多分動く)
[zaki@codeready s2isample]$ cat .s2i/bin/run #!/bin/bash perl /opt/app-root/src/sockserv.pl
イメージのビルド
BuildConfig作成
[zaki@codeready s2isample]$ oc new-build --image-stream=openshift/perl:5.26 --name=sample error: you must specify at least one source repository URL, provide a Dockerfile, or indicate you wish to use binary builds
未指定だとダメ。
[zaki@codeready s2isample]$ oc new-build --image-stream=openshift/perl:5.26 --name=sample --binary --> Found image acdb469 (2 months old) in image stream "openshift/perl" under tag "5.26" for "openshift/perl:5.26" Apache 2.4 with mod_perl/5.26 ----------------------------- Perl 5.26 available as container is a base platform for building and running various Perl 5.26 applications and frameworks. Perl is a high-level programming language with roots in C, sed, awk and shell scripting. Perl is good at handling processes and files, and is especially good at handling text. Perl's hallmarks are practicality and efficiency. While it is used to do a lot of different things, Perl's most common applications are system administration utilities and web programming. Tags: builder, perl, perl526 * A source build using binary input will be created * The resulting image will be pushed to image stream tag "sample:latest" * A binary build was created, use 'oc start-build --from-dir' to trigger a new build --> Creating resources with label build=sample ... imagestream.image.openshift.io "sample" created buildconfig.build.openshift.io "sample" created --> Success
BuildConfigとImageStreamが作成された。
[zaki@codeready s2isample]$ oc get all NAME TYPE FROM LATEST buildconfig.build.openshift.io/sample Source Binary 0 NAME IMAGE REPOSITORY TAGS UPDATED imagestream.image.openshift.io/sample default-route-openshift-image-registry.apps-crc.testing/s2i-sample/sample
build実行
ディレクトリ構造はこの通り
[zaki@codeready s2isample]$ tree -a . ├── .s2i │ └── bin │ ├── assemble │ └── run └── sockserv.pl 2 directories, 3 files
ビルド開始
[zaki@codeready s2isample]$ oc start-build sample --from-dir=. Uploading directory "." as binary input for the build ... .. Uploading finished build.build.openshift.io/sample-1 started
build podが起動。
[zaki@codeready s2isample]$ oc get pod NAME READY STATUS RESTARTS AGE sample-1-build 1/1 Running 0 14s
ビルドログはBuildリソースに対してoc logs
で確認できる。(処理中の場合は-f
すれば良い)
[zaki@codeready s2isample]$ oc get build NAME TYPE FROM STATUS STARTED DURATION sample-1 Source Binary Running 17 seconds ago
[zaki@codeready s2isample]$ oc logs build/sample-1 Receiving source from STDIN as archive ... Caching blobs under "/var/cache/blobs". Getting image source signatures Copying blob sha256:ebd3f573bf3070c27e95131569c9cadc0b791dfef6996010348fb5ed17853aa2 Copying blob sha256:4fbc3bafa3d4400bb97a733c1fe12f2f99bf38b9d5b913d5034f29798739654d Copying blob sha256:e778df67329e8784693961cf548c61861486b53c86af171baef51fe1e93ba6cb Copying blob sha256:34971b2d1eb98e410c2802e6bb3a7f499f9c5bff1b870ed329089ecdb21cf856 Copying blob sha256:1cd26fbb2b2181a18c41ece85dff8b293e399e50dc3abe227b1940450bf6298b Copying config sha256:acdb46932f4b462ef9268ed012f05fed711aea57a0bbeb8b405547bb8b454c65 Writing manifest to image destination Storing signatures Generating dockerfile with builder image image-registry.openshift-image-registry.svc:5000/openshift/perl@sha256:9ce4be160b024ba1a98b87f568ab26361251001370951e0c06f057f8d27fb2ae STEP 1: FROM image-registry.openshift-image-registry.svc:5000/openshift/perl@sha256:9ce4be160b024ba1a98b87f568ab26361251001370951e0c06f057f8d27fb2ae STEP 2: LABEL "io.openshift.build.image"="image-registry.openshift-image-registry.svc:5000/openshift/perl@sha256:9ce4be160b024ba1a98b87f568ab26361251001370951e0c06f057f8d27fb2ae" "io.openshift.build.source-location"="/tmp/build/inputs" STEP 3: ENV OPENSHIFT_BUILD_NAME="sample-1" OPENSHIFT_BUILD_NAMESPACE="s2i-sample" STEP 4: USER root STEP 5: COPY upload/scripts /tmp/scripts STEP 6: COPY upload/src /tmp/src STEP 7: RUN chown -R 1001:0 /tmp/scripts /tmp/src time="2020-02-20T11:30:33Z" level=warning msg="pkg/chroot: error unmounting \"/tmp/buildah069784024/mnt/rootfs\": error checking if \"/tmp/buildah069784024/mnt/rootfs/sys/fs/cgroup/memory\" is mounted: no such file or directory" time="2020-02-20T11:30:33Z" level=warning msg="pkg/bind: error unmounting \"/tmp/buildah069784024/mnt/rootfs\": error checking if \"/tmp/buildah069784024/mnt/rootfs/sys/fs/cgroup/memory\" is mounted: no such file or directory" STEP 8: USER 1001 STEP 9: RUN /tmp/scripts/assemble time="2020-02-20T11:30:34Z" level=warning msg="pkg/chroot: error unmounting \"/tmp/buildah171662949/mnt/rootfs\": error checking if \"/tmp/buildah171662949/mnt/rootfs/sys/fs/cgroup/memory\" is mounted: no such file or directory" time="2020-02-20T11:30:34Z" level=warning msg="pkg/bind: error unmounting \"/tmp/buildah171662949/mnt/rootfs\": error checking if \"/tmp/buildah171662949/mnt/rootfs/sys/fs/cgroup/memory\" is mounted: no such file or directory" STEP 10: CMD /tmp/scripts/run STEP 11: COMMIT temp.builder.openshift.io/s2i-sample/sample-1:380cc258 Getting image source signatures Copying blob sha256:5a92562cd21a35fc8c0ee33c58d88c5b6c1417cde40a2a83e630232435c4c1d5 Copying blob sha256:18ea39fd2965157d2f9011362919a340e134b5a053ebeca62a118f90983cbd58 Copying blob sha256:ac49297ec1c627dbf5c8cd6c447675489ec3550e7651534344bb9f27c4dae6db Copying blob sha256:6953a567dfff60c737bdf8e0d614b2474d472a62ef7263e9bd704570116bbc7b Copying blob sha256:133a5862e485a4ab3527e93d2b2bd8cacab1ce7d7a48c2c6d3bc869f8ccd11df Copying blob sha256:d58f12a4557c807a0550f122c47654824d5f5796b0d45a276cfb6fabe6254225 Copying config sha256:d000858f0572b8217edf7a33cdb13db49380cc7536a860c3b93476ce1e6958d8 Writing manifest to image destination Storing signatures d000858f0572b8217edf7a33cdb13db49380cc7536a860c3b93476ce1e6958d8 Pushing image image-registry.openshift-image-registry.svc:5000/s2i-sample/sample:latest ... Getting image source signatures Copying blob sha256:d58f12a4557c807a0550f122c47654824d5f5796b0d45a276cfb6fabe6254225 Copying blob sha256:e778df67329e8784693961cf548c61861486b53c86af171baef51fe1e93ba6cb Copying blob sha256:1cd26fbb2b2181a18c41ece85dff8b293e399e50dc3abe227b1940450bf6298b Copying blob sha256:34971b2d1eb98e410c2802e6bb3a7f499f9c5bff1b870ed329089ecdb21cf856 Copying blob sha256:ebd3f573bf3070c27e95131569c9cadc0b791dfef6996010348fb5ed17853aa2 Copying blob sha256:4fbc3bafa3d4400bb97a733c1fe12f2f99bf38b9d5b913d5034f29798739654d Copying config sha256:d000858f0572b8217edf7a33cdb13db49380cc7536a860c3b93476ce1e6958d8 Writing manifest to image destination Storing signatures Successfully pushed image-registry.openshift-image-registry.svc:5000/s2i-sample/sample@sha256:d30a49e68a52c51de4c09d62a7d6c95b83df329b221d9c0d78f0e966ac98b1e5 Push successful
ビルドが完了するとイメージが内部レジストリ(default/docker-registry)へpushされる。
Push successful
が出力されていれば正常に完了している。
ここまで確認できれば、build podも処理完了となる。
[zaki@codeready s2isample]$ oc get pod NAME READY STATUS RESTARTS AGE sample-1-build 0/1 Completed 0 100s
[zaki@codeready s2isample]$ oc get all NAME READY STATUS RESTARTS AGE pod/sample-1-build 0/1 Completed 0 103s NAME TYPE FROM LATEST buildconfig.build.openshift.io/sample Source Binary 1 NAME TYPE FROM STATUS STARTED DURATION build.build.openshift.io/sample-1 Source Binary Complete About a minute ago 43s NAME IMAGE REPOSITORY TAGS UPDATED imagestream.image.openshift.io/sample default-route-openshift-image-registry.apps-crc.testing/s2i-sample/sample latest About a minute ago
ImageStreamも更新されている。
アプリのデプロイ
デプロイ
[zaki@codeready s2isample]$ oc new-app sample --> Found image d000858 (2 minutes old) in image stream "s2i-sample/sample" under tag "latest" for "sample" Apache 2.4 with mod_perl/5.26 ----------------------------- Perl 5.26 available as container is a base platform for building and running various Perl 5.26 applications and frameworks. Perl is a high-level programming language with roots in C, sed, awk and shell scripting. Perl is good at handling processes and files, and is especially good at handling text. Perl's hallmarks are practicality and efficiency. While it is used to do a lot of different things, Perl's most common applications are system administration utilities and web programming. Tags: builder, perl, perl526 * This image will be deployed in deployment config "sample" * Port 8080/tcp will be load balanced by service "sample" * Other containers can access this service through the hostname "sample" --> Creating resources ... deploymentconfig.apps.openshift.io "sample" created service "sample" created --> Success Application is not exposed. You can expose services to the outside world by executing one or more of the commands below: 'oc expose svc/sample' Run 'oc status' to view your app.
deploy pod(pod/sample-1-deploy
)がまず起動し、アプリケーションpod(下の出力であればpod/sample-1-8m17z
)がデプロイされる。
[zaki@codeready s2isample]$ oc get dc,rc,pod,svc NAME REVISION DESIRED CURRENT TRIGGERED BY deploymentconfig.apps.openshift.io/sample 1 1 1 config,image(sample:latest) NAME DESIRED CURRENT READY AGE replicationcontroller/sample-1 1 1 1 37s NAME READY STATUS RESTARTS AGE pod/sample-1-8ml7z 1/1 Running 0 28s pod/sample-1-build 0/1 Completed 0 3m49s pod/sample-1-deploy 0/1 Completed 0 37s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/sample ClusterIP 172.30.31.179 <none> 8080/TCP 37s
podがRunningになれば成功
route作成
[zaki@codeready s2isample]$ oc expose svc/sample route.route.openshift.io/sample exposed [zaki@codeready s2isample]$ oc get route NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD sample sample-s2i-sample.apps-crc.testing sample 8080-tcp None
動作確認
[zaki@codeready s2isample]$ curl http://sample-s2i-sample.apps-crc.testing カレーは粉でできてるのでカロリーゼロ
期待通りの動作
アプリの更新
ソースコードを変更し、リビルドするには。
ソースの更新
ちょっと文言を更新します。
[zaki@codeready s2isample]$ diff -u sockserv.pl.org sockserv.pl --- sockserv.pl.org 2020-02-20 20:25:31.230606041 +0900 +++ sockserv.pl 2020-02-20 20:38:27.725759274 +0900 @@ -16,7 +16,7 @@ for (;;) { if (my $client = $server->accept()) { print "connected from " . $client->peerhost() . "\n"; - print $client "カレーは粉でできてるのでカロリーゼロ\n"; + print $client "カレーは汗をかくのでスポーツ\n"; close $client; print "disconected...\n"; }
リビルド
[zaki@codeready s2isample]$ oc start-build sample --from-dir=. Uploading directory "." as binary input for the build ... .. Uploading finished build.build.openshift.io/sample-2 started
※ --from-dir
は必要(無いとイメージに転送するファイルを特定できないためエラーになる)
新しいバージョンのbuild podが実行され、イメージがビルドされる。
[zaki@codeready s2isample]$ oc get build NAME TYPE FROM STATUS STARTED DURATION sample-1 Source Binary Complete 9 minutes ago 43s sample-2 Source Binary Running 12 seconds ago
しばらく待てばビルド完了
[zaki@codeready s2isample]$ oc get build NAME TYPE FROM STATUS STARTED DURATION sample-1 Source Binary Complete 10 minutes ago 43s sample-2 Source Binary Complete 49 seconds ago 43s
ビルド完了と共に新しいイメージが内部レジストリへpushさる。
その際にDeploymentConfigのTriggerにimage
が設定されているため、「イメージが更新された」というトリガーによってアプリケーションpodも再作成される。
[zaki@codeready s2isample]$ oc get dc NAME REVISION DESIRED CURRENT TRIGGERED BY sample 2 1 1 config,image(sample:latest)
[zaki@codeready s2isample]$ oc get pod NAME READY STATUS RESTARTS AGE sample-1-8ml7z 1/1 Running 0 6m55s sample-1-build 0/1 Completed 0 10m sample-1-deploy 0/1 Completed 0 7m4s sample-2-build 0/1 Completed 0 53s sample-2-deploy 1/1 Running 0 10s sample-2-xx796 0/1 ContainerCreating 0 1s [zaki@codeready s2isample]$ oc get pod NAME READY STATUS RESTARTS AGE sample-1-8ml7z 1/1 Terminating 0 7m22s sample-1-build 0/1 Completed 0 10m sample-1-deploy 0/1 Completed 0 7m31s sample-2-build 0/1 Completed 0 80s sample-2-deploy 0/1 Completed 0 37s sample-2-xx796 1/1 Running 0 28s
結果、podも新しいバージョン(sample-2-xvwld
)となり、start-build
によるイメージのビルドからpodデプロイまでが自動で行われる。
[zaki@codeready s2isample]$ curl http://sample-s2i-sample.apps-crc.testing カレーは汗をかくのでスポーツ
新しいソースコードでアプリケーションpodが動作している。
これだけだと「ふーん、、で…?」という感じが少ししますが、これをGitリポジトリと連携・さらにWebhookを設定すれば、ソースをリポジトリへpushすれば最新のソースコードでアプリケーションpodをリビルド&デプロイ、という構成を作れるようになるので、それは次の記事で。
今日のOpenShiftウェビナー第3回でも、ちょうどコンテナアプリケーションのビルド&デプロイのお話がありS2Iも出てくるので、ご参考ください。