nekop's blog

OpenShift / JBoss / WildFly / Infinispanの中の人 http://twitter.com/nekop

OpenShiftやKubernetes上でJavaを動かす際の注意

OpenShift 全部俺 Advent Calendar 2017

OpenShiftやKubernetes上でリソース制限を設定したコンテナでJavaを動かすとき、デフォルト設定のままだとパフォーマンスが悪くなったり、oom-killerに殺されたりします。これはコンテナのcgroupsの制限をJavaが考慮しないためです。

Javaはデフォルトでホストのメモリの1/4を最大Javaヒープメモリに設定し、Java VM本体、スタック、Metaspaceなどの非Javaヒープ領域を含めると最終的にその倍程度のメモリを利用します。たとえば16GBのマシンだと8GBくらいです。これを4GBなどの制限で動作させるとメモリが確保できずエラー終了もしくはoom-killerに殺されます。また、GCThreadsなども効率を最大化するためにCPUコア数とスレッド数を同一に設定します(厳密には8コア超えると変わりますが省略します)。これはメモリほどではありませんが、パフォーマンスを低下させる原因となることがあります。詳細は以下の記事にまとまっています。

https://engineering.linkedin.com/blog/2016/11/application-pauses-when-running-jvm-inside-linux-control-groups

JavaヒープだけならJava 8u131以降なら-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeapで調整してくれるようですが、大抵それだけではなくて他にも調整したいので起動スクリプトでいろいろやることになります。

Red Hatがメンテナンスしている起動スクリプトなどは以下のリポジトリに置いてあります。

https://github.com/jboss-openshift/cct_module/blob/master/os-java-run/added/java-default-options

CPUコア数にもとづいた自動調整はJava VMだけではなく、各種ライブラリでも利用されています。例えば非同期I/Oのワーカースレッドなどは大抵CPUコア数を取得してスレッドプールのサイズを設定します。そういったライブラリの設定もチューニングする必要がでてくるかもしれません。以下のAPIを利用しているものが要注意です。

Runtime.getRuntime().getMaxMemory()
Runtime.getRuntime().availableProcessors()

タイトルには「Javaの」と書きましたが、Javaがデフォルトでオートチューニングするためハイライトしただけで、他の言語やライブラリでも同じことをしていれば同じ問題は発生するのでJavaに限定した問題ではないことに注意してください。ライブラリ実装者などはこのようなオートチューニングを実装する時はcgroups対応を考慮すると良いでしょう。

OpenShiftのRouteとRouter

OpenShift 全部俺 Advent Calendar 2017

OpenShiftにはKubernetesで言うところのIngress, Ingress ControllerであるRouteとRouterが標準で利用できます。Kubernetes 1.0のころにはIngressが無かったので、独自に実装されました。

https://docs.openshift.org/3.11/architecture/networking/haproxy-router.html

https://docs.openshift.org/3.11/architecture/networking/routes.html

OpenShift RouterはフロントエンドロードバランサとしてHAProxyを利用しています。また、HAProxyを起動したり、設定を更新したり、リロードしたりするopenshift-routerプロセスがコンテナプロセスとなっています。Routerはhostnetowrkを利用して80ポートと443ポートをLISTENしており、HTTPリクエストのHostヘッダもしくはTLS SNIのホスト名に対応するPodのエンドポイントにトラフィックを転送します。よくある勘違いとしてRouterはServiceのClusterIPにリクエストを転送する、というのがありますが、Serviceは基本的にヘルスチェックが不完全なL4ロードバランスとなっているため、Serviceにリクエストを転送したりはしません。RouterはPodのEndpointをバックエンドサーバとして登録するようになっており、そちらに転送します。oc exposeではServiceを指定するとRouteが作成されることがこの誤解の原因の一つだと思いますが、設定ファイルhaproxy.configを見るとそれは正しくないことがわかります。Route経由のアクセスでは、Serviceは設定のためのリソースとしてのみ利用されており、ServiceのClusterIPやL4ロードバランス機能は利用されていません。L7でのヘルスチェックありの振り分け制御となります。

OpenShift Routerはdefaultプロジェクトにデプロイされています。

$ oc project default
$ oc get all -o wide
NAME                                REVISION   DESIRED   CURRENT   TRIGGERED BY
deploymentconfigs/docker-registry   1          1         1         config
deploymentconfigs/router            1          1         1         config

NAME                               READY     STATUS      RESTARTS   AGE       IP                NODE
po/docker-registry-1-694ml         1/1       Running     2          14d       172.17.0.2        localhost
po/persistent-volume-setup-hpd4l   0/1       Completed   0          14d       172.17.0.2        localhost
po/router-1-sv5j8                  1/1       Running     2          14d       192.168.124.246   localhost

NAME                   DESIRED   CURRENT   READY     AGE       CONTAINER(S)   IMAGE(S)                                  SELECTOR
rc/docker-registry-1   1         1         1         14d       registry       openshift/origin-docker-registry:v3.7.0   deployment=docker-registry-1,deploymentconfig=docker-registry,docker-registry=default
rc/router-1            1         1         1         14d       router         openshift/origin-haproxy-router:v3.7.0    deployment=router-1,deploymentconfig=router,router=router

NAME                  CLUSTER-IP     EXTERNAL-IP   PORT(S)                   AGE       SELECTOR
svc/docker-registry   172.30.1.1     <none>        5000/TCP                  14d       docker-registry=default
svc/kubernetes        172.30.0.1     <none>        443/TCP,53/UDP,53/TCP     14d       <none>
svc/router            172.30.40.34   <none>        80/TCP,443/TCP,1936/TCP   14d       router=router

NAME                           DESIRED   SUCCESSFUL   AGE       CONTAINER(S)        IMAGE(S)                  SELECTOR
jobs/persistent-volume-setup   1         1            14d       storage-setup-job   openshift/origin:v3.7.0   controller-uid=26ab5554-d62e-11e7-ace1-525400f3baa1

docker-registryのRouteを作ってアクセスてみましょう。自分のマシン -> Routerホスト -> RouterのHAProxy -> ターゲットPod、というフローとなります。アプリケーションURLに利用するホスト名はDNSでRouterホストに解決するように設定します。minishiftではdocker-registryはhttpでRouteなしでセットアップされますが、通常のインストールではhttps passthroughのRouteが最初から設定済みなので注意してください。

