AnsibleプレイブックとCodeReady Workspacesでワークショップのセットアップを自動化する

はじめまして、Red Hatの西村と申します。4月に入社して以来、初めての投稿です。よろしくお願いいたします。

この記事はRed Hat DeveloperAutomate workshop setup with Ansible playbooks and CodeReady Workspacesを、許可を受けて翻訳したものです。


:::James Falkner 2020年7月3日:::

Red Hatでは、お客様やパートナー、その他のオープンソース開発者を対象に、多くの対面および仮想ワークショップを行っています。ほとんどの場合、ワークショップは「自身のデバイスを持ち込む」ものなので、さまざまなハードウェアおよびソフトウェアのセットアップ、企業のエンドポイント保護スキーム、システム知識のレベルの違いといった問題に直面します。

ここ数年、私たちはRed Hat CodeReady Workspaces (CRW)を多用してきました。Eclipse CheをベースにしたCodeReady Workspacesは、ほとんどの開発者に馴染みのあるブラウザで動作するIDEで、プリインストールやシステム内部の知識は必要ありません。ブラウザと頭脳だけあれば、ハンズオンを行うことができます。

また、Quarkusのワークショップを自動化するために、Red Hat Ansible用のプレイブックのセットを作成しました。プレイブックは便利ですが、Kubernetes上でのQuarkus開発用のCodeReady Workspacesの大規模なデプロイを自動化するのに特に役立ちます。この記事ではプレイブックを紹介し、独自の自動化を行う際にそれらを使用する方法を示します。

大規模な自動化の概要

Quarkusのワークショップを自動化するために、私たちはCodeReady Workspacesとこの記事で紹介するAnsibleプレイブックを使用していますが、多くの企業では大規模な新規開発者のオンボーディングを自動化するためにCodeReady Workspacesを使用しています。その場合、CodeReady Workspacesを使用することで、企業の知的財産(つまりソースコード)を保護し、「自分のマシン上では上手くいく」というバグに対する言い訳を最小限に抑えることができます。

ワークショップを実施しているかオンボーディングプロセスを実施しているかに関わらず、可能な限りスムーズに行うためには、かなりのセットアップが必要になります。ワークショップの場合、Red Hat OpenShiftをデプロイし、予想されるユーザー数に合わせてスケールし、すべてのユーザーにCodeReady Workspacesをインストールして設定する必要があります。また、最高の体験を得るためには、各ワークスペースを「事前ウォームアップ」して、イントロのスライドが終わる頃にはすでに実行されているようにするべきです。また、ワークショップの一部として使用するオペレーターをインストールする必要もあります。

次のセクションでは、それぞれの手順と、それらを自動化するために作成したAnsibleプレイブックについて説明します。セットアップのほとんどは、会社のポリシーとITポリシーの両方に準拠し、さらに開発者のニーズにも対応したカスタムスタックの作成に応用できます。

OpenShiftのインストール

OpenShiftはハイブリッドクラウドインフラストラクチャに使用されるため、大規模なデプロイでは、zipファイルをダウンロードして解凍し、デスクトップ上で実行するという古典的な意味での「インストール」は行いません。CodeReady Containersを使用してそのようなタイプのインストールを行うこともできますが、ワークショップで何十人あるいは何百人もの開発者をサポートしている場合、ローカルで実行することは選択肢ではありません。

OpenShiftは、複数の異なるパブリッククラウドとプライベートクラウド上で、簡単にプロビジョニングできます。OpenShiftのデプロイはこの記事の対象外ですが、受講者5人ごとに追加のOpenShiftワーカーノードをデプロイし、各ノードに64GiBのメモリを割り当てるのが有用であるとわかりました。このセットアップにより、すべての受講者が快適なワークショップ体験を受けることができます。

Quarkusワークショップでは、受講者にQuarkusのネイティブビルドを実施してもらいますが、これには余分なメモリを必要です。また、各受講者は独自のKafkaクラスタや他のいくつかのアイテムをデプロイします。そのためワークショップを一度実行して、すべてを実行中にしておき、その上ですべてを足し合わせて、ユーザーごとに必要なメモリの量を決定します。CodeReady Workspacesでは、「ワークスペース単位」の永続ボリューム要求(PVC)戦略を使用することに注意してください。各ワークスペース(つまり各ユーザー)が、独自のストレージを取得します。この戦略に従うことを選択した場合、十分なストレージの容量を確保する必要があります。CPUは多ければ多いほど良いでしょう。

OpenShiftをインストールしたら、ユーザーを作成する必要があります。基本的なLinuxシェルスクリプトやocコマンドを使用して、デフォルトのOpenShiftの認証メカニズムを上書きし、ユーザー(adminユーザーを含む)を包含するhtpasswdファイルを供給することができます。このBashスクリプトには、htpasswdユーティリティyqユーティリティ(バージョン3以上)も必要です。

