LoginSignup
0

More than 1 year has passed since last update.

posted at

updated at

Organization

OCI(Oracle Cloud Infrasturucture)で構築するマイクロサービス

はじめに

この記事はOracle Cloud Infrastructure #2 Advent Calendar 2020の24日目の記事です。

12/17-12/18の2日間に渡って開催されたOracle Developer Daysの2日目にCloud Nativeセッションを担当させて頂きました。

この記事では、上記セッションで使用した簡単なデモアプリケーションについて、その解説と補足をしていきたいと思います。

デモのソースコードについて

使用したデモについては、このレポジトリにありますので、ご自由にお使い頂ければと思います。

セッション資料はこちらです。
動画についてもこちらにアップロードされています。

デモアプリケーションの構成

Greenshot 2020-12-20 22.50.00.png

上図はセッション資料にも含まれている図です。

Nuxt.jsで実装したフロントエンドアプリケーションとHelidon(後述)アプリケーションをGraalVM(後述)でNative Image化(後述)した2種類のバックエンドAPIから構築したアプリケーションです。全体がショボすぎてマイクロサービスなのかというのはありますが、一応サービスごとにコンポーネントが分かれているということで許してください...

フロントエンドへはロードバランサー経由でアクセスします。

フロントエンドからバックエンドAPIへのアクセスはClusterIPを利用します。
ここで、勘が良い方はお気づきかもしれませんが、フロントエンドはいわゆるSSR(サーバサイドレンダリング)で構築しています。

ランタイム環境はKubernetesを利用しています。(ここでは、Oracle Cloud Infrastructureで利用可能なマネージドKubernetesサービスを使っています)

デモアプリケーションで利用している技術スタック

このデモでは、Oracle Cloud Infrastructureで利用可能な以下のサービスやプロダクトを利用して実装しています。

  • GraalVM:オープンソースのランタイムエンジンです。JavaやScalaなどのJVM上で動作する言語以外にもJavaScriptやPythonなども動作させることがでできます。後ほど紹介しますが、Javaのバイトコードを事前にコンパイルするNative Imageという仕組みも利用することができます。今回のデモアプリケーションでは、HelidonアプリケージョンをNative Imageとしてビルドして稼働させています。また、今回は、Nuxt.jsで実装したフロントエンドアプリケーションもGraalVMで動作させています。

  • Helidon:非常に軽量なオープンソースのアプリケーションフレームワークです。マイクロサービス・アーキテクチャを導入する際に非常に相性が良いオープンソースのJavaのアプリケーション・フレームワークとなっています。今回のデモアプリケーションでは、バックエンドアプリケーションをHelidonで実装しています。

  • Oracle Container Engine for Kubernetes(OKE):Oracle Cloud Infrastructureで提供されているマネージドのKubernetesサービスです。今回のデモアプリケーションでは、アプリケーションのランタイム環境としてOKEを利用しています。

デモの構成

このデモは、以下の構成になっています。

.
├── devdays-demo-backend_kohaku
├── devdays-demo-backend_trendword
├── devdays-demo-frontend
├── k8s_manifest

それぞれのディレクトリについて見ていきます。
ソースコードまで見ていくとキリがないので、今回はDockerfileとmanifestを中心に見ていきます。

Dockerfile

まずはそれぞれのアプリケーションのDockerfileを見ていきます。

devdays-demo-backend_kohaku


# 1st stage, build the app
FROM helidon/jdk11-graalvm-maven:20.1.0 as build

今回はNative Imageとしてビルドするためにhelidon公式で公開されているベース・イメージを利用します。
このイメージには、HelidonをビルドするためのJDKの他にGraalVMとmavenコマンドが梱包されています。

WORKDIR /helidon

# Create a first layer to cache the "Maven World" in the local repository.
# Incremental docker builds will always resume after that, unless you update
# the pom
ADD pom.xml .
RUN mvn package -Pnative-image -Dnative.image.skip -Dmaven.test.skip -Declipselink.weave.skip

作業ディレクトリWORKDIRを作成し、ローカルからコンテナにpom.xmlをコピーします。
その後、mavenコマンドでビルドしていきます。
ここでは、pom.xmlに定義されている内容を元にライブラリや依存関係などをローカルにキャッシュするためのプロセスです。
このプロセスを一度実行すると、以降のビルドではpom.xmlが更新されるまではこのプロセスはスキップされます。
また、実際にNative Imageはビルドされません。

