OpenShift(okd 3.11)のDevOps環境でのJava/Mavenのビルド時間をNexus3で短縮する

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/a2ebc74f-28ed-ac86-8154-42b0ac3f7c5a.png)

# 要は(TL;DR;)

- OpenShift環境下のDevOpsパイプランで、Maven/Javaのビルドが毎回遅いなあ、と思ってる方
- OpenShift上にOSSのリポジトリー・マネージャー **Nexus3**をデプロイすれば、Mavenプロキシーとなってキャッシュが効くのでビルドが速くなりますよ！

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/d384fa0b-0eee-5b40-0054-7628ebb25797.png)

# 困ったこと
こんにちわ！石田です。今OpenShiftのDevOpsのデモ環境を作ってるんですが、Java/Mavenのビルドが遅いです。。具体的にはソースの変更をトリガーにJavaアプリケーションをOpenShift上のコンテナでMavenビルドする場合、**コンテナはビルドの都度、新規作成されるのでMavenのキャッシュがありません。結果として初回のビルドも2回目以降のビルドもインターネット上のMavenリポジトリーからのダウンロードが発生し、結構な時間がかかります。**


<details><summary>こんな超シンプルなサーブレットのビルドですが何度やっても毎回６０秒前後かかってます</summary><div>

```java:SimpleServlet.java
package my;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class SimpleServlet
 */
@WebServlet("/*")
public class SimpleServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().print("<h1><font color=green>Simple Servlet ran successfully</font></h1>"
                                   + "Powered by WebSphere Application Server Liberty Profile");
    }

}

```
</div></details>


![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/bc23ec38-33e5-2b0b-3083-6c5309302901.png)

新規コンテナ(Pod)の起動時はキャッシュ($home/.m2/repository)が無いため、repo.maven.apache.orgから以下のように毎回延々ダウンロードが行われています。

```bash:実行ログ例
[INFO] ------------------------< SimpleApp:SimpleApp >-------------------------
[INFO] Building SimpleApp 1.0
[INFO] --------------------------------[ war ]---------------------------------
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.6/maven-resources-plugin-2.6.pom 
Progress (1): 2.2/8.1 kBProgress (1): 5.0/8.1 kBProgress (1): 7.8/8.1 kBProgress (1): 8.1 kB                        Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.6/maven-resources-plugin-2.6.pom  (8.1 kB at 4.4 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/23/maven-plugins-23.pom 
Progress (1): 2.2/9.2 kBProgress (1): 5.0/9.2 kBProgress (1): 7.8/9.2 kBProgress (1): 9.2 kB                        Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/23/maven-plugins-23.pom  (9.2 kB at 48 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/22/maven-parent-22.pom 
.....
```

:warning: 念のため、ですが当記事はDevOpsパイプラインで**OpenShift上でMavenのビルドを行う場合**の話です。つまりs2iを使わない場合＝ソースからのMavenビルドをOpenShiftの外(＝要は普通のOSのコマンド環境など)で実行してwar/earファイルを作成し、OpenShiftにはwar/earを配置(コピー)するだけの場合は通常のMavenのキャッシュが効くので問題になりません。

# mvnビルドの実行時間を短縮する案

まず一般論を言うと、通常の(＝コンテナ環境ではない)場合は初回のmvn ビルドの実行時に$home/.m2/repositoryの下にコンテンツがキャッシュされ、２回目以降はキャッシュが参照されるのでビルド時間が短縮されます。

## 1. s2iのインクリメンタル・ビルドの機構を使って自力で.m2を保管・復元する