#!/bin/bash
NUMUSERS=20
TMPHTPASS=$(mktemp)
for i in {1..$NUMUSERS} ; do
    htpasswd -b ${TMPHTPASS} "user$i" 'somepassword'
done

htpasswd -b ${TMPHTPASS} admin 'adminpassword'

$ oc -n openshift-config delete secret workshop-user-secret

$ oc -n openshift-config create secret generic workshop-user-secret --from-file=htpasswd=${TMPHTPASS}

$ oc -n openshift-config get oauth cluster -o yaml | \
  yq d - spec.identityProviders | \
  yq w - -s htpass-template.yaml | \
  oc apply -f -

sleep 20 # don't shoot the messenger, Operators are "eventually consistent"

$ oc adm policy add-cluster-role-to-user cluster-admin admin

yq(バージョン3)で使用するhtpass-template.yamlテンプレートは以下のようになります。

spec.identityProviders[+]:
  name: htpassidp
  type: HTPasswd
  mappingMethod: claim
  htpasswd:
    fileData:
      name: workshop-user-secret

このテンプレートでスクリプトを実行すると、新しいIDプロバイダがOpenShiftの認証フローにマージされ、ユーザーがログインできるようになります。Ansibleを使ってこの認証プロセスを設定することもできますが、まだそれを実施する時間を見つけられていません。

CodeReady Workspacesのデプロイ

今回のインストールにはCodeReady Workspaces Operatorを使用します。インストールを自動化するために、Ansibleプレイブック内でAnsibleを少し使います。名前空間がまだ存在しない場合は、OperatorGroupとSubscriptionに加えて、k8sモジュールを使用して名前空間を作成します。

# create codeready namespace
- name: create codeready namespace
  k8s:
    state: present
    kind: Project
    api_version: project.openshift.io/v1
    definition:
      metadata:
        name: "codeready"
        annotations:
          openshift.io/description: ""
          openshift.io/display-name: "CodeReady Project"

# deploy codeready operator
- name: Create operator subscription for CodeReady
  k8s:
    state: present
    merge_type:
    - strategic-merge
    - merge
    definition: "{{ lookup('file', item ) | from_yaml }}"
  loop:
  - ./files/codeready_operatorgroup.yaml
  - ./files/codeready_subscription.yaml

# wait for CRD to be a thing
- name: Wait for CodeReady CRD to be ready
  k8s_facts:
    api_version: apiextensions.k8s.io/v1beta1
    kind: CustomResourceDefinition
    name: checlusters.org.eclipse.che
  register: r_codeready_crd
  retries: 200
  delay: 10
  until: r_codeready_crd.resources | list | length == 1

# deploy codeready CR
- name: Create CR for CodeReady
  k8s:
    state: present
    merge_type:
    - strategic-merge
    - merge
    definition: "{{ lookup('file', item ) | from_yaml }}"
  loop:
  - ./files/codeready_cr.yaml

# wait for CodeReady to be up
- name: wait for CRW to be running
  uri:
    url: https://codeready-codeready.{{ route_subdomain }}/dashboard/
    validate_certs: false
  register: result
  until: result.status == 200
  retries: "120"
  delay: "15"

カスタムリソース定義(CRD)を待っているコードは重要です。CRDがシステムに知られる前にCRDに基づいてカスタムリソース(CR)を作成しようとすると、失敗します。さらに、オペレーターがインストールされると、その知識を得るまでに時間がかかります。

最後に、uriモジュールを使用して、CodeReady Workspaces自体を待ちます。次に、追加の構成を行うためです。

OperatorGroup

OperatorGroupは、codeready_operatorgroup.yamlに定義されています。非常にシンプルですが、オペレーターが操作できる必要があります。

apiVersion: operators.coreos.com/v1
kind: OperatorGroup
metadata:
  generateName: codeready-
  annotations:
    olm.providedAPIs: CheCluster.v1.org.eclipse.che
  name: codeready-operator-group
  namespace: codeready
spec:
  targetNamespaces:
    - codeready

Subscription

codeready_subscription.yamlに定義されたSubscriptionも、基本的なものです。

apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
  name: codeready-workspaces
  namespace: codeready
spec:
  channel: latest
  installPlanApproval: Automatic
  name: codeready-workspaces
  source: redhat-operators
  sourceNamespace: openshift-marketplace

CheCluster object

OperatorがCRDをKubernetesに登録したら、最後にcodeready_cr.yamlに定義されたCheClusterオブジェクトを作成します。CheClusterを作成することで、インストールが開始されます。

apiVersion: org.eclipse.che/v1
kind: CheCluster
metadata:
  name: codeready-workspaces
  namespace: codeready