# Do the Maven build!
# Incremental docker builds will resume here when you change sources
ADD src src
RUN mvn package -Pnative-image -Dnative.image.buildStatic -DskipTests

RUN echo "done!"

ここではじめてソースコードをローカルからコピーし、ビルドを行っていきます。
-Pnative-image -Dnative.image.buildStaticオプションを指定することで、Native Imageとしてビルドされます。

遅くなりましたが、ここでNative Imageについて紹介しておきます。
上記で紹介したセッション資料にも記載している絵を利用して説明します。
Greenshot 2020-12-20 15.04.14.png

Native Imageとは、Javaコード(JVMベースの言語)を事前コンパイルし、スタンドアローンで実行可能な形にコンパイルするしたものです。

具体的には

  • 依存関係にあるアプリケーションクラス群
  • 実行時に利用するJDKクラス群(ランタイム環境)
  • 静的にリンクされたJDKのネイティブコード

などを含んでいます。

メリットとしては、

  • ランタイム起動時間の短縮
  • メモリフットプリントの極小化
  • セキュリティの向上(ランタイム環境の隔離)

などがあげられます。

ビルドしたNative Imageはシングルバイナリとして実行することが可能です。

# 2nd stage, build the runtime image
FROM scratch
WORKDIR /helidon

# Copy the binary built in the 1st stage
COPY --from=build /helidon/target/backend-kohaku-app .

ENTRYPOINT ["./backend-kohaku-app"]

EXPOSE 8081

ここからはマルチステージビルドを利用して、非常に軽量なベース・イメージscratchに先ほどビルドしたNative Imageをコピーします。

最後にNative Imageを実行します。

ランタイム環境も含めたバイナリなので、JDKがベース・イメージに不要ですし、java -jarなどのコマンドも付与することなく実行できます。

最後の行のEXPOSE 8081はローカル環境で動作確認をするために付与したものです。

devdays-demo-backend_trendword

こちらもdevdays-demo-backend_kohakuと全く同じDockerfileになっています。

# 1st stage, build the app
FROM helidon/jdk11-graalvm-maven:20.1.0 as build

WORKDIR /helidon

# Create a first layer to cache the "Maven World" in the local repository.
# Incremental docker builds will always resume after that, unless you update
# the pom
ADD pom.xml .
RUN mvn package -Pnative-image -Dnative.image.skip -Dmaven.test.skip -Declipselink.weave.skip

# Do the Maven build!
# Incremental docker builds will resume here when you change sources
ADD src src
RUN mvn package -Pnative-image -Dnative.image.buildStatic -DskipTests

RUN echo "done!"

# 2nd stage, build the runtime image
FROM scratch
WORKDIR /helidon

# Copy the binary built in the 1st stage
COPY --from=build /helidon/target/backend-trendword-app .

ENTRYPOINT ["./backend-trendword-app"]

異なるのは、Native Imageのファイル名だけになります。

devdays-demo-frontend

同様にフロントエンドアプリケーションもDockerfileを見ていきます。

FROM oracle/graalvm-ce:latest

# GraalVMのnpmバイナリをPATHに追加
ENV PATH $PATH:$JAVA_HOME/bin/npm

# ディレクトリ作成
WORKDIR /app

冒頭でお話しした通り、今回はNuxt.jsのアプリケーションをGraalVMで動作させてきます。
GraalVMにはgraaljsというECMAScript2020互換の仕組みが実装されているので、これを利用します。
ちなみにgraaljsはこちらに詳細があります。
ベース・イメージにGraalVM CE(Community Edition)を使用していますが、この中にnpmバイナリが梱包されています。
このnpmバイナリにパスを通すことで、以降のNuxt.jsアプリケーションのビルドに利用していきます。

# パッケージをコピー
COPY package*.json ./

# npm モジュールをインストール
RUN npm install --quiet

# 成果物コピー
COPY . .

# このコマンドをしないといけないとwarnが出たので
RUN npm rebuild

# 本当はいらないが開発環境でvue-cliを使っていたのでそこに含まれているパッケージを使っているようでwarnが出たので入れる
RUN npm install vue-cli -g

# ビルド
RUN npm run build

# なくても良い
ENV HOST 0.0.0.0
# なくても良い
EXPOSE 3000

# 起動
CMD ["npm", "run", "start"]

