Jenkins Groovyスクリプトコンソールをマスターする:高度なシステム管理
Jenkins管理の隠れた力をGroovyスクリプトコンソールで解放しましょう。この包括的なガイドでは、システム管理者向けに、一括設定更新、即時エージェント管理(切断/再接続)、実行中のビルドの強制中止など、複雑なタスクを瞬時に実行するための専門レベルの実用的なGroovyスクリプトを提供します。Jenkinsオブジェクトモデルと直接対話し、比類のない効率性とトラブルシューティング能力を身につけましょう。
Jenkins Groovyスクリプトコンソールをマスターする:高度なシステム管理
Jenkins Groovyスクリプトコンソールは、UIからはきれいに実行できない作業に役立ちます。スタックしたビルドの検索、エージェント状態の確認、ジョブ設定の検査、注意深く範囲を絞った一括変更などです。また、理解していないスクリプトを貼り付けると、Jenkinsコントローラーを損傷する最も簡単な方法の1つでもあります。
コンソールは、本番サーバーへのrootアクセスのように扱ってください。まず読み、変更しようとしているものを印刷し、可能であれば非本番コントローラーでテストし、その後にのみ書き込みを行ってください。
Jenkinsスクリプトコンソールを理解する
Jenkinsスクリプトコンソール(Manage Jenkins -> Script Console)は、Groovyを使用して実行中のJenkinsコントローラーのオブジェクトモデルに直接アクセスできます。ジョブ、ビルド、ノード、ビュー、資格情報メタデータ、プラグイン状態、その他多くのランタイムオブジェクトを検査できます。
なぜスクリプトコンソールを使うのか?
- 即時実行: ジョブのトリガーやパイプラインの開始を待たずに、スクリプトを即座に実行できます。
- システムデバッグ: GUIでは公開されていない内部状態、ログ、設定詳細にアクセスできます。
- 一括操作: 複数のジョブを変更したり、エージェントを再設定したり、インスタンス全体の古いデータを迅速にクリアできます。
- プロトタイプスクリプト: 共有ライブラリや宣言的パイプラインに組み込む前に、Groovyロジックをテストできます。
安全上の注意:直接アクセスの力
警告: コンソールで実行されるスクリプトは、Jenkinsマスター上で完全な管理権限で実行されます。不適切に記述されたスクリプトは、設定を破壊したり、ビルドを削除したり、Jenkinsインスタンスをクラッシュさせる可能性があります。複雑なスクリプトは、最初に非本番環境で徹底的にテストしてください。
必須のGroovyオブジェクトとAPIアクセス
コンソールの力は、コアJenkinsオブジェクトに直接アクセスできることにあります。これらのオブジェクトは、Groovy実行環境内で暗黙的に利用可能です:
Jenkins.instance:実行中のコントローラーを表すコアJenkinsシングルトンオブジェクト。Hudson:Jenkinsのエイリアス。Jenkins.instance.getItemByFullName('JobName'):特定のジョブにアクセスします。Jenkins.instance.getComputer('AgentName'):特定のエージェント(ノード)にアクセスします。
Jenkinsインスタンスへのアクセス
アクセス権があることを確認するには、最も簡単なコマンドはJenkinsバージョンを印刷することです:
println "Jenkins Version: ${Jenkins.instance.version}"
println "Running as user: ${Jenkins.instance.getAuthentication().getName()}"
現在のJenkinsリリースでは、Jenkins.instanceの代わりにJenkins.get()を使用する例を見ることがあります。どちらのパターンも実際のスクリプトで使用されています。新しいスクリプトでは、通常Jenkins.get()の方が明確です:
import jenkins.model.Jenkins
def jenkins = Jenkins.get()
println "Root URL: ${jenkins.getRootUrl()}"
実用的な管理スクリプト
以下は、スクリプトコンソールを介した高度な管理制御を示すいくつかの実用的なスクリプトです。
1. ジョブ設定の一括更新
このスクリプトは、既存のFreestyleジョブを反復処理し、説明にサフィックスを追加します。nullセーフな処理に注意してください。多くのジョブには説明がありません。
import hudson.model.FreeStyleProject
final String SUFFIX = " [Automated Update]"
def count = 0
Jenkins.instance.getAllItems(FreeStyleProject.class).each { job ->
def current = job.getDescription() ?: ""
if (!current.endsWith(SUFFIX)) {
job.setDescription(current + SUFFIX)
job.save()
println "Updated description for: ${job.getName()}"
count++
}
}
println "\nFinished. Total jobs updated: ${count}"
2. Jenkinsエージェント(ノード)の管理
管理者は、メンテナンスのためにエージェントをオフラインにしたり、誤動作しているノードを手動で切断したりする必要がよくあります。
エージェントの一時的な切断
このスクリプトはエージェントを切断し、新しいビルドがそのエージェントで開始されるのを防ぎますが、実行中のビルドは完了できます。
import hudson.model.Computer
final String AGENT_NAME = "my-specific-agent"
def agent = Jenkins.get().getComputer(AGENT_NAME)
if (agent) {
// 一時的にオフラインに設定
agent.setTemporarilyOffline(true, "Maintenance started by Admin Script.")
println "Agent '${AGENT_NAME}' set to temporarily offline."
} else {
println "Agent '${AGENT_NAME}' not found."
}
エージェントの強制オフラインと実行中のタスクの切断
エージェントを即座に停止する必要がある場合は、強制的にオフラインにし、実行中のビルドを切断できます。これにより、設定に応じてビルドは失敗または中止としてマークされます。
import hudson.model.Computer
final String AGENT_NAME = "unresponsive-node-01"
def agent = Jenkins.get().getComputer(AGENT_NAME)
if (agent) {
// 強制オフラインにし、実行中のタスクを即座に切断
agent.doDoDisconnect()
println "Agent '${AGENT_NAME}' forcefully disconnected."
} else {
println "Agent '${AGENT_NAME}' not found."
}
3. 実行中のビルドの操作
重要なビルドがスタックしたり、即座にキャンセルする必要がある場合、スクリプトコンソールが最速の方法を提供します。
特定の実行中ビルドの中止
完全なパス(例:PipelineJob/BuildNumber)で識別されるビルドを中止するには:
// 例:'CriticalDeploy'という名前のジョブのビルド#5を中止
final String JOB_NAME = "CriticalDeploy"
final int BUILD_NUMBER = 5
def job = Jenkins.get().getItemByFullName(JOB_NAME)
def build = job?.getBuild(BUILD_NUMBER)
if (build && build.isBuilding()) {
build.doCancel()
println "Build ${JOB_NAME}#${BUILD_NUMBER} has been cancelled."
} else {
println "Build ${JOB_NAME}#${BUILD_NUMBER} is not running or does not exist."
}
4. 古いビルドレコードのクリーンアップ
ディスク容量を管理するには、多くの場合、古いビルドを積極的に削除する必要があります。このスクリプトは、指定されたジョブの30日より古いすべてのビルドを識別して削除します。
import hudson.model.Job
import java.util.concurrent.TimeUnit
final String TARGET_JOB = "LegacyArchivingJob"
final int DAYS_TO_KEEP = 30
def job = Jenkins.get().getItemByFullName(TARGET_JOB)
if (job instanceof Job) {
long cutoffTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(DAYS_TO_KEEP)
int deletedCount = 0
job.getBuilds().each { build ->
if (build.getTimeInMillis() < cutoffTime) {
println "Deleting old build: ${build.getDisplayName()}"
build.delete()
deletedCount++
}
}
println "\nCleanup complete. Deleted ${deletedCount} builds for ${TARGET_JOB}."
} else {
println "Job '${TARGET_JOB}' not found or is not a standard Job type."
}
コンソールスクリプティングのベストプラクティス
システムレベルの変更を行う場合は、安定性を維持するために以下のベストプラクティスに従ってください:
.save()を使用する: 設定オブジェクト(JobやViewなど)を変更するたびに、Jenkins再起動後に変更を永続化するために、そのオブジェクトで.save()を必ず呼び出してください。設定は保存されるまでメモリ内にのみ保持されます。- オブジェクトの存在を確認する: ジョブ名やエージェント名を間違えた場合にコンソールがクラッシュするのを防ぐために、API呼び出しをチェック(
if (object)またはtry-catch)でラップしてください。 - 永続的なループを避ける: スクリプトは同期的に実行されます。すぐに完了することが確実でない限り、コンソールで長時間実行されるループやプロセスを直接実行しないでください。コンソールUIがブロックされます。
- 組み込みメソッドを活用する: Jenkins Groovyオブジェクトには、特定のヘルパーメソッド(
doCancel()やdoDoDisconnect()など)がよくあります。可能な場合は、内部状態を手動で操作しようとする代わりに、これらを使用してください。 - クワイエットモードを使用する(該当する場合): 過剰なビルドステータス更新を生成する一括操作を実行する場合、イベント通知機能を一時的に無効にすることを検討してください。ただし、これには通常、標準的な管理よりも深いシステムアクセスが必要です。
より安全なドライランパターン
一括変更の場合は、最初にドライランフラグを追加します:
import jenkins.model.Jenkins
import hudson.model.Job
final boolean DRY_RUN = true
final String MATCH = "legacy-"
Jenkins.get().getAllItems(Job.class).findAll { job ->
job.fullName.contains(MATCH)
}.each { job ->
println "${DRY_RUN ? 'Would update' : 'Updating'} ${job.fullName}"
if (!DRY_RUN) {
job.setDescription((job.getDescription() ?: "") + "\nReviewed during cleanup.")
job.save()
}
}
DRY_RUN = trueで一度実行し、出力を変更チケットにコピーし、その後falseでのみ実行します。この小さな習慣が、ほとんどの偶発的な広範な変更を防ぎます。
変更せずにジョブ設定を読む
コンソールは検索ツールとして最もよく使用されることがあります。例えば、古いGitホストをまだ参照しているパイプラインジョブを見つけるには:
import jenkins.model.Jenkins
import org.jenkinsci.plugins.workflow.job.WorkflowJob
final String NEEDLE = "git.old.example.com"
Jenkins.get().getAllItems(WorkflowJob.class).each { job ->
def definition = job.getDefinition()
def text = definition?.getScript()
if (text?.contains(NEEDLE)) {
println "Found ${NEEDLE} in ${job.fullName}"
}
}
この例は、インラインパイプラインスクリプトにのみ機能します。ジョブがSCMからJenkinsfileを使用している場合、Jenkinsはファイルの内容ではなくSCM定義を保存します。その違いは重要です。コンソールはJenkins設定を検査できますが、スクリプトが明示的に行わない限り、すべてのリモートリポジトリのすべてのブランチを魔法のように読み取ることはできません。
推測せずにスタックしたビルドを見つける
インシデント発生時、最初の質問は「今何が実行されているのか?」です。このスクリプトは、実行中のビルドをその期間とエグゼキュータとともに出力します:
import jenkins.model.Jenkins
Jenkins.get().getComputers().each { computer ->
computer.executors.each { executor ->
def executable = executor.currentExecutable
if (executable) {
def build = executable
println "${computer.displayName} :: ${build.fullDisplayName} :: ${build.durationString}"
}
}
}
これを最初に検査スクリプトとして使用します。何かを中止する必要がある場合は、古そうなものをすべてキャンセルするのではなく、既知の1つのビルドをターゲットにします。長時間実行されるデータベースマイグレーション、リリースジョブ、手動承認パイプラインは、外部からは「スタック」しているように見えることがあります。
パイプラインジョブの場合、ビルドが入力のために一時停止しているかどうかを検査することもできます:
import jenkins.model.Jenkins
import org.jenkinsci.plugins.workflow.job.WorkflowRun
import org.jenkinsci.plugins.workflow.support.steps.input.InputAction
Jenkins.get().getAllItems().each { job ->
job.builds?.findAll { it instanceof WorkflowRun && it.isBuilding() }?.each { run ->
def input = run.getAction(InputAction)
if (input) {
println "Waiting for input: ${run.fullDisplayName}"
}
}
}
これにより、意図的に承認を待っているデプロイメントを中止するという一般的な間違いを防ぎます。
プラグインとバージョンの検査
UIが遅い場合や、簡単なインベントリが必要な場合にコンソールが便利です。インストールされているプラグインとバージョンを出力します:
import jenkins.model.Jenkins
Jenkins.get().pluginManager.plugins
.sort { it.shortName }
.each { plugin ->
println "${plugin.shortName}:${plugin.version}"
}
スクリプトコンソールを管理されたプラグイン更新プロセスの代わりとして使用しないでください。プラグインのアップグレードは、ジョブの読み込み、パイプラインの動作、資格情報のバインディング、エージェント接続に影響を与える可能性があります。コンソールは、検査、緊急診断、または小さな対象を絞った修復に最適です。
書き込みスクリプトの前にバックアップ
.save()を呼び出したり、ビルドを削除したり、エージェントを切断したり、ジョブ定義を変更したりするスクリプトを実行する前に、$JENKINS_HOMEまたはコントローラーの管理対象設定ソースの最新のバックアップがあることを確認してください。JenkinsインスタンスがJCasC、Job DSL、Helm値、または別のGitバックアップシステムで設定されている場合、コンソール編集は次の調整で上書きされる可能性があることに注意してください。
そのような環境では、コンソールを使用して問題を確認し、その後、信頼できる情報源を修正します。コンソールのみの修正は緊急時には許容されますが、後で永続的な設定を更新できるように記録してください。
リモートスクリプトコンソールアクセス
多くの管理者はブラウザコンソールを知っていますが、JenkinsはCLIを介してGroovyを実行することもできます(そのアクセスが有効で許可されている場合):
java -jar jenkins-cli.jar -s https://jenkins.example.com/ groovy = < script.groovy
これは、Groovyファイルをバージョン管理に保持し、ピアレビューを実行し、承認された正確なコンテンツを実行できるため、レビューされたスクリプトに役立ちます。また、インシデントチケットで出力をキャプチャしやすくなります。
CLIやリモートスクリプト実行を軽率に有効にしないでください。スクリプトコンソールアクセスに必要な権限は非常に特権的です。信頼できる管理者に制限し、可能な場合は監査ログを使用し、短命な管理セッションを優先してください。組織がロールベースのアクセス制御を使用している場合、コンソールがロックダウンされていると想定する前に、実際にOverall/Administerまたは同等の権限を持っている人を確認してください。
繰り返しのメンテナンスには、制御されたパラメータでレビューされたスクリプトを実行するJenkinsジョブが、アドホックなブラウザコンソール作業よりも優れていることがよくあります。コンソールは緊急ツールのままです。バージョン管理された自動化が、繰り返すと予想されるタスクを処理する必要があります。
リモートスクリプトを実行する前に、出力の先頭にJenkins URLと現在の認証名を出力してください。基本的なことですが、最大の間違いを防ぎます。間違ったコントローラーや間違ったアカウントで本番修復を実行することです。
import jenkins.model.Jenkins
def j = Jenkins.get()
println "Controller: ${j.getRootUrl()}"
println "User: ${j.getAuthentication().getName()}"
コンソールに入れるべきでないもの
長時間スリープするスクリプト、永久にポーリングするスクリプト、大きなリモートファイルをダウンロードするスクリプト、広範なファイルシステム削除を実行するスクリプトは避けてください。コンソールはコントローラープロセス内で実行されます。スクリプトがCPUを消費したり、スレッドをブロックしたり、メモリを満たしたりすると、CIシステム自体に影響を与えます。
また、シークレットを出力することも避けてください。Jenkinsの資格情報オブジェクトは意図的に保護されていますが、管理者は機密資料を公開するスクリプトを作成することもできます。資格情報を監査する必要がある場合は、ID、説明、ドメイン、使用状況参照を出力してください。ブラウザ、ビルドログ、チャットにシークレット値を出力しないでください。
最良のコンソールスクリプトは、短く、退屈で、元に戻せるものです。状態を検査したり、狭い修復を実行したり、既知の管理タスクを自動化したりするために使用してください。スクリプトがテストを必要とするほど長くなった場合は、通常のコードのようにレビューできる共有管理リポジトリまたはJenkins管理ジョブに移動してください。