$ oc expose service docker-registry 
route "docker-registry" exposed
$ oc get route
NAME              HOST/PORT                                       PATH      SERVICES          PORT       TERMINATION   WILDCARD
docker-registry   docker-registry-default.192.168.42.225.nip.io             docker-registry   5000-tcp                 None
$ curl http://docker-registry-default.192.168.42.225.nip.io/v2/
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":null}]}

Router内を見てみましょう。プロセスはopenshift-routerとhaproxyの二つです。

$ oc rsh dc/router sh -c "ps -eaf | cat"
UID        PID  PPID  C STIME TTY          TIME CMD
1001         1     0  0 Dec11 ?        00:16:49 /usr/bin/openshift-router
1001       248     1  0 Dec11 ?        00:00:16 /usr/sbin/haproxy -f /var/lib/haproxy/conf/haproxy.config -p /var/lib/haproxy/run/haproxy.pid -sf 224

中身の/var/lib/haproxyの構成は以下のようになっています。

$ oc rsh dc/router sh -c "pwd && ls -Rla /var/lib/haproxy"
/var/lib/haproxy/conf
/var/lib/haproxy:
total 20
drwxrwxr-x.  7 haproxy root  122 Nov 29 16:57 .
drwxr-xr-x. 16 root    root  211 Nov 29 16:57 ..
-rw-rwSr--.  1    1001 1003   30 Nov 13 22:57 .cccp.yml
-rw-rwSr--.  1    1001 1003 1327 Nov 13 22:57 Dockerfile
drwxrwxr-x.  2 root    root    6 Nov 29 16:57 bin
drwxrwsr-x.  2    1001 1003 4096 Nov 29 16:22 conf
drwxrwxr-x.  2 root    root    6 Nov 29 16:57 log
-rwxrwsr-x.  1    1001 1003 4979 Nov 13 22:57 reload-haproxy
drwxrwxr-x.  4 root    root   53 Dec 11 06:07 router
drwxrwxr-x.  2 root    root   45 Dec 11 06:13 run

/var/lib/haproxy/bin:
total 0
drwxrwxr-x. 2 root    root   6 Nov 29 16:57 .
drwxrwxr-x. 7 haproxy root 122 Nov 29 16:57 ..

/var/lib/haproxy/conf:
total 72
drwxrwsr-x. 2    1001 1003  4096 Nov 29 16:22 .
drwxrwxr-x. 7 haproxy root   122 Nov 29 16:57 ..
-rw-rw-r--. 1 root    root     0 Dec 11 06:13 cert_config.map
-rw-rwSr--. 1    1001 1003  2035 Nov 13 22:57 default_pub_keys.pem
-rw-rwSr--. 1    1001 1003  3278 Nov 13 22:57 error-page-503.http
-rw-rwSr--. 1    1001 1003 30599 Nov 13 22:57 haproxy-config.template
-rw-rw-r--. 1 root    root 10051 Dec 11 06:13 haproxy.config
-rw-rw-r--. 1 root    root    88 Dec 11 06:13 os_edge_http_be.map
-rw-rw-r--. 1 root    root    94 Dec 11 06:13 os_http_be.map
-rw-rw-r--. 1 root    root     0 Dec 11 06:13 os_reencrypt.map
-rw-rw-r--. 1 root    root     0 Dec 11 06:13 os_route_http_expose.map
-rw-rw-r--. 1 root    root    88 Dec 11 06:13 os_route_http_redirect.map
-rw-rw-r--. 1 root    root     0 Dec 11 06:13 os_sni_passthrough.map
-rw-rw-r--. 1 root    root     0 Dec 11 06:13 os_tcp_be.map
-rw-rw-r--. 1 root    root     1 Dec 11 06:13 os_wildcard_domain.map

/var/lib/haproxy/log:
total 0
drwxrwxr-x. 2 root    root   6 Nov 29 16:57 .
drwxrwxr-x. 7 haproxy root 122 Nov 29 16:57 ..

/var/lib/haproxy/router:
total 4
drwxrwxr-x. 4 root    root   53 Dec 11 06:07 .
drwxrwxr-x. 7 haproxy root  122 Nov 29 16:57 ..
drwxrwxr-x. 2 root    root    6 Nov 29 16:57 cacerts
drwxrwxr-x. 2 root    root    6 Nov 29 16:57 certs
-rw-r--r--. 1    1001 root 1378 Dec 11 06:13 routes.json

/var/lib/haproxy/router/cacerts:
total 0
drwxrwxr-x. 2 root root  6 Nov 29 16:57 .
drwxrwxr-x. 4 root root 53 Dec 11 06:07 ..

/var/lib/haproxy/router/certs:
total 0
drwxrwxr-x. 2 root root  6 Nov 29 16:57 .
drwxrwxr-x. 4 root root 53 Dec 11 06:07 ..

/var/lib/haproxy/run:
total 4
drwxrwxr-x. 2 root    root  45 Dec 11 06:13 .
drwxrwxr-x. 7 haproxy root 122 Nov 29 16:57 ..
-rw-r--r--. 1    1001 root   4 Dec 11 06:13 haproxy.pid
srw-------. 1    1001 root   0 Dec 11 06:13 haproxy.sock

openshift-routerはhaproxy-config.templateからhaproxy.configファイルを生成しますが、テンプレートファイルの読み込みは起動時一回のみ行われるのでカスタマイズしたい場合にoc rshなどでコンテナ内で書き換えても意味がないことに注意してください。ConfigMapで差し替えることができます。

HAProxyはログ出力先としてsyslogしかサポートしていないため、運用環境では少なくともROUTER_SYSLOG_ADDRESSは設定しましょう。これは扱いづらいので、OpenShiftでもどうにかしようという議論は長く続いていますが、まだ解決されていません。

追記: syslogコンテナをsidecarにする方法などがあります。

access.redhat.com

ドキュメントに載っているアノテーション環境変数での設定などの他に、コマンドラインパラメータにもいくつか設定があるので載せておきます。

$ oc rsh dc/router openshift-router -h
Start a router 

This command launches a router connected to your cluster master. The router listens for routes and endpoints created by users and keeps a local router configuration up to date with those changes. 

You may customize the router by providing your own --template and --reload scripts. 

The router must have a default certificate in pem format. You may provide it via --default-cert otherwise one is automatically created. 

You may restrict the set of routes exposed to a single project (with --namespace), projects your client has access to with a set of labels (--project-labels), namespaces matching a label (--namespace-labels), or all namespaces (no argument). You can limit the routes to those matching a --labels or --fields selector. Note that you must have a cluster-wide administrative role to view all namespaces.

Usage:
  openshift-router --master=<addr> [flags]
  openshift-router [command]

