OpenShift Do (odo) を利用して OpenShift 上でアプリケーションを開発する


本記事は [OpenShift Advent Calendar 2019](https://qiita.com/advent-calendar/2019/openshift) の5日目のエントリです。

OpenShift の CLI ツールの1つである OpenShift Do (odo) をご紹介します。

# OpenShift Do (odo) とは？

OpenShift Do (odo) は、 開発者が OpenShift 上で動作するアプリケーションの開発時によりよい開発体験を得られることを目的として開発された OpenShift の CLI ツールです。

OpenShift の CLI ツールはこれまで `oc`, あるいは Kubernetes の CLI ツールである `kubectl` を利用するのが一般的でした。しかし、これらの CLI ツールは OpenShift および Kubernetes を運用管理する目的で開発されたツールであり、使いこなすには OpenShift および Kubernetes の仕組みや各オブジェクト、 manifest の書き方等を理解する必要がありました。

もちろん OpenShift, Kubernetes を利用する上で上記を理解しておくことは重要ですが、 開発者は Openshift の運用管理を専門としていない場合が多いと思うので全員がそれらを十分に理解しているとは限りません。
また、アプリケーション開発者に開発作業になるべく時間を使ってもらうため、動作検証のための運用操作は
あまり時間をかけず行えることが望ましいです。
そのため `oc`, `kubectl` は開発者が利用するにはやや冗長、かつ抽象度の低い CLI ツールとなっていました。

`odo` はアプリケーションのデプロイに必要な OpenShift の各オブジェクトを隠蔽し、アプリケーション開発者が OpenShift クラスタを意識することなく開発、動作検証、フィードバックの開発サイクルを簡単かつ高速に行えることをコンセプトとして以下の機能が盛り込まれています。

- Project, Application, Component など、アプリケーション開発において広く認知されている概念を用いて作業を行える(後述します)。
- `odo` を利用するために OpenShift クラスターに対して追加でサーバのデプロイ等の変更は必要なく、CLI ツールのみで完結する。
- Node.js, Java, Ruby, Perl, PHP, Python などの複数の言語とフレームワークを利用可能。
- ローカルのコードへの変更を検出、自動で Openshift クラスタに即時デプロイし変更結果を確認できる。
- 対象の OpenShift クラスターで利用可能な言語やフレームワーク、サービスを一覧表示できる。

# Project, Application, Component について

`odo` ではアプリケーション開発の概念として Project, Application, Component を用いて作業を行います。Project は1つ以上の Application を管理する単位です。そのまま OpenShift の Project や Kubernetes の Namespace に相当します。

1つの Application は1つ以上の Component から構成され、`odo` で開発するアプリケーションはマイクロサービスアーキテクチャのように複数の Component が連携することで機能を実現することになります。

各 Component はそれぞれ異なる言語やフレームワークを利用可能で、`odo` は Node.js, Java, Python などの複数の Component Type を利用できます。

# `odo` で複数 component が連携するアプリケーションを開発してみる

では実際に `odo` コマンドを利用して `Openshift` 上にアプリケーションをデプロイしていきます。

今回検証する環境は以下です。

- ローカルマシン: macOS Mojave 10.14.6
- Red Hat CodeReady Containers: 1.2.0
- OpenShift クラスタ: 4.2.8

今回の検証環境のように`odo` コマンドで開発を行う際の OpenShift クラスタにローカルマシンで起動した CodeReady Containers を利用することで、完全に開発者のローカルマシンに閉じた環境でアプリケーションの開発、動作検証が行えるようになります。

## `odo` コマンドのインストール方法

まず、`odo` コマンドをインストールします。

インストール方法はバイナリ、または Tarball からインストールする方法が用意されています。今回はバイナリからインストールを行います。

```bash
$ curl -L https://mirror.openshift.com/pub/openshift-v4/clients/odo/latest/odo-darwin-amd64 -o /usr/local/bin/odo
$ chmod +x /usr/local/bin/odo
```

今回は macOS に対してインストールを行いましたがもちろん Linux や Windows にもインストール可能です。
その他のインストール方法はドキュメントの[Installing odo](https://docs.openshift.com/container-platform/4.2/cli_reference/openshift_developer_cli/installing-odo.html)に記載されています。

## `odo` コマンドの completion 設定

`odo` コマンドで開発作業を行うにあたり、作業効率化のため事前に補完機能を有効にしておくことをおすすめします。

`odo --complete` を実行すると `Install completion for odo?` と聞かれるので `y` を選択することで補完機能を有効化します。

```bash
$ odo --complete
Install completion for odo? y
Installing...
Done!
```

すると、 shell の設定ファイルに `complete -C /usr/local/bin/odo odo` という行が追加されるので、その設定を反映することで補完が利用できるようになります。

現在は bash, zsh, fish に対応しているようです。

## サンプルアプリケーションのデプロイ

では実際にサンプルアプリケーションの動作確認を OpenShift クラスタ上で行っていきます。
今回は Wild West ([frontend](https://github.com/openshift-evangelists/Wild-West-Frontend), [backend](https://github.com/openshift-evangelists/Wild-West-Backend)) というサンプルアプリケーションを利用します。

Wild West アプリケーションについて簡単に説明しておくと、 OpenShift クラスタの Project 内に存在する OpenShift オブジェクトを的としたシューティングゲームです。OpenShift 上で自動的に再作成される的 (pod など) を撃った場合は点数が加算され、再作成されないオブジェクト (routeなど) を撃った場合はゲームオーバーとなります。

Wild West アプリケーションは OpenShift クラスタから現在のプロジェクト内に存在するオブジェクトのリストを取得する SpringBoot backend コンポーネントと、ゲームのUI、および backend と通信してオブジェクト情報を的として表示する Node.js frontend コンポーネントで構成されています。

### プロジェクト作成

まず OpenShift クラスタへのログインが必要です。今回の環境では `developer` ユーザでログインしています。

```bash
$ odo login -u developer -p developer
Connecting to the OpenShift cluster

Login successful.

You don't have any projects. You can try to create a new project, by running
    odo project create <project-name>
```

次に、以下のコマンドを実行して Wild West アプリケーションを実行する Project を作成します。

```bash
$ odo project create myproject 
 ✓  Project 'myproject' is ready for use
 ✓  New project created and now using project : myproject
```

このとき `oc project` で現在の Project を確認すると、 OpenShift クラスタ上で `myproject` project が作成されカレントの Project が切り替わっていることが確認できます。

```bash
$ oc project
Using project "myproject" on server "https://api.crc.testing:6443".
```

### `default` ServiceAccount への view 権限の付与

Wild West の backend はオブジェクトのリスト取得のため OpenShift の API にアクセスする必要があります。そのため、 backend コンポーネントが利用する `default` ServiceAccount に対して view アクセスを許可する RoleBinding を作成します。

このような RBAC に関する操作は現状 `odo` では行えません。そのため、これらの操作に関しては `oc` コマンド、または Web コンソールから変更を行う必要があります。今回は Web コンソールから操作を行います。

Web コンソールにログインし、 Administrator パースペクティブの [Project]ページで `myproject` を選択します。

<img width="1672" alt="project.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193568/0508a757-eb07-312b-c663-42fdc958d556.png">

コンソールの左側の [Administration] タブを選択し、[RoleBindings] を選択します。[RoleBindings] ページで、 [Create Binding] を選択して、以下に示す情報を入力後 [Create] を選択して RoleBinding オブジェクトを作成します。

- Role Binding
  - Name: defaultview
  - Namespace: myproject
- Role
  - Role Name: view
- Subject
  - Service Accountを選択
  - Subject Name: default

<img width="1680" alt="RoleBinding.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193568/3a8275b4-4587-2389-e1e1-61ea5f54a1df.png">

※ちなみに RoleBinding 作成時に view role ではなく edit role を選択すると、 Wild West のシューティングゲームで OpenShift オブジェクトの的を撃つと実際に該当するオブジェクトが削除される(!)ので気をつけてください。

### ソースコードの準備

ローカルマシン上の任意の場所に `wild-west` というディレクトリを作成し、 frontend および backend のソースコードを `git clone` します。

```bash
$ mkdir wild-west
$ cd wild-west/
$ git clone https://github.com/openshift-evangelists/Wild-West-Backend backend
Cloning into 'backend'...
remote: Enumerating objects: 25, done.
remote: Counting objects: 100% (25/25), done.
remote: Compressing objects: 100% (16/16), done.
remote: Total 391 (delta 1), reused 18 (delta 1), pack-reused 366
Receiving objects: 100% (391/391), 90.31 KiB | 312.00 KiB/s, done.
Resolving deltas: 100% (103/103), done.
$ git clone https://github.com/openshift-evangelists/Wild-West-Frontend frontend
Cloning into 'frontend'...
remote: Enumerating objects: 30, done.
remote: Counting objects: 100% (30/30), done.
remote: Compressing objects: 100% (29/29), done.
remote: Total 290 (delta 15), reused 7 (delta 1), pack-reused 260
Receiving objects: 100% (290/290), 5.57 MiB | 4.44 MiB/s, done.
Resolving deltas: 100% (137/137), done.
```

### 利用可能な Component Type の確認

`odo` では、対象の OpenShift クラスタ上で Developer Catalog として用意されているランタイム、フレームワークを Component Type として利用可能です。

`odo catalog list components` を実行することで利用可能な Component Type を一覧表示できます。

```bash
$ odo catalog list components
Odo Supported OpenShift Components:
NAME       PROJECT       TAGS
java       openshift     11,8,latest
nodejs     openshift     10,8,8-RHOAR,latest

Odo Unsupported OpenShift Components:
NAME              PROJECT       TAGS
dotnet            openshift     2.1,2.2,latest
golang            openshift     1.11.5,latest
httpd             openshift     2.4,latest
modern-webapp     openshift     10.x,latest
nginx             openshift     1.10,1.12,latest
perl              openshift     5.24,5.26,latest
php               openshift     7.0,7.1,7.2,latest
python            openshift     2.7,3.6,latest
ruby              openshift     2.4,2.5,latest
```

これらの Component Type の実態は対象の OpenShift クラスタに登録されている S2I の builder コンテナイメージであり、ImageStream の `spec.tags.annotations` に `tags=builder` が指定されているものがbuilder イメージと認識されます。そのため、任意のカスタム builder イメージをインポート後、 `oc annotate` コマンドで `tags=builder` を付与することによりカスタムの Component Type を追加することができます。

例えば仮に backend コンポーネントのデプロイに `openjdk18` イメージを利用したい場合、 `oc` コマンドでまずコンテナイメージのインポートを行います。

```bash
$ oc import-image openjdk18 \
> --from=registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift --confirm
imagestream.image.openshift.io/openjdk18 imported

Name:                   openjdk18
Namespace:              myproject
Created:                25 hours ago
Labels:                 <none>
Annotations:            openshift.io/image.dockerRepositoryCheck=2019-12-03T19:26:09Z
Image Repository:       image-registry.openshift-image-registry.svc:5000/myproject/openjdk18
Image Lookup:           local=false
Unique Images:          1
Tags:                   1

latest
  tagged from registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift

  * registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift@sha256:308c64dd37bcaad23fd73fd5c12877529b25887c41c136118fcb49c60f20ed96
      25 hours ago

Image Name:     openjdk18:latest
Docker Image:   registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift@sha256:308c64dd37bcaad23fd73fd5c12877529b25887c41c136118fcb49c60f20ed96
Name:           sha256:308c64dd37bcaad23fd73fd5c12877529b25887c41c136118fcb49c60f20ed96
Created:        25 hours ago
Annotations:    image.openshift.io/dockerLayersOrder=ascending
Image Size:     172.8MB in 3 layers
Layers:         76.23MB sha256:17942523bc4bb2db6eb9f7519db38bbb70e47356d3f0ae0f15b967c0628234c6
                1.283kB sha256:4c98734f24339b059854b6f7ad77928ffb6b84756ecd4eeec4a15870b082d906
                96.59MB sha256:123e4fdc29b5afd01eec6d677c3a71630af8124e836a3947733b31303040d788
Image Created:  6 weeks ago
Author:         <none>
Arch:           amd64
Command:        /usr/local/s2i/run
Working Dir:    /home/jboss
User:           185
Exposes Ports:  8080/tcp, 8443/tcp, 8778/tcp
Docker Labels:  architecture=x86_64
                authoritative-source-url=registry.access.redhat.com
                build-date=2019-10-17T10:13:09.686400
                com.redhat.build-host=cpt-1004.osbs.prod.upshift.rdu2.redhat.com
                com.redhat.component=redhat-openjdk-18-openjdk18-openshift-container
                com.redhat.license_terms=https://www.redhat.com/en/about/red-hat-end-user-license-agreements
                description=Source To Image (S2I) image for Red Hat OpenShift providing OpenJDK 8
                distribution-scope=public
                io.cekit.version=3.5.0
                io.fabric8.s2i.version.jolokia=1.6.2-redhat-00002
                io.fabric8.s2i.version.maven=3.5
                io.k8s.description=Platform for building and running plain Java applications (fat-jar and flat classpath)
                io.k8s.display-name=Java Applications
                io.openshift.s2i.destination=/tmp
                io.openshift.s2i.scripts-url=image:///usr/local/s2i
                io.openshift.tags=builder,java
                maintainer=Red Hat OpenJDK <openjdk@redhat.com>
                name=redhat-openjdk-18/openjdk18-openshift
                org.jboss.container.deployments-dir=/deployments
                org.jboss.product=openjdk
                org.jboss.product.openjdk.version=1.8.0
                org.jboss.product.version=1.8.0
                release=5
                summary=Source To Image (S2I) image for Red Hat OpenShift providing OpenJDK 8
                url=https://access.redhat.com/containers/#/registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift/images/1.7-5
                usage=https://access.redhat.com/documentation/en-us/red_hat_jboss_middleware_for_openshift/3/html/red_hat_java_s2i_for_openshift/
                vcs-ref=c47a11f2fd7bf261b6db74ff9bc60b96e7b1b340
                vcs-type=git
                vendor=Red Hat, Inc.
                version=1.7
Environment:    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/s2i
                container=oci
                HOME=/home/jboss
                JAVA_HOME=/usr/lib/jvm/java-1.8.0
                JAVA_VENDOR=openjdk
                JAVA_VERSION=1.8.0
                JBOSS_CONTAINER_OPENJDK_JDK_MODULE=/opt/jboss/container/openjdk/jdk
                S2I_SOURCE_DEPLOYMENTS_FILTER=*.jar
                JBOSS_CONTAINER_S2I_CORE_MODULE=/opt/jboss/container/s2i/core/
                JBOSS_CONTAINER_JAVA_PROXY_MODULE=/opt/jboss/container/java/proxy
                JBOSS_CONTAINER_JAVA_JVM_MODULE=/opt/jboss/container/java/jvm
                JBOSS_CONTAINER_MAVEN_35_MODULE=/opt/jboss/container/maven/35/
                MAVEN_VERSION=3.5
                JBOSS_CONTAINER_UTIL_LOGGING_MODULE=/opt/jboss/container/util/logging/
                JBOSS_CONTAINER_MAVEN_DEFAULT_MODULE=/opt/jboss/container/maven/default/
                JBOSS_CONTAINER_MAVEN_S2I_MODULE=/opt/jboss/container/maven/s2i
                JAVA_DATA_DIR=/deployments/data
                JBOSS_CONTAINER_JAVA_RUN_MODULE=/opt/jboss/container/java/run
                AB_JOLOKIA_AUTH_OPENSHIFT=true
                AB_JOLOKIA_HTTPS=true
                AB_JOLOKIA_PASSWORD_RANDOM=true
                JBOSS_CONTAINER_JOLOKIA_MODULE=/opt/jboss/container/jolokia
                JOLOKIA_VERSION=1.6.2
                AB_PROMETHEUS_JMX_EXPORTER_CONFIG=/opt/jboss/container/prometheus/etc/jmx-exporter-config.yaml
                AB_PROMETHEUS_JMX_EXPORTER_PORT=9799
                JBOSS_CONTAINER_PROMETHEUS_MODULE=/opt/jboss/container/prometheus
                JBOSS_CONTAINER_JAVA_S2I_MODULE=/opt/jboss/container/java/s2i
                JBOSS_IMAGE_NAME=redhat-openjdk-18/openjdk18-openshift
                JBOSS_IMAGE_VERSION=1.7
```

インポート後、 imagestreamtag に `tags=builder` を付与します。

```bash
$ oc annotate istag/openjdk18:latest tags=builder
imagestreamtag.image.openshift.io/openjdk18:latest annotated
```

これにより利用可能な Component Type に `openjdk18` を追加することができました。

```bash
$ odo catalog list components
Odo Supported OpenShift Components:
NAME          PROJECT       TAGS
java          openshift     11,8,latest
nodejs        openshift     10,8,8-RHOAR,latest
openjdk18     myproject     latest

Odo Unsupported OpenShift Components:
NAME              PROJECT       TAGS
dotnet            openshift     2.1,2.2,latest
golang            openshift     1.11.5,latest
httpd             openshift     2.4,latest
modern-webapp     openshift     10.x,latest
nginx             openshift     1.10,1.12,latest
perl              openshift     5.24,5.26,latest
php               openshift     7.0,7.1,7.2,latest
python            openshift     2.7,3.6,latest
ruby              openshift     2.4,2.5,latest
```

### backend コンポーネントのデプロイ

#### ローカルマシン上でビルド

ソースコードの準備ができたので実際に `odo` コマンドで OpenShift 上にデプロイしてみます。
backend コンポーネントはMavenを使用してビルドできるようになっているため、ローカルマシン上でビルドを行い、
作成した jar ファイルを `odo` を使用して Component Type `java` でデプロイします。

まず、 `backend` ディレクトリに移動後 Maven を使用してソースファイルをビルドし、jar ファイルを作成します。

```bash
$ cd backend/
$ mvn package
[INFO] Scanning for projects...
[INFO] 
[INFO] -----------------------< com.openshift:wildwest >-----------------------
[INFO] Building wildwest 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ wildwest ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ wildwest ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ wildwest ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/kentoshirakawa/experimental/odo/my_components/wild-west/backend/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ wildwest ---
[INFO] No sources to compile
[INFO] 
[INFO] --- maven-surefire-plugin:2.18.1:test (default-test) @ wildwest ---
[INFO] No tests to run.
[INFO] 
[INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ wildwest ---
[INFO] Building jar: /Users/kentoshirakawa/experimental/odo/my_components/wild-west/backend/target/wildwest-1.0.jar
[INFO] 
[INFO] --- spring-boot-maven-plugin:1.5.9.RELEASE:repackage (default) @ wildwest ---
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-loader-tools/1.5.9.RELEASE/spring-boot-loader-tools-1.5.9.RELEASE.pom
(省略)
Downloaded from central: https://repo.maven.apache.org/maven2/asm/asm-util/3.2/asm-util-3.2.jar (37 kB at 10 kB/s)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  26.509 s
[INFO] Finished at: 2019-12-05T06:03:26+09:00
[INFO] ------------------------------------------------------------------------
```

#### backend コンポーネントを OpenShift にデプロイするための設定ファイル作成

jarファイルが作成できたら、 `odo create` コマンドで Component の設定ファイルを作成します。

```bash
$ odo create java:8 backend --binary target/wildwest-1.0.jar
 ✓  Validating component [12ms]
Please use `odo push` command to create the component with source deployed
```

この時点ではまだ backend コンポーネントはデプロイされておらず、デプロイに必要な情報を包含した
`config.yaml` という設定ファイルが `backend` ディレクトリの `.odo/` 配下に作成されています。

```yaml:.odo/config.yaml
kind: LocalConfig
apiversion: odo.openshift.io/v1alpha1
ComponentSettings:
  Type: java:8
  SourceLocation: target/wildwest-1.0.jar
  SourceType: binary
  Ports:
  - 8080/TCP
  - 8443/TCP
  - 8778/TCP
  Application: app
  Project: myproject
  Name: backend
```

`odo config view` コマンドでこの設定ファイルの情報を確認できます。

```bash
$ odo config view 
COMPONENT SETTINGS
------------------------------------------------
PARAMETER         CURRENT_VALUE
Type              java:8
Application       app
Project           myproject
SourceType        binary
Ref               
SourceLocation    target/wildwest-1.0.jar
Ports             8080/TCP,8443/TCP,8778/TCP
Name              backend
MinMemory         
MaxMemory         
DebugPort         
Ignore            
MinCPU            
MaxCPU    
```

#### backend コンポーネントを OpenShift にデプロイ

`odo create` コマンドで Component の設定ファイル作成後、 `odo push` コマンドを実行することで実際にローカルマシン上のjarファイルを OpenShift 上にデプロイします。

```bash
$ odo push
Validation
 ✓  Checking component [27ms]

Configuration changes
 ✓  Initializing component
 ✓  Creating component [235ms]

Pushing to component backend of type binary
 ✓  Checking files for pushing [5ms]
 ✓  Waiting for component to start [2m]
 ✓  Syncing files to the component [21s]
 ✓  Building component [2s]
```

`oc` コマンドで作成されたオブジェクトを確認してみると、
`backend-app-1-2sgrb` という Pod が起動していることがわかります。

また、 ImageStream や Service なども作成されていることがわかります。
`odo` を利用することによりこれらの OpenShift オブジェクトをほとんど意識することなく、
ローカルでビルドした jar ファイルを OpenShift 上で動作させることができました。

```bash
$ oc get all
NAME                       READY     STATUS      RESTARTS   AGE
pod/backend-app-1-2sgrb    1/1       Running     0          5m44s
pod/backend-app-1-deploy   0/1       Completed   0          5m57s

NAME                                  DESIRED   CURRENT   READY     AGE
replicationcontroller/backend-app-1   1         1         1         5m57s

NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
service/backend-app   ClusterIP   172.30.151.215   <none>        8080/TCP,8443/TCP,8778/TCP   5m59s

NAME                                             REVISION   DESIRED   CURRENT   TRIGGERED BY
deploymentconfig.apps.openshift.io/backend-app   1          1         1         config,image(java:8)

NAME                                         IMAGE REPOSITORY                                                                TAGS      UPDATED
imagestream.image.openshift.io/backend-app   default-route-openshift-image-registry.apps-crc.testing/myproject/backend-app             
imagestream.image.openshift.io/openjdk18     default-route-openshift-image-registry.apps-crc.testing/myproject/openjdk18     latest    About an hour ago
```

### frontend コンポーネントのデプロイ

#### frontend コンポーネントを OpenShift にデプロイするための設定ファイル作成

frontend コンポーネントは Node.js アプリケーションであるため、 backend のようなビルドのステップは必要ありません。
そのため、最初のステップで`odo create` コマンドで Component の設定ファイルを作成します。

```
$ cd ../frontend/
$ odo create nodejs frontend
 ✓  Validating component [13ms]
Please use `odo push` command to create the component with source deployed
$ odo config view 
COMPONENT SETTINGS
------------------------------------------------
PARAMETER         CURRENT_VALUE
Type              nodejs
Application       app
Project           myproject
SourceType        local
Ref               
SourceLocation    ./
Ports             8080/TCP
Name              frontend
MinMemory         
MaxMemory         
DebugPort         
Ignore            
MinCPU            
MaxCPU            
```

#### frontend コンポーネントを OpenShift にデプロイ

`odo create` の結果特に問題なければ `odo push` を実行してデプロイを行います。

```
$ odo push
Validation
 ✓  Checking component [21ms]

Configuration changes
 ✓  Initializing component
 ✓  Creating component [157ms]

Pushing to component frontend of type local
 ✓  Checking files for pushing [1ms]
 ✓  Waiting for component to start [22s]
 ✓  Syncing files to the component [5s]
 ✓  Building component [50s]
 ✓  Changes successfully pushed to component
```

backend 加えて frontend の Pod および各オブジェクトも作成されていることが確認できます。

```
$ oc get all
NAME                        READY     STATUS      RESTARTS   AGE
pod/backend-app-1-2sgrb     1/1       Running     0          110m
pod/backend-app-1-deploy    0/1       Completed   0          110m
pod/frontend-app-1-b7s2z    1/1       Running     0          2m31s
pod/frontend-app-1-deploy   0/1       Completed   0          2m41s

NAME                                   DESIRED   CURRENT   READY     AGE
replicationcontroller/backend-app-1    1         1         1         110m
replicationcontroller/frontend-app-1   1         1         1         2m42s

NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
service/backend-app    ClusterIP   172.30.151.215   <none>        8080/TCP,8443/TCP,8778/TCP   111m
service/frontend-app   ClusterIP   172.30.172.206   <none>        8080/TCP                     2m42s

NAME                                              REVISION   DESIRED   CURRENT   TRIGGERED BY
deploymentconfig.apps.openshift.io/backend-app    1          1         1         config,image(java:8)
deploymentconfig.apps.openshift.io/frontend-app   1          1         1         config,image(nodejs:latest)

NAME                                          IMAGE REPOSITORY                                                                 TAGS      UPDATED
imagestream.image.openshift.io/backend-app    default-route-openshift-image-registry.apps-crc.testing/myproject/backend-app              
imagestream.image.openshift.io/frontend-app   default-route-openshift-image-registry.apps-crc.testing/myproject/frontend-app             
imagestream.image.openshift.io/openjdk18      default-route-openshift-image-registry.apps-crc.testing/myproject/openjdk18      latest    3 hours ago
```

### component 間をリンクする

backend と frontend 両方の Component を動作させることができました。
しかし、 frontend 側のログを確認すると以下のようなエラーが発生しています。
ログ出力は `odo log` で確認できます。

```
$ odo log | tail -10
[nodemon] watching dir(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node --inspect=5858 server.js`
Debugger listening on ws://127.0.0.1:5858/3bb39e8d-cc41-4689-9dcc-676b0b40b49d
For help, see: https://nodejs.org/en/docs/inspector
CONFIG ERROR: Can't find backend webservices component! 
Use `odo link` to link your front-end component to a backend component.
Listening on 0.0.0.0, port 8080
Frontend available at URL_PREFIX: /
{ Error: 'Backend Component Not Configured' }
```

上記のエラーは frontend 側が backend 側の接続情報を持ち合わせておらず、
通信できていないために発生しています。

frontend 側が backend 側の接続情報を注入するには `odo link` コマンドを使用します。

```
$ odo link backend --component frontend --port 8080
 ✓  Component backend has been successfully linked to the component frontend

The below secret environment variables were added to the 'frontend' component:

· COMPONENT_BACKEND_HOST
· COMPONENT_BACKEND_PORT

You can now access the environment variables from within the component pod, for example:
$COMPONENT_BACKEND_HOST is now available as a variable within component frontend
```

実行後 frontend 側の Pod が再デプロイされていることが確認できます。

```
$ oc get pod
NAME                    READY     STATUS      RESTARTS   AGE
backend-app-1-2sgrb     1/1       Running     0          127m
backend-app-1-deploy    0/1       Completed   0          127m
frontend-app-1-deploy   0/1       Completed   0          19m
frontend-app-2-deploy   0/1       Completed   0          3m58s
frontend-app-2-jrhgm    1/1       Running     0          3m38s
```

もう一度 frontend 側のログ出力を確認すると、先程のエラーが発生していないことが確認できます。

```
$ odo log | tail -10
[nodemon] 2.0.1
[nodemon] to restart at any time, enter `rs`
[nodemon] watching dir(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node --inspect=5858 server.js`
Debugger listening on ws://127.0.0.1:5858/8c1b1ba8-4e73-4e88-b790-9b5d5a9b194c
For help, see: https://nodejs.org/en/docs/inspector
Listening on 0.0.0.0, port 8080
Frontend available at URL_PREFIX: /
Proxying "/ws/*" to 'backend-app:8080'
```

もう少し何が起こったかを確認してみましょう。
secret の有無を確認すると、 backend と frontend それぞれが公開しているポート分の secret が作成されていることが確認できます。

```
$ oc get secret -l app=app
NAME                TYPE     DATA   AGE
backend-app-8080    Opaque   2      130m
backend-app-8443    Opaque   2      130m
backend-app-8778    Opaque   2      130m
frontend-app-8080   Opaque   2      22m
```

今回の接続先であるポート8080が名前に含まれる `backend-app-8080` の key である COMPONENT_BACKEND_HOST, COMPONENT_BACKEND_PORT の値を見てみると、 backend コンポーネントの Service 名とポート番号であることがわかります。

```
$ oc get secrets backend-app-8080 -ojsonpath='{.data.COMPONENT_BACKEND_HOST}' | base64 -D
backend-app
$ oc get secrets backend-app-8080 -ojsonpath='{.data.COMPONENT_BACKEND_PORT}' | base64 -D
8080
```

次に frontend の DeploymentConfig を確認してみます。
すると、先程確認した Secret backend-app-8080 が環境変数として読み込まれるよう定義されていることが確認できます。

```
$ oc get dc frontend-app -oyaml | grep backend-app-8080 -B 2
        envFrom:
        - secretRef:
            name: backend-app-8080
```

`odo link` により上記の Secret を環境変数として読み込む定義が追加された DeploymentConfig で Pod が再デプロイされたことで、元々その環境変数を backend の接続先情報として利用するよう実装されていた frontend 側で backend との接続が確立できるようになっていました。

### アプリケーションの公開

frontend と backend の通信が確立できましたが、まだアプリケーション自体は外部に公開されていません。
`odo` コマンドでは `odo url create` を実行することでアプリケーションを外部公開することができます。

```
$ odo url create frontend --port 8080
 ✓  URL frontend created for component: frontend

To create URL on the OpenShift Cluster, please use `odo push`
```

上記実行後、 `odo url list` で公開状態を確認すると、 STATE が `Not Pushed` となっておりまだ公開されていないことがわかります。

```
$ odo url list
Found the following URLs for component frontend in application app:
NAME         STATE          URL     PORT
frontend     Not Pushed     ://     8080

There are local changes. Please run 'odo push'.
```

`odo push` を実行することで外部URLが作成され、アプリケーションにアクセスできるようになります。
実際には frontend コンポーネントに対して Route が作成されています。

```
$ odo push
Validation
 ✓  Checking component [39ms]

Configuration changes
 ✓  Retrieving component data [52ms]
 ✓  Applying configuration [74ms]

Applying URL changes
 ✓  URL frontend: http://frontend-app-myproject.apps-crc.testing created

Pushing to component frontend of type local
 ✓  Checking file changes for pushing [1ms]
 ✓  No file changes detected, skipping build. Use the '-f' flag to force the build.
```

以下が実際に表示される Wild West アプリケーションの画面です。

<img width="1680" alt="wildwest.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193568/c81075b7-e67c-5291-3109-828c332ebac3.png">


### ソースコードの変更

これまででアプリケーションの最初のバージョンをデプロイし、ブラウザで動作確認することができるようになりました。
次に、ソースコードの変更が入った場合に `odo` で即時に動作確認を行う方法を見ていきます。

`odo` ではバックグラウンドでファイルシステムの変更を監視するための `odo watch` コマンドが用意されています。

```
$ odo watch
Waiting for something to change in /Users/kentoshirakawa/experimental/odo/my_components/wild-west/fronten
```

`odo watch` を実行した状態でゲームタイトルを「Wild West The OpenShift Way！」から「My App The OpenShift Way！」に変更します。
するとソースの変更が検知された後、 `odo` は変更を frontend component に Push し、そのステータスを出力します。

```
$ odo watch
Waiting for something to change in /Users/kentoshirakawa/experimental/odo/my_components/wild-west/frontend
File /Users/kentoshirakawa/experimental/odo/my_components/wild-west/frontend/index.html changed
File /Users/kentoshirakawa/experimental/odo/my_components/wild-west/frontend/assets/explosion.wav changed
File /Users/kentoshirakawa/experimental/odo/my_components/wild-west/frontend/assets/frame.png changed
File /Users/kentoshirakawa/experimental/odo/my_components/wild-west/frontend/assets/gameplayfield.png changed
File /Users/kentoshirakawa/experimental/odo/my_components/wild-west/frontend/assets/gunshot.wav changed
File /Users/kentoshirakawa/experimental/odo/my_components/wild-west/frontend/assets/yeehaw.wav changed
File /Users/kentoshirakawa/experimental/odo/my_components/wild-west/frontend/playfield.png changed
Pushing files...
 ✓  Waiting for component to start [12ms]
 ✓  Syncing files to the component [6s]
 ✓  Building component [10s]
```

`odo watch` によって変更が反映された後、先程の外部URLにアクセスすることで変更結果を確認することができます。このように `odo` コマンドでは変更結果を即時に OpenShift 上で確認できるようになっています。

<img width="1680" alt="myapp.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193568/9b8e6402-e2dd-b0e6-52cd-7a0fde107925.png">


その他にも `odo` コマンドは component にローカルのストレージをアタッチできたり、アプリケーションにローカルのデバッガを接続できるようにする機能 (Technology Preview) などいろいろな機能があるのでぜひお試しください。

# さいごに

OpenShift の CLI ツールの1つである OpenShift Do (odo) についてご紹介しました。
もしすでに自分の PC で CodeReady Containers を利用されている場合、CLIのインストールだけで手軽に利用できますのでぜひ試してみてください。

また、環境がない場合でも、今日ご紹介した内容はすぐに試せる[チュートリアル](https://developers.redhat.com/products/odo/overview)として公開されているのでそちらでお試しいただければと思います。

# 参考

- https://docs.openshift.com/online/starter/cli_reference/openshift_developer_cli/understanding-odo.html
- https://developers.redhat.com/products/odo/overview