fbpx

CL LAB

HOME > CL LAB > CloudStack Community Edition ソースコード ビルド方法

CloudStack Community Edition ソースコード ビルド方法

 ★ 19

1.    はじめに

CloudStack Community Editionはオープンソースでソースコードが公開されています。ビルド方法についてはドキュメントに整理されていないためその方法に悩む方も多いと思います。本記事ではCloudStack Community Editionのビルド方法について整理し、CloudStack Community Editionを利用した環境構築や機能追加に寄与することを目的としています。

説明するビルド環境として CentOS5.5 6bit環境を選択し、作業軽減のためrootユーザにてインストール作業を実施しています。作業対象はCloudStack Community Edition 2.2.12としました。

2.     CloudStack Community Editionダウンロードサイト

CloudStack Community Editionのbinary packageはsourceforgeプロジェクトで公開されています。

http://sourceforge.net/projects/cloudstack

ソースコードについては、gitプロジェクトページから取得可能です。

https://github.com/cloudstack

ソースコードは gitHubのcloudstackレポジトリのページへ移動しダウンロードすることができます。

https://github.com/CloudStack/CloudStack

このページの Downloadsのデフォルト指定はmasterであり最新版となります。最新版のレポジトリは、開発中のコードもマージされている場合もありますので、ビルドや実環境で利用するには注意が必要です。ソースコードビルドを確認したいという目的であれば、CloudStack2.2.12リリースバージョンタグを指定してダウンロードする方が、安定してビルド可能です。タグのネーミングルールは特に説明は記載されていないのでtag名で推測するのが良いでしょう。 ソースコードのダウンロードはgitがなくても可能です。( 詳細は上記URL参照 )

ビルドの確認だけならgitレポジトリは不要ですが、gitレポジトリごと clone しておくとリリースの際のソースコードの差分などを確認することが可能です。

gitレポジトリのcloneを作成したい場合は、まず git をインストールする必要があります。gitはCentOSのyumのデフォルトレポジトリに含まれていませんが、DAGにて提供されていますので以下のようにインストールすることができます。

# wget http://dag.wieers.com/packages/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.i386.rpm
# wget http://dag.wieers.com/packages/RPM-GPG-KEY.dag.txt

ダウンロードしたrpmパッケージをインストールします。

# rpm -ivh rpmforge-release-0.3.6-1.el5.rf.i386.rpm

ダウンロードしたDAGのレポジトリファイル /etc/yum.repos.d/rpmforge.repo の enabled項目は 0 に設定しておきます。その後下記yumコマンドにてgitインストールします。

# yum --enablerepo=rpmforge install git

これで、git のインストールが完了しますので、CloudStackの gitレポジトリの cloneを作成します。

# git clone git://github.com/CloudStack/CloudStack.git

リースの際のソースコードの差分の確認は diff コマンドで行えます。例として、tag-2.2.10とtag-2.2.12の差分確認は下記コマンドで行うことができます。

# git diff tag-2.2.10 tag-2.2.12

3.    ソースコンパイルのための環境準備

3.1.  tomcat

解析のしやすさを考慮し、tomcat 6を選択しました。以下、tomcatをバイナリにてインストールする方法を説明します。tomat はapache tomcatプロジェクトのホームページは以下のURLからダウンロード可能です。

http://tomcat.apache.org

tomcat6のダウンロードリンクを確認して、作業用のディレクトリにダウンロードし解凍します。今回は作業専用のサーバのため、rootのホームディレクトリにダウンロードしました。

# wget http://ftp.kddilabs.jp/infosystems/apache/tomcat/tomcat-6/v6.0.32/bin/apache-tomcat-6.0.32.tar.gz
# tar xvfz apache-tomcat-6.0.32.tar.gz

シェル環境変数としてCATALINE_HOMEを追加します。

# export CATALINA_HOME=/root/apache-tomcat-6.0.32

CloudStack Community Edition 2.2.8では、antによるビルドはCATALINE_HOMEを参照し、waf(後述)によるビルドはTOMCAT_HOMEを参照していましたが、CloudStack Community Edition 2.2.12ではCATALINE_HOMEに統一されました。そのため、現バージョンでは TOMCAT_HOMEの設定は不要です。

3.2.  ソースコード

ソースコードbuildにはantが必要です。antはダウンロードしても良いですが、展開されたCloudStackソースコードに含まれているので、それを利用します。利用のために ANT_HOMEを設定しておきます。

# export ANT_HOME=/root/cloudstack2.2.12/tools/ant/apache-ant-1.7.1
# export PATH=$ANT_HOME/bin:$PATH

3.3.  wafを使用してのパッケージインストール

ここで説明する手順は、CloudStack Community Edition 2.2.12に同封されているREADME.htmlを参考としています。README.htmlは docsディレクトリの下に同梱されています。( README.htmlはバージョンによって、同封されていない場合や、場所が異なりソースコードディレクトリの直下の場合があります。)

wafはソースコードに同封されているビルド用スクリプトです。このwafスクリプト内でantが実行されます。

まずはじめに、wafを使用してbuildに必要なパッケージの確認を行います。ソースコードディレクトリは$HOME/cloudstack2.2.12としました。

# cd cloudstack2.2.12
# waf viewrpmdeps

waf viewrpmdepsで確認すると以下のモジュールが必要なことがわかります。

glibc-devel
gcc
commons-httpclient
java-1.6.0-openjdk-devel
rpm-build
ws-commons-util
tomcat6
MySQL-python
jpackage-utils
/usr/bin/mkisofs

以下のコマンドでyumを使用しての上記パッケージ群がインストールされます。

# waf installrpmdeps

3.4.  MySQLサーバ

ソースコードのビルドにはMySQLサーバは必要ありませんが、CloudStackを動作させるためには必要となります。MySQLサーバは yumを使用してインストールできます。

# yum install mysql-server mysql-client

4.   ビルド

4.1.  wafによるビルド

 

※1 CloudStack 2.2.12 Community Editionのwafスクリプトでは、以下の記述のように、waf build は FAILEになります。CloudStack CommunityのForumdでは、ソースコードbuildにはantを使用するように勧められています。ここでは wafコマンドの理解のために記述をしますが、基本的には後述する antによるビルド を実施してください。

 

※2 本稿記述時はCloudStack 2.2.12での確認でしたが、CloudStack2.2.13 Community Editionリリース後、再確認したところ、wafスクリプトのメンテナンスが実施されており、waf buildが使用可能となっています。本章のwaf build失敗の記述は参考情報としてしてください。

 