同様のことをコンテナ環境に置き換えて考えると、まずはs2iのインクリメンタル・ビルド(s2i build <src> <Image> <app-name> --incremental)を使ってsave-artifactsにて.m2を保管して２回目以降で復元する方法が考えられます。例は[こちら](https://github.com/appuio/s2i-maven-java)など。ただしこの方法は自分でs2iのスクリプトを実装する必要があること、複数のビルドがあった場合に各々個別に実装する必要があることより、イマイチな印象です。

##  2. OpenShiftにNexus3をMaven Proxyとしてデプロイする

より簡単で柔軟な方法として、Sonatype社がOSSで提供しているリポジトリー・マネージャー [Nexus3](https://www.sonatype.com/nexus-repository-oss)を使う方法があります。Nexus3はリポジトリー・マネージャーであり、MavenのみならずDockerレジストリーの代替など様々な用途に使えますが、デフォルトでMavenプロキシーの機能があります。Dockerhub上にコンテナ・イメージが公開されていますので、OpenShift上にデプロイするのも非常に簡単です。OpenShift上でのJava/Mavenビルドの際の参照リポジトリーにNexus3を指定すると、初回はNexus3のMavenプロキシーを介してインターネット上のMavenリポジトリーにアクセスしますが、リソースはNexus3側にキャッシュされます。よって2回目以降のビルドではMavenのリソースがNexus3のキャッシュにヒットするので、ビルド時間が短縮できます。

要はOpenShift内の新規コンテナ(Pod)でmvn packageを実行する場合、

①　デフォルトでは毎回ダウンロードが発生します
②　Nexus3を稼働させると初回はダウンロードしますがキャッシュされます
③　Nexus3を稼働させた場合の２回目以降のビルドではキャッシュにヒットします（のでダウンロードしません）

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/d384fa0b-0eee-5b40-0054-7628ebb25797.png)

# やってみた
以下、Nexus3のセットアップと利用の手順をお示しします。

## おことわり

- 当記事執筆時点(2020/1月)でネットを検索すると、OpenShift上にNexusをデプロイする方法の記事があれこれヒットします。いくつか試したのですが、記事が古くてそのままでは動かないものも多いようでした。また、Nexus3のリポジトリーの指定方法はじめ、記事によって違う方法を記載しているものもあります。当記事は筆者が執筆時点で実際にやってみて動いた手順を記載していますが、それ以外の方法もあるという点、まずはご認識ください。
- 特にOpenShift 3.11の公式ドキュメント :newspaper:  「[Setting Up a Nexus Mirror for Maven](https://docs.openshift.com/container-platform/3.11/dev_guide/dev_tutorials/maven_tutorial.html)」を参考にしました

## 環境

- IBM Cloud(IaaS)上で稼働するOKD 3.11( on ubuntu 18.04)-  oc cluster upで起動したall-in-one構成

## OpenShiftへのNexus3イメージのデプロイ

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/dff77b78-023a-e414-943e-137e26940428.png)

Dockerhub上に上記のイメージが公開されていますので、ocコマンドで簡単にOpenShiftにデプロイできます。

ログインして新しいプロジェクトを作成の上、sonatype/nexus3のイメージをデプロイしてサービスとルートを作ります。

```bash:nexus3のデプロイ方法
oc login -u developer -p dummy
oc new-project nexus3
oc new-app sonatype/nexus3
oc expose svc/nexus3
```

oc statusでルート(上の例ではhttp://nexus3-nexus3.162.133.79.188.nip.io)ができていればオーケーです。


```bash:oc_status
handson01@devops01:~$ oc status
In project nexus3 on server https://162.133.79.188:8443

http://nexus3-nexus3.162.133.79.188.nip.io to pod port 8081-tcp (svc/nexus3)
  dc/nexus3 deploys istag/nexus3:latest
    deployment #1 deployed 2 minutes ago - 1 pod

2 infos identified, use 'oc status --suggest' to see details.

```


管理コンソール上もPodが起動していますね？

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/232dd796-aca8-b79f-6071-56d8bea860d5.png)


## Next3のセットアップ

### 管理者パスワードの入手
次にNexus3のコンソールに管理者(admin)でログインしてセットアップを行いますが、その前に管理者のパスワードを入手する必要があります。

[sonatype/nexus3](https://hub.docker.com/r/sonatype/nexus3)のサイト記述によると、パスワードは**admin.password**というファイルに書き込まれているとのこと。
>**Notes:**
>Default user is admin and the uniquely generated password can be found in the **admin.password** file inside the volume. See Persistent Data for information about the volume.

上記の手順でpod名を特定し、oc rshでpodにログインしてから**/nexus-data/admin.password**の中身をcatしてパスワードを入手します。[^1]

```bash
handson01@devops01:~$ oc get pods
NAME             READY     STATUS    RESTARTS   AGE
nexus3-1-45rpz   1/1       Running   0          2m
handson01@devops01:~$ oc rsh nexus3-1-45rpz
sh-4.4$ find / -name admin.password
find: '/var/cache/ldconfig': Permission denied
find: '/var/lib/private': Permission denied
find: '/var/lib/portables': Permission denied
find: '/lost+found': Permission denied
find: '/proc/tty/driver': Permission denied
/nexus-data/admin.password
sh-4.4$ cat /nexus-data/admin.password
6a37dec6-fc57-41c8-87ac-***********sh-4.4$
```

[^1]: admin.passwordはpod起動後5-10秒くらいしないと生成されないようですので、見つからない場合はしばらく待ってから再実行してみてください



<details><summary>OpenShiftの管理コンソールからPods-Terminalでも同様の操作は可能です。</summary><div>


![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/13094405-9a1d-9ac4-3869-6d70eb4760c0.png)

</div></details>

### Nexus3の管理コンソールへのログイン

ルートで示されたURL(例. http://nexus3-nexus3.162.133.79.188.nip.io) へアクセスし、Nexus3の管理コンソールを開いてから右上の「Sign In」をクリック

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/3a0b41fa-3c85-1033-978a-0a457e377174.png)

ユーザーはadmin, パスワードは先ほど入手したものを入力して「Sign In」
![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/570690f0-d590-b69d-3ef7-90fee8e025b4.png)

### セットアップ・ウイザード

初期セットアップのウイザードが起動するので「Next」
![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/efa826fe-f3ce-c03b-5bb8-cb8a36ed7455.png)

まずは初期パスワードを変更して「Next」
![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/6dfbaeba-df97-1a93-c11f-d66982fe73bc.png)

アノニマスなアクセスを許容するかどうか、を指定。今回はパブリックなmaven2リポジトリーをキャッシュする目的なので機密性はなく、ゆえに「Enable anonymous access」にチェックを入れて「Next」
![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/90e22962-22d2-f738-11c5-0cec423bc79b.png)

これでおしまいです。
![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/058c7598-d6e7-c98d-8d51-d26dc2077d1a.png)

### MavenプロキシーのURLの入手

maven package実行時に指定するべきリポジトリーのURLを入手します。

コンソール上部の「歯車マーク(Server Administration and Configulation) 」- 「Repository」- 「Repositories」で表示されリポジトリーの一覧から**maven-central**をクリック

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/62ba5a7a-845d-53c2-37f4-93e0bb065cd8.png)

URLの個所がMavenで参照先とすべきURLです。

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/4cd29200-32da-54e1-a754-f93d8674efdc.png)


