2012/09/12

Jenkins新規ジョブに所要時間を設定する

Jenkinsのジョブは繰り返しビルドされることが前提。2回目からどれくらいで終了するかプログレス表示される。これが精神衛生上とってもいいわけだけど、前回の結果が分からないと表示されない。

録画システムで使用しているJenkinsのビルド所要時間を最初に設定できないかな?

録画ジョブは性質上1回実行したらお役御免。同じ日時の2回目はない。だけど、どれくらいで終了するかを表示させたい。どうにか出来ないかな?ということでやってみた。

思惑
録画時間は分かっている。エンコード時間も予測できる。時間予測は可能だ。
Jenkinsが過去にビルドした情報として扱ってくれれば出るはず。

所要時間予測
今までの実績から算出すると録画時間に対しての所要時間は2.2倍程度かな。
番組時間が30分程度の場合は1.7〜2.2位、2、3時間の場合は2.2〜2.4位。
単純に、ビルド予想時間=番組時間 * 2.2 ってことでいいかな。
沢山のサンプリングデータから統計を取って非線形計算が出来れば精度は上がるだろうね。

ビルドデータ
#{JENKINS_HOME}/jobs/#{ジョブ名}/builds/#{ビルド日時}/build.xml
に、このビルドについての情報が記述されている。この中で重要なのが、durationタグだ。
Jenkinsは前回のビルドのこの情報をもとにメーター表示しているようだ。
その一例
<?xml version='1.0' encoding='UTF-8'?>
<build>
  <actions>
    <hudson.model.CauseAction>
      <causes>
        <hudson.triggers.TimerTrigger_-TimerTriggerCause/>
      </causes>
    </hudson.model.CauseAction>
  </actions>
  <number>1</number>
  <result>SUCCESS</result>
  <duration>3977984</duration>
  <charset>US-ASCII</charset>
  <keepLog>false</keepLog>
  <builtOn></builtOn>
  <workspace>/var/lib/jenkins/jobs/record_14147/workspace</workspace>
  <hudsonVersion>1.424.6</hudsonVersion>
  <scm class="hudson.scm.NullChangeLogParser"/>
  <culprits/>
</build>

適当なジョブを作って、適当なビルド結果をコピーしてどうなるか試してみた。
Jenkinsのシステム設定からリロードをしたら、情報を拾ってくれて反映した。
色々やって、最低限必要な情報のみにしたbuild.xmlは以下のようになった。
<build>
 <number>0</number>
 <result>SUCCESS</result>
 <duration>#{duration}</duration>
</build>

ビルド番号は0番でいけた。最初のビルドが1番っていう約束事が崩れないで済みそう。

APIじゃ出来なそう
ビルドディレクトリとファイルをあらかじめ作成してしまえばいいわけだけれど、REST-APIじゃ出来ないみたい。Jenkinsプラグインなら?ってちょっと調べたけど、ジョブ作成フックみたいな切り口がないようで断念。Redmineのチケット編集フックみたいに出来ればよかったんだけれど。

パーミッションをどうするかなぁ
Jenkins側を拡張する方法がだめっぽいので外部タスクで実行することになる。そうすると、その辺のディレクトリパスはJenkinsユーザだぜっていうパーミッションが絡んでくる。選択肢はいくつか考えられる。

1. Jenkins側をを実行タスクと同じユーザにする
この場合、redmine(rails)ユーザと同じwww-dataにするってことか。

Jenkinsが扱うパス/ファイルオーナー’をwww-dataにする。
/etc/default/jenkinsファイルのJENKINS_USERをwww-dataにする。

2. 実行タスク側をJenkinsと同じjenkinsにする
Redmine(rails)実行ユーザをJenkinsにするってことか。

redmineが扱うパス/ファイルオーナーをjenkinsにする。
結果、passengerを通してredmine(rails)が、redmine/config/environment.rbのオーナーで実行されることになる。

もしくは、redmine/filesとredmine/public/videosオーナーをjenkinsにして
apacheのpassengerオプションで
PassengerUserSwitching off
PassengerDefaultUser jenkins
を追加記述する。

3. Jenkinsをソースからいじって、誰でも書き込めるようにする
これはだめでしょ。

4. ビルド#0を作成する処理をJenkinsジョブにしてリモート実行する
これかなぁ。

1と2は簡単だし普通はこれかなぁと思うけど、システム依存だし保守性(オリジナルやデフォルトを極力変更しない)が落ちてしまうなぁ。3は可能だけど論外。最後の4の方法でやることにした。

hudson-remote-apiを使ってjob作成&実行するrubyスクリプト
/opt/task/prebuild.rb
#!/usr/bin/ruby                                                                                     
# -*- coding: utf-8 -*-                                                                             

require 'hudson-remote-api'

# Jenkins                                                                                           
Hudson.settings = {:url => 'http://localhost/jenkins', :crumb => false }

def create_estimated_build record_job, record_sec

  # estimated duration                                                                              
  duration = (1000 * record_sec * 2.2).round

  # build datetime                                                                                  
  build_name = Time.now.strftime("%Y-%m-%d_%H-%M-00")

  puts "create_estimated_build: #{record_job} #{record_sec} #{duration} #{build_name}"

  build_dir = "/var/lib/jenkins/jobs/#{record_job}/builds/#{build_name}"
  build_xml = "<build><number>0</number><result>SUCCESS</result><duration>#{duration}</duration></build>"

  # create estimated build                                                                          
  Dir::mkdir(build_dir)
  xml = File.open(build_dir+"/build.xml",'w')
  xml.puts build_xml
  xml.close

  # reload jenkins                                                                                  
  system("curl %s/reload" % Hudson[:url])
end

# MAIN START                                                                                        

abort "no arguments" if ARGV.size<2
job_name = ARGV[0]
rec_time = ARGV[1]
create_estimated_build job_name, rec_time.to_i
build#0として必要最低限の情報を記述したbuild.xmlを今の時間に実行したっていうビルドディレクトリに作成する処理。最後にJenkinsのリロードをcurlを使って実行する。
hudson-remote-apiではreload-APIがなかったので、こんななった。

以前の記事で作成した
/opt/task/video-jobs.rbに以下のメソッドを追加
def remote_estimated_build record_job, record_sec
  puts "remote_estimated_build"

  job_name = "prebuild_#{record_job}"
  job = Hudson::Job.create job_name

  command = "/opt/task/prebuild.rb #{record_job} #{record_sec}"

  config = REXML::Document.new job.config
  element = config.elements['/project/builders'].add_element 'hudson.tasks.Shell'
  element.add_element('command').add_text command
  job.update config.to_s
  job.build
#  job.wait_for_build_to_finish                                                                     
#  job.delete                                                                                       
end

create_jobメソッドの最後に以下の1行を追加
remote_estimated_build jobname, info[:duration]

このcreate_jobはJenkinsジョブとして動いている自動予約スクリプトから呼ばれたり、Redmineフックスクリプトから呼ばれたりする。

出来てしまえば簡単なのだけれど、最初のアプローチで、どうやってJenkinsシステムに分かってもらえるのかをあれこれ調べるのが手間がかかったかな。

実際に活用してみると、録画処理が後どれくらいで終わるのかが視覚的に分かるようになったので、「なんか走ってるなぁ」じゃなくて「後60分くらいかぁ」ってなるので気分がすっきりする。

0 件のコメント:

コメントを投稿