Jenkinsパイプライン構文:初心者向けの総合ガイド

この初心者向け総合ガイドで、Jenkinsパイプライン構文の謎を解き明かしましょう。エージェント、ステージ、ステップ、ポストアクション、環境、パラメーター、ベストプラクティスなど、DeclarativeおよびScriptedパイプラインの基本を学びます。実践的な例と実用的なアドバイスにより、堅牢なCI/CDワークフローを効果的に構築する力を身につけましょう。

Jenkins パイプライン構文:初心者のための包括ガイド

Jenkinsパイプライン構文を使うと、ビルド、テスト、デプロイのプロセスをコードと一緒に管理するJenkinsfileに記述できます。これにより、パイプラインの変更をアプリケーションコードと同様にレビュー、バージョン管理、ロールバックできるようになります。

Jenkinsパイプラインが初めてなら、Declarativeパイプライン構文から始めましょう。構造化されていて読みやすく、Groovyの知識をあまり必要とせずにほとんどのCI/CDワークフローをカバーできます。

Declarative vs. Scripted パイプライン

Jenkinsは2つのパイプラインスタイルをサポートしています。

Declarativeパイプラインは、厳格なpipeline { ... }構造を使用します。Jenkinsがファイルの形状を検証し、UIでステージをきれいに表示できるため、ほとんどのチームにとって最適なデフォルトです。

Scriptedパイプラインは、node { ... }ブロック内でGroovyを使用します。より多くの制御が可能ですが、チームがGroovyに慣れていないと、保守が難しいパイプラインを作成しやすくなります。

まずはDeclarativeを使用しましょう。Declarativeでうまく表現できないロジックが必要な場合にのみ、Scriptedを検討してください。

最小限のDeclarativeパイプライン

基本的なJenkinsfileには、エージェント、ステージ、ステップが含まれます:

pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'アプリケーションをビルド中'
                sh 'mvn clean package'
            }
        }

        stage('Test') {
            steps {
                sh 'mvn test'
            }
        }
    }
}

agent anyは、利用可能な任意のエージェントでパイプラインを実行するようJenkinsに指示します。各stageはJenkins UIに個別に表示されます。stepsブロックには、Jenkinsが実行するコマンドが含まれます。

Windowsエージェントでは、shの代わりにbatを使用します:

bat 'gradlew.bat test'

agent ディレクティブ

agentディレクティブは、パイプラインまたはステージが実行される場所を決定します。

利用可能な任意のエージェントで実行:

agent any

特定のラベルを持つエージェントで実行:

agent { label 'linux && docker' }

グローバルエージェントをスキップし、ステージごとに選択:

pipeline {
    agent none

    stages {
        stage('Build') {
            agent { label 'linux' }
            steps {
                sh './build.sh'
            }
        }
    }
}

agent noneは、異なるステージで異なる環境(例:ビルド用のLinuxとインストーラーテスト用のWindows)が必要な場合に便利です。

ステージとステップ

ステージを使用して、パイプラインを開発者が一目で理解できる作業単位(チェックアウト、ビルド、テスト、パッケージ、デプロイ)に分割します。

stages {
    stage('Checkout') {
        steps {
            checkout scm
        }
    }

    stage('Unit Tests') {
        steps {
            sh 'npm test'
        }
    }
}

ステージには意味のある名前を付けましょう。Runという名前のステージに50個のコマンドがあるとデバッグが困難です。20個の小さなステージがあるパイプラインはノイズが多くなります。実際のワークフローのチェックポイントに合ったステージを目指しましょう。

環境変数

複数のステージで必要な値にはenvironmentを使用します:

pipeline {
    agent any

    environment {
        APP_NAME = 'orders-api'
        IMAGE = "registry.example.com/team/orders-api:${env.BUILD_NUMBER}"
    }

    stages {
        stage('Build Image') {
            steps {
                sh 'docker build -t "$IMAGE" .'
            }
        }
    }
}

Groovy文字列では、env.APP_NAMEを通じて変数を読み取れます。シェルステップでは、Jenkinsが環境変数をエクスポートするため、$APP_NAME$IMAGEがシェル内で機能します。

シークレットはenvironmentに置かないでください。Jenkins CredentialsとwithCredentialsを使用してください。

パラメータ

パラメータを使用すると、手動でビルドを開始する際に値を選択できます:

pipeline {
    agent any

    parameters {
        string(name: 'BRANCH_NAME', defaultValue: 'main', description: 'ビルドするGitブランチ')
        booleanParam(name: 'RUN_TESTS', defaultValue: true, description: 'ユニットテストを実行')
        choice(name: 'DEPLOY_ENV', choices: ['dev', 'staging', 'prod'], description: 'ターゲット環境')
    }

    stages {
        stage('Show Inputs') {
            steps {
                echo "ブランチ: ${params.BRANCH_NAME}"
                echo "デプロイターゲット: ${params.DEPLOY_ENV}"
            }
        }
    }
}

実行ごとに変わる可能性があるものにはパラメータを使用します。本番環境の変更をバイパスするためにパラメータを使用しないでください。

when による条件付きステージ

whenディレクティブは、ステージを実行するかどうかを制御します:

stage('Deploy to Production') {
    when {
        branch 'main'
    }
    steps {
        sh './deploy-prod.sh'
    }
}

式を使用することもできます:

stage('Test') {
    when {
        expression { return params.RUN_TESTS }
    }
    steps {
        sh 'npm test'
    }
}

whenはステージに配置し、stepsの中には配置しないでください。

ポストアクション

postブロックは、ステージまたはパイプラインの完了後に実行されます。テストレポートの公開、ビルド成果物のアーカイブ、クリーンアップに便利です。

pipeline {
    agent any

    stages {
        stage('Test') {
            steps {
                sh 'mvn test'
            }
            post {
                always {
                    junit 'target/surefire-reports/*.xml'
                }
            }
        }
    }

    post {
        failure {
            echo 'パイプラインが失敗しました'
        }
        always {
            cleanWs()
        }
    }
}

junitはテストが失敗してもテスト結果を公開します。cleanWs()はWorkspace Cleanupプラグインに由来するため、使用する前にそのプラグインをインストールしてください。

オプション

optionsを使用してパイプラインの動作を設定します:

pipeline {
    agent any

    options {
        timestamps()
        timeout(time: 30, unit: 'MINUTES')
        disableConcurrentBuilds()
        buildDiscarder(logRotator(numToKeepStr: '20'))
    }

    stages {
        stage('Build') {
            steps {
                sh './build.sh'
            }
        }
    }
}

これらのオプションにより、ログが読みやすくなり、ハングしたビルドを停止し、同じジョブの重複実行を防ぎ、古いビルド履歴を制限します。

実践的な初心者向けパイプライン

この例では、コードのチェックアウト、Javaプロジェクトのビルド、テスト結果の公開、パッケージ化されたJARのアーカイブを行います:

pipeline {
    agent { label 'linux' }

    options {
        timestamps()
        timeout(time: 30, unit: 'MINUTES')
        disableConcurrentBuilds()
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Build') {
            steps {
                sh 'mvn clean package'
            }
        }

        stage('Publish') {
            steps {
                junit 'target/surefire-reports/*.xml'
                archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
            }
        }
    }
}

これは、巨大なデプロイパイプラインよりも良い出発点です。ビルドが信頼できるようになったら、イメージ公開、セキュリティスキャン、デプロイメントステージを追加してください。

Scriptedパイプラインの基本

シンプルなScriptedパイプラインは次のようになります:

node('linux') {
    stage('Checkout') {
        checkout scm
    }

    stage('Build') {
        sh 'mvn clean package'
    }
}

Declarativeのpipeline {}ブロックをScriptedパイプライン内にラップしないでください。Declarativeのscript { ... }ブロック内に小さなScriptedセクションを混在させる具体的でテスト済みの理由がない限り、2つのスタイルは分けてください。

良いパイプラインの習慣

Jenkinsfileをソース管理に保存してください。シークレットはJenkins Credentialsに保管してください。ステージには、その作業内容に合った名前を付けてください。レポートはpostブロックで公開し、失敗しても有用な証拠が残るようにしてください。ビルドが一晩中ハングしないようにタイムアウトを追加してください。

初心者のパイプラインの問題のほとんどは、暗黙の前提から生じます:あるエージェントにはツールが存在するが別のエージェントにはない、シークレットがハードコードされている、レポートが成功時のみ公開されるなど。これらの前提をパイプラインで明示的にすることで、Jenkinsははるかに信頼しやすくなります。