zaki work log

作業ログやら生活ログやらなんやら

[OpenShift]自作のS2Iスクリプトを使ってローカルにあるPerlスクリプトをS2Iビルドしてデプロイする

単体で動作する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について

詳しく説明されている方が何人もいらっしゃるのでそちらを参考してください。。

qiita.com

nekop.hatenablog.com

enakai00.hatenablog.com

一言で言うと、Dockerfileを使わずにコンテナイメージをビルドする仕組みです。
Dockerfileの完全な代替になるかというと、そうではないですが、

  • イメージ作成前の処理(ソースコードから実行バイナリをビルドしたり)
  • イメージ内のファイル構成(必要なパッケージをyumしたり↑のバイナリを配置したりスクリプトコピーしたり)
  • コンテナデプロイ時に実行する処理(CMDから呼ばれるスクリプトの実装)

といったことをS2Iで実現できます。
逆に、

  • Listenするポート番号を指定(EXPOSE)
  • Volumeを指定(VOLUME)

のようにイメージそのもののメタな設定はS2Iでは(たぶん)行えません。
これは、S2Iビルドする際のベースイメージ側で設定しておきます。(本記事ではこちらは扱いません)

OpenShiftではデフォルトでS2Iの仕組みを利用でき、S2Iが期待している構成をしているアプリケーションであれば、oc new-appでpodをデプロイする際にそのアプリケーションのリポジトリを指定するだけで勝手にビルド&デプロイを行うことができる便利なツールです。

例えばこんな感じ

zaki-hmkc.hatenablog.com

ただし、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さんのブログにも説明がある通り、作りは単純です。

  1. --scripts-url指定のディレクト
  2. .sti/binディレクト
  3. label io.openshift.s2i.scripts-urlで指定のディレクト

の順序で、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をリビルド&デプロイ、という構成を作れるようになるので、それは次の記事で。

zaki-hmkc.hatenablog.com


今日のOpenShiftウェビナー第3回でも、ちょうどコンテナアプリケーションのビルド&デプロイのお話がありS2Iも出てくるので、ご参考ください。

www.redhat.com