## mavenビルド時の指定

これでNexus3は使えるようになったので、あとはMavenでビルドする際のリポジトリーとして（デフォルトのrepo.maven.apache.orgではなく) Nexus3のMavenプロキシーを参照するようにURLを指定します。
具体的には以下のようにmvn packageによるビルド時に `-s nexus-settings.xml`[^2]でユーザー設定ファイルを指定します。

[^2]: -s,--settings <arg>   Alternate path for the user settings file の意味


```bash:s2iでのassemble指定例
mvn package -s nexus-settings.xml
cp -f target/*.war /config/dropins/
```

ユーザー設定ファイルの内容は下記で、URL 3個所を書き換えます。[^3]
記述は記事 :newspaper: [OpenShift Nexus](https://kb.novaordis.com/index.php/OpenShift_Nexus)を参考にしました。

[^3]: Nexus3の設定ウイザードでアノニマスなアクセスを有効にしたのでusernameとpasswordは指定不要です

```xml:nexus-settings.xml
<settings>
    <servers>
        <server>
            <id>nexus</id>
            <!-- username>admin</username -->
            <!-- password>password</password -->
        </server>
    </servers>
    <mirrors>
        <mirror>
            <id>nexus</id>
            <mirrorOf>*</mirrorOf>
            <url>http://nexus3-nexus3.162.133.79.188.nip.io/repository/maven-central/</url>
        </mirror>
    </mirrors>
    <profiles>
        <profile>
            <id>nexus</id>
            <!--Enable snapshots for the built in central repo to direct -->
            <!--all requests to nexus via the mirror -->
            <repositories>
                <repository>
                    <id>central</id>
                    <url>http://nexus3-nexus3.162.133.79.188.nip.io/repository/maven-central/</url>
                    <releases><enabled>true</enabled></releases>
                    <snapshots><enabled>true</enabled></snapshots>
                </repository>
            </repositories>
            <pluginRepositories>
                <pluginRepository>
                    <id>central</id>
                    <url>http://nexus3-nexus3.162.133.79.188.nip.io/repository/maven-central/</url>
                    <releases><enabled>true</enabled></releases>
                    <snapshots><enabled>true</enabled></snapshots>
                </pluginRepository>
            </pluginRepositories>
        </profile>
    </profiles>
    <activeProfiles>
        <activeProfile>nexus</activeProfile>
    </activeProfiles>
</settings>
```

## 結果

上記の指定をしたうえで再度Mavenビルドを実行すると、初回はダウンロードのたえ60秒以上かかりますが、２回目以降はキャッシュが効いて15秒前後でビルドが終了しました！

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108535/fe80371d-51ef-3673-ff7a-12a11dccfead.png)