ここから先は、GraalVMに梱包されているnpmバイナリでビルドしていくだけです。

manifest

続いて、各アプリケーションのmanifestを見ていきます。

backend_kohaku.yaml

要所をピックアップしていきます。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-kohaku-app
  labels:
    app: backend-kohaku
spec:
  replicas: 2
  selector:
    matchLabels:
      app: backend-kohaku

今回レプリカ数は2個で冗長化しています。

    spec:
      containers:
      - name: backend-kohaku
        image: phx.ocir.io/orasejapan/devdays2020/backend-kohaku-app
        ports:
        - containerPort: 8081

コンテナイメージは、Oracle Cloud Infrastructure Registry(OCIR)に登録しています。
アプリケーションは8081ポートで公開します。

      - name: h2kohaku
        image: oscarfonts/h2
        env:
        - name: H2_OPTIONS
          value: "-ifNotExists"
        ports:
        - containerPort: 81
        - containerPort: 1521

今回は、サイドカーとしてもう一つのコンテナをデータソース(データベース)として利用します。
サイドカーパターンについてはこちらを参考にしてください。

サイドカーに対してはlocalhostでアクセスできます。

次にアプリケーションに対するServiceの設定です。
フロントエンドのサービスからバックエンドアプリケーションをルーティングするための設定です。

apiVersion: v1
kind: Service
metadata:
  name: backend-kohaku-svc
spec:
  type: ClusterIP 
  ports:
  - port: 8081
    targetPort: 8081
  selector:
    app: backend-kohaku

ポートについては、アプリケーション側で利用している8081ポートをそのまま流しています。
今回はClusterIPタイプを利用しています。
今回はフロントエンドをSSR(サーバサイドレンダリング)で実装しているので、バックエンドをサーバサイド(ブラウザではなく)で呼び出しているので、ClusterIPとしました。

backend_trendword.yaml

ほぼbackend_kohaku.yamlと同様です。異なるのはポート番号のみです。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-trendword-app
  labels:
    app: backend-trendword
spec:
  replicas: 2
  selector:
    matchLabels:
      app: backend-trendword
  template:
    metadata:
      labels:
        app: backend-trendword
    spec:
      containers:
      - name: backend-trendword
        image: phx.ocir.io/orasejapan/devdays2020/backend-trendword-app 
        ports:
        - containerPort: 8082
      - name: h2trendword
        image: oscarfonts/h2
        env:
        - name: H2_OPTIONS
          value: "-ifNotExists"
        ports:
        - containerPort: 81
        - containerPort: 1521
---
apiVersion: v1
kind: Service
metadata:
  name: backend-trendword-svc
spec:
  type: ClusterIP 
  ports:
  - port: 8082
    targetPort: 8082
  selector:
    app: backend-trendword

frontend_app.yaml

最後にフロントエンドアプリケーションのmanifestを見ていきたいと思います。

こちらも要所要所で解説していきます。


apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-app
  labels:
    app: frontend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend

フロントエンドアプリケーションも、バックエンドアプリケーションと同様に2個のレプリカで冗長化しています。

    spec:
      containers:
      - name: frontend
        image: phx.ocir.io/orasejapan/devdays2020/frontend-app
        ports:
        - containerPort: 3000
        env:
        - name: HOST
          value: "0.0.0.0"

コンテナイメージはバックエンド同様にOCIRにpushしています。

コンテナポートは3000ポートで公開しています。

apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  type: LoadBalancer
  ports:
  - port: 3000
  selector:
    app: frontend

こちらはインターネット側からのルーティングとしてLoadBalancerを利用しています。
ポート番号はアプリケーションと同様に3000番ポートで流しています。

以上がmanifestの紹介になります。

まとめ

今回はOracle Cloud Infrastructureで利用できる各種テクノロジーでマイクロサービスを実装してみました。

もちろん、マイクロサービス自体は今回解説したものだけで簡単に実装できてしまうものではありません。

ただ、今回利用したような技術スタックを利用していけば、少しでも効率的に実装や運用を行うことができるのではないかと思います。

最後に...

今回ご紹介したGraalVM(Community Edition)とHelidonはオープンソースで公開されています。
積極的に開発が進められているプロダクトになりますので、ぜひお試しいただければと思います!

ちなみにGraalVM Enterprise EditionはOracle Cloud Infrastructureで無償でご利用いただけます!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0