spec:
  server:
    cheImageTag: ''
    cheFlavor: codeready
    devfileRegistryImage: ''
    pluginRegistryImage: ''
    tlsSupport: true
    selfSignedCert: false
    serverMemoryRequest: '2Gi'
    serverMemoryLimit: '6Gi'
    customCheProperties:
      CHE_LIMITS_WORKSPACE_IDLE_TIMEOUT: "0"
  database:
    externalDb: false
    chePostgresHostName: ''
    chePostgresPort: ''
    chePostgresUser: ''
    chePostgresPassword: ''
    chePostgresDb: ''
  auth:
    openShiftoAuth: false
    identityProviderImage: ''
    externalIdentityProvider: false
    identityProviderURL: ''
    identityProviderRealm: ''
    identityProviderClientId: ''
  storage:
    pvcStrategy: per-workspace
    pvcClaimSize: 1Gi
    preCreateSubPaths: true

メモリ制限に注意してください。これは、私たちのCodeReady Workspacesのカスタムスタックのコンテナ用にチューニングされています。また、ここでは CHE_LIMITS_WORKSPACE_IDLE_TIMEOUTも設定しています。しばらく席を離れてラボがタイムアウトし、戻ってきたときには更新(または再ログイン)が必要なのは、かなり面倒です。もちろんこれらの設定は、どちらも本番環境では使用すべきではありません。

Keycloakのチューニング

OpenShiftの組み込みの認証メカニズムを使用して、ワークスペースを事前に作成し、起動しておくことはできません。そのためには、各ユーザーが OpenShift にログインし、ユーザーのアカウント詳細をRed Hat Single Sign-Onにリンクする必要があります。(ちなみに、CheClusterリソースでopenShiftoAuth: falseとなっているのはこのためです。)

この問題の回避策は、以下のようにCodeReady Workspacesで再びAnsibleを使用して、同じユーザーセットを作成することです。

- name: create codeready users
  include_tasks: add_che_user.yaml
  vars:
    user: "{{ item }}"
  with_list: "{{ users }}"

この例では、usersはAnsibleのユーザ名の配列にすぎません(例えば、[user1, user2, ....])。ループ処理をして、add_che_user.yamlにユーザーを追加します。以下のように、CodeReady Workspaces REST APIを使用してSSO管理者ユーザーの資格情報を取得し、ユーザーを作成します。

- name: Get codeready SSO admin token
  uri:
    url: https://keycloak-codeready.{{ route_subdomain }}/auth/realms/master/protocol/openid-connect/token
    validate_certs: false
    method: POST
    body:
      username: "{{ codeready_sso_admin_username }}"
      password: "{{ codeready_sso_admin_password }}"
      grant_type: "password"
      client_id: "admin-cli"
    body_format: form-urlencoded
    status_code: 200,201,204
  register: codeready_sso_admin_token

- name: Add user {{ user }} to Che
  uri:
    url: https://keycloak-codeready.{{ route_subdomain }}/auth/admin/realms/codeready/users
    validate_certs: false
    method: POST
    headers:
      Content-Type: application/json
      Authorization: "Bearer {{ codeready_sso_admin_token.json.access_token }}"
    body:
      username: "{{ user }}"
      enabled: true
      emailVerified: true
      firstName: "{{ user }}"
      lastName: Developer
      email: "{{ user }}@no-reply.com"
      credentials:
        - type: password
          value: "{{ workshop_che_user_password }}"
          temporary: false
    body_format: json
    status_code: 201,409

このプレイブックには、いくつかの変数があります。

  • route_subdomainは、クラスターのデフォルトのOpenShiftサブドメインです(クラスターを検出するには、oc whoami --show-clusterを使います)。
  • workshop_che_user_passwordは、ユーザーが希望するパスワードです。
  • codeready_sso_admin_username/codeready_sso_admin_passwordは、CodeReady Workspacesが使用するKeycloakインスタンスの管理者ユーザー名とパスワードです。

デプロイされたKeycloakの環境変数から、Keycloak管理者ユーザー名とパスワードをプログラムで検出するには、Ansibleのコードとk8s_factsモジュールをもう少し使います。

- name: Get codeready keycloak deployment
  k8s_facts:
    kind: Deployment
    namespace: codeready
    name: keycloak
  register: r_keycloak_deployment

- name: set codeready username fact
  set_fact:
    codeready_sso_admin_username: "{{ r_keycloak_deployment.resources[0].spec.template.spec.containers[0].env | selectattr('name','equalto','SSO_ADMIN_USERNAME') |map (attribute='value') | list | first }}"

- name: set codeready password fact
  set_fact:
    codeready_sso_admin_password: "{{ r_keycloak_deployment.resources[0].spec.template.spec.containers[0].env | selectattr('name','equalto','SSO_ADMIN_PASSWORD') |map (attribute='value') | list | first }}"