Available Commands:
  version     Display client and server versions

Flags:
      --allow-wildcard-routes                    Allow wildcard host names for routes
      --allowed-domains stringSlice              List of comma separated domains to allow in routes. If specified, only the domains in this list will be allowed routes. Note that domains in the denied list take precedence over the ones in the allowed list
      --as string                                Username to impersonate for the operation
      --as-group stringArray                     Group to impersonate for the operation, this flag can be repeated to specify multiple groups.
      --azure-container-registry-config string   Path to the file container Azure container registry configuration information.
      --bind-ports-after-sync                    Bind ports only after route state has been synchronized
      --certificate-authority string             Path to a cert file for the certificate authority
      --ciphers string                           Specifies the cipher suites to use. You can choose a predefined cipher set ('modern', 'intermediate', or 'old') or specify exact cipher suites by passing a : separated list.
      --client-certificate string                Path to a client certificate file for TLS
      --client-key string                        Path to a client key file for TLS
      --cluster string                           The name of the kubeconfig cluster to use
      --config string                            Path to the config file to use for CLI requests.
      --context string                           The name of the kubeconfig context to use
      --default-certificate string               The contents of a default certificate to use for routes that don't expose a TLS server cert; in PEM format
      --default-certificate-dir string           A path to a directory that contains a file named tls.crt. If tls.crt is not a PEM file which also contains a private key, it is first combined with a file named tls.key in the same directory. The PEM-format contents are then used as the default certificate. Only used if default-certificate and default-certificate-path are not specified. (default "/etc/pki/tls/private")
      --default-certificate-path string          A path to default certificate to use for routes that don't expose a TLS server cert; in PEM format (default "/etc/pki/tls/private/tls.crt")
      --default-destination-ca-path string       A path to a PEM file containing the default CA bundle to use with re-encrypt routes. This CA should sign for certificates in the Kubernetes DNS space (service.namespace.svc). (default "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt")
      --denied-domains stringSlice               List of comma separated domains to deny in routes
      --disable-namespace-ownership-check        Disables the namespace ownership checks for a route host with different paths or for overlapping host names in the case of wildcard routes. Please be aware that if namespace ownership checks are disabled, routes in a different namespace can use this mechanism to 'steal' sub-paths for existing domains. This is only safe if route creation privileges are restricted, or if all the users can be trusted.
      --enable-ingress                           Enable configuration via ingress resources
      --extended-validation                      If set, then an additional extended validation step is performed on all routes admitted in by this router. Defaults to true and enables the extended validation checks. (default true)
      --fields string                            A field selector to apply to routes to watch
      --google-json-key string                   The Google Cloud Platform Service Account JSON Key to use for authentication.
      --hostname-template string                 If specified, a template that should be used to generate the hostname for a route without spec.host (e.g. '${name}-${namespace}.myapps.mycompany.com')
      --include-udp-endpoints                    If true, UDP endpoints will be considered as candidates for routing
      --insecure-skip-tls-verify                 If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
      --interval duration                        Controls how often router reloads are invoked. Mutiple router reload requests are coalesced for the duration of this interval since the last reload time. (default 5s)
      --kubernetes string                        The address of the Kubernetes server (host, host:port, or URL). If omitted defaults to the master. (default "http://localhost:8080")
      --labels string                            A label selector to apply to the routes to watch
      --listen-addr string                       The name of an interface to listen on to expose metrics and health checking. If not specified, will not listen. Overrides stats port. (default "0.0.0.0:1936")
      --log-flush-frequency duration             Maximum number of seconds between log flushes (default 5s)
      --loglevel int32                           Set the level of log output (0-10)
      --logspec string                           Set per module logging with file|pattern=LEVEL,...
      --master string                            The address the master can be reached on (host, host:port, or URL). (default "http://localhost:8080")
      --max-connections string                   Specifies the maximum number of concurrent connections.
      --metrics-type string                      Specifies the type of metrics to gather. Supports 'haproxy'. (default "haproxy")
      --name string                              The name the router will identify itself with in the route status (default "router")
  -n, --namespace string                         If present, the namespace scope for this CLI request
      --namespace-labels string                  A label selector to apply to namespaces to watch
      --override-hostname                        Override the spec.host value for a route with --hostname-template
      --project-labels string                    A label selector to apply to projects to watch; if '*' watches all projects the client can access
      --reload string                            The path to the reload script to use (default "/var/lib/haproxy/reload-haproxy")
      --request-timeout string                   The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
      --resync-interval duration                 The interval at which the route list should be fully refreshed (default 10m0s)
      --router-canonical-hostname string         CanonicalHostname is the external host name for the router that can be used as a CNAME for the host requested for this route. This value is optional and may not be set in all cases.
      --server string                            The address and port of the Kubernetes API server
      --stats-password string                    If the underlying router implementation can provide statistics this is the requested password for auth. (default "x6RVrWmXwV")
      --stats-port string                        If the underlying router implementation can provide statistics this is a hint to expose it on this port. Ignored if listen-addr is specified. (default "1936")
      --stats-user string                        If the underlying router implementation can provide statistics this is the requested username for auth. (default "admin")
      --strict-sni                               Use strict-sni bind processing (do not use default cert).
      --template string                          The path to the template file to use (default "/var/lib/haproxy/conf/haproxy-config.template")
      --token string                             Bearer token for authentication to the API server
      --user string                              The name of the kubeconfig user to use
      --version version[=true]                   Print version information and quit
      --working-dir string                       The working directory for the router plugin (default "/var/lib/haproxy/router")

Use "openshift-router [command] --help" for more information about a command.

OpenShiftでStatefulSetを使ってみる

OpenShift 全部俺 Advent Calendar 2017

今までは普通のデプロイメントを利用していましたが、今回はちょっと特殊な性格を持つStatefulSetを使ってみましょう。StatefulSetのレプリカは連番のsuffixが付与され、スケール時の順序制御があったりvolumeClaimTemplatesによる個々のPodへそれぞれPVを割り当てたりできます。主にクラスタ系の分散前提のソフトウェアに利用されます。

https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/

StatefulSetはKubernetes 1.8の時点でbeta機能であり、OpenShiftでも3.5から含まれてはいますがまだフルサポートされていません。

いつものRubyプロジェクトをベースにします。

$ oc new-project test-ruby
$ oc new-app http://github.com/nekop/hello-sinatra
$ oc get all
NAME                         TYPE      FROM      LATEST
buildconfigs/hello-sinatra   Source    Git       1

NAME                     TYPE      FROM          STATUS     STARTED          DURATION
builds/hello-sinatra-1   Source    Git@cf28c79   Complete   13 minutes ago   47s

NAME                         DOCKER REPO                                                TAGS      UPDATED
imagestreams/hello-sinatra   docker-registry.default.svc:5000/test-ruby/hello-sinatra   latest    12 minutes ago

NAME                              REVISION   DESIRED   CURRENT   TRIGGERED BY
deploymentconfigs/hello-sinatra   1          1         1         config,image(hello-sinatra:latest)

NAME                       READY     STATUS      RESTARTS   AGE
po/hello-sinatra-1-build   0/1       Completed   0          13m
po/hello-sinatra-1-s2ls7   1/1       Running     0          12m

NAME                 DESIRED   CURRENT   READY     AGE
rc/hello-sinatra-1   1         1         1         12m

NAME                   CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
svc/hello-sinatra      172.30.82.101   <none>        8080/TCP   13m

ここでビルドされたイメージをデプロイするStatefulSetを作ってみましょう。まだbeta機能なのでyamlを直接記述する必要があります。

$ cat <<EOF | oc create -f -
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: hello-sinatra-ss
spec:
  replicas: 2
  serviceName: hello-sinatra-ss
  template:
    metadata:
      labels:
        app: hello-sinatra-ss
    spec:
      containers:
      - image: docker-registry.default.svc:5000/test-ruby/hello-sinatra:latest
        imagePullPolicy: Always
        name: hello-sinatra-ss
        ports:
        - containerPort: 8080
          protocol: TCP
        resources: {}
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: hello-sinatra-ss
  name: hello-sinatra-ss
spec:
  ports:
  - name: 8080-tcp
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: hello-sinatra-ss
EOF

これでStatefulSetが作成され、指定した通りレプリカが2つ起動しています。

$ oc get all
NAME                         TYPE      FROM      LATEST
buildconfigs/hello-sinatra   Source    Git       1

NAME                     TYPE      FROM          STATUS     STARTED              DURATION
builds/hello-sinatra-1   Source    Git@cf28c79   Complete   About a minute ago   47s

NAME                         DOCKER REPO                                                TAGS      UPDATED
imagestreams/hello-sinatra   docker-registry.default.svc:5000/test-ruby/hello-sinatra   latest    About a minute ago

NAME                              REVISION   DESIRED   CURRENT   TRIGGERED BY
deploymentconfigs/hello-sinatra   1          1         1         config,image(hello-sinatra:latest)

NAME                       READY     STATUS      RESTARTS   AGE
po/hello-sinatra-1-build   0/1       Completed   0          1m
po/hello-sinatra-1-s2ls7   1/1       Running     0          1m
po/hello-sinatra-ss-0      1/1       Running     0          12s
po/hello-sinatra-ss-1      1/1       Running     0          9s

NAME                 DESIRED   CURRENT   READY     AGE
rc/hello-sinatra-1   1         1         1         1m

NAME                   CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
svc/hello-sinatra      172.30.82.101   <none>        8080/TCP   1m
svc/hello-sinatra-ss   172.30.165.8    <none>        8080/TCP   12s

NAME                            DESIRED   CURRENT   AGE
statefulsets/hello-sinatra-ss   2         2         12s

OpenShiftのs2iイメージをカスタマイズする

OpenShift 全部俺 Advent Calendar 2017

昨日は忘年会でした。全部俺 Advent Calendarがすごく大変そう、と言われましたが各エントリ20分程度で書いていますし、そうなるように内容を調整しています。例えば昨日のエントリは17時くらいに同僚に何書いて欲しい?と聞いてから最新の情報をちょっと調べて書いて飲みに行った、くらいのノリです。

今日はデフォルトのs2iイメージをカスタマイズしてみましょう。例ではRubyを利用します。

$ oc new-project test-custom-s2i
$ oc new-app http://github.com/nekop/hello-sinatra
$ oc get all
oc NAME                         TYPE      FROM      LATEST
buildconfigs/hello-sinatra   Source    Git       1

NAME                     TYPE      FROM          STATUS    STARTED          DURATION
builds/hello-sinatra-1   Source    Git@cf28c79   Running   49 seconds ago   

NAME                         DOCKER REPO                                                      TAGS      UPDATED
imagestreams/hello-sinatra   docker-registry.default.svc:5000/test-custom-s2i/hello-sinatra             

NAME                              REVISION   DESIRED   CURRENT   TRIGGERED BY
deploymentconfigs/hello-sinatra   0          1         0         config,image(hello-sinatra:latest)

NAME                       READY     STATUS    RESTARTS   AGE
po/hello-sinatra-1-build   1/1       Running   0          49s

oc describe bc hello-sinatra、そしてoc describe is ruby -n openshiftと追っていくと、利用されているImageStreamruby:2.4registry.access.redhat.com/rhscl/ruby-24-rhel7:latestを参照していることがわかります。

このイメージをDockerfileビルドを利用してカスタマイズしていきます。今回は単純にtouch /foobarなどとしてみます。

cat <<EOF | oc new-build --dockerfile=- --to=custom-ruby
FROM registry.access.redhat.com/rhscl/ruby-24-rhel7:latest
USER root
RUN touch /foobar
USER 1001
EOF

ビルドが開始されます。

$ oc get all
NAME                         TYPE      FROM         LATEST
buildconfigs/custom-ruby     Docker    Dockerfile   1
buildconfigs/hello-sinatra   Source    Git          1

NAME                     TYPE      FROM          STATUS     STARTED          DURATION
builds/custom-ruby-1     Docker    Dockerfile    Pending                     
builds/hello-sinatra-1   Source    Git@cf28c79   Complete   10 minutes ago   1m3s

NAME                         DOCKER REPO                                                      TAGS      UPDATED
imagestreams/custom-ruby     docker-registry.default.svc:5000/test-custom-s2i/custom-ruby               
imagestreams/hello-sinatra   docker-registry.default.svc:5000/test-custom-s2i/hello-sinatra   latest    9 minutes ago
imagestreams/ruby-24-rhel7   docker-registry.default.svc:5000/test-custom-s2i/ruby-24-rhel7   latest    5 seconds ago

NAME                              REVISION   DESIRED   CURRENT   TRIGGERED BY
deploymentconfigs/hello-sinatra   1          1         1         config,image(hello-sinatra:latest)

