Chef Provisioningを使ってUbuntuのVSO Agentサーバをプロビジョニングしてみる
Visual Studio Onlineのビルド機能は結構イケてる。想像もしていなかったが、実はWindows系だけではなく、UbuntuとかMacなどのビルドもできたりする。
ただし、Ubuntuでビルドしたい場合、Visual Studio Online(以下VSO)のエージェントを入れたビルドサーバーを作る必要がある。本番でAgentを入れたビルドサーバーをいちいちつくるのは面倒だ。
そこで、今回、AzureとChef-Provisioningを使って実際にあるプロジェクトのビルドサーバーを自動でプロビジョニングするコードを書いてみた。
今回題材につかっているのはHOL - Parts Unlimited MRP App Continuous Integration with Visual Studio Online Build
にあるこのプロジェクトをVSOをつかってビルドする手順をUbuntuのバーチャルマシンを払い出すところから自動化してみることにした。
- 事前準備
さて、事前準備としてはAzure CLIがインストールされて、設定されている必要がある。この詳細は次のページに書いておいたので参考にされたい。
Azure CLIとChefを始めるためのSubscription関係まとめ
ほかにはGitが必要だ。この手順はWindows10で作成したが、コマンドラインのコマンドはMacも同じなので、読みかえていただければMacでも動作すると思われる。
- ChefDKのインストール
これもWindows10の手順だが、ChefDKをインストールしたい。
まずその前にWindowsでもコマンドラインでごそごそといろいろ入れられるツールChocolateyをぶち込んでおいて、それでChefDKも入れてみよう。
2.1. Chocolatelyをダウンロードする
Chocolateyのページにいくつかの方法が載っている。私はcmd.exeを管理者モードで動かして、次のコマンドを打つ方法を採用した。
@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin
2.2. ChefDKのインストール
このツールを入れておくと、次のようにコマンドラインでいろいろなアプリケーションをインストールできる。たまにバージョンが古いものしかない場合もあるので注意が必要だ。Chocolateyのページでバージョンがご希望のものか検索してみよう。
SETXコマンドでパスを永続的に設定できるようだ。入れただけだとパスは通らない様子。
> choco install chefdk -y
> SETX /M PATH "%PATH%;c:\opscode\chefdk\bin"
> SETX /M PATH "%PATH%;c:\opscode\chefdk\embedded\bin"
> SET PATH=%PATH%;c:\opscode\chefdk\bin;c:\opscode\chefdk\embedded\bin"
2.3. Chef-Provisioning-Azureのインストール
次にChef-Provisioning-Azureのドライバをぶち込む。
> chef gem install chef-provisioning-azure
これで準備完了。ちなみに、本日2015/9/10にアップデートがかかって、Ubuntuのユーザ名の指定が失敗するバグが解決されているので、ぜひアップデートされたい。
以前にインストールしている人は次のコマンドでアップデート可能だ。
> chef gem update chef-provisioning-azure
- Chefのレシピとプロビジョニング用のコードを書く
3.1. サンプルコードとその実行
さて、今回のコードはすべてGitHubにあげておいたので、皆様もクローンして、ちょっと変えたらいつでも動作するだろう。
Visual Studio Online Build Agent Machine for Ubuntu
Gitがインストールされているなら、次のコマンドで取得できる
> git clone https://github.com/TsuyoshiUshio/vsoagentserver.git
ぜひ楽しんでください。そのままだと、私のサーバーとかぶってしまうので、provisioning.rbと、attributes/defailt.rbを適当に変更して動かしてみてください。
provisioning.rb
のvm_name, cloud_service_nameあたりを変えるだけで動くだろう。
ちなみにプロビジョニングさせるコマンドは次の通り。
> cd vsoagentserver
> chef-client --local provision.rb
[chef-client](https://docs.chef.io/chef_client.html)
ここでは割愛するがHOL - Parts Unlimited MRP App Continuous Integration with Visual Studio Online Buildにあるとおり、
VSO側の手順が存在するので、それを実施したのちサーバーにログインし次のコマンドを打つ。ちなみにサーバーのパスワードは、provision.rbに書いてあるのでこれも変えておくとよい。
> ssh azureuser@pendricachefdemo01.cloudapp.net
> cd myagent
> node agent/vsoagent
ちなみにこのサンプルコードを作成するにあたっては、ChefDKをインストールしたのち
> chef generate repo partsmrp
> cd partsmrp
> git init
> git add .
といった手順でテンプレートを生成したところから、始まる。これから、書き換えた部分のみを簡単に解説していく。
3.2. コードの解説
3.2.1. Provisionファイル
Chef Provisionは、既に存在するマシンにプロビジョニングをするだけではなく、マシンの生成事態も、コードで実施できるイケメンだ。
様々なドライバが存在するが、Azureの場合はchef-provisioning-azureだ。
これは先ほどの手順ですでにインストール済みである。
次のコードはリポジトリのトップにあるprovisioning.rbだ。これが今回の起点となるコードだ。中身をみてみよう。
machine_optionsに様々なVMを上げるときのオプションが書いている。これらを指定できる。大体みていただければなんとなくお分かりかと思うが、
ポイントのみ解説したい。:passwordと:vm_userは各セクションが違うので注意。:vm_userは指定しないとデフォルトはubuntu
になる。これはVMへsshログインするときのユーザに
なっている。vm_sizeで指定できる値はNew D-Series Virtual Machine Sizesで確認可能。
:locationで指定可能な値はazure vm location list
コマンドで確認できる。そして、machineリソースでmachine_optionsで指定した内容でマシンを起動する。
次のポイントはrecipeでVM起動後に実行されているレシピを指定しているところにある。ちなみに今はclassicVMにしか対応していない。
またvm_sizeの小さいものはSmall
が指定できてエコノミーだが、プロビジョニングするときに遅すぎてタイムアウトしたりするケースがあったので、多少強力なマシンにしている。
プロビジョニング後に小さなものに変えると安くていいだろう。
require 'chef/provisioning/azure_driver'
with_driver 'azure'
machine_options = {
:bootstrap_options => {
:cloud_service_name => 'pendricachefdemo01',
:storage_account_name => 'pendricachefdemo01',
:vm_size => "Standard_D11",
:vm_user => "azureuser",
:location => 'East Asia'
},
:image_id => 'b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_2-LTS-amd64-server-20150706-en-us-30GB',
:vm_name => "penchefdemo01",
:password => "P2ssw0rd"
}
machine 'penchefdemo01' do
machine_options machine_options
recipe 'vsoagent'
converge true
end
3.2.2. VSO agent cookbookのレシピ
レシピは、起動したVMに対してツールをインストールするなどの指定をする。今回のコードは次の通り。
cookbooks/vsoagent/recipes/default.rb
これもコードを切り取って少しづつ解説したい。基本的にChefのいいところは冪等性を保証してくれているところだ。
つまり、何回プロビジョニングしても同じ状態になってくれる。例えば前回途中までしかいかなかったとすると、途中までの分はうまくスキップ
してくれたりして、常に同じ状態になってくれる。これは地味に便利だ。ところで、本当はpackageのインストールの周りはまとめることができる。なぜまとめてないかというと、まとめるとしょぼいマシンをつかったときにタイムアウトが発生したからだ orz
execute "add-apt-repository ppa:openjdk-r/ppa" do
user "root"
end
execute "apt-get update" do
user "root"
end
package "gradle" do
action :install
end
package "openjdk-8-jdk" do
action :install
end
package "openjdk-8-jre" do
action :install
end
package "mongodb" do
action :install
end
execute "curl --silent --location https://deb.nodesource.com/setup_0.12 | sudo bash -" do
user "root"
end
package "nodejs" do
action :install
end
execute "/usr/bin/npm install vsoagent-installer -g" do
user "root"
end
directory "/home/#{node["vsoagent"]["vm_user"]}/myagent" do
owner node["vsoagent"]["vm_user"]
group node["vsoagent"]["vm_group"]
mode '0755'
action :create
end
execute "/usr/bin/vsoagent-installer" do
cwd "/home/#{node["vsoagent"]["vm_user"]}/myagent"
user node["vsoagent"]["vm_user"]
group node["vsoagent"]["vm_group"]
not_if {File.exist?("/home/#{node["vsoagent"]["vm_user"]}/myagent/agent")}
end
template "/home/#{node["vsoagent"]["vm_user"]}/.bash_profile" do
mode '0664'
source '.bash_profile.erb'
user node["vsoagent"]["vm_user"]
group node["vsoagent"]["vm_group"]
not_if { File.exist?("/home/#{node["vsoagent"]["vm_user"]}/.bash_profile")}
end
template "/home/#{node["vsoagent"]["vm_user"]}/myagent/.agent" do
mode '0664'
source '.agent.erb'
user node["vsoagent"]["vm_user"]
group node["vsoagent"]["vm_group"]
not_if { File.exists?("/home/#{node["vsoagent"]["vm_user"]}/myagent/.agent")}
end
3.2.2.1. executeリソース
これは、普通に対象サーバーでコマンドを実行してくれるものだ。後述するが、冪等性をカバーするために、工夫が必要
何回も実行されても影響のないコマンドなら気にしなくてよい。
execute "add-apt-repository ppa:openjdk-r/ppa" do
user "root"
end
3.2.2.2. packageリソース
これは、パッケージをインストールするためのもの。Ubuntuの場合apt-getが実行される。Yes-Noの確認をすることをスキップする-y
は指定される。
package "mongodb" do
action :install
end
3.2.2.3. attributes
レシピを書くときに変数を使いたいケースがある。そのような場合次のように書ける。
directory "/home/#{node["vsoagent"]["vm_user"]}/myagent" do
owner node["vsoagent"]["vm_user"]
group node["vsoagent"]["vm_group"]
mode '0755'
action :create
end
これらの変数はもとはcookbook内にあるcookbooks/vsoagent/attributes/default.rbで定義されている。これはデフォルト値のようなものなので設定によっては上書き可能だ。
default["vsoagent"]["pool_name"] = "demo01pool"
default["vsoagent"]["server_url"] = "https://devopsjapan.visualstudio.com"
default["vsoagent"]["agent_name"] = "demo01agent"
default["vsoagent"]["vm_user"] = "azureuser"
default["vsoagent"]["vm_group"] = "azureuser"
3.2.2.4. templateリソース
最後にResource Template。これは、何らかのファイルを書きたいときによく使う。
template "/home/#{node["vsoagent"]["vm_user"]}/.bash_profile" do
mode '0664'
source '.bash_profile.erb'
user node["vsoagent"]["vm_user"]
group node["vsoagent"]["vm_group"]
not_if { File.exist?("/home/#{node["vsoagent"]["vm_user"]}/.bash_profile")}
end
これは、sourceのところで指定されたテンプレートの中身でファイルを作る。この例ではcookbooks/vsoagent/templates/default/.bash_profile.erbのテンプレートが適用されて、bash_profileが作られる内容はこうだ
# Set environment variables for Java
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export PATH=$PATH:/usr/lib/jvm/java-8-openjdk-amd64/bin
もちろん、テンプレートにも変数は使える。ほかの.agentなどのコードを見てほしい。
- 今回苦労したところ
自動化野郎としては、エージェントのサービス起動まで自動化したかったところだ。VSOエージェントは今のままだと、どうしても手作業が入ってしまう。VSOエージェントのページをみると、実はサービスで動かすモードがある
らしい。これは熱い!!!Microsoft Cross Platform Build Agentしかし、残念ながらこのページを見ると、MacではあるがUbuntuでのサポートはまだのようだ。無念。
次の作戦はDockerHubだ!DockerHubは自動化野郎の心のふるさと。こまったらここだ。しっかりとjgarverick/vsoagentなんてのがある。
これ使ったらDockerだといちころよね。しかも、Dockerfileみると設定がわかる!というわけで見てみよう。Dockerfile
:
RUN mkdir /opt/vsoagent
RUN mkdir /opt/vsoagent/_work
WORKDIR /opt/vsoagent
RUN vsoagent-installer
WORKDIR /opt/vsoagent/agent
COPY agent.config /opt/vsoagent/agent/.agent
RUN chown -R vsoservice /opt/vsoagent
:
世の中あまくなかった。.agentファイルに、visual studio onlineを指定する場所などを記載するところがあり、node agent/vsoagent
を初回起動したときにいろいろごねごね入れる手順のみが
省けるようで、今のままだと自分のエイリアスユーザコードとパスワードを入力するのは避けられない。対話式のコマンドをスクリプト化する方法みたいな方法でなんとかなるのかな。
でも本質的にはサービス化待ちのほうが本番はいいですね。
- おわりに
というわけで、最後の一歩だけまだですが、Azure + Chef ProvisiningでがっつりVM生成、プロビジョニングを決めてVSOエージェントのビルドサーバーを作れたのでよしとしましょう。
ワンコマンドでエージェント動作します。こういうインフラストラクチャのコード化をしていると、いつでも安心して環境を再構築できるので、ほんま気が楽ですね!
ではよいコーディングを!