nekop's blog

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

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'となっている部分の説明とカスタマイズについて書く予定です。

OpenShiftでPVの中身を別のPVへ移行する

OpenShift 全部俺 Advent Calendar 2017

OpenShiftやKubernetesでPVを作って使ってたらPVが一杯になってしまったので大きなサイズのPVへ移行したい、であるとか、HDDのPVを使ってたけどSSDのPVに変えよう、というようなデータ移行のユースケースが発生します。

OpenShiftのdocker-reigstyのPVを移行してみましょう。

oc project default
### 現在のPVC名とボリューム名の確認 (この例では registry と registry-storage)
oc get pvc
### docker-registryを一旦停止
oc scale dc docker-registry --replicas=0
### データ移行に利用するno-opコンテナを起動
oc run sleep --image=registry.access.redhat.com/rhel7 -- tail -f /dev/null
### 現在のPVCをno-opコンテナにマウント
oc volume dc/sleep --add -t pvc --name=registry --claim-name=registry --mount-path=/old-registry
### 新しいPV/PVCをno-opコンテナにマウント(dynamic provisioning前提、dynamic provisioningがないならPVを事前に設定)
oc volume dc/sleep --add -t pvc --name=new-registry --claim-name=new-registry --mount-path=/new-registry --claim-mode=ReadWriteMany --claim-size=400Gi
### no-opコンテナにrsh
oc rsh sleep-X-XXXXX
### no-opコンテナ上でデータをrsyncで移行
rsync -avxHAX --progress /old-registry /new-registry
### docker-registryを現在のPVから新しいPVへスイッチ
oc volume dc/docker-registry --remove --name=registry-storage --confirm
oc volume dc/docker-registry --add -t pvc --name=registry-storage --claim-name=new-registry --mount-path=/registry
### docker-registryを再開
oc scale dc docker-registry --replicas=1

問題なければ古いPVCは消して良いでしょう。

KubernetesだとOpenShiftのoc volumeのようなコマンドが無いと思いますが、新PVCとデータ移行Podをapplyして同じ作業をすれば良いはずです。

他にkubectl cpでデータ吸い出してPV/PVC切り替えて新しいPodに書き戻す、という安直なアプローチもありますが、アプリが動いたままで書き込み途中のデータを吸い出してしまったり、バックアップ後に書き込まれたデータがロストしたり、新しいPodがデータを上書きされる想定ではないので誤動作する、などの可能性があるので注意しましょう。

OpenShiftでDockerfileやバイナリビルド利用してコンテナをビルドする

OpenShift 全部俺 Advent Calendar 2017

昨日はギョウザパーティでした。基本のs2iビルドではソースコードからビルドしましたが、今回はDockerfileを利用します。

ソースコードと同じようにDockerfileが置いてあるGit URLを指定すると同じようにビルドできるのですが、それでは面白くないのでちょっとアドホックなビルドをしてみましょう。

tail -f /dev/nullを実行するコンテナイメージを作成するDockerfileをGitを利用せずにOpenShiftに食べさせてみます。oc new-buildでビルドしてからoc new-appでImageStreamを指定してアプリケーションを作成という流れになります。

oc new-project test-dockerbuild
cat <<EOF > Dockerfile 
FROM registry.access.redhat.com/rhel
CMD tail -f /dev/null
EOF
cat Dockerfile | oc new-build --dockerfile=- --to=sleep
oc logs sleep-1-build -f
oc new-app sleep

できました。デプロイされたコンテナを見ると実行されていますね。

$ oc rsh dc/sleep ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
1000080+     1  0.0  0.0   4352   352 ?        Ss   06:34   0:00 tail -f /dev/nu
1000080+    16  0.0  0.0  47448  1612 ?        Rs+  06:35   0:00 ps aux

この方式だとDockerfileはBuildConfigに埋め込まれているので、以降内容を編集する場合はoc edit bcで直接編集できます。

$ oc export bc sleep
kind: BuildConfig
spec:
  source:
    dockerfile: |
      FROM registry.access.redhat.com/rhel
      CMD tail -f /dev/null
    type: Dockerfile

ただし、上記のDockerfile埋め込みだとビルドコンテキストが渡せないので、ファイルをイメージ内にコピーしたりしたいときに困ります。そのような場合はバイナリビルドにしてoc start-build時にディレクトリをまるごと転送する、というのもできます。

oc new-build --strategy=docker --binary=true --name=$APP_NAME
oc start-build $APP_NAME --from-dir=.
oc new-app $APP_NAME

バイナリビルドはDockerfileに限らず、s2iのソースツリーでも同じようにGitを経由しないビルドができます。

ちなみにDockerfileを利用したビルドは現状docker daemon内、つまりrootで実行されているためセキュリティレベルとしては最低です。このため、OpenShift OnlineではDockerビルドは無効化されています。そのうちbuildahなどを利用してdocker daemonとは無関係の通常権限でのDockerfileビルドもできるようになるとは思いますが、そこまでしてDockerfile形式を利用しなければならない理由もあまり無いので、より扱いやすいコンテナイメージの記述およびビルド方式が先にサポートされる気もします。