Posts filed under ‘Java’

WebLogic on Azure VM が正式リリース

Azure Linux 仮想マシンで OracleWebLogic Server(WLS)が正式に利用可能になったことをお知らせします。
このリリースは、Microsoft と Oracle の幅広いパートナーシップの一環として、WebLogic チームと共同で開発しました。
このパートナーシップには、Oracle/Microsoft および Azure で実行されているさまざまな Oracle ソフトウェアのサポートも含まれます。

Continue Reading 2020年9月29日 at 1:29 午後 コメントを残す

Azure AD で OpenID Connect を使用したセキュアな Open Liberty アプリケーションの構築

原文はコチラ
https://cloudblogs.microsoft.com/opensource/2020/08/10/securing-open-liberty-applications-azure-active-directory-openid-connect/

* Application Development
* Cloud
* Tutorials and Demos
* Community/partners

Web アプリケーションで、ユーザーアカウントを独自に作成したり、認証・認可を個別に実装・管理する時代は終わりました。

その代わりに、現在のアプリケーションは、外部プロバイダーを利用して、上記の機能(Identity and Access Management略してIAM)を実装します。 完全な Javaアプリケーションの実行基盤として存在する、Open Liberty は、外部プロバイダーの IAM を利用するための機能を備えています。Open Liberty はソーシャルメディア・ログイン、SAML Web シングルサインオン、OpenID Connect クライアントなど IAM の主要な機能をサポートしています。

Bruce Tiffany のブログ・エントリ 「Securing Open Liberty apps and microservices with MicroProfile JWT and Social Media login」では、Open Liberty ソーシャルメディア・ログインの機能を使用して、既存のソーシャルメディアの認証情報を使用してユーザー認証する方法を紹介しています。

このブログ・エントリでは上記とは別の方法をご紹介します。ここで Microsoft の Azure Active Directory で Java アプリケーションを保護するために、Liberty ソーシャルログイン機能を OpenID Connectクライアントとして実装する方法を見てみましょう。

このブログで紹介するサンプルコードは、全て コチラの GitHub リポジトリで公開しています。ブログをご覧頂く前後で、コードをチェックし、ユーザーガイドに従い Java EE サンプル・アプリケーションを実行してください。

Azure Active Directory のセットアップ

Azure Active Directory(Azure AD)は、認証プロトコルとして OAuth 2.0 上に OpenID Connect(OIDC)を実装しているため、Azure AD を利用したアプリケーションのセキュアなユーザー認証ができるようになっています。

サンプルコードをご覧いただく前に、Azure AD テナントをセットアップし、リダイレクト URL の設定とクライアントシークレットでアプリケーションを登録してください。

Azure AD のテナント・セットアップ時に取得する、テナントID、アプリケーション(クライアント)ID、およびクライアントシークレットは、Open Liberty が Azure AD と接続し OAuth 2.0 承認フローを行うために使用しますので情報を控えておいてください。

Azure AD の設定方法は、下記の記事をご参照ください。

* クイック スタート:テナントを設定する
* クイック スタート:Microsoft ID プラットフォームにアプリケーションを登録する
* 新しいアプリケーション シークレットを作成する

OpenID Connect クライアントとしてソーシャルログインを構成

下記のサンプルコードは、Open Liberty サーバーで実行されているアプリケーションが、どのようにして、Azure AD を使用した OpenID Connectプロバイダーに対して、socialLogin-1.0 の機能を持つ OpenID Connect クライアントからユーザー認証を行うのか、その方法を示しています。

関連するサーバー構成:server.xml


GitHub の server.xml へのリンクはコチラ

oidcLogin 要素は、Open Liberty が持つ多数の設定可能要素の一つです。

サンプル・コードで示すように、Azure AD は検出用のエンドポイントをサポートしているため、Azure AD を利用する場合、多くの設定は不要で、サンプル・コードで示すように、いくつかのオプションのみを使用します。

検出用のエンドポイントを利用すると、ほとんどの OpenID Connect の設定情報をクライアントが自動的に取得できるため、設定が大幅に簡素化できます。

さらに、特定の Azure AD インスタンスは、検出用エンドポイント URL に対して既知のパターンを適用するため、テナントID を使用して、URL をパラメーター化することもできます。
また、クライアントIDとシークレットが必要で、署名アルゴリズムとして RS256 を使用します。

userNameAttribute 属性は、Azure AD のトークンと Liberty の一意のサブジェクト ID にマップするために使用します。
使用可能な Azure AD トークンは、この一覧に示すようにいくつかあります。
v1.0 と v2.0 で必要なトークンは異なるため、注意が必要です(v2.0は一部のv1.0トークンをサポートしていません)。
preferred_username もしくは oid は共に安全に使用できますが、通常 preferred_username の使用をお勧めします。

Azure ADを使用すると、アプリケーションは、Microsoft の認証局 (Root CA) によって署名された証明書を使用できます。

この証明書を、JVM のデフォルトの cacerts に追加します。 JVM のデフォルトの cacerts を信頼することで、OIDC クライアントと Azure AD 間で SSL ハンドシェイクが成功することが保証されます(つまり、defaultSSLConfig trustDefaultCerts値を true に設定する)。
この場合、Azure AD を介して認証されたすべてのユーザーにユーザーロールを割り当てます。必要に応じて、Liberty でより複雑なロールマッピングも可能です。

OpenID Connect を使用したユーザー認証


サンプル・アプリケーションは JSF アプリとして公開します。ここでは “users” のロールを持つユーザーのみがアクセスできる Java EE セキュリティ規約を定義しています。

web.xmlの関連構成:

GitHub の web.xml へのリンクはコチラ

ワークフロー

図1:Azure AD で OpenID Connect を利用したトークン取得フロー

これは Java EE の標準セキュリティです。
認証されていないユーザーが JSF のアプリケーションに対してアクセスしようとすると、Azure AD の資格情報を取得するために Microsoft のサイトにリダイレクトされます。

成功すると、ブラウザは認証コードを含んでクライアントにリダイレクトします。

次に、クライアントは、再度 Microsoft のサイトに接続し、認証コード、クライアントID、シークレットを使用して IDトークンとアクセストークンを取得します。最後に、クライアントで認証済みユーザーを作成し、JSF アプリに対してアクセスします。

認証されたユーザー情報を取得するには、@ Injectアノテーションを使用してjavax.security.enterprise.SecurityContextへの参照を取得し、getCallerPrincipal() メソッドを呼び出します。

GitHub の Cafe.java へのリンクはコチラ

JWT RBACを使用したセキュアな内部 RESTful 呼び出し

JAX-RS で実装した RESTful Web サービスである Cafe Bean は CafeResource に依存し、コーヒーの作成、読み取り、更新、削除を行います。

CafeResourceは、MicroProfile JWT を使用して RBAC(ロールベースのアクセス・コントロール)を実装し、トークンのグループ要求を検証します。


GitHub の CafeResource.java へのリンクはコチラ

admin.group.id は、ConfigProperty アノテーションで定義する事で、MicroProfile Config を使用し、アプリケーションの起動時に値を注入します。
MicroProfile JWT では JWT(JSON Web トークン)を @Inject で注入きます。

CafeResource REST エンドポイントは、OpenID Connect の承認ワークフローに従い、Azure AD によって発行された ID トークンから、preferred_username および groups クレームを含む JWT を受け取ります。

ID トークンは、com.ibm.websphere.security.social.UserProfileManager およびcom.ibm.websphere.security.social.UserProfile API を使用して取得できます。

関連する設定:server.xml

GitHub の server.xml へのリンクはコチラ

注意:
グループ claim はデフォルトでは伝搬されないため、Azure AD の追加設定が必要です。
ID トークンにグループ claim を 追加するためには、Azure ADでタイプが「セキュリティ」のグループを作成し、1 名以上のメンバーをそのグループに追加する必要があります。
Azure AD の設定で、アプリケーションの登録時に、下記の情報を取得する必要があります。
「Token configuration」 を選択> 「Add groups claim」 を選択 >「セキュリティグループ」を選択(ID トークンを含むグループ) >「ID」を展開 > 「Customize token Properties」から「Group ID」を選択

詳細はコチラをご覧ください。

* Azure Active Directory を使用して基本グループを作成してメンバーを追加する
* グループの省略可能な要求を構成する

まとめ


このブログエントリでは、OpenID Connect と Azure Active Directory を使用してOpen Libertyアプリケーションを効果的にセキュアにする方法を示しました。
ここの記載内容に加え、オフィシャルの Azure サンプルも、WebSphere Liberty 上で簡単に動作します。

この取り組みは、Jakarta EE (Java EE)、および MicroProfile を使用する開発者に有用な情報とツールを提供するため、Microsoft と IBM が協力してできた物の一つです。

* Java EE はベンダー中立のオープンソースガバナンスの下で Jakarta EE として Eclipse Foundation に移管されました
* MicroProfileは、Java EEテクノロジーに基づいて構築され、マイクロサービスドメインを対象とする一連のオープンソース仕様です

どのようなツールやガイダンスが必要かご意見をお聞かせください。
また、特に、クラウド・マイグレーションにご興味があり、(無償で)私たちと緊密に連携したいご要望がある場合、
可能でしたら、このブログ・エントリに関する 5 分間のアンケートにご協力・ご記入頂き、貴重なフィードバックをください。
どうぞ宜しくお願いします。

2020年9月3日 at 2:24 午後 コメントを残す

Java 開発者のための Docker Multi Stage Build の活用方法

Java 開発者のために Docker Multi Stage Build を活用した本番用イメージの作成方法と、監視やデバッグが可能な検証用イメージの同時作成方法についてビデオにまとめました。どうぞご覧ください。

下記のような Multi Stage Build に対応した Dockerfile を作成します。(動画中は少し Dockerfile の書き方が変わっていますが、基本的には下記と同じです。)

# syntax = docker/dockerfile:1.0-experimental
###############################################
# Create Runtime Bases Image
###############################################
FROM mcr.microsoft.com/java/jre:11u6-zulu-alpine as base

RUN apk update \
 && apk add tzdata \
 && rm -rf /var/cache/apk/*

ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP
ENV LC_ALL ja_JP.UTF-8

ENV TZ='Asia/Tokyo'
ENV APP_HOME /home/java

RUN addgroup -g 2000 -S java && adduser -u 2000 -S java -G java
USER java

WORKDIR $APP_HOME

###############################################
# Build Source Code
###############################################
FROM maven:3.6.3-jdk-11-slim as BUILD
#FROM mcr.microsoft.com/java/maven: as BUILD

WORKDIR /build
COPY pom.xml .
COPY src/ /build/src/

## Maven のローカル・レポジトリをキャッシュ
RUN --mount=type=cache,target=/root/.m2 \
     mvn clean package

###############################################
# Create Final Image
###############################################
FROM base as final

COPY --from=BUILD /build/target/sample-java-app-0.0.1-SNAPSHOT.jar /home/java/app.jar

ENTRYPOINT ["java", "-jar", "/home/java/app.jar"]
EXPOSE 8080

###############################################
# Create Debuggable Monitorable Image
###############################################
FROM base as debug

COPY --from=BUILD /build/target/sample-java-app-0.0.1-SNAPSHOT.jar /home/java/app.jar

ENTRYPOINT ["java", "-Xdebug", 
"-Xrunjdwp:server=y,transport=dt_socket,address=*:5050,suspend=n",
"-Dcom.sun.management.jmxremote.local.only=false",
"-Djava.rmi.server.hostname=127.0.0.1",
"-Dcom.sun.management.jmxremote",
"-Dcom.sun.management.jmxremote.port=18686",
"-Dcom.sun.management.jmxremote.rmi.port=18686",
"-Dcom.sun.management.jmxremote.authenticate=false",
"-Dcom.sun.management.jmxremote.ssl=false", 
"-jar", "/home/java/app.jar"]
EXPOSE 8080 5050 18686

上記の Dockerfile を元にイメージを作成する際、それぞれ下記のようにターゲットを指定してビルドを行ってください。

本番用イメージの作成 (–target=final)

$ docker build -t tyoshio2002/spring-sample:1.0 . --target=final
$ docker run -p 8080:8080 -it tyoshio2002/spring-sample:1.0

検証環境イメージの作成と起動 (–target=debug)

$ docker build -t tyoshio2002/spring-sample-debug:1.0 . --target=debug
$ docker run -p 8080:8080 -p 5050:5050 -p 18686:18686 -it tyoshio2002/spring-sample-debug:1.0

その他の特記事項


上記の Dockerfile は先頭行に # syntax = docker/dockerfile:1.0-experimental と記載しています。これを記述すると Docker の Exprimental の機能 (BuildKit) が利用できるようになります。これによりビルド処理を並列で実行できるようになるほか、Dockerfile 中の下記の行のように、Multi-stage Build で Maven のローカル・レポジトリをキャッシュできるようになります。

RUN --mount=type=cache,target=/root/.m2 \
     mvn clean package

より詳しく説明すると、今まではマルチステージ・ビルド中では、Maven のローカル・レポジトリをキャッシュできなかったため、ビルドの度に毎回大量の依存ライブラリをパブリック・レポジトリからダウンロードしなければなりませんでしたが、この–mount=type=cacheで毎回のダウンロードは不要になります。

こうした Docker の新しい機能もどうぞご活用ください。

2020年5月11日 at 6:31 午後

Deploying Java EE apps to Azure: Part 1

この記事は Deploying Java EE apps to Azure: Part 1 の記事の日本語翻訳版です。

クラウド環境用に Java アプリケーションを開発し展開するためには下記のような様々な選択肢があります。

  • IaaS(Infrastructure-as-a-Service)
  • PaaS(Platform-as-a-Service)
  • CaaS(Containers-as-a-Service)
  • Kubernetes
  • サーバーレス
  • その他

上記は、いずれか一つだけを選択して使用するというよりも、それぞれに「長所と短所」があるため、利用者のニーズに応じて適材適所で組み合わせて利用することが必要です。最終的にはビジネス要件に従いどれを使用するかを決定してください。利用者にとって「選択肢」が複数あることはとても良いことです。

ここでは、Azure 上で Java EE アプリケーションを実行するための選択肢をいくつかご紹介します。
はじめに、従来のオンプレミス環境で実施していた方法と同じ手法をご紹介します。まず Microsoft Azure 上に仮想マシンを構築し、その上にアプリケーション・サーバをインストールします。そして Java EE アプリケーションをアプリケーション・サーバにデプロイします。アプリケーションはバックエンドのデータベースに接続をしますが、ここでは Azure Database for PostgreSQL をバックエンドのデータベース・サーバとして利用します。この構成は Java EE アプリケーションの構築・配備において最も典型的な構成で、本質的には IaaS(Azure VM)とPaaS(マネージドPostgreSQL on Azure)を組み合わせた構成です。

コンテナーやKubernetesなどの他の構成については、今後の投稿で取り上げます

本エントリで使用するアプリケーションのサンプルは、JAX-RS、EJB、CDI、JPA、JSF、Bean Validation など Java EE 8 の仕様に準拠した 基本的な3階層アプリケーションです。 今回 IaaS で Payara Server を構成し、バックエンドの PostgreSQL に対して接続をします。

チュートリアルは、下記の手順にしたがい行います。

  1. Azure 上に仮想マシンと Postgres を構築
  2. 仮想マシン上に Payara Server をインストール
  3. Java EEアプリケーションを配備
  4. アプリケーションの動作確認

このチュートリアルで使用するアプリケーションは一部を除いて、Reza Rahman が作成したこのプロジェクトを利用します。

前提条件

チュートリアルを実行するには、Microsoft Azure アカウントAzure CLI が必要です。
Microsoft Azureアカウントをお持ちでない場合は、無料アカウントにサインアップしてください! Azure CLIは、Azureリソースを管理するためのクロスプラットフォームのコマンドラインエクスペリエンスです。こちらの手順に従ってインストールしてください。

最初に

Azure CLIを使用して、チュートリアルで使用する Azure のサブスクリプション ID を設定します。
AzureサブスクリプションIDを設定するには下記のコマンドを実行してください。

export AZURE_SUBSCRIPTION_ID=[to be filled]
az account set --subscription $AZURE_SUBSCRIPTION_ID

次に、このチュートリアルで作成するサービス(リソース)を含むリソース・グループを作成します。

リソースグループは、Azure 上で構築する関連リソースをまとめて管理する論理的なコンテナー(ディレクトリ)のようなものです。
リソースグループを作成するために、下記のコマンドを実行してください。

export AZURE_RESOURCE_GROUP_NAME=[to be filled]
export AZURE_LOCATION=[to be filled]
az group create --name $AZURE_RESOURCE_GROUP_NAME ¥ 
--location $AZURE_LOCATION

Azure 上に PostgreSQL のインストール

Azure Database for PostgreSQL はオープンソースの Postgres データベースを利用した完全マネージドなリレーショナル・データベースサービスです。 単一サーバーもしくは、ハイパースケール(Citus)なクラスターのいずれかを選択し構築出来ます。

このチュートリアルでは、単一サーバーのオプションを選択して構築します。
Azure 上に Postgres Server のインスタンスを作成するには、az postgres server create コマンドを使用します。 まずサーバ名、管理ユーザーやパスワードなどを設定します。

export AZURE_POSTGRES_SERVER_NAME=[to be filled]
export AZURE_POSTGRES_ADMIN_USER=[to be filled]
export AZURE_POSTGRES_ADMIN_PASSWORD=[to be filled]
export SKU=B_Gen5_1
export STORAGE=5120

ストレージとSKU で指定可能なオプションについては、こちらのドキュメントをご参照ください

下記のコマンドを実行してデータベース・インスタンスを作成します。

az postgres server create ¥
  --resource-group $AZURE_RESOURCE_GROUP_NAME ¥
  --name $AZURE_POSTGRES_SERVER_NAME  ¥
  --location $AZURE_LOCATION ¥
  --admin-user $AZURE_POSTGRES_ADMIN_USER ¥
  --admin-password $AZURE_POSTGRES_ADMIN_PASSWORD ¥
   --storage-size $STORAGE ¥
   --sku-name $SKU

コマンド実行後、構築完了まで数分かかります。しばらくお待ちください。
作成した Postgres データベースの詳細を確認するには az postgres server show コマンドを実行します。

az postgres server show ¥
  --resource-group $AZURE_RESOURCE_GROUP_NAME ¥
  --name $AZURE_POSTGRES_SERVER_NAME

コマンドを実行すると、JSON が表示されます。のちほど、Postgres インスタンスに接続するので、fullyQualifiedDomainName の値を書き留めてください。

[AZURE_POSTGRES_DB_NAME].postgres.database.azure.com
のように表示されています。

Azure 上に仮想マシンをインストール

Azure 上に 仮想マシン(UbuntuベースのLinux VM)を構築し、JavaEE の仕様に準拠した Payara アプリケーションサーバーをインストールします。

Linux VM の構築に必要な情報を設定します。

export AZURE_VM_NAME=[to be filled]
export AZURE_VM_USER=[to be filled]
export AZURE_VM_PASSWORD=[to be filled]
export VM_IMAGE=UbuntuLTS

az vm create コマンドを実行して Linux VM のインスタンスを構築します

az vm create ¥
  --resource-group $AZURE_RESOURCE_GROUP_NAME ¥
  --name $AZURE_VM_NAME ¥
  --image $VM_IMAGE ¥
  --admin-username $AZURE_VM_USER ¥
  --admin-password $AZURE_VM_PASSWORD

VM のプロビジョニングには数分かかりますのでしばらくお待ちください。

パブリック IP アドレスを取得するために、az vm list-ip-addresses コマンドを実行してください。

az vm list-ip-addresses ¥
  --resource-group $AZURE_RESOURCE_GROUP_NAME  ¥
   --name $AZURE_VM_NAME

コマンドを実行すると JSON が表示されます。publicIpAddresses の箇所を確認し、ipAddress プロパティの値を確認します。
以降の手順で使用するため、環境変数に設定しておいてください。

export VM_IP=[to be filled]

Linux VM から Postgres データベースへのアクセス許可設定

Postgres データベースはデフォルトで外部からのアクセスが許可されていません。 Linux VM から Postgres インスタンスにアクセスするために、ファイアウォールでルールを追加し、アクセス許可設定を行います。az postgres server firewall-rule create コマンドを実行し、Linux VM 上にデプロイされた JavaEE アプリケーションから Postgres へ通信できるようにします。

export FIREWALL_RULE_NAME=AllowJavaEECafeAppOnVM
az postgres server firewall-rule create ¥
  --resource-group $AZURE_RESOURCE_GROUP_NAME ¥
  --server $AZURE_POSTGRES_SERVER_NAME ¥
  --name $FIREWALL_RULE_NAME ¥
  --start-ip-address $VM_IP ¥
  --end-ip-address $VM_IP

Linux VM へ Payara サーバのインストール

Payara Serverは、GlassFish から派生したオープンソースのアプリケーションサーバーです。オンプレミス、クラウド、ハイブリッド環境などいずれの環境でも、高信頼でセキュアな Java EE(Jakarta EE)/ MicroProfile の実行環境を提供します。

詳細については、GitHubのプロジェクトもしくはドキュメントをご参照ください。

Linux VM の IP に対してユーザ名を指定し SSH 接続します。

ssh $AZURE_VM_USER@$VM_IP

プロンプトが表示されたらパスワードを入力してください。Linux VM にログインしたのち、下記の手順を実行してください。

必要なツールセットのインストール

Payara サーバーをインストールする前に、JDK/Maven のインストールを行います。

sudo apt-get update
sudo apt install openjdk-11-jdk
sudo apt install maven

Payara サーバーのセットアップ

このチュートリアル執筆時点の最新の Payara サーバーのバージョンは Payara Server 5.201 です。
Payara のセットアップは、zipファイルをダウンロードして解凍するだけです。

export PAYARA_VERSION=5.201
wget https://s3-eu-west-1.amazonaws.com/payara.fish/Payara+Downloads/$PAYARA_VERSION/payara-$PAYARA_VERSION.zip
sudo apt install unzip
unzip payara-$PAYARA_VERSION.zip

asadmin コマンドを実行しサーバの起動

ls ~/payara5/ コマンドを実行し、インストールされたファイルやディレクトリなどのを確認してください。
次に、bin ディレクトリ下にある asadmin コマンドを使用してサーバーを起動します

ls ~/payara5/
~/payara5/bin/asadmin start-domain

サーバーが起動するまで少し時間がかかります。 起動が完了すると下記のようなログが表示されます。

Waiting for domain1 to start .........
Successfully started the domain : domain1
domain  Location: /root/payara5/glassfish/domains/domain1
Log File: /root/payara5/glassfish/domains/domain1/logs/server.log
Admin Port: 4848
Command start-domain executed successfully.

アプリケーションのセットアップとデプロイ

以上で Linux VM を構築し Payara サーバをインストールし起動しました。そこでアプリケーションのデプロイを行います。
アプリケーションをデプロイするため Git リポジトリをクローンしてください。

git clone https://github.com/abhirockzz/javaee-on-azure-iaas
export APP_FOLDER_NAME=javaee-on-azure-iaas

Azure の Postgres データベースに接続するために、設定ファイル中の JDBC 接続URLを更新する必要があります。
設定ファイルは、web.xmlファイル(javaee-on-azure-iaas/src/main/webapp/WEB-INF のディレクトリ下)に存在し、ファイル中の セクションの 属性を更新してください。

jdbc:postgresql://POSTGRES_FQDN:5432/postgres?user=AZURE_POSTGRES_ADMIN_USER@=AZURE_POSTGRES_SERVER_NAME&password=AZURE_POSTGRES_ADMIN_PASSWORD&sslmode=require

以下は、JDBC URL の設定項目に関するリストです。

  • POSTGRES_FQDN は Postgres インスタンスが稼働するサーバの完全修飾ドメイン名を指定
  • AZURE_POSTGRES_ADMIN_USER は Postgres インストール時に設定した管理者ユーザー名
  • AZURE_POSTGRES_SERVER_NAME は Postgres のインストール時に設定したサーバー名
  • AZURE_POSTGRES_ADMIN_PASSWORD は Postgres のインストール時に設定した管理者のパスワード

環境変数に設定します。

export POSTGRES_FQDN=[to be filled]
export AZURE_POSTGRES_ADMIN_USER=[to be filled]
export AZURE_POSTGRES_SERVER_NAME=[to be filled]
export AZURE_POSTGRES_ADMIN_PASSWORD=[to be filled]

下記のコマンドを実行して、設定項目を修正します。

export FILE_NAME=javaee-on-azure-iaas/src/main/webapp/WEB-INF/web.xml
sed -i 's/POSTGRES_FQDN/'"$POSTGRES_FQDN"'/g' $FILE_NAME
sed -i 's/AZURE_POSTGRES_SERVER_NAME/'"$AZURE_POSTGRES_SERVER_NAME"'/g' $FILE_NAME
sed -i 's/AZURE_POSTGRES_ADMIN_USER/'"$AZURE_POSTGRES_ADMIN_USER"'/g' $FILE_NAME
sed -i 's/AZURE_POSTGRES_ADMIN_PASSWORD/'"$AZURE_POSTGRES_ADMIN_PASSWORD"'/g' $FILE_NAME

下記に セクションの例を示します。

<data-source>
    <name>java:global/JavaEECafeDB</name>
    <class-name>org.postgresql.ds.PGPoolingDataSource</class-name>
    <url>jdbc:postgresql://foobar-pg.postgres.database.azure.com:5432/postgres?user=foobar@foobar-pg&amp;amp;password=foobarbaz&amp;amp;sslmode=require</url>
</data-source>

アプリケーションの設定を変更しましたので、ソースコードをビルドしましょう!

mvn clean install -f $APP_FOLDER_NAME/pom.xml

上記コマンドを実行すると、war ファイルが生成されます。下記のコマンドで war ファイルが生成されているか確認してください。

ls -lrt $APP_FOLDER_NAME/target | grep javaee-cafe.war

アプリケーション設定の最後に、Postgres の JDBC ドライバーをダウンロードして Payara サーバーに追加しましょう。
ここではドライバーのバージョンは 42.2.12 を使用しています

export PG_DRIVER_JAR=postgresql-42.2.12.jar
wget https://jdbc.postgresql.org/download/$PG_DRIVER_JAR

asadmin add-library コマンドを実行し入手した jar ファイルを Payara に追加できます。

~/payara5/glassfish/bin/asadmin add-library $PG_DRIVER_JAR

もしくは、Maven の pom.xml ファイルに下記の依存関係を追加してください。

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.2.12</version>
</dependency>

最後に、WAR ファイルを Payara サーバにデプロイします。デプロイは Payara ドメインの autodeploy ディレクトリにコピーするだけでとても簡単です。

cp $APP_FOLDER_NAME/target/javaee-cafe.war ~/payara5/glassfish/domains/domain1/autodeploy

デプロイにはしばらく時間がかかりるため、それまでの間、次のコマンドでログを表示し、作業状況を確認します。

tail -f ~/payara5/glassfish/domains/domain1/logs/server.log

正常に、javaee-cafe アプリケーションのデプロイが完了すると、下記のようなメッセージが表示されます。

[2019-11-18T13:34:21.317+0000] [Payara 5.193] [INFO] [NCLS-DEPLOYMENT-02035] [javax.enterprise.system.tools.deployment.autodeploy] [tid: _ThreadID=104 _ThreadName=payara-executor-service-scheduled-task] [timeMillis: 1574084061317] [levelValue: 800] [[
[AutoDeploy] Successfully autodeployed : /home/abhishgu/payara5/glassfish/domains/domain1/autodeploy/javaee-cafe.war.]]

アプリケーションの動作確認

デプロイが完了したので、JavaEE アプリケーションの動作確認を行います。このアプリケーションは Web ブラウザーを利用してアクセスできます。しかし現時点で、Postgres のインスタンスと同様、Payara サーバーへパブリックのインターネットからは接続できません。アクセスするためには、az vm open-port を使用してファイアウォールルールを作成します。ここでは Payaraサーバーが使用するデフォルトのHTTPポート番号 8080 を公開します。

az vm open-port --port 8080 ¥
  --resource-group $AZURE_RESOURCE_GROUP_NAME  ¥
  --name $AZURE_VM_NAME

JSF のフロントエンドにアクセス

ブラウザで http://[ENTER_VM_IP]:8080/javaee-cafe にアクセスしてください。GUIで、コーヒーの作成、削除、表示できます。

REST API の使用

このアプリケーションは、コーヒーの作成、削除、表示のための REST API も公開しています。

export VM_IP=[to be filled]

コーヒーの作成

curl -X POST $VM_IP:8080/javaee-cafe/rest/coffees ¥
  -d '{"name":"cappuccino","price":"10"}' ¥ 
  -H "Content-Type: application/json"

curl -X POST $VM_IP:8080/javaee-cafe/rest/coffees ¥
  -d '{"name":"caffe-latte","price":"15"}' ¥
  -H "Content-Type: application/json"

コーヒー一覧の取得

curl -H "Accept: application/json" ¥
  $VM_IP:8080/javaee-cafe/rest/coffees

追加されているコーヒーのオプションのリスをを JSON で表示

ID 指定によるコーヒーの取得

curl -H "Accept: application/json"  ¥
   $VM_IP:8080/javaee-cafe/rest/coffees/1

ID 指定によるコーヒーの削除

curl -X DELETE $VM_IP:8080/javaee-cafe/rest/coffees/1
curl -H "Accept: application/json" ¥
  $VM_IP:8080/javaee-cafe/rest/coffees

カプチーノが削除され、一覧から削除されている事を確認

リソースのクリーンアップ

アプリケーションの動作確認が完了したら、リソースを削除します。今回、リソースグループで一元管理したので、単一のコマンドを実行して削除できます。
これにより、チュートリアルの一部で作成した全リソース(VM、Postgresなど)を削除できます、ご注意ください。

az group delete --name $AZURE_RESOURCE_GROUP_NAME

まとめ

ここでは、仮想マシンにデプロイしたアプリサーバーを使用して、Java EE アプリケーションを Azure にデプロイする方法と、長期的な永続化のためにマネージド・データベースを使用する方法を学びました。
前述したように、各オプションには独自の長所と短所があります。IaaS の場合、アプリケーション、デプロイメント用のインフラストラクチャ、スケーリング方法などを完全に制御できます。一方、インフラの管理、アプリケーションのサイジング、セキュリティ保護などは、 アプリケーション機能の一部として、ご自身で責任を持たなければなりません。

次のパートでは、Dockerコンテナープラットフォームを使用してJava EEアプリケーションをデプロイする方法について説明します。 どうぞお楽しみにみしてください!

2020年5月7日 at 3:32 午後 1件のコメント

大きなファイルを 各 Pod から参照する際、DaemonSet をキャッシュとして扱う

今回とあるお客様と出会い、ハックフェストを実施した際に出てきた課題と、私からお客様に提案した対処方法が、他でも同様のニーズがあった場合に有効になるのではないかと想定し、ここにその方法を共有します。

今回のやり方にあうユースケース:

  1. 各 Pod から同一の巨大ファイル(GBクラスのファイル)を読み込む必要がある
  2. ファイルは読み込みのみで、書き込みは発生しない
    (書き込みが必要な場合、別の手法で対応可能)
  3. 巨大ファイルをできるだけ早く読み込みたい

※ ご注意:ユースケースに応じて手法は各自でご検討ください。

上記のようなユースケースの場合、DaemonSet をキャッシュとして利用する方法もご検討ください。各 Node に一つ Pod を起動する DaemonSet を利用し、DaemonSetの各 Pod にファイルのコピーを持たせます。そしてファイルを参照・取得したい Pod は、DaemonSet の Pod にあるコピー・ファイルを取得・参照する事で、ネットワークを跨いだ通信を削減できるだけでなく、ローカルでのファイル転送になるため短時間でファイルを取得できるようになります。
まぁ、普通に考えて当たり前の事を当たり前に説明しているだけなのですが。

一般的な Kubernetes での Volume のマウント方法


Kubernetes の各 Pod で永続的なボリュームを扱う際、Volume を利用して各 Pod にマウントする手法が用意されています。そして多くのクラウド・プロバイダは各クラウドのストレージ上で Persistence Volume を扱うためのプラグインを提供し、各 Pod から ストレージ内のディスクをマウントできる手法を用意しています(方法は3種類:ReadWriteOnce、ReadOnlyMany、ReadWriteMany)。

Kubernetes のドキュメントをご参照:
* Volumes
* Persistent Volumes

Azure で各 Pod からディスクを 1対1 でマウントする場合 (ReadWriteOnce)、Azure Disk を利用します。また複数の Pod から同一 Disk を共有したい場合 (ReadOnlyMany、ReadWriteMany)、Azure Files を利用します。 今回のユースケースでは、サイズの大きなファイルを複数の Pod から参照したいので、通常であれば Azure Files (ReadOnlyMany) を選択します。

具体的には、下記の手順で Azure Files を作成し、Kubernetes の Persistence Volume として Azure Files を 利用できます。

$ export AKS_PERS_STORAGE_ACCOUNT_NAME=myfilestorageaccount
$ export AKS_PERS_RESOURCE_GROUP=Yoshio-Storage
$ export AKS_PERS_LOCATION=japaneast
$ export AKS_PERS_SHARE_NAME=aksshare

$ az storage account create -n $AKS_PERS_STORAGE_ACCOUNT_NAME -g $AKS_PERS_RESOURCE_GROUP -l $AKS_PERS_LOCATION --sku Standard_LRS

$ export AZURE_STORAGE_CONNECTION_STRING=`az storage account show-connection-string -n $AKS_PERS_STORAGE_ACCOUNT_NAME -g $AKS_PERS_RESOURCE_GROUP -o tsv`

$ az storage share create -n $AKS_PERS_SHARE_NAME

$ STORAGE_KEY=$(az storage account keys list --resource-group $AKS_PERS_RESOURCE_GROUP --account-name $AKS_PERS_STORAGE_ACCOUNT_NAME --query "[0].value" -o tsv)

$ kubectl create secret generic azure-secret --from-literal=azurestorageaccountname=$AKS_PERS_STORAGE_ACCOUNT_NAME --from-literal=azurestorageaccountkey=$STORAGE_KEY

続いて、これを Pod からマウントするために volumeMounts, volumes の設定を各 Pod で行います。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ubuntu
spec:
  replicas: 2
  selector:
    matchLabels:
      app: ubuntu
  template:
    metadata:
      labels:
        app: ubuntu
        version: v1
    spec:
      containers:
      - name: ubuntu
        image: ubuntu
        command:
          - sleep
          - infinity
        volumeMounts:
          - mountPath: "/mnt/azure"
            name: volume
        resources:
          limits:
            memory: 4000Mi
          requests:
            cpu: 1000m
            memory: 4000Mi
        env:
          - name: NODE_IP
            valueFrom:
              fieldRef:
                fieldPath: status.hostIP
      volumes:
        - name: volume
          azureFile:
            secretName: azure-secret
            shareName: aksshare
            readOnly: true

※ 今回ファイルサイズの大きなファイルをコピーするため4GBのメモリをアサインしています。

上記のファイルを deployment.yaml として保存し下記のコマンドを実行してください。

$ kubectl apply -f deployment.yaml

Pod が正常に起動したのち、Pod 上で mount コマンドを実行すると Azure Files が /mnt/azure にマウントされていることを確認できます。

$ kubectl exec -it ubuntu-884df4bfc-7zgkz mount |grep azure
//**********.file.core.windows.net/aksshare on /mnt/azure type cifs (rw,relatime,vers=3.0,cache=strict,username=********,domain=,uid=0,noforceuid,gid=0,noforcegid,addr=40.***.***.76,file_mode=0777,dir_mode=0777,soft,persistenthandles,nounix,serverino,mapposix,rsize=1048576,wsize=1048576,echo_interval=60,actimeo=1)

この時、このファイルの実態は Azure Storage の Files Share (***************.file.core.windows.net/aksshare) 上に存在しています。

Pod から /aksshare に存在するファイルを参照する際にファイルを SMB プロトコルを利用してダウンロードします。
実際に、/mnt/azure にあるサイズの大きなファイルを Pod 内にコピーします。

# date
Wed Jan 15 16:29:03 UTC 2020
# cp /mnt/azure/Kuberenetes-Operation-Demo.mov ~/mount.mov
# date
Wed Jan 15 16:29:54 UTC 2020 (51秒)

上記を確認すると Pod に約 3 Gb の動画ファイルをコピーするのに 51 秒ほど時間を要している事がわかります。仮に複数の Pod が起動し同一ファイルを参照した場合、ネットワークをまたいでファイルコピーが繰り返されます。これが通常の利用方法になります。

そして今回は、このファイル共有におけるネットワーク転送回数、スピードなどを DaemonSet を利用し改善します。

DaemonSet をキャッシュとして利用するための環境構築


今回、DaemonSet 上で稼働する Pod を nginx を利用して作成します。nginx のコンテキスト・ルート (/app) 配下に、Azure Blog Storage にあるファイルをコピーし、HTTP でファイルを取得できるようにします。
下記の設定により /app にあるコンテンツを http://podIP/ 経由で取得できるように設定します。

FROM alpine:3.6

RUN apk update && \
    apk add --no-cache nginx

RUN apk add curl

ADD default.conf /etc/nginx/conf.d/default.conf

EXPOSE 80
RUN mkdir /app
RUN mkdir -p /run/nginx

WORKDIR /app
CMD nginx -g "daemon off;"

上記の内容を Dockerfile として保存してください。

server {
  listen 80 default_server;
  listen [::]:80 default_server;

  root /app;

  location / {
  }
}

上記の内容を default.conf として保存してください。

このイメージをビルドし Azure Container Registry に Push します。

$ docker build -t tyoshio2002/nginxdatamng:1.1 .
$ docker tag tyoshio2002/nginxdatamng:1.1 yoshio.azurecr.io/tyoshio2002/nginxdatamng:1.1
$ docker login -u yoshio yoshio.azurecr.io 
Password: 
$ docker push yoshio.azurecr.io/tyoshio2002/nginxdatamng:1.1

次に Kubernetes から Azure Container Registry に接続するための Secret を作成します。

kubectl create secret docker-registry docker-reg-credential --docker-server=yoshio.azurecr.io --docker-username=yoshio  --docker-password="***********************" --docker-email=foo-bar@microsoft.com

Azure Container Registry にイメージを Push したので、Daemonset のマニュフェストを記述します。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: mydaemonset
  labels:
    app: mydaemonset
spec:
  selector:
    matchLabels:
      name: mydaemonset
  template:
    metadata:
      labels:
        name: mydaemonset
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      imagePullSecrets:
        - name: docker-reg-credential
      volumes:
        - name: host-pv-storage
          hostPath:
            path: /mnt
            type: Directory
      containers:
      - name: nginx
        image: yoshio.azurecr.io/tyoshio2002/nginxdatamng:1.1
        volumeMounts:
         - name: host-pv-storage
           mountPath: /mnt
        resources:
          limits:
            memory: 4000Mi
          requests:
            cpu: 1000m
            memory: 4000Mi
        lifecycle:
          postStart:
            exec:
              command:
                - sh
                - -c
                - "curl https://myfilestorageaccount.blob.core.windows.net/fileshare/Kuberenetes-Operation-Demo.mov -o /app/aaa.mov"
      terminationGracePeriodSeconds: 30

※ 今回は検証を簡単にするため、単一ファイル (Kuberenetes-Operation-Demo.mov) を postStart のフェーズで /app 配下にダウンロードしています。複数ファイルを扱う場合、コンテナのイメージ内で複数ファイルをダウンロードする仕組みを別途実装してください。また Nginx をカスタマイズ、もしくは他の Web サーバ、App サーバを利用する事でコンテンツキャッシュを効かせるなど効率化をはかることも可能かと想定します。

上記マニュフェストファイルを daemonset.yaml として保存し、下記のコマンドを実行してください。

$ kubectl apply -f daemonset.yaml

DaemonSet を作成したのち、DaemonSet を Service として登録します。Service を作成する事で各ノードの 30001 番ポート番号にアクセスし Nginx に Node の IP アドレスとポート番号でアクセスできるようにします。

apiVersion: v1
kind: Service
metadata:
  labels:
    app: daemonset-service
  name: daemonset-service
spec:
  ports:
  - port: 80
    name: http
    targetPort: 80
    nodePort: 30001
  selector:
    name: mydaemonset
  sessionAffinity: None
  type: NodePort

上記を service.yaml として保存し、下記のコマンドを実行してください。

$ kubectl apply -f service.yaml

Nginx の DaemonSet と Ubuntu の Deployment をそれぞれ配備したのち、下記のコマンドを実行し、どのノードでどの Pod が動作しているかを確認します。

$ kubectl get no -o wide
NAME                                STATUS   ROLES   AGE    VERSION                         INTERNAL-IP   EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
aks-agentpool-41616757-vmss000000   Ready    agent   176m   v1.15.7                         10.240.0.4            Ubuntu 16.04.6 LTS   4.15.0-1064-azure   docker://3.0.8
aks-agentpool-41616757-vmss000001   Ready    agent   176m   v1.15.7                         10.240.0.35           Ubuntu 16.04.6 LTS   4.15.0-1064-azure   docker://3.0.8
aks-agentpool-41616757-vmss000002   Ready    agent   176m   v1.15.7                         10.240.0.66           Ubuntu 16.04.6 LTS   4.15.0-1064-azure   docker://3.0.8
virtual-node-aci-linux              Ready    agent   175m   v1.14.3-vk-azure-aci-v1.1.0.1   10.240.0.48                                  
$ kubectl get po -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE                                NOMINATED NODE   READINESS GATES
mydaemonset-gqlrw        1/1     Running   0          72m   10.240.0.24   aks-agentpool-41616757-vmss000000              
mydaemonset-nnn5l        1/1     Running   0          72m   10.240.0.50   aks-agentpool-41616757-vmss000001              
mydaemonset-pzvzx        1/1     Running   0          72m   10.240.0.82   aks-agentpool-41616757-vmss000002              
ubuntu-884df4bfc-7zgkz   1/1     Running   0          64m   10.240.0.69   aks-agentpool-41616757-vmss000002              
ubuntu-884df4bfc-gd26h   1/1     Running   0          63m   10.240.0.33   aks-agentpool-41616757-vmss000000              
ubuntu-884df4bfc-vh7rg   1/1     Running   0          63m   10.240.0.49   aks-agentpool-41616757-vmss000001              

上記では、ubuntu-884df4bfc-vh7rg は aks-agentpool-41616757-vmss000001 のノード上で動作しており、aks-agentpool-41616757-vmss000001 のノード上では mydaemonset-nnn5l の DaemonSet の Pod がうごしています。
そして aks-agentpool-41616757-vmss000001 のノードの IP アドレスは 10.240.0.35 である事がわかります。Ubuntu の Pod 内では Pod が稼働している Node の IP アドレスを環境変数 $NODE_IP で取得できるように設定しています。

$ kubectl exec -it ubuntu-884df4bfc-vh7rg env|grep NODE_IP
NODE_IP=10.240.0.35

補足
最後に、ここでは環境構築方法の詳細説明を割愛しますが、Azure Blob Storage で Standard と Premium (高速) のそれぞれ作成し、それぞれのストレージに同一の動画ファイルを配置しておきます。
※ Azure Blob の Premium Storage は AKS の VNET と接続し VNET 経由でファイル取得しています。

その他の参考情報

* Azure Premium Storage: 高パフォーマンス用に設計する
* Troubleshoot Azure Files problems in Linux
* Troubleshoot Azure Files performance issues

検証内容

今回、下記の 4 点を検証しました。

1. Azure Files をマウントしたディレクトリに存在するファイルを Pod 内にコピーする際にかかる所用時間
2. Azure Blob (Standard) に存在するファイルを Pod で取得する際の所用時間
3. Azure Blob (Premium) に存在するファイルを Pod で取得する際の所用時間
4. DaemonSet 内にコピーしたファイルから取得する際の所用時間

1回目(実際の検証内容の動画)

下記のコマンド実行で確認しています

kubectl exec -it ubuntu-884df4bfc-vh7rg

## File Put on Azure Files (File shares : Samba)
$ date
$ cp /mnt/azure/Kuberenetes-Operation-Demo.mov ~/mount.mov
$ date

## File Put on Azure Blob (Containers)
$ curl https://myfilestorageaccount.blob.core.windows.net/fileshare/Kuberenetes-Operation-Demo.mov -o ~/direct.mov
$ curl https://mypremiumstorageaccount.blob.core.windows.net/fileshare/Kuberenetes-Operation-Demo.mov -o ~/premium2.mov
$ curl http://$NODE_IP:30001/aaa.mov -o ~/fromNodeIP.mov
回数 PV からのコピー Standard Storage からの入手 Premium Storage からの入手 DaemonSet キャッシュからの入手
1回目 51 秒 69 秒 27 秒 20 秒
2回目 60 秒 60 秒 23 秒 11 秒
3回目 53 秒 57 秒 20 秒 8 秒



※ ちなみに、DaemonSet の Nginx を利用した際も、初回アクセス時は時間がかかっています。これはおそらく Nginx のメモリに乗っていない為、取得に時間がかかっているように想定します。他の環境でも2回目アクセス以降は早くなっています。

検証結果のまとめ

上記、3回の実行結果より、一旦 DaemonSet にファイルを取得し、DaemonSet のキャッシュからファイルを入手するのが最も高速に入手できる事がわかりました。
Premium のストレージを利用する事で Standard のストレージよりも高速にファイル転送ができますが、Premium にすると Standard より高コストになります。また、Storage からの入手の場合、毎回ネットワークを経由してのダウンロードになる事から、それを考えても、一旦同一のノード(VM) にファイルを取ってきて、そこから入手する方が最も効率的で早い事は当たり前ですね。

DaemonSet を利用する利点
ネットワーク通信量の削減
ファイル取得スピードの高速化
DaemonSet 内のコンテナの実装を機能豊富にすれば、他にも用途が広がる(例:書き込み対応など)

懸念事項

* 現在は、特定の単一ファイルを取得していますが、複数ファイルを扱う場合、もしくはファイルの更新があった場合は、DaemonSet の内部で定期的に更新をチェックし、更新分をダウンロードする仕組みなどを実装する必要あり
* DaemonSet の Pod 内のファイルサイズが肥大化する可能性がありクリーン・アップをする必要がある
* ファイルの書き込みについては、今回の検証方法をそのまま利用した場合は対応は難しいが、DaemonSet 内のコンテナで独自に In Memory Grid 製品のような機能を実装すれば対応かと想定します。

最後に:
その他にも、内部的に検証をしたのですが、説明内容が増え視点がぼやける可能性があった為、特に効果的な点だけに絞ってまとめました。また、極力簡単にするために、DaemonSet で Nginx を使用していますが、Nginx の部分をさらにチューニングをしたり、Nginx の部分を他の実装に変更するなどで、さらなるパフォーマンス向上も見込まれるかと思います。

ぜひ、こちらの情報を元にさらなるチューニングなどを施していただければ誠に幸いです。

2020年1月17日 at 6:05 午後

日本全国 Java & AKS (k8s) サマー・ツアー 開始

7月下旬から、8月下旬の約1ヶ月をかけて、日本全国で Java & AKS (Kubernetes) のサマー・ツアーを開始します。

既に、昨日(7月27日)、一昨日(7月28日)と仙台で Kubernetes のハッカソン・イベントを開催してきました。

今後の日本全国サマー・ツアーの予定は下記の通りです。ぜひ各地方のエンジニアの皆様どうぞお越しください。

仙台(7/27-7-28):https://jazug.connpass.com/event/135986/

札幌(7/29):https://jazug.connpass.com/event/137069/

東京女子部(8/3):https://jazug.connpass.com/event/137077/
Java 女子部(8/3):https://javajo.doorkeeper.jp/events/94041

名古屋(8/5):https://75az.connpass.com/event/137002/

大阪 Day 1(8/7):https://jazug.connpass.com/event/136591/
大阪 Day 2(8/8):https://kanjava.connpass.com/event/139275/

岡山(8/10):https://okajug.doorkeeper.jp/events/94008

広島(8/11):https://hiroshima-jug.connpass.com/event/135650/

福岡(8/17-8/18):https://jazug.connpass.com/event/138372/

熊本(8/19):https://kumamotojava.doorkeeper.jp/events/94011

沖縄(8/24-8/25):https://java-kuche.doorkeeper.jp/events/94636

● 東京(8/31) : Coming Soon

皆様とお会いできるのを心より楽しみにしています。

2019年7月29日 at 5:15 午後 1件のコメント

Kubernetes の導入時に考えるべきこと

Azure にシステム導入・移行するお客様に日頃ご支援をさせていただいているのですが、多くのお客様やエンジニアの皆様から、たびたび似たようなご質問を頂くので、その内容をここに紹介したいと思います。(実際、今日もとあるお客様で下記のご相談を頂いたので)

ご質問:

お客様からたびたび頂くご質問の例

  • このシステムを Kubernetes (AKS) 上で構築するのは正しいですか?
  • とあるコンサルから Kubernetes を導入しないと会社の未来はないと言われたのですが、本当ですか?
  • 今、Azure Web App for Container か AKS (Kubernetes) の何れかの選択で悩んでいるのですが、どちらが良いですか?
  • 今、このような Kubernetes(AKS) のシステム構成を考えているのですが、正しいですか?

質問の背景:

これらのご質問は、メッセージは異なるのですが基本的には同じ質問です。結局どのような場合に kubernetes (AKS) を活用するのが効果的なのかが理解出来ていないため出てくる質問です。

個人的には当たり前になっていたので、この手の内容はまとめなくても良いかな?と思っていたのですが、度々、お問い合わせを頂くので多くの方がお悩みなのかな?と思ったのと、今日ミーティングに同席された方から、ぜひまとめて欲しいとのご要望を承ったので書くことにしました。

ご質問いただいた際の私の対応:

このようなご質問をいただく際、私からは必ず、質問者に対して下記の2つを問い合わせます。

  1. そのシステムは変更の激しいシステムですか?例えば今後サービスの追加は多いですか?もしくは修正が多いですか?
  2. コンテナ化するサービスは大量の CPU やメモリのリソースを消費しますか?

Kubernetes を選択するか否か:

A. Kubernetes が適する場合

上記の質問に対する答えに対し、1 が「はい」、2 が「いいえ」 の条件を共に満たす場合は Kubernetes の導入が適するかと思います。(前提:アジャイル、DevOps などが出来る環境が整っている会社の場合)

1: はい (変更が激しい)
2: いいえ (大量の CPU/メモリは消費しない)

B. Kubernetes が適さない場合:

一方で、上記の質問に対する答えに対し、1 が「いいえ」、2 が「はい」の条件のどちらか片方でも条件を満たす場合は、Kubernetes の導入は適さないと思います。

1: いいえ (変更は激しくない)
2: はい (大量の CPU,メモリを消費する)

理由:

システムは変更の激しいシステムか否か

まず、1 の質問の背景はマイクロサービス化をしたいと考えるお客様に対しても同様の質問をするのですが、「変更」には2つの観点があります。一つは、「既存のサービスに対し頻繁な更新・バージョン・アップ」、そしてもう一つは「新機能の追加が多くある」です。

「既存のサービスに対し頻繁な更新・バージョン・アップ」が必要な場合、一早いサービスの入れ替えが必要になります。それを実現するためには、アジャイルでの開発を実践し、DevOps でビルドやリリースのパイプラインを自動化していきます。そして、古いバージョンから新しいバージョンに安全に入れ替えるために、ローリング・アップデートやブルー・グリーン・デプロイ、カナリー・デプロイなどの手法を用いて入れ替えていく必要があります。
その際、システム構築スピードであったり、構築やサービス入れ替えの容易性が重要になってきます。デプロイ先として IaaS を選ぶ場合、VM の構築には時間が掛かります。そして上記のようなローリング・アップグレードやブルー・グリーン・デプロイ、カナリー・デプロイなどの手法を取り入れるためには、ご自身でこれらを実現するための環境を整える必要があり多大な労力を伴います。
こうした手間や労力を軽減してくれるのが PaaS (Azure では Azure Web App)であったり Kubernetes(AKS) になります。

この時点では、まだ選択肢として PaaS, Kubernetes 何れもありうる状況ですが、次に考えるのがもう一つの「新機能の追加が多くある」か否かです。デプロイするサービス数が増えて来たり、サービス間の連携などが増えてくると、PaaS では管理が煩雑になる場合があります。その場合、Kubernetes クラスター内でまとめて管理をする方がサービス間の管理やサービス全体の運用管理が楽になったり見通しが楽になったりします。
具体的なサービスの追加数は、個々のシステムや企業における運用・管理ポリシーによって異なるので本来ならば明示は避けたい所ですが、追加するサービス数が 5-10 程度ならば、迷わず PaaS をお勧めします。
しかし、サービス数が、20を超えるようならば Kubernetes の導入をお勧めします。
もちろん、 5-10 程度でも Kubernetes をご利用いただいても良いかと思いますが、導入するサービス数に対して、運用・管理、さらに学習コストの方が高くつくのではないかと想定します。

CPU やメモリのリソースを消費するか否か

また、2 の稼働させるサービスは「大量の CPU やメモリのリソースを消費しますか?」についてですが、こちらも選択する際の重要な要素と考えています。

Kubernetes 上で稼働する Pod (コンテナを束ねた物) が実際にはどこで動いているのかを考えて頂くと納得していただけるのですが、Pod(コンテナ) は、実際には、Kubernetes クラスタのノード上で動いています。ノードは実際には 1VM になります。例えば Kubernetes クラスタを 3 ノード (3 VM) で構成し、各ノードの VMに CPU 4 コア、16GB の VM を割り当てた場合を考えます。

この場合、3ノード全体で利用できる CPU やメモリーのリソースは下記の通りです。

CPU: 4 Core * 3 node = 12 Core(1200m)
Memory: 16 Gb * 3 node = 48 GB

自身が提供するサービスは、上記の 12 Core, 48 GB を超えてスケール・アウト(増やす)事は出来ません。実際には、Kubernetes を動作させるために必要な管理用の Pod(kube-system内のpod)で利用する CPU や Memory のリソースも含まれるため、上記の MAX 分までを自身のサービスを利用することも出来ません。

極端な例でわかりやすく説明すると、仮に自身が提供するサービスが 1 Pod (コンテナ) あたり CPU 1Core 分を消費するようなアプリケーションの場合、1ノードあたり 2 〜 3 Pod、トータルで 6 〜 10 pod 位しか作れないのです。また、1 Pod (コンテナ) あたり 2 GB 位メモリを消費するようなアプリの場合は、約 20 pod 位しか作れないことになります。
もし、CPU や メモリーを大量に消費するようなサービスを提供したい場合は、わざわざ Node の CPU やメモリを共有する Kubernetes 環境で提供するよりも、IaaS 環境で 1 VM に1サービスをデプロイして提供した方が、Kubernetes のオーバヘッドもなく、パフォーマンスも発揮するかと想定します。

つまり、Kubernetes で提供するサービスは、簡単にスケール・アウトしやすいサービスのデプロイには向いていますが、パフォーマンスを上げるためにスケール・アップが必要なサービスには向かないと考えています。
その場合、IaaS でも PaaS でも Serverless でも違う選択肢を考えた方が良いかと思います。

さいごに

Kubernetes は決して銀の弾丸ではないため、どうぞ適材・適所で稼働に向くサービスを稼働させるようにしてください。

2019年7月20日 at 1:51 午前

Quarkus: コンテナ上で Java アプリを高速起動する新しい手法のご紹介

Docker 環境上で Java のアプリを起動するのは遅いと思っていらっしゃる方は必見!!
どうぞ下記の内容をご参照いただき、どうぞお試しください!!

先日、Red Hat から Quarkus (https://quarkus.io) という新しい技術が発表されました。こちらを実際に試して見ましたが、想定通りというか、まさにこれを待っていた!!という技術でした。今後、私の中で注目の技術の一つになりそうです。もし、Docker/k8s 上で Java アプリを動かす方は、こちらの方法をご覧いただき、ぜひ試しください。

Quarkus を簡単にご説明すると、Java のソースコードを GraalVM を利用して Linux の Native バイナリを作成し、その Linux バイナリをコンテナ上で起動することにより、今まで Java アプリの課題であった起動時間を大幅に短縮することができる技術です。

※ GraalVM については、きしださんや Java Champion である阪田さんがまとめてくださっていますので、ここではその詳細説明を割愛させていただきます。
* GraalVMについて (きしださん)
* 詳説GraalVM(1) イントロダクション(阪田さん)

事前準備

GraalVM を入手したのち、.bash_profile などに環境変数 GRAALVM_HOME を指定しておきます。

GRAALVM_HOME=/Library/Java/JavaVirtualMachines/graalvm-ce-1.0.0-rc13/Contents/Home
export GRAALVM_HOME

それでは、実際に試して見ましょう。

1. Quarkus のクィック・スタートに従い Java のプロジェクトを作成します

$ mvn io.quarkus:quarkus-maven-plugin:0.11.0:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=getting-started \
    -DclassName="org.acme.quickstart.GreetingResource" \
    -Dpath="/hello"

プロジェクトを作成すると下記のようなファイルが作成されます。

├── pom.xml
└── src
    ├── main
    │   ├── docker
    │   │   └── Dockerfile
    │   ├── java
    │   │   └── org
    │   │       └── acme
    │   │           └── quickstart
    │   │               └── GreetingResource.java
    │   └── resources
    │       └── META-INF
    │           ├── microprofile-config.properties
    │           └── resources
    │               └── index.html
    └── test
        └── java
            └── org
                └── acme
                    └── quickstart
                        ├── GreetingResourceTest.java
                        └── NativeGreetingResourceIT.java

2. Java のソースコードをビルドし Linux の Native バイナリを作成します

$ mvn package -Pnative -Dnative-image.docker-build=true

-Dnative-image.docker-build=true は Docker on Linux 上で実行するバイナリを生成する事を明示するオプションです。これを指定しない場合、ビルドをした環境(例えば Mac OS/X なら Mac )用のバイナリが生成されます。

2.1 作成された Linux 用のバイナリを確認します


target ディレクトリ配下に成果物ができあがります。

$ ls -l target/getting-started-1.0-SNAPSHOT-runner
-rwxr-xr-x 1 yoterada staff 20112760 3 11 13:52 getting-started-1.0-SNAPSHOT-runner

2.2 file コマンドでファイル内容を確認すると Linux ELF バイナリが作成されています

$ file getting-started-1.0-SNAPSHOT-runner
getting-started-1.0-SNAPSHOT-runner: ELF 64-bit LSB executable, 
x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, 
for GNU/Linux 2.6.32, BuildID[sha1]=61ef9e78267993b688b9cf2de04e2aff9f1a4bfa, 
with debug_info, not stripped

3. Docker のビルド

Dockerfile のサンプルも下記の Dockerfile が自動的に作成されていますので、こちらをそのまま利用してビルドを行います。
17行目の COPY でビルド時に生成された Linux ELF バイナリを追加しています。

自動生成された Dockerfile

####
# Before building the docker image run:
#
# mvn package -Pnative -Dnative-image.docker-build=true
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile -t quarkus/getting-started .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/getting-started
#
###
FROM registry.fedoraproject.org/fedora-minimal
WORKDIR /work/
COPY target/*-runner /work/application
RUN chmod 775 /work
EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

Dockerfile からコンテナ・イメージをビルドしてください。

$ docker build -f src/main/docker/Dockerfile -t tyoshi2002/quarkus-quickstart:1.0 .

4. Docker の起動


作成したコンテナ・イメージを利用してコンテナを起動します。

$ docker run -i --rm -p 8080:8080 tyoshi2002/quarkus-quickstart:1.0
2019-03-11 05:10:42,697 INFO [io.quarkus] (main) Quarkus 0.11.0 started in 0.005s. Listening on: http://0.0.0.0:8080
2019-03-11 05:10:42,699 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]

私の環境では 0.005 秒で CDI+REST アプリが起動できました。
Quarkus 0.11.0 started in 0.005s. Listening on: http://0.0.0.0:8080

5. 動作確認


curl コマンド、もしくはブラウザを利用して起動したアプリケーションにアクセスします。

$ curl http://localhost:8080/hello
hello

6. 備考


Quarkus は開発モード(Development Mode)も用意されており、開発時にホット・デプロイもできるようになっています。
ホット・デプロイを行うためには、下記のコマンドを実行します。

$ mvn compile quarkus:dev

こちらで起動したのち、ソースコードを修正しコンテンツを再読み込みします。
すると、再起動は不要で修正されたコードの内容を確認することができます。

7. 注意事項


Quarkus で内部的に利用されている CDI の実装 (ArC と呼ぶ) は、CDI の仕様に完全準拠しているのではなく、一部の仕様を実装したサブ・セットの実装になっています。下記にそれぞれ CDI におけるサポート、未サポートの機能についての機能一覧が説明されていますので、こちらをどうぞご確認ください。
CDI でサポートされている機能一覧
CDI で未サポートの機能一覧

8. Extension

Quarkus のアプリケーションに対して、追加のエクステンションを追加できます。追加可能なエクステンションのリストは下記から参照できます。

$ mvn quarkus:list-extensions
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------------< org.acme:getting-started >----------------------
[INFO] Building getting-started 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- quarkus-maven-plugin:0.11.0:list-extensions (default-cli) @ getting-started ---
[INFO] Available extensions:
[INFO] 	 * Agroal - Database connection pool (io.quarkus:quarkus-agroal)
[INFO] 	 * Arc (io.quarkus:quarkus-arc)
[INFO] 	 * AWS Lambda (io.quarkus:quarkus-amazon-lambda)
[INFO] 	 * Camel Core (io.quarkus:quarkus-camel-core)
[INFO] 	 * Camel Infinispan (io.quarkus:quarkus-camel-infinispan)
[INFO] 	 * Camel Netty4 HTTP (io.quarkus:quarkus-camel-netty4-http)
[INFO] 	 * Camel Salesforce (io.quarkus:quarkus-camel-salesforce)
[INFO] 	 * Eclipse Vert.x (io.quarkus:quarkus-vertx)
[INFO] 	 * Hibernate ORM (io.quarkus:quarkus-hibernate-orm)
[INFO] 	 * Hibernate ORM with Panache (io.quarkus:quarkus-hibernate-orm-panache)
[INFO] 	 * Hibernate Validator (io.quarkus:quarkus-hibernate-validator)
[INFO] 	 * Infinispan Client (io.quarkus:quarkus-infinispan-client)
[INFO] 	 * JDBC Driver - H2 (io.quarkus:quarkus-jdbc-h2)
[INFO] 	 * JDBC Driver - MariaDB (io.quarkus:quarkus-jdbc-mariadb)
[INFO] 	 * JDBC Driver - PostgreSQL (io.quarkus:quarkus-jdbc-postgresql)
[INFO] 	 * Kotlin (io.quarkus:quarkus-kotlin)
[INFO] 	 * Narayana JTA - Transaction manager (io.quarkus:quarkus-narayana-jta)
[INFO] 	 * RESTEasy (io.quarkus:quarkus-resteasy)
[INFO] 	 * RESTEasy - JSON-B (io.quarkus:quarkus-resteasy-jsonb)
[INFO] 	 * Scheduler (io.quarkus:quarkus-scheduler)
[INFO] 	 * Security (io.quarkus:quarkus-elytron-security)
[INFO] 	 * SmallRye Fault Tolerance (io.quarkus:quarkus-smallrye-fault-tolerance)
[INFO] 	 * SmallRye Health (io.quarkus:quarkus-smallrye-health)
[INFO] 	 * SmallRye JWT (io.quarkus:quarkus-smallrye-jwt)
[INFO] 	 * SmallRye Metrics (io.quarkus:quarkus-smallrye-metrics)
[INFO] 	 * SmallRye OpenAPI (io.quarkus:quarkus-smallrye-openapi)
[INFO] 	 * SmallRye OpenTracing (io.quarkus:quarkus-smallrye-opentracing)
[INFO] 	 * SmallRye Reactive Messaging (io.quarkus:quarkus-smallrye-reactive-messaging)
[INFO] 	 * SmallRye Reactive Messaging - Kafka Connector (io.quarkus:quarkus-smallrye-reactive-messaging-kafka)
[INFO] 	 * SmallRye Reactive Streams Operators (io.quarkus:quarkus-smallrye-reactive-streams-operators)
[INFO] 	 * SmallRye Reactive Type Converters (io.quarkus:quarkus-smallrye-reactive-type-converters)
[INFO] 	 * SmallRye REST Client (io.quarkus:quarkus-smallrye-rest-client)
[INFO] 	 * Spring DI compatibility layer (io.quarkus:quarkus-spring-di)
[INFO] 	 * Undertow (io.quarkus:quarkus-undertow)
[INFO] 	 * Undertow WebSockets (io.quarkus:quarkus-undertow-websockets)
[INFO] 
Add an extension to your project by adding the dependency to your project or use `mvn quarkus:add-extension -Dextensions="name"`
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.696 s
[INFO] Finished at: 2019-03-11T19:45:32+09:00
[INFO] ------------------------------------------------------------------------

上記のリストを元に、追加するエクステンションの groupId と artifactId を指定し追加ができます。

$ mvn quarkus:add-extension -Dextensions="groupId:artifactId" 

例:

$ mvn quarkus:add-extension -Dextensions="io.quarkus:quarkus-smallrye-health" 

9. さいごに


Quarkus は他に、CompletionStage を利用した非同期処理の実装や、JUnit のテストも REST Assured を利用して非常に書きやすくなっており、全体的にとても良くできた技術だと感じています。
ご興味ある方はぜひ、お試しください!

その他の Java on Azure に関する日本語情報へのリンク

2019年3月11日 at 3:27 午後 1件のコメント

Kubernetes を本番環境に適用するための Tips

先日 JAPAN CONTAINERDAYS v18.12 に参加し 「40 Topic of Kubernetes in 40 Minutes」 という内容で 40 個の Kubernetes に関するトピックを 40 分でご紹介する内容を発表させていただきました。

第一弾の記事:JAPAN CONTAINERDAYS v18.12 での発表について

時間の関係上、セッション中でも詳しくお伝えできなかった所もあるので、ここで真意も含めて共有したいと思います。特に Kubernetes に関連した Topic 26-33 の部分を詳しくご紹介します。



26. すべての k8s 機能を利用する必要はない
Please Remember J2EE !!

27. 複雑な構成を作らない
運用構成を変えればできることを無理して k8s でしない
Simple is the Best

28. K8s にはポータビリティがある?
ある点とない点がある

29. どのバージョンを利用していますか?
バージョン毎に設定や機能の差異がある

30. 大規模 k8s クラスタを構築しない !!
ノード数を多く作らない

31. K8s のバージョン・アップは慎重に!!

32. ボリュームを扱う際には注意しましょう
Persistence Volume は極力使わないで

33. DB はマネージド・サービスを利用 !!
開発環境は OK !! でも本番は NG !!


26. すべての k8s 機能を利用する必要はない
Please Remember J2EE !!

今、Kubernetes は急速な勢いでエコシステムを拡大しています。エコシステム全体を通すと、並行して 2000 を超えるプロジェクトが走っており、日々何か新しい k8s 関連のツールや機能ができていると言っても過言ではありません。これは、もちろん Kubernetes のコンセプトが素晴らしく、多くの可能性が秘められているため、多くの方が賛同しこの状況が生まれていると思います。一方で、私は過去の経験から、今の急速な k8s の拡大に危惧している点もあります。出来る事が多くなればなる程、教育コストや運用・管理コストは大きくなっていきます。

私は Java を古くから見ている人間なので、今の状況を J2EE の時と同じような目で見ています。全てのエンタープライズ開発のニーズに応える為に J2EE はどんどん肥大化していき、結果として複雑で扱いづらくなりました。J2EE 当時は XML での設定でしたが、今 k8s では YAML で設定してます、そして YAML が辛いとか言ってらっしゃるのをお見かけします。ちょっと似てますね。

自分のやりたい事、実現したいことに対して、本当に、k8s の全機能は必要でしょうか?

例えば、マイクロサービスを作って行く事を考えると、Deployment は必須で一番の核となる機能です。一方で、それ以外の機能はどうでしょうか?すべてのマイクロサービス開発者や運用者にとって必要でしょうか?(理解しておくのは必要ですが使うかどうかは別)例えば、Job にしても Cron Job にしても、本当に k8s で実行する必要あるのでしょうか?他の別の場所でも実行出来ませんか?全てを k8s 中心で考えるのではなく、こうした事を考えるのはとても重要です。また DaemonSet にしても log を収集したりする際には設定が必要ですが、マイクロサービスを開発する多くの場合においては、多くの開発者にとっては必要無いでしょう。こうした考えを持つ事で、優先的にどの技術から検証をして行けば良いのか優先づけを行う事ができるようになります。

古いたとえですが、
J2EE で言うところの「JSP, Servlet は便利で使うけど、EJB は難しいし設定も大変なので積極的には使わない」というのに似ています(おっさんなので例えが古く、若い方にはわからない例え話で申し訳ありませんが)

でもこれ以外にも、J2EE と (k8s + エコシステム)って色々な点で良く似ているなと思います。例えばコンテナの Init Container や Pod の PostStart や PreStop なんて、Java で言う所のコンストラクタであったり、PostConstruct, PreDestroy と同様の概念ですし、Istio なんかは、AOP(Aspect Oriented Programming)をコンテナの世界に持ってきたような物です。つまり、今までエンタープライズ Java とかをやってきた人にとっては、k8s は決して目新しい物ではなく、今まで Java でやってきた事がコンテナに置き換わったんだなと思える部分は多々あると思います。

その観点で言うと、時代は繰り返すと言いますが、J2EE において Struts や Spring が出てきたように、今の k8s に対して、新しい簡単で軽量な別のオーケストレーション・ツールが出てきてもおかしくないかな?それを作った人は第二の Rod Johnson (Springの作者)になり、もしかしたらそこに新しい別のビジネスチャンスがあるのでは?と思う位です。

結局、ここで何が言いたいかと言うと、k8s に僕たちが使われるのではなく、k8s の便利な機能を私たちが選んで使うんだ!と言う気持ちや感覚が大切なのです。

無理して全部使わなくても良いし別の手段があるならば別を使っても良いと思っています。

27. 複雑な構成を作らない
運用構成を変えればできることを無理して k8s でしない
Simple is the Best

これも、26 によく似た話なのですが、k8s では複雑な構成を組むこともできます。名前空間の概念があるので、1つの k8s クラスタでマルチテナントとか、開発、テスト、本番環境を構築することも「できます」。さらに、RBAC も利用できる為、各ユーザ毎に細かい認証・認可の設定も「できます」。

でも、やります?

できるのと、実際やるかどうかは別の話で、少なくとも本番環境と他は分けるべきと私は思っています。本番環境のアプリと開発・テストのアプリが同一 Linux VM で動作するのは気持ち悪いです。
頑張って、名前空間を切り RBAC を設定し、さらには NodeSelector や Node Affinity の設定をして特定の POD を特定の Node (Linux VM) 上で走らせる事で影響を少なくできるでしょう。確かにできるのですが、やればやるほど設定は増えて行くし、設定ミスを誘発しやすくなります。そして設定をミスした時の影響はより大きくなります。

そこで、設定にしても構成にしても難しい構成にしないように留めておくべきと私は考えます。なぜなら人間は必ずミスをするからです。

私ならば、本番環境と開発・テスト環境などを分けて構築します。本番環境を触れられる人を物理的に制限しておく方が RBAC で一生懸命頑張って設定するよりも簡単ですし、確実に守れるからです。このように構成を変える事で簡単にそしてより安全にできるならば、そちらを選んだ方が私は良いのではないかと思っています。

28. k8s にはポータビリティがある?
ある点とない点がある

k8s で良く言われることに、k8s はポータビリティがあるから導入したいとか、k8s にするとベンダーロックインがなくなり移行しやすいと言われる事があるのですが、これは正しくもあり間違いもあります。

一部の設定に限って言うと同じ設定を他の環境で動かすこともできます。

しかし、細かい話をすると、実際にはノードで動いている Linux VM の種類やカーネルバージョン、さらにはコンテナ・ランタイム(Docker)のバージョンも各環境で違います。また、筆者は Azure Kubernetes Engine を利用してカスタムの k8s クラスタ環境を構築した経験もありますが、利用するテンプレートによって、機能が有効になったり無効になったりと構築時のオプション指定で、同じ k8s バージョンでも利用できる機能、できない機能を使い分ける事ができます。
これは、同じバージョンの k8s でも環境によっては使える機能、使えない機能があると言う事を示しています。

さらに言うと、k8s はご存知の通りバージョン・アップが頻繁に行われていますが、バージョンが変わる事で YAML の書き方や、場合によってはコマンドの引数が変わることまであります。
各ベンダーで提供している k8s のバージョンも時期によってメジャーバージョンやマイナーバージョンで差異が大きくありますので、全く同時期に同じバージョンのクラスタを構築するのは難しいかもしれません。

同じベンダーの k8s を利用した場合であっても、バージョンが異なると動かなくなる可能性があるのに、異なるベンダー環境 (OS, コンテナ・ランタイム(Docker)) で動かす事は本当にポータビリティがあると言って良いのでしょうか?

プレゼンでの発表時は例として、LoadBalancer や Volumeが違うことを例にあげましたが、それだけではない差がある事を、ここでお分かりいただけるかと思います。

私は、ポータビリティがあるからといって k8s の事をお勧めされる方のお話にはご注意くださいと、お客様には申し上げています。

29. どのバージョンを利用していますか?
バージョン毎に設定や機能の差異がある

28 で既に、バージョンの差異における課題について申し上げましたが、上記のような理由からどのバージョンを利用しているのかを強く意識する必要があります。

30. 大規模 k8s クラスタを構築しない !!
ノード数を多く作らない

AKS では 100 ノード、AKS Engine を使うと 1200 ノードまで、Kubernetes のノード数を増やす事が「できます」。もちろん、ニーズに応じて大規模クラスタを構築して1つのクラスタ内で全てを完結したいと思われる方もいらっしゃるでしょう。しかし、それほど大規模なクラスタを構築するのが果たして本当に必要なのかどうなのか十分に考えてお作りください。特に、メンテナンス性を考えて構築することを強くお勧めします。

たとえば、仮に1度クラスタを作ってしまえば、クラスタのメンテナンスが一切不要と言うことでしたら、大規模クラスタを構築するのもありでしょう。しかし実際には k8s もメンテナンスが必要です。各ノードの Linux VM にパッチを適用する必要もありますしバージョン・アップが必要になる場合もあります。各ノードをローリング・アップグレードで更新し完了するまでどれ程の時間を要するでしょうか。またそのメンテナンスの際、ノードが予期せぬ不具合にあった場合、もしくは API サーバ(管理サーバ)にトラブルが発生した場合どのような事態が発生するかを考えてみていただけないでしょうか。最悪の場合 k8s クラスタが壊れてまったく操作できなくなる事もありえます。その場合、またイチから 100-1200 ノードの k8s クラスタを構築するのにどれくらい時間がかかるでしょうか。再構築する間は一切のサービスが受けられなくなる程の大影響を及ぼしてしまいます。

POD を利用しコンテナ・レベルでいくら、マイクロサービスの考えに従って回復性や、可用性やスケールなどを考えていても、一番下側の VM 側がメンテナンスしやすいように作られていなければ、結局モノリシックに構築しているのと全く変わらなくなってしまいます。1つのクラスタに対するトラブルで全サービスに影響が出てしまいます。

私ならば、k8s のクラスタレベルでも適度なサイズ(もちろんマイクロやミニのレベルではなくてよく)、ミドルサイズくらい、もっと言うならば何か不足な事態が発生してもすぐに対応がしやすいようなサイズで構築することをお勧めします。限界にチャレンジしてもあまり良いことはないように思います。

31. K8s のバージョン・アップは慎重に!!

30 でも少しバージョン・アップに対して触れましたが k8s のバージョン・アップはとても危険を伴います。ベンダーが提供するバージョン・アップ・ツールやコマンドを利用すると簡単にバージョン・アップが「できる」でしょう。ただしその恩恵が得られるのは、コマンドやツールが正常に完了した場合のみです。コマンドやツールが失敗する事もありますし、その場合、最悪クラスタが壊れる事もありえます。

さらに言うならば、k8s はバージョン・アップする事で古い YAML の設定ファイルが、新しいバージョンの環境で必ず動くと言う保証はどこにもありません。そのような事態が発生する可能性がある中で、それを実行しますか?
本番サービスに直ちに影響を及ぼしても良いでしょうか?

せっかく、Deployment や Istio などを使って、ブルー・グリーン・デプロイやカナリー・デプロイなどの手法を使って、pod レベルでは本番に影響しないように頑張っても、クラスタ・レベルでも同様の事を考えていなければ意味がありません。影響は pod の比ではないほど甚大です。

もし、k8s クラスタのバージョン・アップをするならば、私ならば k8s レベルでも、新規にクラスタを構築し、リクエストのルーティングをクラスタ・レベルで 8:2 などで試しながら安全にバージョン・アップしていくでしょう。

32. ボリュームを扱う際には注意しましょう
Persistence Volume は極力使わないで

追記:この項目は Azure 用です (2018/12/07)

k8s では PV (Persistence Volue), PVC (Persistence Volume Claim) を利用して外部ストレージをマウントする事ができます。しかし、筆者の経験では PV や PVC を扱いボリュームをマウントさせた際に、今まで様々な問題に遭遇してきました。

代表的なのは下記の URL の記事のような問題です。

* How to Understand & Resolve “Warning Failed Attach Volume” and “Warning Failed Mount” Errors in Kubernetes on Azure
* Getting Unstuck with EBS: Primer on How to Use Docker and EBS

簡単に説明します。Deployment で2つの replica を常時起動しているように設定し、それぞれの pod にボリュームを1つづつマウントさせる場合を考えます。

1 pod に 1 ボリュームをマウントした場合、内部的にそれぞれ Attatch, Detach の状態を保持しているのですが、仮に1つの Pod が不意に死んだ場合、綺麗にボリュームのマウント状態を Detach してくれない場合があります。
この場合、k8s のスケジューラは1つが停止したため、新しい Pod の再作成を試みますが、ボリュームが Detach されていないため、再作成の Pod がマウントするボリュームがないため再起動に失敗する場合があります。一応、コマンドを打てば修正する方法はあるのですが、pod がいつ不意に停止するかは想定できません。

また、それ以外にもボリュームのメンテナンスはそれなりに大変です。実際にボリュームのマウントには時間を要します (実装に応じて異なる)。そして、k8s v1.13 までは、使用中のボリューム・サイズを拡張する事は出来ませんでしたので、マウントしているボリュームがいっぱいになると別のボリュームを再作成しマウントし直すなどの必要もありました。(v 1.13 に対応しているのはまだ少ないので多くの方がまだこの状況)

さらには、k8s や AKS の GitHub の Issue を見ても数多くの PV, PVC に関する問題が報告されています。

https://github.com/kubernetes/kubernetes/issues
https://github.com/Azure/acs-engine/issues

将来的には、PV, PVC のクオリティも向上し、利用する場面におけるベストプラクティスなどが出て安心して使用できるようになる時がくる事が期待されますが、現時点では、私ならば極力本番環境には適用したくないと言うのが本心です。

もしも、違う方法で回避ができるならばそちらの方法をお勧めしています。
例えば、ファイルの保存などが目的なのであれば、Stroage 用の SDK を使ってプログラム側から直接、保存・更新・削除などを実装する方法をお勧めしています。こちらの方が、POD 増加・減少それぞれにおいてもスケールしますし、Attach, Detach を気にする事もありません。

追記:2018/12/07 ここから —



また、上記以外でも私は、PV, PVC を積極的に採用しない理由があります。
実装するアプリケーションやサービス側から見て、static でマウントするにせよ dynamic でマウントするにせよ、1:1 でマウントする場合は、ボリュームのメンテも必要になります。なぜならはk8s の v1.13 まではボリューム・サイズを固定で設定しなければならずボリュームの拡張もできません。結果として管理項目も増えます。そしてアプリケーションのスケールの観点でもスケールがし辛くなります。

私ならば、プログラム的に Azure Storage などの外部ストレージに書き出した方が、アプリケーションのスケールもしやすいと考えますし、ディスク管理や制限に対する考え方も容易になります。

プログラムやサービス側からは、インメモリ・グリッドのような横にスケール・アウトさせる事ができるようなサービスを利用する方が、アプリケーションをスケールさせ易いので、私ならば、こうした違う方法を模索します。

そして、同僚の真壁さん (@tmak_tw) からのご意見はコチラ




追記:2018/12/07 ここまで —

それでも、もちろんボリュームをマウントして使いたいと言うニーズもあると思います。その際は上記のようなことが発生する可能性や他にも issue に上がっているような問題を認識した上でお使いいただく事で問題が発生した時に迅速に対応ができるようになるかと思います。ぜひお気をつけてお使いください。

33. DB はマネージド・サービスを利用 !!
開発環境は OK !! でも本番は NG !!

追記:この項目は Azure 用です (2018/12/07)

32. の「ボリュームを極力使わないでください」の延長線上で、ボリュームを扱うサービスは極力外に出したいです。例えば、代表的な所で DB が一番最初にきますが、プライベートの Docker レジストリや、Git のレポジトリなどボリュームを扱かわなければならないようなサービスは k8s の中ではなく、極力外で管理をしたいです。特に DB などのサービスはマネージドな DB の方が自分で管理をするよりも簡単に安全にご使用できる場合がありますので、仮想ネットワーク経由で安全な接続を利用した上で外部サービスに接続する方法をお勧めしたいと思います。

ご参考:AKS access to Azure Database for MySQL via VNet

最後に

k8s は今、エンタープライズ・システムの構築に必要な機能やサービスを、どんどんと追加しています。その際に、我々は一旦立ち止まって、全てを k8s 上に実装しなくても良いのでは?という考えを持つ事が重要なのではないかと思います。
それにより簡単に運用管理ができるような場面もあると思いますし、思い切ってここは使うけど、ここは使わないと言うご検討をいただくのがより有効にご活用いただけるようになるのではないかと思います。

この記事は 2018 年 12 月時点で、私が k8s HackFest などを通じて経験してきた内容をまとめていますが、もちろん今後 k8s は品質も向上しさらなる機能追加も行われていくと思います。今後、私たちは常に k8s の最新動向をウォッチしながら、できる事と、試す事、実際に本番に適用する事を分けて考えて考えて行かなければならないと思います。

ご自身にとって最適な構成をご検討・構築いただければ誠に幸いです。

私も完璧な人間ではありませんので、もし、ここは違うよなどがあれば、この記事をもとに議論したりさらなるご検討をいただければ誠に幸いです。

2018年12月6日 at 8:20 午後 1件のコメント

最近の Microsoft の Java 事情とインタビュー記事の日本語化

はじめに:最近の Microsoft における Java について


この記事の本題に入る前に、マイクロソフトの Java 活動にとって、とても嬉しい情報を共有いたします。私が入社したのは約2年半前になります。その際私以外にグローバルで Java on Azure に関する啓蒙活動をしている方は一人 (Brian Benz) しかいませんでした。そして私の入社から2年半を経て、この直近数ヶ月の間に 3 人もの Java 関連の方々がマイクロソフトに入社してきてくださいました。その内二人は元オラクルで、一人は、TomEE を提供する Tomitribe から入ってきてくださいました。

Bruno Borges (Developer Advocate)
Jonathan Giles (Developer Advocate)
Theresa Nguyen (Sr. Product Marketing Manager)

この中で、Bruno Borges は去年の JavaOne San Francisco で基調講演にも登壇したブラジル人で、オラクル時代から Java のコミュニティとの関係が強く、様々なカンファレンスなどで登壇しています。一時期ブラジルにいる頃 Java EE エバンジェリストをしていたこともあったように記憶しています。そして彼はマイクロソフトで Java の Developer Advocate チームを率いるという話を聞いています。

また、Jonathan は私同様 Sun Microsystems 出身で、彼はエンジニアリングチームに所属し、JavaOne のコンテンツ選定委員 (CFP で応募されたコンテンツを選定する側) を務めたり、JavaOne の優秀なスピーカーにだけ与えられる称号 Java Rock Star の称号も持っています。さらに昨年 Duke’s Choice Award なども受賞している、ガチの Java エンジニアです。

最後に、Java 専門のプロダクトマネージャとして最近入社されたのは、TomEE (Tomcat をベースとした Java EE コンテナ) を提供する Tomitribeで Vice President を務めていた方が入社してきてくださいました。

いずれの方も、グローバルでは Java 開発者コミュニティーから認知度の高い、Java コミュニティに精通する方ばかりです。Jonathan はJava SDK の API 実装においても Java らしい実装のレビューなどに多大な貢献が期待されますし、Bruno にはグローバルの Java コミュニティに対して、幅広くコンタクトしてくれると思いますので、ますます Java on Azure の拡大に期待が持てる、そんなハイアリングとなりました。前述した Brian と共に、この 4 名と一緒に Java on Azure の啓蒙活動を続けていく事をとても楽しみにしています。

下記のエントリの内容は、先日上記の Jonathan Giles からインタビューを受けましたので、インタビュー内容 (オリジナルの英語) を日本語化した内容を記載します。

Jonathan Giles からのインタビューの日本語訳


今日から Azure で Java を利用、または開発している方々とのインタビューシリーズを開始しています。 すでに他にも何名ものインタビューを企画しているため、これをシリーズ化したいと考えています。私からインタビューを受けたい方は、私にご連絡ください。

最初のインタビューは、Java チャンピオンであり、JUG リーダーでもある寺田 佳央さんです。彼は日本マイクロソフトで働く Java エバンジェリストで、私と彼は Sun Microsystems と Oracle で共に働いていたので、以前から彼の事を知っていました。 寺田さんは Java に長くそして深く関わっていますので、彼とのインタビューを喜んでいます。どうぞ読者の皆様もお楽しみください。

こんにちは寺田さん – みなさんに自己紹介をしていただけないでしょうか?


このような素晴らしい機会を与えていただきありがとうございます。 私は 2015年7月から日本 マイクロソフト でJava Evangelist として働いています。また Japan Java Users Group の幹事メンバーの一人でもあります。

マイクロソフトに入社する前は、日本 Sun Microsystems で GlassFish エバンジェリストとして働いていました。 Oracle による Sun の買収後、日本 Oracle で Java/Java EE のエバンジェリストとして5年間働いていました。 日本 Oracle での私の仕事は、JavaOne Tokyo、Java Day Day Tokyo、Java ユーザーズ・グループのイベントなど、日本の多くの Java 関連のイベントで、企画、運営・管理、登壇などをしていました。そして、マイクロソフト入社後 2016 年 7 月、私は Java チャンピオンになりました。

あなたはしばらくの間マイクロソフトで勤めているんですね。私はちょうど 12 月にマイクロソフトに入社したばかりなので、私よりもとても長いですね。私たちは二人とも比較的にた経歴を持っています。私もSun Microsystems と Oracle で勤めていましたが、私は開発のエンジニアリング・チームに所属していました。
私はあなたにマイクロソフトにおける Java の印象について聞きたいです。そして Java に関して時間がたつにつれてマイクロソフトがどのように変わったのか教えていただけないでしょうか?

マイクロソフト入社前、私の経歴が Unix と Java に携わる事が多かったため、マイクロソフトで働くことを全く想像していませんでした。しかし、現在マイクロソフトはクラウドプラットフォームの企業になっています。そこで .NET や Windows、Office 製品だけでなく、Unix やその他のプログラミング言語も強く推進しています。 すでに Azure のシステムの 30% 以上が Linux 上で稼働しており、これらのシステムでは多くの顧客が Azure 上で Java を使用しています 。 たとえば、 Pivotal Cloud FoundryService FarbricOpenShiftKubernetes などがあります。Java 開発者が Azure でサービスを実行するための選択肢はたくさんあります 。

日本を拠点とするマイクロソフトのJavaエバンジェリストとしての日々どのような事をされているのですか?


最近は、Java のエバンジェリストとして、お客様のために多くの HackFest を開催しています。HackFest ではお客様が実際にビジネスをされている上でかかえる課題について直接改善してまいります。 お客様と NDA を締結した上で、お客様のソースコードにアクセスし、改善のためのアドバイスや直接ソースコードを編集する事もあります。いままでの経験を活かして、Azure におけるシステム運用方法や、アーキテクチャ設計に関するアドバイスなどを行います。

たとえば、最近ハックフェストを実施したとあるお客様では、オンプレミスで運用されている既存の Java Web アプリケーションを Azure 上のコンテナに移行したいと考えていらっしゃいました。当初、お客様は Kubernetes についてよくご存知ではなかったのですが、 HackFest を通じて Azure Container Service (AKS) の構築方法や、操作方法について情報を提供し、結果として、わずか5日間で既存のシステムを AKS に移行することができました。2日間で Lift & Shift し、残りの 3 日間で、ソースコードに手を加え、例えば Serverless の Azure Functions 等も試され、アプリケーションのモダナイズを行いました。HackFest では開発の視点だけでなく、運用的な観点からも助言しています。 その結果、そのお客様はこの HackFest の成果に非常に満足されていました。

この HackFest は、Azure をご利用される世界中の Java 開発者も利用できます。 読者のみなさまの中で Azure 上で Java を利用し、それを有効活用したい、そのために私のような Java エバンジェリストと緊密に会話したい場合は、私に連絡するか、各国に存在するエバンジェリストに HackFest についてお問い合わせください(※ 実際には HackFest を実施するためには利用可能なテクノロジー領域に条件があります)。 また私自身、マイクロソフトのグローバル組織に所属していますので、HackFest への参加のご要望をいただいた場合、国を問わず、北米、ヨーロッパ、ブラジルなど世界中のさまざまな場所に参加できるかもしれません。

Azure は Java(または Kotlin、Scala などの JVM 上の言語)を基盤とする開発者や組織に適していますか?


はい、Azure は Java 開発者にとって適しています。多くのサービスで Java SDK が用意され、開発は頻繁に行われています。 また一部のサービスでは SDK が存在しないものもありますが、Azure では多くのサービスが RESTful インターフェイスを提供しているため、任意のプログラミング言語からこれらのサービスにアクセスできるようになっています。たとえば、Office 365 をご利用されている場合は、REST 呼び出しで Office 365 を操作することも可能です。

おそらく、マイクロソフトであなたはいくつかのクールなデモやチュートリアルを時間をかけて作成してきたと思います。読者が興味をもちそうなプロジェクトや GitHub のレポジトリについてご紹介いただけないでしょうか?

すべてのデモやプロジェクトを公開しているわけではありませんが、私は GitHub リポジトリに多くのデモとサンプルをアップロードしています。

最新のデモには、多くの面白い最新技術を含んでいます(スライドはこちら)。 1回のデモに含めた Azure の技術要素として下記を含んでいます。

Azure の Bot Framework を利用して Slack からメッセージの送受信を行うチャット・ボットを作成しています。このチャット・ボットの処理の実装には、Serverless の「Azure Functions for Java」を利用しています。Slack から k8s の「アカウントサービスのポッド数を増やしてください」という質問を投げかけると、Azure Functions は LUIS を呼び出して、文を解析しどの意図(入力内容を判定)が最も適切かを判断します。その結果、Kubernetes 上で稼働するアカウントサービスのポッド数を増やします。

仮に「顧客サービスのポッド数を減らしてください」と入力すると、Kubernetes の「顧客サービス」は減少します。 このように、私はチャットボットから Azure のKubernetes を操作できるようにしています。

さらに Microsoft Translator のデモも行なっています。上記のチャット・ボットではテキストでの入力を行いましたが、このデモでは音声入力を行います。 たとえば、Microsoft Translator に対して「アカウントサービスのポッド数を増やしてください」または「顧客サービスのポッド数を減らしてください」と言葉で語りかけると、Azure 上の Kubernetes のポッド数が増減します。

上記のすべてを Java で実装し、Java 開発者が利用できる多くの Azure サービスを利用しています。

エバンジェリスト(または私のようなクラウド・デベロッパー・アドボケイト)は、開発者や利用者などとの対外的な会話と、社内開発チームとの会話のバランスをとることが重要です。 私はすべてのデベロッパー・アドボケイトがこの範囲で異なる優先順位を持っていると思います。私自身は、ドキュメント、APIの改善など、開発者の経験を向上させることに重点を置いています。あなたは、この範囲においてどこに優先順位を置いて仕事をなさっていますか?

私は開発者やお客様と直接会話をするのが好きです。もちろん、私は API を変更したりドキュメントを改善する必要がある場所を見つけることが多いので、必要に応じてやっています。ただ、まだ多くのお客様が CI/CD、マイクロサービス、Cognitive ServicesAzure FunctionsMicrosoft TranslatorIoTPowerBI などの利点についてご存知ないので、これらの技術を Java 開発者の皆様やお客様にその利点や利用方法や実装方法について紹介していきたいです。

昨今 Azure は非常に多くのサービスを提供し、その多くは本当に素晴らしいサービスです。 あなたが今より詳細に調査していることは特にありますか、全体的にあなたの好きなサービスは何ですか?

これはとても難しい質問です。なぜなら、マイクロソフトの技術と世界的な技術トレンドは以前と比べとても速く変化しているためです。 ちょうど今私が上で紹介したデモンストレーションは、現時点で私が好きな、そしてお勧めの技術から構成しています しかし、上記に取り上げた Kubernetes だけでなく、Pivotal Cloud FoundryOpenshift も非常に面白いです。

特に、お客様が巨大なデータセンターを所有されている場合は、 Azure Stack の利用をご検討いただくことをお勧めします。Pivotal Cloud Foundry も Azure Stack 上で動作します。運用における管理コストを削減したい場合、Azure Stack は選択肢の 1 つになります。

マイクロソフトは歴史的に Java の非サポートで知られていますが、最近はマイクロソフトが真にオープンソースに舵をきり大きな進歩を遂げたと思います。Java エンジニアと会話をしてAzure を紹介するとき、参加者の皆様の印象は如何でしょうか?

約2年半前、私は日本マイクロソフトに加わりました。当時すでに一部の開発者やエンジニアの中で、マイクロソフトの変化に気づいている方がいらっしゃいました。 しかしこの2年間で、私はマイクロソフトの印象がさらに変化していると感じています。私はお客様やコミュニティのメンバーとコミュニケーションをとるとき、ほとんどの人から肯定的なフィードバックをいただいています。

特に、AI 技術の観点でマイクロソフトについて多くの肯定的なフィードバックをいただいています。 build 2016 のカンファレンスで AI プロトタイプを発表したSatya Nadella の発表を見たとき、私は本当に感銘を受け、私の周りのすべてのJava開発者にビデオを共有しました。このビデオは、我々が将来を想像すると、エンジニアが世界を変える方法を示しました。本当に素晴らしい内容でした!Cognitive Services のすべてを Java アプリケーションから利用できることも重要です。

初めて他のエンジニアに Cognitive Services のデモンストレーションを行った際、多くの開発者が私もこれを試したいと言いました。 私はこの技術が世界を変えることができると思っていましたが、この技術が会社の将来をも変えることができると私は考えました。

インタビューのお時間をとっていただきありがとうございました。最後に、他にコミュニティの皆様にメッセージがあれば頂けないでしょうか?

私は過去2年間、マイクロソフトで働いており、多くのツール、テクノロジ、サービスが Java 開発者にとって利用できることを理解してきました。たとえばプログラマの皆様にとっては、Visual Studio Team Services(VSTS)はソースコード管理や運用を行うために、ぜひご利用いただきたいサービスです。CI/CD におけるビルドとデプロイのパイプラインを使用することができます。もちろん開発チームに応じて Jenkins のような他の CI/CDツール をご利用されたい場合もあるでしょう、この場合にも、VSTSからそれらのツールにアクセスできるようになっています。

またコンテナ技術をご利用されたい場合は、Web App for Container, Azure Container Service(AKS)Azure Container Instance(ACI)Pivotal Cloud FoundryOpenShift などのご利用のニーズに応じてコンテナ環境を構築できます。さらに、IoT、BigData、NoSQLなどをご利用したい場合は、マイクロソフトは、それらのサービスを提供しています。

私は、エンジニアが想像することができれば、エンジニアが世界を変えることができると信じています!そして私は、そのような素晴らしい Java の開発者の皆様とともに時間を過ごしたいと思います。 私は Java が大好きです!

2018年2月22日 at 8:30 午前 1件のコメント

過去の投稿


Java Champion & Evangelist

Translate

ご注意

このエントリは個人の見解であり、所属する会社の公式見解ではありません

カレンダー

2020年10月
 1234
567891011
12131415161718
19202122232425
262728293031  

カテゴリー

Twitter

  • RT @JavaAtMicrosoft: Day 1 of #JDConf looks neat! Keynote by @ixchelruiz and Julia Liuson, followed by 8 amazing people who will share all… 1 day ago
  • RT @JJUG: お待たせしました。JJUG CCC 2020 Fallの参加登録を開始しました。11/7(土)にオンラインで開催します。 buff.ly/31yqkYD 皆様のご参加をお待ちしています! #jjug #jjug_ccc 1 day ago
  • RT @Sharat_Chander: To all my dear #Java colleagues in Japan, please consider attending #JJUGCCC. The @JJUG always has amazing in-person ev… 1 day ago
  • RT @cero_t: 11/7(土)に開催するJJUG CCCにて、アンカンファレンスを行います! アンカンファレンスは参加者の皆さんにZoomに入ってもらう形式です。 テーマを募集しますので、これを話したい、これを聞いてみたい、という人がいれば、リプライ・RTコメント・D… 2 days ago
  • RT @sKriemhild: Join me at our first #JavaOnAzure short tutorial: Deploy your Java Web App to Azure App Service easily with Maven 💜 https:… 2 days ago

clustermap

ブログ統計情報

  • 1,193,829 hits

RSSフィード

アーカイブ