NAME                       READY     STATUS      RESTARTS   AGE
po/custom-ruby-1-build     0/1       Init:0/1    0          4s
po/hello-sinatra-1-build   0/1       Completed   0          10m
po/hello-sinatra-1-ddx5v   1/1       Running     0          9m

NAME                 DESIRED   CURRENT   READY     AGE
rc/hello-sinatra-1   1         1         1         9m

NAME                CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
svc/hello-sinatra   172.30.223.174   <none>        8080/TCP   10m

ビルドが終わったらbc/hello-sinatraのspec.strategy.sourceStragtegyのruby:2.4custom-ruby:latestに変更します。これでカスタマイズしたs2iイメージが利用されます。

$ oc edit bc/hello-sinatra
    sourceStrategy:
      from:
        kind: ImageStreamTag
        name: ruby:2.4
        namespace: openshift

変更後はこうなります。

    sourceStrategy:
      from:
        kind: ImageStreamTag
        name: custom-ruby:latest

BuildConfigはConfigChange triggerがデフォルトで有効になっているので、ビルドが自動的に開始され、レジストリにpushされ、DeploymentConfigのImageChange triggerが発動してデプロイされます。

$ oc get all
NAME                         TYPE      FROM         LATEST
buildconfigs/custom-ruby     Docker    Dockerfile   1
buildconfigs/hello-sinatra   Source    Git          2

NAME                     TYPE      FROM          STATUS     STARTED          DURATION
builds/custom-ruby-1     Docker    Dockerfile    Complete   6 minutes ago    54s
builds/hello-sinatra-1   Source    Git@cf28c79   Complete   16 minutes ago   1m3s
builds/hello-sinatra-2   Source    Git@cf28c79   Complete   4 minutes ago    19s

NAME                         DOCKER REPO                                                      TAGS      UPDATED
imagestreams/custom-ruby     docker-registry.default.svc:5000/test-custom-s2i/custom-ruby     latest    5 minutes ago
imagestreams/hello-sinatra   docker-registry.default.svc:5000/test-custom-s2i/hello-sinatra   latest    3 minutes ago
imagestreams/ruby-24-rhel7   docker-registry.default.svc:5000/test-custom-s2i/ruby-24-rhel7   latest    6 minutes ago

NAME                              REVISION   DESIRED   CURRENT   TRIGGERED BY
deploymentconfigs/hello-sinatra   2          1         1         config,image(hello-sinatra:latest)

NAME                       READY     STATUS      RESTARTS   AGE
po/custom-ruby-1-build     0/1       Completed   0          6m
po/hello-sinatra-1-build   0/1       Completed   0          16m
po/hello-sinatra-2-build   0/1       Completed   0          4m
po/hello-sinatra-2-t4dpb   1/1       Running     0          3m

NAME                 DESIRED   CURRENT   READY     AGE
rc/hello-sinatra-1   0         0         0         15m
rc/hello-sinatra-2   1         1         1         3m

NAME                CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
svc/hello-sinatra   172.30.223.174   <none>        8080/TCP   16m

新しくデプロイされたコンテナに入ると、ちゃんとカスタマイズが反映されています。

$ oc rsh dc/hello-sinatra ls -la /foobar
-rw-r--r--. 1 root root 0 Dec 13 08:41 /foobar

OpenShiftのRBACを完全に理解する

OpenShift 全部俺 Advent Calendar 2017

よくわかりにくいと言われがちなOpenShift / KubernetesのRBACについて書いてみましょう。RBACは元々OpenShiftで実装され、それを元にKubernetes側へ実装された経緯があり、OpenShiftのclusterroleというリソースオブジェクトとKubernetesのclusterroleリソースの短縮名が衝突してしまっています。そのため、OpenShift側ではclusterrole.rbacという名前でリソースを指定する必要があります。ちなみに省略しないリソース名はそれぞれclusterrole.authorization.openshift.ioclusterrole.rbac.authorization.k8s.ioです。

さて本題。事前に定義されているロールの一覧はoc get clusterrole.rbacで取得できます。

$ oc get clusterrole.rbac
NAME                                                                  AGE
admin                                                                 12d
asb-access                                                            12d
asb-auth                                                              12d
basic-user                                                            12d
cluster-admin                                                         12d
cluster-debugger                                                      12d
cluster-reader                                                        12d
cluster-status                                                        12d
edit                                                                  12d
hawkular-metrics-admin                                                12d
management-infra-admin                                                12d
namespace-viewer                                                      12d
registry-admin                                                        12d
registry-editor                                                       12d
registry-viewer                                                       12d
sar-creator                                                           12d
self-access-reviewer                                                  12d
self-provisioner                                                      12d
service-catalog-controller                                            12d
servicecatalog-serviceclass-viewer                                    12d
storage-admin                                                         12d
sudoer                                                                12d
system:auth-delegator                                                 12d
system:basic-user                                                     12d
system:build-controller                                               12d
system:build-strategy-custom                                          12d
system:build-strategy-docker                                          12d
system:build-strategy-jenkinspipeline                                 12d
system:build-strategy-source                                          12d
system:certificate-signing-controller                                 12d
system:controller:attachdetach-controller                             12d
system:controller:certificate-controller                              12d
system:controller:cronjob-controller                                  12d
system:controller:daemon-set-controller                               12d
system:controller:deployment-controller                               12d
system:controller:disruption-controller                               12d
system:controller:endpoint-controller                                 12d
system:controller:generic-garbage-collector                           12d
system:controller:horizontal-pod-autoscaler                           12d
system:controller:job-controller                                      12d
system:controller:namespace-controller                                12d
system:controller:node-controller                                     12d
system:controller:persistent-volume-binder                            12d
system:controller:pod-garbage-collector                               12d
system:controller:replicaset-controller                               12d
system:controller:replication-controller                              12d
system:controller:resourcequota-controller                            12d
system:controller:route-controller                                    12d
system:controller:service-account-controller                          12d
system:controller:service-controller                                  12d
system:controller:statefulset-controller                              12d
system:controller:ttl-controller                                      12d
system:daemonset-controller                                           12d
system:deployer                                                       12d
system:deployment-controller                                          12d
system:deploymentconfig-controller                                    12d
system:discovery                                                      12d
system:disruption-controller                                          12d
system:endpoint-controller                                            12d
system:garbage-collector-controller                                   12d
system:gc-controller                                                  12d
system:heapster                                                       12d
system:hpa-controller                                                 12d
system:image-auditor                                                  12d
system:image-builder                                                  12d
system:image-pruner                                                   12d
system:image-puller                                                   12d
system:image-pusher                                                   12d
system:image-signer                                                   12d
system:job-controller                                                 12d
system:kube-aggregator                                                12d
system:kube-controller-manager                                        12d
system:kube-dns                                                       12d
system:kube-scheduler                                                 12d
system:master                                                         12d
system:namespace-controller                                           12d
system:node                                                           12d
system:node-admin                                                     12d
system:node-bootstrapper                                              12d
system:node-problem-detector                                          12d
system:node-proxier                                                   12d
system:node-reader                                                    12d
system:oauth-token-deleter                                            12d
system:openshift:controller:build-config-change-controller            12d
system:openshift:controller:build-controller                          12d
system:openshift:controller:cluster-quota-reconciliation-controller   12d
system:openshift:controller:deployer-controller                       12d
system:openshift:controller:deploymentconfig-controller               12d
system:openshift:controller:horizontal-pod-autoscaler                 12d
system:openshift:controller:image-import-controller                   12d
system:openshift:controller:image-trigger-controller                  12d
system:openshift:controller:origin-namespace-controller               12d
system:openshift:controller:pv-recycler-controller                    12d
system:openshift:controller:resourcequota-controller                  12d
system:openshift:controller:sdn-controller                            12d
system:openshift:controller:service-ingress-ip-controller             12d
system:openshift:controller:service-serving-cert-controller           12d
system:openshift:controller:serviceaccount-controller                 12d
system:openshift:controller:serviceaccount-pull-secrets-controller    12d
system:openshift:controller:template-instance-controller              12d
system:openshift:controller:template-service-broker                   12d
system:openshift:controller:unidling-controller                       12d
system:openshift:templateservicebroker-client                         12d
system:persistent-volume-provisioner                                  12d
system:registry                                                       12d
system:replicaset-controller                                          12d
system:replication-controller                                         12d
system:router                                                         12d
system:scope-impersonation                                            12d
system:sdn-manager                                                    12d
system:sdn-reader                                                     12d
system:statefulset-controller                                         12d
system:webhook                                                        12d
view                                                                  12d

