GitLab CIを使った初めてのCI/CDパイプライン実践ガイド

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

なぜCI/CDパイプラインが必要なのか?

新しい機能を追加してプッシュしたあと、次は何をしますか?もしあなたのプロセスに、手動でサーバーにSSH接続し、コードをプルし、テストを実行し、サービスを再起動する、といった作業が含まれているなら、それは危険な状態です。その手動プロセスは遅く、エラーが発生しやすく、膨大な時間の無駄です。CI/CD(継続的インテグレーション/継続的デプロイメント)パイプラインは、このワークフロー全体を自動化します。`git push`から本番環境で稼働するアプリケーションまで、信頼性が高く再現性のある橋渡しをしてくれるのです。

私たちがGitLab CIに焦点を当てるのは、それがGitLabに直接組み込まれているからです。サードパーティのサービスや複雑な統合は必要ありません。コードがGitLab上にあれば、始めるために必要なものはすべて揃っています。プロセス全体は、リポジトリ内の単一ファイル`.gitlab-ci.yml`によって制御されます。新しいコードをプッシュすると、GitLabはこのファイルを見つけ、GitLab Runnerを使って自動化されたワークフローを実行します。

ステップ1:GitLab Runnerのセットアップ

GitLab Runnerは、CI/CDプロセスの「働き者」です。これはサーバーにインストールする軽量なエージェントで、あなたが定義したジョブを受け取って実行します。GitLabは共有の自動スケーリングランナーを提供していますが、独自のランナーをインストールすることで、環境を完全に制御し、プロセス全体を自社のインフラ内に留めることができます。これは、特定の依存関係や強化されたセキュリティが必要なプロジェクトに最適です。

DebianまたはUbuntuサーバーにランナーをインストールしてみましょう。

1. GitLab Runnerパッケージのインストール

まず、公式のGitLabリポジトリを追加し、いくつかのコマンドでランナーパッケージをインストールします。

# リポジトリスクリプトをダウンロードして追加
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash

# GitLab Runnerの最新バージョンをインストール
sudo apt-get install gitlab-runner -y

2. プロジェクトにランナーを登録する

次に、インストールしたランナーをGitLabプロジェクトにリンクする必要があります。この重要なステップにより、ランナーは新しいジョブをどこで待機すべきかを知ることができます。

GitLabプロジェクトで、Settings > CI/CD に移動し、Runnersセクションを展開します。次のステップで必要となるコーディネーターURLと登録トークンが表示されます。このページは開いたままにしておいてください。

次に、サーバーで登録コマンドを実行します:

sudo gitlab-runner register