まず waf configureを実行します。この際、MySQL接続ユーザやパスワードを指定します。今回は prefixで展開先を指定しています。デフォルトでは/usr/localとなります。

# waf configure --prefix=/root/usr/local --with-db-user=root --with-db-pw=root --with-db-host=localhost

ビルド実行のコマンドは以下となります。

# waf build

waf buildでコンパイルされたオブジェクトはartifacts/defaultディレクトリへ出力されます。後述する雛形ファイルのtoken文字変換はartifacts/c4che/default.cache.pyへ出力されます。

# waf install

waf clean、waf distcleanなどのコマンドも用意されています。(waf –helpを参照)

help出力には明示的に表示されませんが、waf dist(tar.gz形式の配布物作成)以外に waf rpm, waf deb オプションも用意されています。

CloudStack Community Edition 2.2.12では、waf buildは以下のエラーが出力され FAILEになります。

BUILD FAILED

/root/cloudstack2.2.12/build/package.xml:160: problem opening /root/cloudstack2.2.12/dist/client/client.war

Total time: 1 minute 48 seconds

Waf: Leaving directory `/root/cloudstack2.2.12/artifacts'

Build failed: Ant command ['tools/ant/apache-ant-1.7.1/bin/ant', '-Dthirdparty.classpath=deps/cloud-commons-dbcp-1.2.2.jar,deps/cloud-gson.jar,deps/cloud-bcprov-jdk16-1.45.jar,deps/cloud-trilead-ssh2-build213.jar,deps/vmware-lib-jdom.jar,deps/vmware-lib-saxpath.jar,deps/cloud-commons-pool-1.4.jar,deps/cloud-manageontap.jar,deps/cloud-jnetpcap.jar,deps/cloud-jstl-1.2.jar,deps/cloud-ws-commons-util-1.0.2.jar,deps/cloud-log4j.jar,deps/cloud-wsdl4j.jar,deps/cloud-email.jar,deps/vmware-lib-mailapi.jar,deps/cloud-xenserver-5.6.100-1.jar,deps/cloud-junit.jar,deps/vmware-lib-jaxen-core.jar,deps/cloud-commons-collections-3.2.1.jar,deps/cloud-httpcore-4.0.jar,deps/cloud-commons-codec-1.4.jar,deps/vmware-apputils.jar,deps/cloud-xstream-1.3.1.jar,deps/cloud-xmlrpc-client-3.1.3.jar,deps/vmware-lib-wbem.jar,deps/cloud-commons-logging-1.1.1.jar,deps/vmware-lib-xalan.jar,deps/cloud-commons-discovery.jar,deps/vmware-lib-activation.jar,deps/cloud-ehcache.jar,deps/cloud-jsch-0.1.42.jar,deps/vmware-lib-smtp.jar,deps/cloud-backport-util-concurrent-3.0.jar,deps/vmware-vim25.jar,deps/vmware-lib-xerces.jar,deps/cloud-cglib.jar,deps/vmware-lib-xml-apis.jar,deps/cloud-libvirt-0.4.5.jar,deps/cloud-iControl.jar,deps/cloud-mysql-connector-java-5.1.7-bin.jar,deps/cloud-commons-httpclient-3.1.jar,deps/cloud-axis.jar,deps/jetty-util-6.1.26.jar,deps/vmware-lib-jaxrpc.jar,deps/vmware-vim.jar,deps/vmware-credstore.jar,deps/vmware-lib-jaxen-jdom.jar,deps/cloud-log4j-extras.jar,deps/cloud-xmlrpc-common-3.1.3.jar,deps/jetty-6.1.26.jar,deps/cloud-servlet-api.jar,/usr/share/java/commons-collections.jar,/usr/share/java/commons-dbcp.jar,/usr/share/java/commons-logging.jar,/usr/share/java/commons-logging-api.jar,/usr/share/java/commons-pool.jar,/usr/share/java/commons-httpclient.jar,/usr/share/java/ws-commons-util.jar,/usr/share/java/jnetpcap.jar,/usr/share/java/tomcat6-servlet-2.5-api.jar,/usr/share/java/tomcat6-jsp-2.1-api-6.0.24.jar,/usr/share/java/tomcat6-el-1.0-api-6.0.24.jar,/root/apache-tomcat-6.0.33/bin/bootstrap.jar,/root/apache-tomcat-6.0.33/bin/commons-daemon.jar,/root/apache-tomcat-6.0.33/bin/tomcat-juli.jar,/root/apache-tomcat-6.0.33/lib/tomcat-i18n-ja.jar,/root/apache-tomcat-6.0.33/lib/tomcat-coyote.jar,/root/apache-tomcat-6.0.33/lib/annotations-api.jar,/root/apache-tomcat-6.0.33/lib/el-api.jar,/root/apache-tomcat-6.0.33/lib/servlet-api.jar,/root/apache-tomcat-6.0.33/lib/ecj-3.3.1.jar,/root/apache-tomcat-6.0.33/lib/catalina-tribes.jar,/root/apache-tomcat-6.0.33/lib/catalina-ha.jar,/root/apache-tomcat-6.0.33/lib/catalina.jar,/root/apache-tomcat-6.0.33/lib/jasper-el.jar,/root/apache-tomcat-6.0.33/lib/catalina-ant.jar,/root/apache-tomcat-6.0.33/lib/tomcat-i18n-es.jar,/root/apache-tomcat-6.0.33/lib/cloud-mysql-connector-java-5.1.7-bin.jar,/root/apache-tomcat-6.0.33/lib/jsp-api.jar,/root/apache-tomcat-6.0.33/lib/tomcat-i18n-fr.jar,/root/apache-tomcat-6.0.33/lib/tomcat-dbcp.jar,/root/apache-tomcat-6.0.33/lib/jasper.jar', 'build-all', '-Dimpl.version=2.2.20111020130226', '-Dtarget.dir=target', '-Ddist.dir=dist', '-Dbase.dir=/root/cloudstack2.2.12', '-f build.xml'] failed with error value 1

 

4.2.  antによるビルド

antによるビルドの手順は以下に説明があります。ここでは以下サイト情報に従い説明を行います。

http://cloud.mindtouch.us/Knowledge_Base/How_to_set_up_a_CloudStack_development_environment

まず waf installrpmsdeps を行い必要なパッケージはインストールしておきます。antによるビルド実行のコマンドは以下となります。

# ant build-all

ant build-allによりdist、targetディレクトリが作成されます。targetディレクトリには、deployment用のクラスファイル群が生成されます。distには、tomcatにdeployされるwebアプリケーションが配置されます。

ant build-allにより、docsが生成されjavadocファイルも生成されるが、ドキュメント生成されるソースコードの一部が省略されています。開発時の調査には、提供されているソースコード全てに関して、自分でjavadocを実行するとより詳細なドキュメントが得られます。javadocに準じたツール、Doxygenやデータベースの構造解析については後述します。

CloudStack Management Serverを以下のコマンドでデプロイします。デプロイはCATALINA_HOME環境変数を参照するので、実行前に環境変数設定を確認しておく必要があります。

# ant deploy-server

以下のコマンドでデータベースの初期化を行います。

# ant deploydb

ant deploydbにより、CloudStack用データベース(cloud)、MySQLユーザ cloud(パスワードはcloud)及び、各種テーブルが作成されます。該当 cloud データベースはCloudStack Management Serverアプリケーション(webアプリケーション名client)からアクセスされます。
CloudStack Community Edition 2.2.9以前のバージョンではdeploydbにより、deploy-serverとデータベース設定両方の処理が実行されており、データベースを初期化すると、同時に、webアプリケーションが上書きされてしまうので注意が必要でしたが、現在の ant deploydb ではデータベース更新しか実施していないので、webアプリケーションの更新ついては考慮せずdeploydbの実行が可能となりました。

5.    Management Server起動

CloudStack Management Server起動します。前述したとおり waf buildコマンドは FAILE するため、waf runコマンドを使用せず、antで起動します。内容的にはwaf runはant runと同等となります。

# ant debug

または

# ant run

を実行します。debug を指定するとdebugモードのログ出力が行われます。

ブラウザから http://localhost:8080/client にアクセスし、Management Serverが動作していることを確認します。

 

※ CloudStack Community Edition 2.2.9にて初回のManagement Server起動時に停止し、以下のメッセージ出力後、起動シーケンスがスタックしたことがありました。参考のため、状況及び対処方法を以下に説明します。
以下のプロンプトが作成されSystem VM用のRHAキーファイル作成がユーザに促されます。


<pre><span style="font-family: monospace;">INFO  [cloud.server.ConfigurationServerImpl] (main:) Systemvm keypairs not found in database. Need to store them in the database Enter passphrase (empty for no passphrase): </span></pre>
 
 

ManagementSever起動時に下記のスクリプトが実行されますが、ブートシーケンスでは入力プロンプトを表示されないため入力パスフレーズを受け取れず、アプリケーションブート処理がスタックします。
対処方法としては起動前にパスフレーズ設定を行うために、以下の内容のスクリプトをマニュアルで実行します。


#!/bin/bash -c
if [ -f ~/.ssh/id_rsa ]
then
    true
else
    yes '' | ssh-keygen -t rsa -q
fi

上記スクリプトを1回手動で実行すれば、Management Server起動が停止する問題は解決します。

 

6.    CloudStack環境構築

Management Serverの起動が終了したら、CloudStack Installation Guideの手順に従って、環境の構築を行います。データベース設定は済んでいるので、以下の作業を実施します。

  • Hostの用意
  • Primary Storageの用意
  • Secondary Storageの用意
  • 共有ストレージあるいはNFSの設定iptablesの設定

System VM templateダウンロード用コマンド、cloud-install-sys-tmpltはant build-allにより、targetディレクトリへコピーされます。target/scripts/storage/secondary以下のファイルを全てexecutableにし、Installation Guideに従ったコマンドを実行します。cloud-install-sys-tmpltから該当ディレクトリ配下の*.shがコールされるため、該当ディレクトリ配下の全てのファイルに対して、executableとします。下記コマンド実行例は、XenServer用System VM templateダウンロード実行を実施しています。

# cd target/scripts/storage/secondary
# chmod oug+x ./*.sh cloud-install-sys-tmplt
# mount –t nfs <secondary storage export point> /mnt
# cloud-install-sys-tmplt -m /mnt -u http://download.cloud.com/releases/2.2.0/systemvm.vhd.bz2 -h xenserver –F
# umount /mnt

ant debugあるいはant runでManagement Serverを起動し、ZoneやPodの設定を行います。

 

7.    binary packageとsource code buildの比較

7.1.  binary packageインストール

binary packageにはinstall.shが提供されており、該当シェルスクリプトにより必要なソフトウェアは自動インストールされます。インターネットに接続可能な状況であれば、事前にインストールしておく必要はありません。

install.shにてインストールされるパッケージは以下となります。

antlr-2.7.6-4jpp.2.x86_64
cloud-agent-scripts-2.2.12-1.el5.x86_64
cloud-client-ui-2.2.12-1.el5.x86_64
cloud-client-2.2.12-1.el5.x86_64
cloud-core-2.2.7-12.el5.x86_64
cloud-deps-2.2.7-12.el5.x86_64
cloud-python-2.2.12-1.el5.x86_64
cloud-server-2.2.12-1.el5.x86_64
cloud-setup-2.2.12-1.el5.x86_64
cloud-utils-2.2.12-1.el5.x86_64
ecj-3.3.1.1-3.jpp5.noarch
eclipse-ecj-3.2.1-19.el5.centos.x86_64
gjdoc-0.7.7-12.el5.x86_64
giflib-4.1.3-7.1.el5_3.1.x86_64
jakarta-commons-collections-tomcat5-3.2-2jpp.3.x86_64
jakarta-commons-collections-3.2-2jpp.3.x86_64
jakarta-commons-dbcp-1.2.1-7jpp.1.x86_64
jakarta-commons-dbcp-tomcat5-1.2.2-2.jpp5.noarch
jakarta-commons-daemon-1.0.1-7.jpp5.noarch
jakarta-commons-httpclient-3.0-7jpp.1.x86_64
jakarta-commons-logging-1.0.4-6jpp.1.x86_64
jakarta-commons-pool-1.3-5jpp.1.x86_64
jakarta-commons-pool-tomcat5-1.3-11.jpp5.noarch
java-1.4.2-gcj-compat-1.4.2.0-40jpp.115.x86_64
java-1.6.0-openjdk-1.6.0.0-1.7.b09.el5.x86_64
jpackage-utils-5.0.0-1.jpp5.noarch
jpackage-utils-compat-el5-0.0.1-1.noarch
mkisofs-2.01-10.7.el5.x86_64
mx-2.0.6-2.2.2.x86_64
MySQL-client-community-5.1.55-1.rhel5.x86_64
MySQL-devel-community-5.1.55-1.rhel5.x86_64
MySQL-python-1.2.1-1.x86_64
MySQL-server-community-5.1.55-1.rhel5.x86_64
MySQL-shared-compat-5.1.55-1.rhel5.x86_64
python-crypto-2.0.1-4.el5.2.x86_64
python-devel-2.4.3-27.el5.i386
python-devel-2.4.3-27.el5.x86_64
python-paramiko-1.7.6-1.el5.noarch
tomcat6-6.0.24-2.jpp5.noarch
tomcat6-el-1.0-api-6.0.24-2.jpp5.noarch
tomcat6-jsp-2.1-api-6.0.24-2.jpp5.noarch
tomcat6-lib-6.0.24-2.jpp5.noarch
tomcat6-servlet-2.5-api-6.0.24-2.jpp5.noarch
xml-commons-1.3.04-5.jpp5.noarch
xml-commons-jaxp-1.3-apis-1.3.04-5.jpp5.noarch
ws-commons-java5-1.0.1-2.jpp5.noarch
ws-commons-util-1.0.2-2.jpp5.noarch

CloudStack binary packageは、開発及びQAに使用されるサードパーティパッケージも同封されています。一部はyumコマンドによりインターネットサイトからインストールされますが、一部は展開したローカルのファイルからインストールされます。どのようなパッケージが同封されているかは、${binary package展開先ディレクトリ}/deps/RPMSディレクトリを確認すること把握できます。

※ CentOSのCoreインストール環境にて、CloudStack binary packageをインストールしたとき、cloud-setup-databases, cloud-setup-management実行時に、以下のエラーが発生することがあります。

# cloud-setup-databases cloud:root@localhost --deploy-as=root:root

Traceback (most recent call last):
File “/usr/bin/cloud-setup-databases”, line 271, in <module>
try: checkselinux()
File “/usr/bin/cloud-setup-databases”, line 218, in checkselinux
check_selinux()
File “/usr/lib/python2.4/site-packages/cloud_utils.py”, line 314, in check_selinux
if config_enforcing:
UnboundLocalError: local variable ‘config_enforcing’ referenced before assignment

SELinuxがインストールされていない環境では、該当スクリプト内のcheckselinuxを実施している箇所をコメントアウトすることにより、cloud-setup-databases, cloud-setup-managementを実行させることができます。

7.2.  binary packageのディレクトリリスト

CloudStack Community Edition2.2.12に内包されていたREADME.htmlに記述ある各インストールモジュール先ディレクトリの説明を以下に示します。一部古いバージョンの内容も含まれます。

  • $PREFIX:
    クラウドスタック本体のインストール先。The default prefix where entire stack in installed
    デフォルトはルートユーザで実行された場合は、/usr/local。
    通常のユーザで実行された場合は、$HOME/cloudstack
    Windows環境ではC:\CloudStack
    binary packageのビルド指定は/usr
  • $SYSCONFDIR/cloud:
    コンフィグレーションファイルインストール先
    ソースコードビルドのデフォルト指定は$PREFIX/etc
    binary packageのビルド指定は/etc/cloud
  • SYSCONFDIR/init.d:
    スクリプトファイルインストール先
    ソースコードビルドのデフォルト指定$PREFIX/etc/init.d
    binary packageのビルド指定は/etc/init.d
  • $BINDIR:
    CloudStackインストールプログラム配布先
    ソースコードビルドのデフォルト指定は$PREFIX/bin
    binary packageのビルド指定は/usr/bin
  • $LIBEXECDIR:
    CloudStackサービスプログラムインストール先
    ソースコードビルドのデフォルト指定$PREIX/libexec
    binary packageのデフェルト指定/usr/libexec (Ubntubtuの場合は/usr/bin)

Install.shによりCloudStack2.2.12モジュールのインストール先のディレクトリ構成は以下となります。README.htmlの記述とは若干の相違があります。

  • /etc/cloud/management
    tomcatアプリケーションのコンフィグレーションファイル。 $CATALINA_BASE/confのシンボリックリンク先。
  • /etc/init.d
    下記のデーモン起動スクリプトがインストールされる。

    • cloud-ipallocator
      内部でコールしているcloud-deamonizeがAgentインストールでないとインストールされないため、Agentで使用されているデーモンと推測している。
    • cloud-management
      tomcat webアプリケーションであるManagement Serverデーモン。port 8080使用。
  • /usr/bin
    pythonあるいはbashのスクリプトで作成されたCloudStackコマンド群。マニュアルに表記がある以外のコマンドについては—helpなどのオプションで用途確認のこと。

    • cloud-external-ipallocator.py
    • IP確保コマンド。/etc/init.d/cloud-ipallocatorより呼び出される。
    • cloud-update-xenserver-licenses
    • Management Serverが認識している特定あるいは全てのXenServerホストにライセンスファイルをデプロイコマンド。
    • cloud-sysvmadm
    • システムVM及びドメイン内仮想ルーターの起動及び停止スクリプト。
    • cloud-set-guest-sshkey
    • ssh公開鍵ダウンロードクライアント。
    • cloud-set-guest-password
    • パスワードダウンロードクライアント。
    • cloud-migrate-databases
    • CloudStack2.1.3、2.2.0、2.2.1からCloudSatck2.2.4以上へのdatabaseスキーマ更新スクリプト。
    • cloud-sccs
    • CloudStackドキュメントインストールツール。
    • cloud-gitrevs
    • CloudStackドキュメントインストールツール。
  • /usr/share/cloud/setup
    CloudStackで使用するsqlスクリプト群。
  • /usr/share/cloud/ui-backup
    CloudStack再インストールあるいはアップグレード時に生成されるweb UIのバックアップ(bz2形式)。
  • /usr/share/cloud/management
    $CATALINA_HOMEディレクトリ。
  • 7.3.  source code buildスクリプトファイル

    source code buildの環境ではスクリプトファイル群はant build-allコマンドより以下のディレクトリに生成されます。

    <ソースコードディレクトリ>/target/scripts

    7.4.  ログファイル

    binary packageインストールでは、Management Serverログファイルのは以下のファイルに出力されます。

    /var/log/cloud/management/management-server.log

    API実行のログは以下のファイルに出力されます。

    /var/log/cloud/management/api.log

    source code build環境では、Management Serverログファイルのはant debugあるいはant runを実行したディレクトリ、つまりソースコードディレクトリにvmops.logとapi.logが生成されます。

    8.    再インストールあるいはアップグレード

    8.1.  データベースの退避

    予め、mysqldumpを用いてMySQLデータベース(データベース名cloud)を保存しておきます。

    8.2.  設定ファイル退避

    以下のディレクトリ以下の設定ファイルを保存しておきます。

    $CATALINA_HOME/conf

    binary packageインストールでは$CATALINA_HOMEは/etc/cloud/managementとなります。

     

    8.3.  カスタマイズファイル退避

    後述のUIカスタマイゼーションを実施している場合には、該当のjspやcssファイルなどのカスタマイズ実施ファイルも保存しておきます。その他カスタマイズしているソースコードがある場合も同様に保存しておきます。

    8.4.  データベースのアップグレード

    Management Serverのソースコードをビルドしtomcatへdeployした後は、データベースの初期化(ant deploydb )を実施せず、手動でデータベースアップグレードします。<ソースコードディレクトリ>/target/dbの下にsqlスクリプトが生成されるので該当アップグレードによる実施されるsqlスクリプトを確認します。アップデート前後のtagを指定し git diff コマンドを実施し、該当sqlスクリプトを検出します。

    8.5.  binary packageのupdateあるいはインストール

    binary packageのインストールについては、install.shにU) Upgrade the CloudStack package installed on this computerを選択、実行します。詳細についてはリリースノートの後部にアップデート手順の詳細、留意点が記述していあるので、その内容を確認しておきましょう。

    GUIのカスタマイズしている場合には、以下のファイルを退避・マージ作業が必要となります。install.shでアップグレードや再インストールを実施した場合、UI関係のファイルは/etc/cloud/ui-backupにバックアップが取得されるので、インストールされたファイルを手動でマージします。

    9.    CloudStack Community Edition再配布

    9.1.  cloud-setup-*スクリプトファイル

    現時点のバージョンでは、CloudStackインストールセットアップに必要なスクリプト、cloud-setup-databases、cloud-setup-managementは、antによるbuildでは生成されません。waf buildにより、該当コマンドは生成されartfactsディレクトリに格納されます。waf installによりインストール先prefixへ該当コマンド群がコピーされます。現在waf build、waf installはメンテナンスがされていない模様ですが、雛形ファイルcloud-setup-databases.in, cloud-setup-management.inを直接修正することにより、エンドユーザーに該当スクリプトを提供することは可能と考えられます。

    9.2.  現状での再配布案

    現状でのCloudStack再配布案は以下のようになります。

    • tomcat webアプリケーションの配布(ユーザは手動でwebアプリケーションをデプロイする)
    • wafでしか生成されないスクリプトファイルの手動コピー及び修正

    10. antコマンドを使用したagentインストール

    Agentインストール作業はCentOS6.0で実施しました。

    10.1.  Hostにおける必要パッケージ

    Hostにtomcat6をインストールしておきます。
    install.shの動作を参考とし、他に必要なpackageをインストールします。

    jakarta-commons-collections.noarch 0:3.2.1-3.4.el6
    jakarta-commons-dbcp.noarch 0:1.2.1-13.8.el6
    jakarta-commons-httpclient.x86_64 1:3.1-0.6.el6
    jakarta-commons-logging.noarch 0:1.0.4-10.el6
    jakarta-commons-pool.x86_64 0:1.3-12.7.el6
    libcgroup.x86_64 0:0.37-2.el6
    libvirt.x86_64 0:0.8.7-18.el6_1.1
    qemu-img.x86_64 2:0.12.1.2-3.160.el6
    qemu-kvm.x86_64 2:0.12.1.2-3.113.el6
    augeas-libs.x86_64 0:0.7.4-1.el6
    celt051.x86_64 0:0.5.1.3-0.el6
    dnsmasq.x86_64 0:2.48-4.el6
    ebtables.x86_64 0:2.0.9-6.el6
    gpxe-roms-qemu.noarch 0:0.9.7-6.7.el6
    iscsi-initiator-utils.x86_64 0:6.2.0.872-21.el6
    libpciaccess.x86_64 0:0.10.9-4.el6
    libvirt-client.x86_64 0:0.8.7-18.el6_1.1
    lzop.x86_64 0:1.02-0.9.rc1.el6
    netcf-libs.x86_64 0:0.1.7-1.el6
    nfs-utils.x86_64 1:1.2.3-7.el6
    qemu-kvm.x86_64 2:0.12.1.2-3.113.el6
    radvd.x86_64 0:1.6-1.el6
    seabios.x86_64 0:0.6.1.2-3.el6
    spice-server.x86_64 0:0.4.2-15.el6
    vgabios.noarch 0:0.6b-3.6.el6
    yajl.x86_64 0:1.0.7-3.el6
    gnutls-utils.x86_64 0:2.8.5-4.el6
    libevent.x86_64 0:1.4.13-1.el6
    lzo.x86_64 0:2.03-3.1.el6
    nfs-utils-lib.x86_64 0:1.1.5-3.el6
    qemu-kvm.x86_64 2:0.12.1.2-3.113.el6
    rpcbind.x86_64 0:0.2.0-8.el6
    spice-server.x86_64 0:0.4.2-15.el6

    10.2.  antによるagentパッケージ作成

    Cloudstack Community Editionに同梱されいてるREADME.htmlに従い手順を説明します。
    Management Serverをコンパイルした環境で以下のコマンドを実行しagent.zipを作成します。

    # ant pagkage-agent

    Host側にscpコマンドなどを使用し以下のファイルを転送します。

    dist/agent.zip
    dist/deploy-agent.sh

    以下のコマンドを実行しagent.zipを展開します。-dで指定するディレクトリはagentウェブアプリケーション展開先のディレクトリです。

    computing nodeの場合

    ./deploy-agent.sh –d <deploy先ディレクトリ> -h <management server IP> -t computing -m expert

    routing serverの場合

    ./deploy-agent.sh –d <deploy展開先ディレクトリ> -h <management server IP> -t routing -m expert

    storage serverの場合

    ./deploy-agent.sh –d <deploy展開先ディレクトリ> -h <management server IP> -t storage -m expert

    binary packageのinstall.shから生成された状況から比較すると、zipでアーカイブされたagent.zipには、パッケージ・スクリプトが幾つか不足しています。(スクリプトファイル群で、cloud-setup-agent、cloud-external-ipallocator.py、cloud-deamonize、cloud-sccs、cloud-gitrevsのコマンドがない。)
    このためソースコードからのAgentインストールは、cloud-setup-agentの処理を自前で実施する必要があります。cloud-setup-agentで実施される内容は以下と考えられます。

  • agent.propertiesの設定
  • ネットワークブリッジの作成(ifcfg-cloudbr0)
  • デフォルトネットワークへのブリッジ設定(ifcfg-ethX)
  • /etc/sysconfig/iptablesの設定。以下のtcpポートオープン。
  • -A INPUT -p tcp -m tcp --dport 49152:49216 -j ACCEPT
    -A INPUT -p tcp -m tcp --dport 5900:6100 -j ACCEPT
    -A INPUT -p tcp -m tcp --dport 16509 -j ACCEPT

    10.3.  Agent binary install

    CloudStack Installation Guideの記述通り、install.shからInstall Agentを指定することで行えます。

    11.       CloudStackソースコード階層

    CloudStack Community Edition2.2.12に内包されていたREADME.htmlを参考とし、ソースコード階層のパターンについて以下に記す。情報が古い部分もあると思われるため、現状のソースコードを比較し、必要と思われる記述を補足した。

    • deps/*jar
      @JAVADIR@/ディレクトリにインストールされるjarファイル群。
      @JAVADIR@デフォルト設定は/usr/local/java
    • thirdparty/*jar
      @PREMIUMJAVADIR@/ディレクトリにインストールされるjarファイル群
      @PREMIUMJAVADIR@デフォルト設定は/usr/local/share/cloud-premium
    • scripts/*
      該当ディレクトリ以下のファイルのtoken文字列はwaf configureで設定された文字列に置換され、スクリプトファイル群を生成する。
      @AGENTLIBDIR@/scriptsディレクトリにインストールされるスクリプトファイル群。
      @AGENTLIBDIR@デフォルト設定は/usr/local/lib/cloud/agent
    • patches/<virttech>/*
      該当ディレクトリ以下のファイルのtoken文字列はwaf configureで設定された文字列に置換され、patch用圧縮ファイルを生成する。
      tarred up in a file called patch-<virttech>.tgz (the name is only used in the artifacts directory)
      tarball auto-installed as @AGENTLIBDIR@/scripts/vm/hypervisor/<virttech>/patch.tgz
      @AGENTLIBDIR@デフォルト設定は/usr/local/lib/cloud/agent
    • <project>/src/**/*java
      antコマンドによりbuildされ、cloud-<project>.jarが生成される。
      @JAVADIR@/ディレクトリに配置される。
      @JAVADIR@デフォルト設定は/usr/local/java
    • <project>/bindir/*
      該当ディレクトリに雛形のinファイルが存在すれば、それを元にwaf configureで設定されたtoken文字列に置換されたファイルが生成される。
      @BINDIR@/にインストールされるコマンド群。
      @BINDIR@デフォルト設定は/usr/local/bin
    • <project>/libexec/*
      該当ディレクトリに雛形のinファイルが存在すれば、それを元にwaf configureで設定されたtoken文字列に置換されたファイルが生成される。
      @LIBEXECDIR@/にインストールされるライブラリ群。
      @LIBEXECDIR@デフォルト設定は/usr/local/libexec
    • <project>/tomcatconf/*
      該当ディレクトリに雛形のinファイルが存在すれば、それを元にwaf configureで設定されたtoken文字列に置換されたファイルが生成される。
      @MSCONF@/ディレクトリにインストールされるコンフィグレーションファイル群。
      @MSCONF@デフォルト設定は/usr/local/etc/cloud/management
    • <project>/db/*
      @SETUPDATADIR@/ディレクトリにインストールされるsqlスクリプト群。
      @SETUPDATADIR@デフォルト設定は/usr/local/share/cloud/setup
    • <project>/<distro>/SOMEDIRECTORY
      該当ディレクトリに雛形のinファイルが存在すれば、それを元にwaf configureで設定されたtoken文字列に置換されたファイルが生成される。
      auto-installed only in specific <distro>, in @SOMEDIRECTORY@/ (that is, the uppercase directory name is taken to be an identifier and substituted dynamically for the path that it represents).
    • plugins/<project>/src/**/*java
      antコマンドによりbuildされ、cloud-<project>.jarが生成される。
      @PLUGINJAVADIR@/ディレクトリに配置される。
      @PLUGINJAVADIR@デフォルト設定は/usr/local/java/cloud-plugins
      該当クラスへはmanagement server起動時のclasspathへ自動追加される。
      デフォルトでは該当pluginsディレクトリは存在しない。
    • vendor/<vendor>/tomcatconf/*
      該当ディレクトリに*.inファイルが存在すれば、それを元にwaf configureで設定されたtoken文字列に置換されたファイルが生成される。
      @MSCONF@/vendor/<vendor>/ディレクトリにインストールされるtomcatコンフィグレーションファイル群。
      @MSCONF@デフォルト設定は/usr/local/etc/cloud/management
      該当クラスへはmanagement server起動時のclasspathへ自動追加され、@MSCONF@ディレクトリ直下のクラスは<vendor>配下のクラスにオーバライドされる。
      デフォルトでは該当vendorディレクトリは存在しない。

    各プロジェクトディレクトリ内に散在している*.inは、cloud-setup-*ファイルや*.shなどのスクリプトファイルあるいは設定ファイルなどの雛形ファイルです。inファイルはsedを使用して編集され、CloudStackで使用のスクリプトや設定ファイルとして提供されます。

    inファイル内の置換文字列@<string>@は、waf showconfigの出力により確認可能です。また、waf configureにオプション指定により各token(@<string>@)を設定することも可能です。 tokenファイル定義は、waf build実行時にartifacts/c4che/default.cache.pyへ出力されます。

    12.       User Interfaceカスタマイズ

    UIカスタマイゼーションについては、下記のURL参照してください。

    http://docs.cloud.com/CloudStack_Documentation/Customizing_the_CloudStack_UI

    デプロイアプリケーション名はclient。上記URLドキュメントは、$CATALINA_HOME/webapps/clientにデプロイされたディレクトリ構造について説明しています。これは、前述のソースコード階層のuiプロジェクトに該当します。

    • index.jsp
      メインHTML/JSPページ。
    • favicon.ico
      デフォルトCloud.comファビコン。必要に応じて置き換え可。
    • /images/*.*
      CloudStack UIで使用している画像ファイル群。
    • /css/main.css
      CloudStack UIにて使用しているメインcssファイル
    • /css/ jquery-ui.custom.css
      jQuery UIにて使用しているcssファイル。ダイアログのcss定義は全てこのファイルで定義される。
    • /custom/*.*
      カスタマイズサンプルの画像及びcssファイル
    • /jsp/*.jsp
      UIの各機能に関連したJSPファイル群。
    • /script/jquery*.js
      CloudStack UIで使用されるJavascriptライブラリ群の定義。ユーザーによる修正不可。
    • /script/cloud.core.callback.js
      CloudStack UIログアウト時の動作を記述する。single-sign-onをインテグレーションしたときに使用する。
    • /script/cloud.core.js
      CloudStack UIの主な共通機能を実現するJava scriptファイル群
    • /script/cloud.core.init.js
      CloudStack UI初期化処理Java Scriptファイル。AJAXのデフォルトAPコールURLを変更したい場合などは、該当ファイルを修正する。
    • /script/cloud.core.*.js
      CloudStack UIの主な機能を実現するJava scriptファイル群

    また、ユーザーインタフェースカスタマイズ方法詳細についてはCloudStack User Interface Customizationドキュメントも参照してください。該当ドキュメントには、以下の内容が説明されています。

  • UIの変更方法
  • API URLの変更方法
  • ローカライゼーション(言語設定)
  • ログインセッションタイムアウト設定変更方法
  • single-sign-onインテグレーションサンプル
  • カスタマイズに当たってのCSRF(Cross Site Request Forgery)防止への注意点
  • 13.       CloudStack解析ツール

    13.1.  javadoc

    前述の ant build-allによりjavadocが生成されます。以下のコマンドでもjavadoc生成が可能です。

    # ant javadoc

    前述のとおり、antによるjavadoc生成は、全ソースコードに対する javadocの生成はされません。詳細なドキュメントを生成したい場合には、javadocコマンドを自分で実行しましょう。

    13.2.  Doxygen

    Doxygenというツールでは、javadocより詳細なクラス参照情報を出力が可能です。これにGraphvizというクラス相関図生成ツールを組み合わせると、javadocより詳細な情報を網羅したドキュメントを出力することができます。形式もHTMLやLeTeXの指定が可能です。

    ※ CloudStackの全ソースコードで継承図や参照元など全ての相関図作成を指定したところ、実行が終了するまでに6時間以上かかったため、バックグランド実行などで実行した方が良いです。

    http://www.doxygen.jp
    http://www.graphviz.org/

    DoxygenでGraphvizインストールパスを設定し、出力したい相関パラメータやフォントを指定して実行しますが、GUIでの設定ファイル作成も可能ですので、マニュアルを調査しなくても、ウィザードに従って設定ファイルを作成すれば、すぐに使用することができます。

    13.3.  databaseリバース

    CloudStackはMySQLにデータを保存し動作します。テーブル数は100以上あり、ソースコードからの全部のテーブルを査読することは難しいです。MySQL workbenchを使用し、テーブル相関図を表示することでCloudStackのデザインの解析の助けとなります。MySQL Workbenchの取得URLは以下となります。

    http://www-jp.mysql.com/products/workbench/

    MySQL Workbenchから、「Database」メニュー → 「Reverse Engineer」 を選ぶ。MySQL Workbenchが、接続可能なデータベースに対して、create table情報を参照し、テーブル相関図を生成可能です。socket接続でもTCP/IP接続でも、データベースに接続できれば問題なく使えます。
    CloudStackはビューを使用せずテーブルのみを使用しているため、MySQL Workbenchで出力されるテーブル相関図のみを調査すれば、ある程度の用途が掴めます。以下の出力イメージを示します。下記の画像はCloudStack2.2.8のデータベース相関図となります。

    cloudstack 2.2.8 table schema

    14.       CloudStackコマンド実行

    GUIあるいはAPIからのコールによって、依頼された処理をコマンド実行により実現します。コマンドには以下の2種類があります。

    • 同期
    • 非同期

    各コマンド群は それぞれ、同期jobマネージャ及び非同期jobマネージャーが実行管理を行います。同期コマンドはキューイングされ、jobは順次処理されます。非同期コマンドは、実行を該当クラスにリクエストを送信しjobマネージャがjob終了までを監視します。
    APIの一部のコマンド履歴はMySQLデータベースのcloud.eventにレコードとして保存されます。該当テーブルを参照することにより、コマンド実行状況を確認できます。api.logにも同様にイベントログとして出力されます。データが取得されるコマンド(イベント)の一覧はCloudStack Administration Guide、15.1.3 Event Log Queriesに記述されています。
    CloudStack Administration Guideの15.2節には以下の記述があります。

    15.2 Working with Server Logs

    The CloudStack Management Server logs all web site, middle tier, and database activities for diagnostics purposes in /var/log/cloud/management/. The CloudStack logs a variety of error messages. We recommend this command to find the problematic output in the Management Server log:

    grep -i -E 'exc|unable|fail|invalid|leak|invalid|warn|error' /var/log/cloud/management/management-server.log

    The CloudStack processes requests with a Job ID. If you find an error in the logs and you are interested in debugging the issue you can grep for this job ID in the management server log. For example, suppose that you find the following ERROR message:

    2010-10-04 13:49:32,595 ERROR [cloud.vm.UserVmManagerImpl] (Job-Executor-11:job-

    1076) Unable to find any host for [User|i-8-42-VM-untagged]

    Note that the job ID is 1076. You can track back the events relating to job 1076 with the following grep:

    grep "job-1076)" management-server.log

    非同期コマンドはjob-idが振られ、該当idをトレースすれば、management-server.logからコマンド実行経過をトレースできます。job-idが振られると、データベース上のcloud.async_jobにレコードが追加されます。該当テーブルは定期的にクリーンアップされる模様です。( 該当テーブルにコマンド実行失敗の履歴が取得されている形式はありません。)
    コマンドが失敗した場合、特にデータベースにレコードが追加されるような処理、Host追加、Template追加、snapshot追加などの処理は失敗すると、ステータスがERRORのレコードとして追加されます。このエラーとなったレコードは、GUI上からは無効なレコードのため、情報は見えません。管理画面から見ると追加がされてないように見えます。内部的には、Management Serverがコマンド実行失敗を認識しても、再度エラーが発生しているレコードを収集し、コマンドを再試行しているように見えます。このリトライ処理はManagement Serverが再起動されても、MySQLにエラーとなったレコードが残っている限りリトライされる模様です。この場合、SQLで該当レコードを削除すれば、コマンド実行リトライは停止されますが、各テーブルのreference keyなどを充分確認の上、リスクを承知で実行する必要があります。基本的には参照キーに対してON DELETE CASCADE制約が付与されています。

    15.       CloudStackソースコード解析

    15.1.  Allocator

    CloudStack内でのAllocatorとは、 指定された仮想モジュールを実行可能環境のためにリソースロックするクラスです。ソースコードを確認すると以下のAllocatorが用意されています。

    • HostAllocator
    • IPAddressAllocator
    • PodAllocator
    • SecondaryStorageAllocator
    • StoragePoolAllocator
    • LocalStorageAllocator
    • ConsoleProxyBalanceAllocator

    Allocationの状況を確認したところ、以下のAllocation処理があると考えられます。

    • sequenceテーブルよりリソースIDを降順に割り振る
    • databaseに保存された情報からランダムに割り振る
    • Hypervisor API経由でHostとネゴシエーションしてリソースを割り当てる

    15.2.  Adapter

    Management ServerにおけるAdapter継承クラス一覧を以下に列挙します。InjectアノテーションによりAdapterクラスの依存性が決定される実装の外枠のクラスです。

    (com.cloud.utils.component.Adapter: interfaceクラス)

    com.cloud.acl.SecurityChecker
    com.cloud.alert.AlertAdapter
    com.cloud.cluster.agentlb.ClusterBasedAgentLoadBalancerPlanner
    com.cloud.cluster.ClusterServiceServletAdapter
    com.cloud.consoleproxy.ConsoleProxyAllocator
    com.cloud.deploy.DeploymentPlanner
    com.cloud.ha.FenceBuilder
    com.cloud.ha.Investigator
    com.cloud.hypervisor.HyperVisorGuru
    com.cloud.network.element.NetworkElement
    com.cloud.network.guru.NetworkGuru
    com.cloud.network.IpAddrAllocator
    com.cloud.network.vpn.RemoteAccessVpnElement
    com.cloud.resource.Discoverer
    com.cloud.resource.DiskPrepare
    com.cloud.resource.NetworkPrepare
    com.cloud.server.auth.UserAuthenticator
    com.cloud.storage.allocator.StoragePoolAllocator
    com.cloud.storage.secondary.SecondaryStorageVmAllocator
    com.cloud.storage.StorageGuru
    com.cloud.storage.StoragePoolDiscover
    com.cloud.storage.template.Processor
    com.cloud.storage.template.TemplateAdapter
    com.cloud.utils.backoff.BackoffAlgorithm

    (com.cloud.utils.component.AdapterBase: com.cloud.utils.component.Adapterを実装したクラス)

    com.cloud.acl.DomainChecker
    com.cloud.deploy.PlannerBase
    com.cloud.ha.CheckOnAgentInvestigator
    com.cloud.ha.RecreatableFencer
    com.cloud.ha.XenServerInvestigator
    com.cloud.hypervisor.HypervisorGuruBase
    com.cloud.network.element.DhcpElement
    com.cloud.network.element.OvsElement
    com.cloud.network.guru.DirectNetworkGuru
    com.cloud.network.guru.GuestNetworkGuru
    com.cloud.network.guru.PodBasedNetworkGuru
    com.cloud.network.guru.PublicNetworkGuru
    com.cloud.storage.allocator.AbstractStoragePoolAllocator

    15.3.  annotation

    annotation定義(Inject, DB, StateMachine)はcom.cloud.utils.componetクラスで実装されています。

    Target Annotation名 属性名 デフォルト値 必須/任意 内 容
    FIELD Inject adapter Adapter.class 必須 Adapterセッティング
    TYPE METHOD DB Txn TRUE 任意 Annotates a method or a class as using the in-memory transaction.  Running with assertions on, will find all classes that are not using this but is using in-memory transactions.There are only three circumstances where you should use this.1. Annotate method that starts and commits DB transactions.Transaction txn = Transaction.currentTxn();txn.start();...txn.commit();2. Annotate methods that uses a DAO’s acquire method._dao.acquireInLockTable(id);..._dao.releaseFromLockTable(id);3. Annotate methods that are inside a DAO but doesn’t use the Transaction class. Generally, these are methods that are utility methods for setting up searches. In this case use @DB(txn=false) to annotate the method.While this is not required, it helps when you’re debugging the code and it saves on method calls during runtime.(Optional) Specifies that the method does not use transaction.  This is useful for utility methods within DAO classes which are automatically marked with @DB. By marking txn=false, the method is not surrounded with transaction code.
    FIELD StateMachine DB state / eventセッティング

     

    javaxのannotation定義(SequenceGenerator)

    Target Annotation名 属性名 デフォルト値 必須/任意 内 容
    TYPE
    METHOD
    FIELD
    SequnceGenrator Name 必須 A unique generator name that can be referenced by one or more classes to be the generator for primary key values.
    sequenceName "" 任意 The name of the database sequence object from which to obtain primary key values. Defaults to a provider-chosen value.
    initialValue 1 任意 The value from which the sequence object is to start generating.
    allocationSize 50 任意 The amount to increment by when allocating  sequence numbers from the sequence.

     

    16.       まとめ

    本記事では、CloudStack Community Editionのsource code buildからbinary packageの比較、ソースコード解析、コマンド発行管理、Adapterクラス概要について述べました。CloudStackの開発はスピードが速くアップグレードも頻繁に実施されるため、本記事が合致しなくなる可能性もありますが、Adapterクラスや非同期コマンドの管理方法などは基本的な機能ですおで、今後も継承されていくと思わます。CloudStack Community Editionのソースコード全体量は膨大ですが、Adapterクラスなど機能追加がしやすいな設計構成となっており、Storage形式やHypervisor種別の拡張などのユーザの期待に応える用意がされています。今後もCloudStack Community Editionのソースコードを見てわかったことを随時アップデートする予定です。

    CL LAB Mail Magazine

    CL LABの情報を逃さずチェックしよう!

    メールアドレスを登録すると記事が投稿されるとメールで通知します。

    メールアドレス: 登録

    ※登録後メールに記載しているリンクをクリックして認証してください。

    Related post