なんだかすごくいっぱい出てきましたが、system:とついているものはシステムコンポーネントで利用されているロールで利用者視点では真っ先に無視して構わないものです。除外してみます。

$ oc get clusterrole.rbac | grep -v system:
NAME                                                                  AGE
admin                                                                 12d
asb-access                                                            12d
asb-auth                                                              12d
basic-user                                                            12d
cluster-admin                                                         12d
cluster-debugger                                                      12d
cluster-reader                                                        12d
cluster-status                                                        12d
edit                                                                  12d
hawkular-metrics-admin                                                12d
management-infra-admin                                                12d
namespace-viewer                                                      12d
registry-admin                                                        12d
registry-editor                                                       12d
registry-viewer                                                       12d
sar-creator                                                           12d
self-access-reviewer                                                  12d
self-provisioner                                                      12d
service-catalog-controller                                            12d
servicecatalog-serviceclass-viewer                                    12d
storage-admin                                                         12d
sudoer                                                                12d
view                                                                  12d

いくつかまだOpenShiftが利用する目的のロールであり利用者には無関係なロールがあるものの、だいぶすっきりしました。

各ロールが実際に何ができるかはdescribeすればわかります。たとえばnamespace-viewernamespaceオブジェクトのget list watchが許可されているロールです。

$ oc describe clusterrole.rbac namespace-viewer
Name:       namespace-viewer
Labels:     <none>
Annotations:    <none>
PolicyRule:
  Resources     Non-Resource URLs   Resource Names  Verbs
  ---------    -----------------    --------------  -----
  namespaces    []                  []              [get list watch]

カスタムのロールを作るときにResourcesやVerbsに何が指定できるかはoc describe clusterrole.rbacなどで全出力を見ればわかるでしょう。カスタムロールはプロジェクト(namespace)固有のものはrole.rbacとして、クラスタ全体で利用したいロールは管理者権限でclusterrole.rbacとして作成します。

ロールの割り当て状況はoc get clusterrolebinding.rbac -o wideを確認するとわかります。割り当てられていないロールは非表示となるので注意しましょう。以下system:を除外した結果です。

$ oc get clusterrolebinding.rbac -o wide | grep -v system:
NAME                                                                  AGE       ROLE                                                                  USERS                            GROUPS                                         SERVICEACCOUNTS
admin                                                                 12d       admin                                                                                                                                                 openshift-infra/template-instance-controller, kube-service-catalog/default, openshift-ansible-service-broker/asb
asb-access                                                            12d       asb-access                                                                                                                                            openshift-ansible-service-broker/asb-client
asb-auth                                                              12d       asb-auth                                                                                                                                              openshift-ansible-service-broker/asb
hawkular-metrics-admin                                                12d       hawkular-metrics-admin                                                                                                                                management-infra/management-admin
management-infra-admin                                                12d       management-infra-admin                                                                                                                                management-infra/management-admin
service-catalog-controller-binding                                    12d       service-catalog-controller                                                                                                                            kube-service-catalog/service-catalog-controller
service-catalog-controller-namespace-viewer-binding                   12d       namespace-viewer                                                                                                                                      kube-service-catalog/service-catalog-controller
service-catalog-namespace-viewer-binding                              12d       namespace-viewer                                                                                                                                      kube-service-catalog/service-catalog-apiserver
service-catalog-sar-creator-binding                                   12d       sar-creator                                                                                                                                           kube-service-catalog/service-catalog-apiserver

User, Group, Service Accountというのが出てきました。ユーザは一般的なユーザ、およびシステムユーザが含まれます。グループは一般的なグループ、システムグループ、特殊なシステムグループであるバーチャルグループがあります。

system:adminなどのシステムユーザやシステムグループのドキュメントや一覧は用意されていませんが、なんとなく名前で役割はわかると思います。これらはソースコード中にハードコードされているので、oc get user, oc get groupでは表示されず、上記clusterrolebinding.rbacの出力が唯一の手掛かりとなります。

system:authenticatedsystem:unauthenticatedは名前の通り、認証済みユーザ、認証していないユーザを表すバーチャルシステムグループです。これを利用して、例えばsystem:unauthenticatedregistry-viewerを付与すると、そのプロジェクトのコンテナイメージは認証なしでpullできる状態となります。

https://docs.openshift.com/container-platform/3.7/architecture/additional_concepts/authentication.html#users-and-groups

Userは人間が操作するユーザに割り当てるのに対し、Service Account (SA)は、Podに割り当てるユーザです。権限設定が必要なPodや、tokenを抜き出して外部から操作したりといった各種自動化などに利用されます。たとえば各プロジェクトにはdefault, builder, deployerというSAが自動的に作成され、deployerにはプロジェクトのロールとしてsystem:deployerが設定されます。これはoc get rolebinding.rbac -o wideで確認できます。確認するとわかりますが、Podがデフォルトで利用するSAであるdefaultは初期状態ではロールが割り当てられていない一般ユーザとなり、Kubernetes APIへのアクセスは一切できません。

一般的に利用されるロールを軽く説明します。

  • cluster-admin
    • クラスタ管理者権限。一般ユーザに付与するにはoc adm policy add-cluster-role-to-user cluster-admin USERNAME
  • cluster-reader
    • クラスタ読み込み権限。Kubernetesクラスタのモニタリングなどを行うservice accountなどに付与するケースが多い
  • view, edit, admin
    • プロジェクトの読み込み、読み書き、管理権限。読み込み権限はKubernetes APIをクエリするようなユーザやSAに、読み書きは追加で変更が必要なユーザやSA、管理権限は権限付与も含めて行えるので、プロジェクトの管理者にしたいユーザに付与します。たとえばoc policy add-role-to-user view -z defaultで、defaultのSAで動作するPodにKubernetes APIのクエリを行う権限が付与できます。
  • registry-viewer, registry-editor
    • プロジェクト内のコンテナイメージのpullおよびpull/push権限。別プロジェクトのSAに付与して利用する。

というわけで、以下の4つさえ押さえておけばRBAC完璧です。

  • clusterrole.rbac
  • clusterrolebinding.rbac
  • role.rbac
    • プロジェクト(namespace)固有のロール
  • rolebinding.rbac
    • プロジェクト(namespace)固有のロール割り当て

OpenShiftのPipelineビルドで利用するイメージをカスタマイズする

OpenShift 全部俺 Advent Calendar 2017

Google Home買いました。日本語設定でいろいろやってみて、3歳児が「おーけーぐるぐる、てべりをちゅけてください、おねがいします!」と話しかけてるのを一通り楽しんでから英語設定にスイッチして使ってます。これで子供が英語覚えたりするかな。まぁしないだろうな。

前回Jenkins PipelineジョブをSlaveとなる別のPodで実行しました。今回はこのslave podをカスタマイズしてみましょう。ドキュメントは以下のURLです。

https://docs.openshift.org/3.11/using_images/other_images/jenkins.html

https://github.com/jenkinsci/kubernetes-plugin

config.xmlにデフォルトの定義mavennodeがあるので、それをコピーして新しい設定を作成します。適当にgrepして一つ目を取り出してみます。ConfigMapに突っ込むファイルはCRLFだとEditするときにひどいことになるのでLFに変換します。

$ oc rsh dc/jenkins cat /var/lib/jenkins/config.xml | grep -m1 -A33 PodTemplate | perl -pe 's/^\s{8}//' | dos2unix > pod-template.xml

中身はmavenのもので、以下のような定義となっています。

<org.csanchez.jenkins.plugins.kubernetes.PodTemplate>
  <inheritFrom></inheritFrom>
  <name>maven</name>
  <privileged>false</privileged>
  <alwaysPullImage>false</alwaysPullImage>
  <instanceCap>2147483647</instanceCap>
  <slaveConnectTimeout>0</slaveConnectTimeout>
  <idleMinutes>0</idleMinutes>
  <label>maven</label>
  <serviceAccount>jenkins</serviceAccount>
  <nodeSelector></nodeSelector>
  <customWorkspaceVolumeEnabled>false</customWorkspaceVolumeEnabled>
  <volumes/>
  <containers>
    <org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate>
      <name>jnlp</name>
      <image>openshift/jenkins-slave-maven-centos7</image>
      <privileged>false</privileged>
      <alwaysPullImage>false</alwaysPullImage>
      <workingDir>/tmp</workingDir>
      <command></command>
      <args>${computer.jnlpmac} ${computer.name}</args>
      <ttyEnabled>false</ttyEnabled>
      <resourceRequestCpu></resourceRequestCpu>
      <resourceRequestMemory></resourceRequestMemory>
      <resourceLimitCpu></resourceLimitCpu>
      <resourceLimitMemory></resourceLimitMemory>
      <envVars/>
    </org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate>
  </containers>
  <envVars/>
  <annotations/>
  <imagePullSecrets/>
</org.csanchez.jenkins.plugins.kubernetes.PodTemplate>

nameが作成されるslave pod名、labelがPipelineで指定するslaveのラベル名になっています。

両方custom-mavenに変更して、環境変数を追加してビルドしてみましょう。

<org.csanchez.jenkins.plugins.kubernetes.PodTemplate>
  <inheritFrom></inheritFrom>
  <name>custom-maven</name>
  <privileged>false</privileged>
  <alwaysPullImage>false</alwaysPullImage>
  <instanceCap>2147483647</instanceCap>
  <slaveConnectTimeout>0</slaveConnectTimeout>
  <idleMinutes>0</idleMinutes>
  <label>custom-maven</label>
  <serviceAccount>jenkins</serviceAccount>
  <nodeSelector></nodeSelector>
  <customWorkspaceVolumeEnabled>false</customWorkspaceVolumeEnabled>
  <volumes/>
  <containers>
    <org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate>
      <name>jnlp</name>
      <image>openshift/jenkins-slave-maven-centos7</image>
      <privileged>false</privileged>
      <alwaysPullImage>false</alwaysPullImage>
      <workingDir>/tmp</workingDir>
      <command></command>
      <args>${computer.jnlpmac} ${computer.name}</args>
      <ttyEnabled>false</ttyEnabled>
      <resourceRequestCpu></resourceRequestCpu>
      <resourceRequestMemory></resourceRequestMemory>
      <resourceLimitCpu></resourceLimitCpu>
      <resourceLimitMemory></resourceLimitMemory>
      <envVars>
        <org.csanchez.jenkins.plugins.kubernetes.PodEnvVar>
          <key>foo</key>
          <value>bar</value>
        </org.csanchez.jenkins.plugins.kubernetes.PodEnvVar>
      </envVars>
    </org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate>
  </containers>
  <envVars/>
  <annotations/>
  <imagePullSecrets/>
</org.csanchez.jenkins.plugins.kubernetes.PodTemplate>

role=jenkins-slaveというラベルが付与されているConfigMapに入れるとOpenShift Jenkins Sync Pluginが勝手に反映してくれるようになっています。