いくつかの詳細について尋ねられます:

  • GitLabインスタンスのURLを入力してください: これはRunnersページに記載されているURLです(例: https://gitlab.com/)。
  • 登録トークンを入力してください: 同じページからトークンをコピー&ペーストします。
  • ランナーの説明を入力してください: 「My Project Runner」のように、分かりやすい名前をつけましょう。
  • ランナーのタグを入力してください: タグを使用すると、特定のジョブをこのランナーに割り当てることができます。たとえば、このランナーに `production` というタグを付けて、デプロイジョブのみを処理させることができます。今は空のままでも構いません。
  • executorを入力してください: これは最も重要な選択で、ジョブが実行される環境を定義します。主な選択肢は `shell`、docker、`kubernetes` です。私たちは `docker` を強く推奨します。これにより、ジョブごとに新しく隔離されたコンテナが作成され、クリーンな状態が保証され、依存関係の競合が防止されます。`docker` を選択した場合、デフォルトのイメージ(例: `node:18` や `python:3.9`)を尋ねられます。`alpine:latest`のような最小限のイメージから始めて、後でジョブごとにイメージを指定することもできます。

プロンプトへの入力を完了すると、ランナーはインストール、登録され、作業準備が整います。

ステップ2:.gitlab-ci.ymlでパイプラインを設定する

ランナーが指示を待っている状態になったので、次はその指示を書きましょう。パイプライン全体を `.gitlab-ci.yml` という名前の単一ファイルに定義し、リポジトリのルートに配置します。

構造の理解:ステージとジョブ

パイプラインは `stages`(ステージ)に整理され、ステージには `jobs`(ジョブ)が含まれます。同じステージ内のすべてのジョブは並行して実行されますが、ステージ自体は順次実行されます。典型的で効果的な構造は `build` -> `test` -> `deploy` です。

以下は、これらのステージを定義し、それぞれにプレースホルダージョブを1つ含む簡単な例です。

# .gitlab-ci.yml

stages:
  - build
  - test
  - deploy

build_job:
  stage: build
  script:
    - echo "このジョブはプロジェクトをビルドします。"
    - echo "例えば、ここで 'npm install' や 'composer install' を実行できます。"

run_tests:
  stage: test
  script:
    - echo "このジョブはテストを実行します。"
    - echo "例えば、'pytest' や 'npm test' を実行します。"

deploy_job:
  stage: deploy
  script:
    - echo "このジョブはプロジェクトをサーバーにデプロイします。"
    - echo "ここでは 'scp'、'rsync'、またはSSHコマンドを使用するかもしれません。"

このファイルをコミットすると、GitLabは最初のパイプラインを開始します。`build_job` を実行し、それが成功すれば `run_tests` を、そして最後に `deploy_job` を実行します。

実践例:Node.jsアプリケーション

Node.jsアプリケーションのためにより現実的なパイプラインを作成してみましょう。Docker executorを活用し、ジョブに特定のNode.jsイメージを定義します。

# .gitlab-ci.yml

default:
  image: node:18 # デフォルトですべてのジョブにNode.js 18を使用

stages:
  - build
  - test
  - deploy

install_dependencies:
  stage: build
  script:
    - echo "サーバーの依存関係をインストール中..."
    - npm install
  artifacts:
    paths:
      - node_modules/
    expire_in: 1 hour

test_app:
  stage: test
  script:
    - echo "テストを実行中..."
    - npm test
  needs:
    - install_dependencies

deploy_to_production:
  stage: deploy
  script:
    - echo "本番サーバーにデプロイ中..."
    # 実際のデプロイスクリプトでは、GitLabの変数に保存されたSSHキーを使用します
    - ssh [email protected] 'cd /path/to/app && git pull && pm2 restart app_name'
  environment:
    name: production
  when: on_success
  rules:
    - if: $CI_COMMIT_BRANCH == 'main'

この例の新しいキーワードを分解してみましょう:

  • default: image: はデフォルトのDockerイメージを設定するため、各ジョブで `image: node:18` を指定する必要がありません。
  • artifacts: これは重要な概念です。ジョブ完了後に指定された `paths`(この場合は `node_modules/` ディレクトリ)を保存し、後のステージのジョブで利用できるようにします。これにより、`npm install` を複数回実行する必要がなくなります。
  • needs: これは依存関係を作成し、`test_app` ジョブが `install_dependencies` の成功後にのみ開始されることを保証します。
  • environment: これはGitLabのインターフェース内でデプロイを追跡するのに役立ち、どのコードがいつデプロイされたかの記録を作成します。
  • rules: これはジョブがいつ実行されるかを制御する現代的な方法です。ここでは、`main` ブランチへのコミット時にのみデプロイジョブが実行されるようにし、フィーチャーブランチからの誤ったデプロイを防ぎます。

デプロイに関する重要な注意点:SSHキーのような認証情報を `.gitlab-ci.yml` ファイルにハードコードしないでください。これは重大なセキュリティ脆弱性です。代わりに、GitLabに組み込まれているCI/CD変数(Settings > CI/CD > Variables)を使用してください。プライベートSSHキーを「File」タイプの変数として安全に保存できます。そうすれば、スクリプトはリポジトリやログにキーを公開することなく、この変数を使用して認証できます。この実践は、本番グレードのパイプラインにとって交渉の余地のないステップです。

ステップ3:パイプラインの確認と監視

`.gitlab-ci.yml` をコミットした後、それが実際に動作しているのをどのように確認するのでしょうか?GitLabプロジェクトの CI/CD > Pipelines セクションに移動してください。この画面は、あなたの自動化のミッションコントロールです。

すべてのパイプライン実行のリストが表示され、それぞれにステータス(pending, running, passed, または failed)が表示されます。いずれかのパイプラインをクリックすると、そのステージとジョブを見ることができます。ジョブが失敗すると、赤い「X」が問題の箇所を正確に示します。失敗したジョブをクリックすると、直接ログ出力に移動し、何が問題だったのかをデバッグするためのエラーメッセージを見つけることができます。フィードバックループが即座に行われることは、適切に実装されたCI/CDプロセスの主要な利点の一つです。

すべてがスムーズに実行されるようになったら、プロジェクトの `README.md` にパイプラインステータスバッジを追加して、ビルドステータスを表示できます。このバッジの埋め込みコードは、Settings > CI/CD > General pipelines から取得できます。

Share: