2026年7月17日
2026年7月17日
GitLab CI/CDのパイプライン設定方法(.gitlab-ci.ymlの基本からデプロイまで)
GitLab CI/CD のパイプラインは、リポジトリ直下の .gitlab-ci.yml にステージとジョブを書くだけで動き始めます。この記事では、WordPress テーマを例に「構文チェック → テスト → 手動承認つきの本番デプロイ」という流れを組み立てながら、rules・needs・cache・artifacts・CI/CD 変数の使い分けを、つまずきやすい点と一緒に解説します。Docker イメージのビルドとレジストリへの登録は別の記事で扱うので、ここではパイプラインの骨組みに集中します。
パイプラインの構成要素を押さえる
最初に用語を整理しておくと、後の設定が読みやすくなります。
- ジョブ: 実行の最小単位です。
scriptに書いたコマンドを Runner が順に実行します。 - ステージ: ジョブのグループです。既定では同じステージのジョブは並列に、ステージ同士は順番に実行されます。
- パイプライン: プッシュやマージリクエストをきっかけに作られる、ステージとジョブのまとまりです。
- Runner: ジョブを実際に実行するエージェントです。GitLab.com では GitLab が用意する Runner を使えるほか、自分のサーバーに
gitlab-runnerを入れて登録することもできます。
stages を書かない場合、既定の build・test・deploy が使われます(前後に .pre / .post もあります)。順番を明示したほうが読み手に親切なので、本記事では毎回書きます。
最小構成で動かしてみる
まずは構文チェックだけのパイプラインです。PHP の公式イメージで php -l を全ファイルに実行します。
stages:
- lint
- test
- deploy
php-lint:
stage: lint
image: php:8.3-cli
script:
- find . -name '*.php' -not -path './vendor/*' -print0 | xargs -0 -n1 php -l
コミットしてプッシュすると、プロジェクトの「Build」→「Pipelines」付近(メニュー名はバージョンで変わります)にパイプラインが表示されます。YAML の書き間違いはプッシュ前にパイプラインエディタの検証機能で確認できるので、慣れないうちは活用してください。
image はジョブごとに指定できます。指定が無い場合は Runner 側の既定イメージが使われるため、Runner によって結果が変わる原因になります。ジョブごとに明示しておくのがおすすめです。
いつ動かすかを rules で決める
以前は only / except が使われていましたが、現在は rules が推奨です。rules は上から順に評価され、最初に一致した条件が適用されます。
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH
この workflow: rules は、マージリクエストを開いているブランチにプッシュしたときに「ブランチ用」と「マージリクエスト用」の 2 本のパイプラインが重複して走るのを防ぐ、よく使われる書き方です。重複パイプラインは Runner の時間を無駄にするだけでなく、デプロイジョブが二重に動く原因にもなります。
ジョブ単位でも rules を書けます。たとえばデプロイはデフォルトブランチだけで動かします。
deploy-production:
stage: deploy
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
when: manual にすると、パイプライン画面にボタンが表示され、人が押すまで実行されません。本番反映の直前に人の確認を挟みたいときに使います。
依存関係のキャッシュと成果物の受け渡し
GitLab では「キャッシュ」と「アーティファクト」が別物で、ここを混同するとつまずきます。
- cache: 次回以降のパイプラインを速くするための一時保存です。消えても再取得できるもの(
vendor/やnode_modules/)に使います。存在する保証はありません。 - artifacts: 同じパイプラインの後続ジョブに渡すための成果物です。ビルド済みの CSS や配布用 zip など、後のジョブが前提にするものはこちらです。
variables:
COMPOSER_CACHE_DIR: "$CI_PROJECT_DIR/.composer-cache"
phpcs:
stage: test
image: composer:2
cache:
key:
files:
- composer.lock
paths:
- .composer-cache/
- vendor/
script:
- composer install --no-progress --prefer-dist
- vendor/bin/phpcs --standard=WordPress --extensions=php --ignore='vendor/*,node_modules/*,.composer-cache/*' .
build-assets:
stage: test
image: node:20
cache:
key:
files:
- package-lock.json
paths:
- .npm/
script:
- npm ci --cache .npm --prefer-offline
- npm run build
artifacts:
paths:
- assets/dist/
expire_in: 1 week
cache:key:files に lock ファイルを指定すると、依存関係が変わったときだけキャッシュが作り直されます。phpcs の例では WordPress Coding Standards(wp-coding-standards/wpcs)を composer.json の開発依存に入れている前提です。Node のバージョンはテーマのビルド環境に合わせて変えてください。
キャッシュは Runner の種類や設定によって共有範囲が変わります。複数の Runner で動かすと「キャッシュが効かない」ことがありますが、これは異常ではなく、分散キャッシュを設定していない場合の仕様です。
needs でステージを待たずに進める
既定ではステージの全ジョブが終わるまで次のステージに進みません。needs を使うと、必要なジョブが終わった時点で後続ジョブを開始でき、全体の待ち時間を短くできます。
deploy-production:
stage: deploy
needs:
- job: build-assets
artifacts: true
- job: phpcs
needs を書いたジョブは、指定したジョブのアーティファクトだけを受け取ります。リストに無いジョブの成果物は届かないので、「ファイルが見つからない」エラーが出たら needs の指定を確認してください。
CI/CD 変数で秘密情報を扱う
サーバーの接続先や SSH 鍵は、.gitlab-ci.yml に直接書かずにプロジェクトの CI/CD 変数として登録します。登録画面はプロジェクトの「Settings」→「CI/CD」→「Variables」にあります(表記はバージョンにより異なります、2026年10月現在)。
登録時に意識したいオプションは次のとおりです。
- Protected: 保護ブランチ・保護タグのパイプラインでしか使えなくします。本番用の鍵は保護しておけば、作業ブランチからの持ち出しを防げます。
- Masked: ジョブログに値が出たときに伏せ字にします。ただし値の形式に条件があり、伏せ字はあくまで表示上の対策です。
echoで中身を出力するような書き方は避けてください。 - Type: File: 値を一時ファイルに書き出し、変数にはそのファイルのパスが入ります。SSH 秘密鍵のような複数行の値に向いています。
本番デプロイのジョブを書く
ここでは rsync over SSH でテーマを配置する例を示します。SSH_PRIVATE_KEY(File 型)、SSH_KNOWN_HOSTS、DEPLOY_USER、DEPLOY_HOST、DEPLOY_PATH を CI/CD 変数に登録しておきます。
deploy-production:
stage: deploy
image: alpine:3.20
needs:
- job: build-assets
artifacts: true
- job: phpcs
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
environment:
name: production
url: https://example.com
resource_group: production
before_script:
- apk add --no-cache openssh-client rsync
- chmod 600 "$SSH_PRIVATE_KEY"
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
script:
- >
rsync -az --delete
--exclude='.git' --exclude='node_modules' --exclude='.composer-cache'
-e "ssh -i $SSH_PRIVATE_KEY"
./ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH"
いくつか補足します。
SSH_KNOWN_HOSTS には、手元で ssh-keyscan などで取得したホスト鍵を、正しいサーバーのものか確認したうえで登録します。StrictHostKeyChecking=no でホスト鍵の確認を無効にする例を見かけますが、接続先のすり替えに気づけなくなるので避けてください。
--delete は転送元に無いファイルを転送先から削除します。DEPLOY_PATH をテーマのディレクトリではなく wp-content や公開ディレクトリのルートにしてしまうと、アップロード画像や他のテーマまで消えます。初回は --dry-run を付けて差分だけを確認し、本番サーバーのバックアップを取ってから実行してください。
resource_group を指定すると、同じグループのジョブは同時に 1 つしか動きません。デプロイが重なってファイルが中途半端な状態になるのを防げます。environment を書くと、GitLab の環境一覧にデプロイ履歴が残り、どのコミットが本番に出ているかを追えます。
デプロイ用のユーザーは、テーマのディレクトリに書き込めるだけの権限に絞った専用ユーザーにしておくと、鍵が漏れたときの影響を小さくできます。いきなり本番に向けず、まずステージング環境向けのジョブで同じ流れを試すのが安全です。
動かないときの確認順
- パイプライン自体が作られない:
workflow: rulesや各ジョブのrulesがどれにも一致していない可能性があります。条件を 1 つずつ外して確認します。 - ジョブが「pending」のまま進まない: 使える Runner が無いか、ジョブの
tagsと Runner のタグが一致していません。プロジェクトの Runner 設定を確認します。 - 変数が空になる: 保護ブランチ以外で Protected 変数を使っていることが多い原因です。
- 前のジョブのファイルが無い: cache と artifacts の取り違え、または
needsの指定漏れを疑います。
まとめ
.gitlab-ci.ymlにステージとジョブを書き、ジョブごとにimageを明示すると環境差を減らせます。- 実行条件は
rulesで書き、workflow: rulesで重複パイプラインを防ぎます。 - 再取得できるものは
cache、後続ジョブが前提にするものはartifactsで渡します。 - 秘密情報は Protected / Masked / File 型の CI/CD 変数で扱い、ログに出さないようにします。
- 本番デプロイは
when: manual・resource_group・--dry-runでの事前確認と、バックアップを組み合わせて慎重に行います。