$ vi pod-template.xml
$ oc create configmap jenkins-slave --from-file=pod-template.xml
$ oc label configmap jenkins-slave role=jenkins-slave
$ oc start-build test-pipeline

これで生成されたcustom-maven-XXXXというPodではfoo=barという環境変数が設定されている状態となります。Pipelineでsh "echo $foo"など実行することで確認できます。

OpenShiftでJenkins Pipelineビルドを利用する

OpenShift 全部俺 Advent Calendar 2017

OpenShiftにはソースコードからビルドするs2iビルド、Dockerfileを利用してビルドするDockerビルドの他にJenkinsを利用したPipelineビルドというのがあります。s2iビルドやDockerビルドはコンテナイメージを生成してpushする、というビルドですが、PipelineビルドはJenkinsで実行する、という少し性格の異なるビルドになっています。

PipelineビルドもJenkinsfileがあるGit URLをoc new-appで渡すことができますが、Dockerfileのように埋め込むこともできます。個人的にはテスト目的での使用が多いので、埋め込みスタイルを多用しています。

oc new-buildには--dockerfile相当の--jenkinsfileのようなオプションは実装されていないので、Jenkinsfine埋め込みyamlを直接ぶち込みます。

$ cat <<EOF | oc create -f -
kind: BuildConfig
apiVersion: v1
metadata:
  name: test-pipeline
  labels:
    name: test-pipeline
spec:
  strategy:
    type: JenkinsPipeline
    jenkinsPipelineStrategy:
      jenkinsfile: |-
        pipeline {
          agent { label 'maven' }
          stages {
            stage('Stage 1') {
              steps {
                sh "echo Stage 1"
              }
            }
            stage('Stage 2') {
              steps {
                sh "echo Stage 2"
              }
            }
          }
        }
EOF

pipeline、旧syntaxではnode('maven') { sh "echo test" }で大丈夫なのですが、新しいsyntaxになってからブロックのネストがいっぱいになってちょっと冗長な雰囲気になりました。

例では"echo Stage 1"などの実行しかしていませんが、別のビルドを実行したりイメージを別環境にリリースしたり、統合テストをキックしたりなど好きなことができます。いろいろやっている例が https://blog.openshift.com/building-declarative-pipelines-openshift-dsl-plugin/ に掲載されています。

Pipelineビルドを作成すると、実行のためのJenkinsが自動でデプロイされます。

$ oc get all
NAME                         TYPE              FROM      LATEST
buildconfigs/test-pipeline   JenkinsPipeline             0

NAME                        REVISION   DESIRED   CURRENT   TRIGGERED BY
deploymentconfigs/jenkins   1          1         1         config,image(jenkins:latest)

NAME             HOST/PORT                                    PATH      SERVICES   PORT      TERMINATION     WILDCARD
routes/jenkins   jenkins-test-jenkins.192.168.42.143.nip.io             jenkins    <all>     edge/Redirect   None

NAME                 READY     STATUS    RESTARTS   AGE
po/jenkins-1-dl7tq   1/1       Running   0          4m

NAME           DESIRED   CURRENT   READY     AGE
rc/jenkins-1   1         1         1         4m

NAME               CLUSTER-IP       EXTERNAL-IP   PORT(S)     AGE
svc/jenkins        172.30.211.203   <none>        80/TCP      4m
svc/jenkins-jnlp   172.30.28.12     <none>        50000/TCP   4m
$ oc start-build test-pipeline

Jenkinsのログをみると、agent指定されているのでagentとなる新しいPodをスケジューリングして実行しているのがわかります。

$ oc logs dc/jenkins
Dec 08, 2017 9:37:57 AM io.fabric8.jenkins.openshiftsync.BuildConfigWatcher updateJob
INFO: Updated job test-jenkins-test-pipeline from BuildConfig NamespaceName{test-jenkins:test-pipeline} with revision: 111669
Dec 08, 2017 9:37:57 AM io.fabric8.jenkins.openshiftsync.BuildSyncRunListener onStarted
INFO: starting polling build job/test-jenkins-test-pipeline/1/
Dec 08, 2017 9:38:01 AM org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxResolvingClassLoader$4$1 load
WARNING: took 334ms to load/not load jenkins.model.Class from classLoader hudson.PluginManager$UberClassLoader
Dec 08, 2017 9:38:16 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud provision
INFO: Excess workload after pending Spot instances: 1
Dec 08, 2017 9:38:16 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud provision
INFO: Template: Kubernetes Pod Template
Dec 08, 2017 9:38:17 AM okhttp3.internal.platform.Platform log
INFO: ALPN callback dropped: HTTP/2 is disabled. Is alpn-boot on the boot class path?
Dec 08, 2017 9:38:17 AM hudson.slaves.NodeProvisioner$StandardStrategyImpl apply
INFO: Started provisioning Kubernetes Pod Template from openshift with 1 executors. Remaining excess workload: 0
Dec 08, 2017 9:38:18 AM okhttp3.internal.platform.Platform log
INFO: ALPN callback dropped: HTTP/2 is disabled. Is alpn-boot on the boot class path?
Dec 08, 2017 9:38:18 AM org.csanchez.jenkins.plugins.kubernetes.ProvisioningCallback call
INFO: Created Pod: maven-vb87g in namespace test-jenkins
Dec 08, 2017 9:38:18 AM org.csanchez.jenkins.plugins.kubernetes.ProvisioningCallback call
INFO: Waiting for Pod to be scheduled (0/100): maven-vb87g
Dec 08, 2017 9:38:24 AM okhttp3.internal.platform.Platform log
INFO: ALPN callback dropped: HTTP/2 is disabled. Is alpn-boot on the boot class path?
Dec 08, 2017 9:38:24 AM org.csanchez.jenkins.plugins.kubernetes.ProvisioningCallback call
INFO: Container is waiting maven-vb87g [jnlp]: ContainerStateWaiting(message=null, reason=ContainerCreating, additionalProperties={})
Dec 08, 2017 9:38:24 AM org.csanchez.jenkins.plugins.kubernetes.ProvisioningCallback call
INFO: Waiting for Pod to be scheduled (1/100): maven-vb87g

このPod maven-vb87g の中で指定したステップが実行されます。

Pipelineビルドの状況はWebコンソールから確認でき、View Logを押すとJenkinsの画面に遷移してログを確認できます。

f:id:nekop:20171208184927p:plain

f:id:nekop:20171208184448p:plain

次は'maven'となっている部分の説明とカスタマイズについて書く予定です。