次に、SSOトークンの有効期限とSSOセッションのタイムアウト時間を増やします(これにより、ワークショップ中のイライラするログアウトを避けることができます)。

- name: Increase codeready access token lifespans
  uri:
    url: https://keycloak-codeready.{{ route_subdomain }}/auth/admin/realms/codeready
    validate_certs: false
    method: PUT
    headers:
      Content-Type: application/json
      Authorization: "Bearer {{ codeready_sso_admin_token.json.access_token }}"
    body:
      accessTokenLifespan: 28800
      accessTokenLifespanForImplicitFlow: 28800
      actionTokenGeneratedByUserLifespan: 28800
      ssoSessionIdleTimeout: 28800
      ssoSessionMaxLifespan: 28800
    body_format: json
    status_code: 204

ユーザーのワークスペースの事前ウォームアップ

最後に、CRWのワークスペースを事前に作成し、事前ウォームアップしておく準備が整いました。

- name: Pre-create and warm user workspaces
  include_tasks: create_che_workspace.yaml
  vars:
    user: "{{ item }}"
  with_list: "{{ users }}"

ユーザーのワークスペースを作成するために行ったのと同じようなループ処理を実行します。今回は、CodeReady Workspaces REST APIを使用したcreate_che_workspace.yamlを呼び出します。

- name: "Get Che {{ user }} token"
  uri:
    url: https://keycloak-codeready.{{ route_subdomain }}/auth/realms/codeready/protocol/openid-connect/token
    validate_certs: false
    method: POST
    body:
      username: "{{ user }}"
      password: "{{ workshop_che_user_password }}"
      grant_type: "password"
      client_id: "admin-cli"
    body_format: form-urlencoded
    status_code: 200
  register: user_token

- name: Create workspace for {{ user }} from devfile
  uri:
    url: "https://codeready-codeready.{{ route_subdomain }}/api/workspace/devfile?start-after-create=true&namespace={{ user }}"
    validate_certs: false
    method: POST
    headers:
      Content-Type: application/json
      Authorization: "Bearer {{ user_token.json.access_token }}"
    body: "{{ lookup('template', './templates/devfile.json.j2') }}"
    body_format: json
    status_code: 201,409
  register: workspace_def

devfileについて

devfile.json.j2は、CodeReady devfileAnsible Jinja2 テンプレートです。今回のdevfileの例はこちらで参照できます。興味深いのは、以下の部分です。

  "components": [
    {
      "id": "redhat/quarkus-java11/latest",
      "type": "chePlugin"
    },

devfileには、ワークスペース用のQuarkusプラグインが含まれていることに注意してください。これにより、オートコンプリートやその他のヒントなどのIDE機能が提供されます。

      "image": "image-registry.openshift-image-registry.svc:5000/openshift/quarkus-stack:2.1",

ここでは、事前に生成され、以下の定義を使ってOpenShiftにImageStreamとしてデプロイされたCheのスタックを参照します。

apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
  name: quarkus-stack
  namespace: openshift
spec:
  tags:
  - annotations:
      description: Quarkus stack for Java and CodeReady Workspaces
      iconClass: icon-java
      supports: java
      tags: builder,java
      version: "2.1"
    from:
      kind: DockerImage
      name: quay.io/openshiftlabs/quarkus-workshop-stack:2.1
    name: "2.1"

スタックは、ユーティリティ(ockntknGraalVM)を含むDockerfileを使用して構築しました。ユーザーがワークショップを開始するたびにインターネットからダウンロードしないように、イメージにMaven .m2リポジトリを事前入力するいくつかのテストビルドを実行します。このイメージをOpenShiftに事前にプルすることで、ワークスペースの起動時間を大幅に短縮します。また、まだ使ったことのないImage Pullerもあります。このロジックの一部を解消するのに、期待できそうです。

結論

要約すると、CodeReady Workspacesのデプロイを大規模に自動化することで、受講者のワークショップ体験を大幅に改善することができます。できるだけ多くのことを前もって行うことで、受講者はインストールやウォームアップなどを待たずに学習に取り掛かることができます。

この記事では、ワークショップの自動化とユーザーエクスペリエンスの向上のために作成した Ansibleプレイブックの一部を紹介しました。追加オプションとして以下のようなものがあります。

  • 他のオペレーター(Strimzi、Jaegerなど)をデプロイする。
  • ワークショップ用のカスタムKeycloakレルムを作成する。
  • ワークショップの他のコンポーネントが正しくデプロイされているかどうかを確認する。

Deploy Quarkus Workshop into an OpenShift 4 Cluster playbookをご覧ください。他にも利用できるものがあるかもしれません!また、新規開発者のオンボーディングの例に興味がある方は、CodeReady Workspaces Delivers Kubernetes-Native IDEの記事を参照してください。

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。