podのビルドのログでもNexus3を参照していることが分かります。(Downloading from nexus:～)

```bash
[INFO]
[INFO] ------------------------< SimpleApp:SimpleApp >-------------------------
[INFO] Building SimpleApp 1.0
[INFO] --------------------------------[ war ]---------------------------------
Downloading from nexus: http://nexus3-nexus3.162.133.79.188.nip.io/repository/maven-central/org/apache/maven/plugins/maven-resources-plugin/2.6/maven-resources-plugin-2.6.pom 
Progress (1): 4.1/8.1 kBProgress (1): 7.7/8.1 kBProgress (1): 8.1 kB                        Downloaded from nexus: http://nexus3-nexus3.162.133.79.188.nip.io/repository/maven-central/org/apache/maven/plugins/maven-resources-plugin/2.6/maven-resources-plugin-2.6.pom  (8.1 kB at 43 kB/s)
Downloading from nexus: http://nexus3-nexus3.162.133.79.188.nip.io/repository/maven-central/org/apache/maven/plugins/maven-plugins/23/maven-plugins-23.pom 
Progress (1): 4.1/9.2 kBProgress (1): 7.8/9.2 kBProgress (1): 9.2 kB                        Downloaded from nexus: http://nexus3-nexus3.162.133.79.188.nip.io/repository/maven-central/org/apache/maven/plugins/maven-plugins/23/maven-plugins-23.pom  (9.2 kB at 766 kB/s)
...
```

# おわりに

OpenShift環境でのNexus3のMavenプロキシーは導入や環境設定も非常に簡単で、JavaアプリケーションのDevOpsパイプラインを構築する際のMaven ビルド時間の短縮に効果があります。ぜひお試しください。また、Nexus3を用いてプライベートなDockerイメージ・レジストリーを構築することもできます。OpenShiftには独自のイメージ・レジストリーが標準装備されていますので必要性は薄いかもしれませんが、別の選択肢として覚えておくとよいかもしれません。以上です。