著者は、 Write for DOnations プログラムの一環として、 Free and Open SourceFundを選択して寄付を受け取りました。

序章

GitLab は、コードリポジトリのホストを超えた強力な機能を提供するオープンソースのコラボレーションプラットフォームです。 問題の追跡、パッケージとレジストリのホスト、Wikiの保守、継続的インテグレーション(CI)および継続的デプロイメント(CD)パイプラインの設定などを行うことができます。

このチュートリアルでは、GitLabを使用して継続的デプロイパイプラインを構築します。 Dockerイメージを構築し、それをGitLabコンテナーレジストリにプッシュし、SSHを使用してサーバーにデプロイするようにパイプラインを構成します。 パイプラインは、リポジトリにプッシュされたコミットごとに実行されます。

小さな静的Webページを展開しますが、このチュートリアルの焦点はCDパイプラインの構成です。 静的なWebページは、デモンストレーションのみを目的としています。 デプロイに他のDockerイメージを使用して同じパイプライン構成を適用することもできます。

このチュートリアルを終了すると、ブラウザでhttp://your_server_IPにアクセスして、自動展開の結果を確認できます。

前提条件

このチュートリアルを完了するには、次のものが必要です。

  • Ubuntu 18.04初期サーバーセットアップガイドに従ってセットアップされた1つのUbuntu18.04サーバー(sudo非rootユーザーとファイアウォールを含む)。 少なくとも1GBのRAMと1つのCPUが必要です。
  • Ubuntu18.04ガイドにDockerをインストールして使用する方法に従ってサーバーにDockerをインストールします。
  • コンテナレジストリが有効になっているGitLabインスタンスのユーザーアカウント。 公式GitLabインスタンスの無料プランは要件を満たしています。 Ubuntu 18.04ガイドにGitLabをインストールして構成する方法に従って、独自のGitLabインスタンスをホストすることもできます。

ステップ1—GitLabリポジトリを作成する

まず、GitLabプロジェクトを作成し、それにHTMLファイルを追加します。 後でHTMLファイルをNginxDockerイメージにコピーし、サーバーにデプロイします。

GitLabインスタンスにログインし、新しいプロジェクトをクリックします。

The new project button in GitLab

  1. 適切なプロジェクト名を付けてください。
  2. オプションで、プロジェクトの説明を追加します。
  3. 要件に応じて、可視性レベルプライベートまたはパブリックに設定してください。
  4. 最後にプロジェクトの作成をクリックします

The new project form in GitLab

プロジェクトの概要ページにリダイレクトされます。

HTMLファイルを作成しましょう。 プロジェクトの概要ページで、新しいファイルをクリックします。

The new file button on the project overview page

ファイル名index.htmlに設定し、ファイル本体に次のHTMLを追加します。

index.html
<html>
<body>
<h1>My Personal Website</h1>
</body>
</html>

ページ下部のコミット変更をクリックしてファイルを作成します。

このHTMLは、ブラウザで開いたときに個人のWebサイトを示す1つの見出しのある空白のページを生成します。

Dockerfileは、DockerがDockerイメージを構築するために使用するレシピです。 Dockerfileを作成して、HTMLファイルをNginxイメージにコピーしてみましょう。

プロジェクトの概要ページに戻り、 + ボタンをクリックして、新しいファイルオプションを選択します。

New file option in the project's overview page listed in the plus button

ファイル名Dockerfileに設定し、次の手順をファイル本体に追加します。

Dockerfile
FROM nginx:1.18
COPY index.html /usr/share/nginx/html

FROM命令は、継承元の画像(この場合はnginx:1.18画像)を指定します。 1.18は、Nginxバージョンを表す画像タグです。 nginx:latestタグは最新のNginxリリースを参照しますが、将来的にアプリケーションが破損する可能性があるため、修正バージョンをお勧めします。

COPY命令は、index.htmlファイルをDockerイメージの/usr/share/nginx/htmlにコピーします。 これは、Nginxが静的HTMLコンテンツを保存するディレクトリです。

ページ下部のコミット変更をクリックしてファイルを作成します。

次のステップでは、GitLabランナーを構成して、デプロイジョブを実行できるユーザーを制御します。

ステップ2—GitLabランナーの登録

SSH秘密鍵と接触する環境を追跡するために、サーバーをGitLabランナーとして登録します。

デプロイメントパイプラインで、SSHを使用してサーバーにログインする必要があります。 これを実現するには、SSH秘密鍵をGitLab CI / CD変数に保存します(ステップ5)。 SSH秘密鍵は、サーバーへのエントリチケットであるため、非常に機密性の高いデータです。 通常、秘密鍵は、それが生成されたシステムを離れることはありません。 通常の場合、手動でログインして展開ルーチンを実行するために、ホストマシンでSSHキーを生成してから、サーバーでSSHキーを承認します(つまり、公開キーをサーバーにコピーします)。

ここで状況が少し変わります。サーバーへの自律権限(GitLab CI / CD)アクセスを許可して、展開ルーチンを自動化する必要があります。 したがって、秘密鍵は、それが生成されたシステムを離れ、GitLabやその他の関係者に信頼を与える必要があります。 自分の秘密鍵が、自分で制御または信頼されていない環境に入ることは決してありません。

GitLabに加えて、GitLabランナーは、秘密鍵が入力されるもう1つのシステムです。 パイプラインごとに、GitLabはランナーを使用して重い作業を実行します。つまり、CI/CD構成で指定したジョブを実行します。 つまり、デプロイジョブは最終的にGitLabランナーで実行されるため、SSHを使用してサーバーにログインできるように、秘密鍵がランナーにコピーされます。

不明なGitLabランナー(たとえば、共有ランナー)を使用して展開ジョブを実行する場合、システムが秘密鍵と接触していることに気付かないでしょう。 GitLabランナーはジョブの実行後にすべてのデータをクリーンアップしますが、独自のサーバーをGitLabランナーとして登録することで、未知のシステムに秘密鍵を送信することを回避できます。 秘密鍵は、あなたが管理するサーバーにコピーされます。

サーバーにログインすることから始めます。

  1. ssh sammy@your_server_IP

gitlab-runnerサービスをインストールするには、公式のGitLabリポジトリを追加します。 インストールスクリプトをダウンロードして検査します。

  1. curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh > script.deb.sh
  2. less script.deb.sh

スクリプトの安全性に満足したら、インストーラーを実行します。

  1. sudo bash script.deb.sh

わかりにくいかもしれませんが、続行するには非rootユーザーのパスワードを入力する必要があります。 前のコマンドを実行すると、出力は次のようになります。

Output
[sudo] password for sammy: % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 5945 100 5945 0 0 8742 0 --:--:-- --:--:-- --:--:-- 8729

curlコマンドが終了すると、次のメッセージが表示されます。

Output
The repository is setup! You can now install packages.

次に、gitlab-runnerサービスをインストールします。

  1. sudo apt install gitlab-runner

サービスステータスを確認して、インストールを確認します。

  1. systemctl status gitlab-runner

出力にはactive (running)が含まれます。

Output
● gitlab-runner.service - GitLab Runner Loaded: loaded (/etc/systemd/system/gitlab-runner.service; enabled; vendor preset: enabled) Active: active (running) since Mon 2020-06-01 09:01:49 UTC; 4s ago Main PID: 16653 (gitlab-runner) Tasks: 6 (limit: 1152) CGroup: /system.slice/gitlab-runner.service └─16653 /usr/lib/gitlab-runner/gitlab-runner run --working-directory /home/gitlab-runner --config /etc/gitla

ランナーを登録するには、プロジェクトトークンとGitLabURLを取得する必要があります。

  1. GitLabプロジェクトで、設定> CI / CD>ランナーに移動します。
  2. 特定のランナーを手動でセットアップするセクションには、登録トークンとGitLabURLがあります。 両方をテキストエディタにコピーします。 次のコマンドで必要になります。 これらは、https://your_gitlab.comおよびproject_tokenと呼ばれます。

The runners section in the ci/cd settings with the copy token button

ターミナルに戻り、プロジェクトのランナーを登録します。

  1. sudo gitlab-runner register -n --url https://your_gitlab.com --registration-token project_token --executor docker --description "Deployment Runner" --docker-image "docker:stable" --tag-list deployment --docker-privileged

コマンドオプションは次のように解釈できます。

  • -nは、registerコマンドを非対話的に実行します(すべてのパラメーターをコマンドオプションとして指定します)。
  • --urlは、GitLabのランナーページからコピーしたGitLabURLです。
  • --registration-tokenは、GitLabのランナーページからコピーしたトークンです。
  • --executorはエグゼキュータタイプです。 dockerは、Dockerコンテナー内の各CI / CDジョブを実行します( GitLabのエグゼキューターに関するドキュメントを参照)。
  • --descriptionはランナーの説明であり、GitLabに表示されます。
  • --docker-imageは、明示的に指定されていない場合、CI/CDジョブで使用するデフォルトのDockerイメージです。
  • --tag-listは、ランナーに割り当てられたタグのリストです。 タグをパイプライン構成で使用して、CI/CDジョブの特定のランナーを選択できます。 deploymentタグを使用すると、この特定のランナーを参照して展開ジョブを実行できます。
  • --docker-privilegedは、CI/CDジョブごとに作成されたDockerコンテナーを特権モードで実行します。 特権コンテナーは、ホストマシン上のすべてのデバイスにアクセスでき、コンテナーの外部で実行されているプロセスとほぼ同じようにホストにアクセスできます(ランタイム特権とLinux機能に関するDockerのドキュメントを参照)。 特権モードで実行する理由は、Docker-in-Docker( dind )を使用してCI/CDパイプラインにDockerイメージを構築できるようにするためです。 コンテナに必要な最小要件を与えることをお勧めします。 Docker-in-Dockerを使用するには、特権モードで実行する必要があります。 この特定のプロジェクトにのみランナーを登録したことに注意してください。このプロジェクトでは、特権コンテナーで実行されるコマンドを制御できます。

gitlab-runner registerコマンドを実行すると、次の出力が表示されます。

Output
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

GitLabの設定>CI / CD > ランナーに移動して、登録プロセスを確認します。ここに、登録済みのランナーが表示されます。

The registered runner in the runners section of the ci/cd settings

次のステップでは、デプロイメントユーザーを作成します。

ステップ3—デプロイメントユーザーの作成

展開タスク専用のユーザーを作成します。 後で、そのユーザーでサーバーにログインするようにCI/CDパイプラインを構成します。

サーバーで、新しいユーザーを作成します。

  1. sudo adduser deployer

ユーザー作成プロセスをガイドします。 強力なパスワードと、オプションで指定する追加のユーザー情報を入力します。 最後に、Yでユーザーの作成を確認します。

ユーザーをDockerグループに追加します。

  1. sudo usermod -aG docker deployer

これにより、 deployerdockerコマンドを実行できるようになります。これは、デプロイメントの実行に必要です。

警告: Dockerグループにユーザーを追加すると、rootユーザーと同等の権限が付与されます。 これがシステムのセキュリティにどのように影響するかについての詳細は、 Docker Daemon AttackSurfaceを参照してください。

次のステップでは、SSHキーを作成して、deployerとしてサーバーにログインできるようにします。

ステップ4—SSHキーを設定する

デプロイメントユーザー用のSSHキーを作成します。 GitLab CI / CDは後でキーを使用してサーバーにログインし、展開ルーチンを実行します。

SSHキーを生成する新しく作成されたdeployerユーザーに切り替えることから始めましょう。

  1. su deployer

ユーザーの切り替えを完了するために、deployerパスワードの入力を求められます。

次に、4096ビットのSSHキーを生成します。 ssh-keygenコマンドの質問に正しく答えることが重要です。

  1. 最初の質問:ENTERで答えてください。これにより、キーがデフォルトの場所に保存されます(このチュートリアルの残りの部分では、キーがデフォルトの場所に保存されていると想定しています)。
  2. 2番目の質問:SSH秘密鍵(認証に使用される鍵)を保護するためのパスワードを構成します。 パスフレーズを指定すると、秘密鍵を使用するたびにパスフレーズを入力する必要があります。 一般に、パスフレーズはSSHキーに別のセキュリティレイヤーを追加します。これは良い習慣です。 秘密鍵を所有している人は、鍵を使用するためにパスフレーズも必要になります。 このチュートリアルでは、CI / CDパイプラインが非対話的に実行されるため、パスフレーズを入力できないため、パスフレーズが空であることが重要です。

要約すると、次のコマンドを実行し、ENTERで両方の質問を確認して、4096ビットのSSHキーを作成し、空のパスフレーズを使用してデフォルトの場所に保存します。

  1. ssh-keygen -b 4096

deployer ユーザーのSSHキーを認証するには、authorized_keysファイルに公開キーを追加する必要があります。

  1. cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

~は、Linuxのユーザーホームの略です。 catプログラムは、ファイルの内容を出力します。 ここでは、>>演算子を使用して、catの出力をリダイレクトし、authorized_keysファイルに追加します。

このステップでは、CI/CDパイプラインがログインしてアプリケーションをデプロイするためのSSHキーペアを作成しました。 次に、秘密鍵をGitLabに保存して、パイプラインプロセス中にアクセスできるようにします。

ステップ5—秘密鍵をGitLab CI/CD変数に保存する

SSH秘密鍵をGitLabCI/ CDファイル変数に保存して、パイプラインがその鍵を使用してサーバーにログインできるようにします。

GitLabがCI/CDパイプラインを作成すると、すべての変数が対応するランナーに送信され、ジョブの期間中、変数は環境変数として設定されます。 特に、 file 変数の値はファイルに保存され、環境変数にはこのファイルへのパスが含まれます。

変数セクションにいる間に、サーバーIPとサーバーユーザーの変数も追加します。これにより、ログイン先のサーバーとユーザーについてパイプラインに通知されます。

SSH秘密鍵を表示することから始めます。

  1. cat ~/.ssh/id_rsa

出力をクリップボードにコピーします。 -----END RSA PRIVATE KEY-----の後に必ず改行を追加してください。

〜/ .ssh / id_rsa
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----


次に、GitLabプロジェクトの設定> CI / CD > 変数に移動し、変数の追加をクリックします。 次のようにフォームに記入します。

  • キー:ID_RSA
  • 値:クリップボードからSSH秘密鍵を貼り付けます(最後の改行を含む)。
  • タイプ:ファイル
  • 環境範囲:すべて(デフォルト)
  • 保護変数:チェック済み
  • マスク変数:チェックなし

注:正規表現の要件を満たしていないため、変数をマスクできません(マスクされた変数に関するGitLabのドキュメントを参照)。 ただし、秘密鍵がコンソールログに表示されることはないため、マスクは廃止されます。

秘密鍵を含むファイルが各CI/CDジョブのランナーに作成され、そのパスが$ID_RSA環境変数に格納されます。

サーバーIPを使用して別の変数を作成します。 変数の追加をクリックし、次のようにフォームに入力します。

  • キー:SERVER_IP
  • 値:your_server_IP
  • タイプ:変数
  • 環境スコープ:すべて(デフォルト)
  • 保護変数:チェック済み
  • マスク変数:チェック済み

最後に、ログインユーザーで変数を作成します。 変数の追加をクリックし、次のようにフォームに入力します。

  • キー:SERVER_USER
  • 値:deployer
  • タイプ:変数
  • 環境スコープ:すべて(デフォルト)
  • 保護変数:チェック済み
  • マスク変数:チェック済み

これで、秘密鍵がGitLab CI / CD変数に保存されました。これにより、パイプラインの実行中に鍵を使用できるようになります。 次のステップでは、CI/CDパイプラインの構成に進みます。

ステップ6—.gitlab-ci.ymlファイルの設定

GitLab CI/CDパイプラインを構成します。 パイプラインはDockerイメージを構築し、それをコンテナレジストリにプッシュします。 GitLabは、プロジェクトごとにコンテナレジストリを提供します。 次の場所に移動すると、コンテナレジストリを調べることができます。 パッケージとレジストリ >> コンテナレジストリ GitLabプロジェクトで(詳細は GitLabのコンテナレジストリドキュメント 。)パイプラインの最後のステップは、サーバーにログインし、最新のDockerイメージをプルし、古いコンテナーを削除して、新しいコンテナーを開始することです。

次に、パイプライン構成を含む.gitlab-ci.ymlファイルを作成します。 GitLabで、プロジェクトの概要ページに移動し、 + ボタンをクリックして、新しいファイルを選択します。 次に、ファイル名.gitlab-ci.ymlに設定します。

(または、リポジトリのクローンを作成し、ローカルマシンの.gitlab-ci.ymlに次のすべての変更を加えてから、コミットしてリモートリポジトリにプッシュすることもできます。)

開始するには、以下を追加します。

.gitlab-ci.yml
stages:
  - publish
  - deploy

各ジョブはステージに割り当てられます。 同じステージに割り当てられたジョブは並行して実行されます(十分な数のランナーが利用可能な場合)。 ステージは指定された順序で実行されます。 ここでは、publishステージが最初になり、deployステージが2番目になります。 後続のステージは、前のステージが正常に終了したとき(つまり、すべてのジョブが合格したとき)にのみ開始されます。 芸名は任意に選ぶことができます。

このCD構成を、アプリをテストおよびビルドする既存のCIパイプラインと組み合わせる場合は、既存のステージの後にpublishおよびdeployステージを追加して、デプロイを行うことができます。テストに合格した場合にのみ実行されます。

これに続いて、これを.gitlab-ci.ymlファイルに追加します。

.gitlab-ci.yml
. . .
variables:
  TAG_LATEST: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:latest
  TAG_COMMIT: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA

変数セクションは、ジョブのscriptセクションのコンテキストで使用できる環境変数を定義します。 これらの変数は、通常のLinux環境変数として使用できます。 つまり、$TAG_LATESTのようにドル記号を前に付けることで、スクリプトでそれらを参照できます。 GitLabは、ジョブごとにいくつかの定義済み変数を作成し、ブランチ名やジョブが処理しているコミットハッシュなどのコンテキスト固有の情報を提供します(事前定義変数の詳細を参照してください)。 ここでは、事前定義された変数から2つの環境変数を作成します。 それらは以下を表します:

  • CI_REGISTRY_IMAGE:特定のプロジェクトに関連付けられているコンテナレジストリのURLを表します。 このURLはGitLabインスタンスによって異なります。 たとえば、 gitlab.com プロジェクトのレジストリURLは、registry.gitlab.com/your_user/your_projectのパターンに従います。 ただし、GitLabがこの変数を提供するため、正確なURLを知る必要はありません。
  • CI_COMMIT_REF_NAME:プロジェクトがビルドされるブランチまたはタグの名前。
  • CI_COMMIT_SHORT_SHA:プロジェクトがビルドされるコミットリビジョンの最初の8文字。

両方の変数は事前定義された変数で構成されており、Dockerイメージにタグを付けるために使用されます。

TAG_LATESTは、latestタグを画像に追加します。 これは、常に最新のリリースを表すタグを提供するための一般的な戦略です。 デプロイごとに、latestイメージは、新しくビルドされたDockerイメージでコンテナーレジストリ内でオーバーライドされます。

一方、TAG_COMMITは、デプロイされているコミットSHAの最初の8文字をイメージタグとして使用するため、コミットごとに一意のDockerイメージが作成されます。 Dockerイメージの履歴をGitコミットの粒度まで追跡できるようになります。 これは、デプロイメントに欠陥がある場合に古いバージョンのコードをすばやくデプロイできるため、継続的なデプロイメントを行う場合の一般的な手法です。

次の手順で説明するように、デプロイを古いGitリビジョンにロールバックするプロセスは、GitLabで直接実行できます。

$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAMEは、Dockerイメージのベース名を指定します。 GitLabのドキュメントによると、Dockerイメージ名は次のスキームに従う必要があります。

image name scheme
<registry URL>/<namespace>/<project>/<image>

$CI_REGISTRY_IMAGEは、<registry URL>/<namespace>/<project>の部分を表し、プロジェクトのレジストリルートであるため、必須です。 $CI_COMMIT_REF_NAMEはオプションですが、さまざまなブランチのDockerイメージをホストするのに役立ちます。 このチュートリアルでは、1つのブランチのみを操作しますが、拡張可能な構造を構築することをお勧めします。 一般に、GitLabでサポートされているイメージリポジトリ名には次の3つのレベルがあります。

repository name levels
registry.example.com/group/project:some-tag registry.example.com/group/project/image:latest registry.example.com/group/project/my/image:rc1

TAG_COMMIT変数には、2番目のオプションを使用しました。ここで、imageはブランチ名に置き換えられます。

次に、.gitlab-ci.ymlファイルに以下を追加します。

.gitlab-ci.yml
. . .
publish:
  image: docker:latest
  stage: publish
  services:
    - docker:dind
  script:
    - docker build -t $TAG_COMMIT -t $TAG_LATEST .
    - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY
    - docker push $TAG_COMMIT
    - docker push $TAG_LATEST

publishセクションは、CI/CD構成の最初のジョブです。 それを分解しましょう:

  • imageは、このジョブに使用するDockerイメージです。 GitLabランナーは、ジョブごとにDockerコンテナーを作成し、このコンテナー内でスクリプトを実行します。 docker:latestイメージは、dockerコマンドが使用可能であることを保証します。
  • stageは、ジョブをpublishステージに割り当てます。
  • servicesは、Docker-in-Docker(dindサービス)を指定します。 これが、GitLabランナーを特権モードで登録した理由です。

publishジョブのスクリプトセクションは、このジョブに対して実行するシェルコマンドを指定します。 これらのコマンドが実行されると、作業ディレクトリはリポジトリルートに設定されます。

  • docker build ...Dockerfileに基づいてDockerイメージを構築し、変数セクションで定義された最新のコミットタグでタグ付けします。
  • docker login ...:Dockerをプロジェクトのコンテナレジストリにログインします。 事前定義された変数$CI_BUILD_TOKENを認証トークンとして使用します。 GitLabはトークンを生成し、ジョブの存続期間中有効なままになります。
  • docker push ...:両方のイメージタグをコンテナレジストリにプッシュします。

これに続いて、deployジョブを.gitlab-ci.ymlに追加します。

.gitlab-ci.yml
. . .
deploy:
  image: alpine:latest
  stage: deploy
  tags:
    - deployment
  script:
    - chmod og= $ID_RSA
    - apk update && apk add openssh-client
    - ssh -i $ID_RSA -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP "docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY"
    - ssh -i $ID_RSA -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP "docker pull $TAG_COMMIT"
    - ssh -i $ID_RSA -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP "docker container rm -f my-app || true"
    - ssh -i $ID_RSA -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP "docker run -d -p 80:80 --name my-app $TAG_COMMIT"

Alpineは軽量Linuxディストリビューションであり、ここではDockerイメージとして十分です。 ジョブをdeployステージに割り当てます。 デプロイメントタグは、ステップ2で構成したランナーなど、deploymentのタグが付けられたランナーでジョブが実行されることを保証します。

deployジョブのscriptセクションは、次の2つの構成コマンドで始まります。

  • chmod og= $ID_RSAgroupおよびothersのすべてのアクセス許可を秘密鍵から取り消して、所有者のみが使用できるようにします。 これは要件です。そうでない場合、SSHは秘密鍵の操作を拒否します。
  • apk update && apk add openssh-client:Alpineのパッケージマネージャー(apk)を更新し、sshコマンドを提供するopenssh-clientをインストールします。

4つの連続するsshコマンドが続きます。 それぞれのパターンは次のとおりです。

ssh connect pattern for all deployment commands
ssh -i $ID_RSA -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP "command"

sshステートメントで、リモートサーバーでcommandを実行しています。 そのためには、秘密鍵を使用して認証します。

オプションは次のとおりです。

  • -iアイデンティティファイルを表し、$ID_RSAは秘密鍵ファイルへのパスを含むGitLab変数です。
  • -o StrictHostKeyChecking=noは、リモートホストを信頼するかどうかに関係なく、質問をバイパスするようにします。 この質問は、パイプラインなどの非対話型のコンテキストでは回答できません。
  • $SERVER_USER$SERVER_IPは、ステップ5で作成したGitLab変数です。 SSH接続のリモートホストとログインユーザーを指定します。
  • commandはリモートホストで実行されます。

展開は、最終的にサーバーで次の4つのコマンドを実行することによって行われます。

  1. docker login ...:Dockerをコンテナレジストリにログインします。
  2. docker pull ...:コンテナレジストリから最新のイメージを取得します。
  3. docker container rm ...:既存のコンテナが存在する場合は削除します。 || trueは、my-appという名前で実行されているコンテナーがなかった場合でも、終了コードが常に成功することを確認します。 これにより、コンテナーが存在しない場合(たとえば、最初のデプロイメントの場合)にパイプラインを中断することなく、 deleteifexistsルーチンが保証されます。
  4. docker run ...:レジストリからの最新のイメージを使用して新しいコンテナを開始します。 コンテナの名前はmy-appになります。 ホストのポート80は、コンテナーのポート80にバインドされます(順序は-p host:containerです)。 -dは、コンテナーをデタッチモードで開始します。そうしないと、コマンドが終了するのを待ってパイプラインがスタックします。

注:コマンドを実行するGitLabランナーがまったく同じサーバーであることを考えると、SSHを使用してサーバー上でこれらのコマンドを実行するのは奇妙に思えるかもしれません。 ただし、ランナーはDockerコンテナーでコマンドを実行するため、SSHを使用せずにコマンドを実行する場合は、サーバーではなくコンテナー内にデプロイする必要があるため、必須です。 Dockerをランナーエグゼキューターとして使用する代わりに、シェルエグゼキューターを使用してホスト自体でコマンドを実行できると主張する人もいるかもしれません。 ただし、これによりパイプラインに制約が生じます。つまり、ランナーはデプロイ先のサーバーと同じサーバーである必要があります。 ある日、アプリケーションを別のサーバーに移行したり、別のランナーサーバーを使用したりする可能性があるため、これは持続可能で拡張可能なソリューションではありません。 いずれにせよ、SSHを使用して展開コマンドを実行することは理にかなっていますが、それは技術的または移行関連の理由による場合があります。

これを.gitlab-ci.ymlの展開ジョブに追加して次に進みましょう。

.gitlab-ci.yml
. . .
deploy:
. . .
  environment:
    name: production
    url: http://your_server_IP
  only:
    - master

GitLab環境を使用すると、GitLab内のデプロイを制御できます。 操作>環境に移動すると、GitLabプロジェクトの環境を調べることができます。 パイプラインがまだ終了していない場合、これまでのところ展開が行われていないため、利用可能な環境はありません。

パイプラインジョブがenvironmentセクションを定義すると、GitLabは、ジョブが正常に終了するたびに、指定された環境(ここではproduction)のデプロイメントを作成します。 これにより、GitLab CI/CDによって作成されたすべてのデプロイメントをトレースできます。 デプロイメントごとに、関連するコミットとそれが作成されたブランチを確認できます。

古いバージョンのソフトウェアにロールバックできる再展開に使用できるボタンもあります。 展開の表示ボタンをクリックすると、environmentセクションで指定されたURLが開きます。

唯一のセクションは、ジョブが実行されるブランチとタグの名前を定義します。 デフォルトでは、GitLabはリポジトリへのプッシュごとにパイプラインを開始し、すべてのジョブを実行します(.gitlab-ci.ymlファイルが存在する場合)。 onlyセクションは、ジョブの実行を特定のブランチ/タグに制限する1つのオプションです。 ここでは、masterブランチの展開ジョブのみを実行します。 ジョブを実行するかどうかに関するより複雑なルールを定義するには、rules構文を参照してください。

注: 2020年10月、GitHubはデフォルトブランチの命名規則masterからmainに変更しました。 GitLabや一般的な開発者コミュニティなどの他のプロバイダーは、このアプローチを採用し始めています。 このチュートリアルでは、masterブランチという用語を使用して、別の名前が付けられている可能性のあるデフォルトのブランチを示します。

完全な.gitlab-ci.ymlファイルは次のようになります。

.gitlab-ci.yml
stages:
  - publish
  - deploy

variables:
  TAG_LATEST: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:latest
  TAG_COMMIT: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA

publish:
  image: docker:latest
  stage: publish
  services:
    - docker:dind
  script:
    - docker build -t $TAG_COMMIT -t $TAG_LATEST .
    - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY
    - docker push $TAG_COMMIT
    - docker push $TAG_LATEST

deploy:
  image: alpine:latest
  stage: deploy
  tags:
    - deployment
  script:
    - chmod og= $ID_RSA
    - apk update && apk add openssh-client
    - ssh -i $ID_RSA -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP "docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY"
    - ssh -i $ID_RSA -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP "docker pull $TAG_COMMIT"
    - ssh -i $ID_RSA -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP "docker container rm -f my-app || true"
    - ssh -i $ID_RSA -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP "docker run -d -p 80:80 --name my-app $TAG_COMMIT"
  environment:
    name: production
    url: http://your_server_IP
  only:
    - master

最後に、GitLabのページの下部にあるコミット変更をクリックして、.gitlab-ci.ymlファイルを作成します。 または、Gitリポジトリのクローンをローカルに作成したら、ファイルをコミットしてリモートにプッシュします。

Dockerイメージを構築してサーバーにデプロイするためのGitLabCI/CD構成を作成しました。 次のステップでは、デプロイメントを検証します。

ステップ7—展開の検証

次に、GitLabのさまざまな場所、サーバー、ブラウザーでのデプロイを検証します。

.gitlab-ci.ymlファイルがリポジトリにプッシュされると、GitLabはそれを自動的に検出し、CI/CDパイプラインを開始します。 .gitlab-ci.ymlファイルを作成した時点で、GitLabは最初のパイプラインを開始しました。

GitLabプロジェクトのCI/ CD > Pipelines に移動して、パイプラインのステータスを確認します。 ジョブがまだ実行中/保留中の場合は、完了するまで待ちます。 合格パイプラインに2つの緑色のチェックマークが表示され、公開およびデプロイジョブが正常に実行されたことを示します。

The pipeline overview page showing a passed pipeline

パイプラインを調べてみましょう。 ステータス列の合格ボタンをクリックして、パイプラインの概要ページを開きます。 次のような一般的な情報の概要が表示されます。

  • パイプライン全体の実行期間。
  • パイプラインが実行されたコミットとブランチ。
  • 関連するマージリクエスト。 担当支店のオープンマージリクエストがある場合は、ここに表示されます。
  • このパイプラインで実行されたすべてのジョブとそのステータス。

次に、 deploy ボタンをクリックして、デプロイジョブの結果ページを開きます。

The result page of the deploy job

ジョブの結果ページで、ジョブのスクリプトのシェル出力を確認できます。 これは、失敗したパイプラインをデバッグするときに探す場所です。 右側のサイドバーには、このジョブに追加したデプロイメントタグがあり、 DeploymentRunnerで実行されたことがわかります。

ページの一番上までスクロールすると、このジョブは本番環境にデプロイされていますメッセージが表示されます。 GitLabは、ジョブの環境セクションが原因でデプロイが行われたことを認識しています。 本番リンクをクリックして、本番環境に移動します。

The production environment in GitLab

すべての本番環境の展開の概要がわかります。 これまでのところ、展開は1つだけでした。 展開ごとに、右端に再展開ボタンがあります。 再デプロイは、その特定のパイプラインのデプロイジョブを繰り返します。

再デプロイが意図したとおりに機能するかどうかは、パイプラインの構成によって異なります。これは、同じ状況でデプロイジョブを繰り返す以上のことは行わないためです。 コミットSHAをタグとして使用してDockerイメージをデプロイするように構成したため、パイプラインで再デプロイが機能します。

注:GitLabコンテナーレジストリに有効期限ポリシーがある場合があります。 有効期限ポリシーは、コンテナレジストリから古いイメージとタグを定期的に削除します。 結果として、有効期限ポリシーより古いデプロイメントは再デプロイに失敗します。これは、このコミットのDockerイメージがレジストリから削除されるためです。 有効期限ポリシーは、設定> CI/CD>コンテナレジストリタグの有効期限ポリシーで管理できます。 有効期限は通常、90日などの高い値に設定されます。 ただし、有効期限ポリシーのためにレジストリから削除されたイメージをデプロイしようとした場合は、その特定のパイプラインのpublishジョブを再実行することで問題を解決できます。 、指定されたコミットのイメージを再作成してレジストリにプッシュします。

次に、展開の表示ボタンをクリックすると、ブラウザーでhttp://your_server_IPが開き、個人のWebサイトの見出しが表示されます。

最後に、サーバーにデプロイされたコンテナーを確認します。 すでに切断している場合は、ターミナルにアクセスして再度ログインしてください(sammydeployerの両方のユーザーで機能します)。

  1. ssh sammy@your_server_IP

次に、実行中のコンテナを一覧表示します。

  1. docker container ls

my-appコンテナを一覧表示します。

Output
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5b64df4b37f8 registry.your_gitlab.com/your_gitlab_user/your_project/master:your_commit_sha "nginx -g 'daemon of…" 4 hours ago Up 4 hours 0.0.0.0:80->80/tcp my-app

Dockerコンテナーの管理の詳細については、 Ubuntu18.04にDockerをインストールして使用する方法ガイドをお読みください。

これで、展開が検証されました。 次のステップでは、デプロイメントをロールバックするプロセスを実行します。

ステップ8—展開のロールバック

次に、Webページを更新します。これにより、新しいデプロイメントが作成され、GitLab環境を使用して以前のデプロイメントが再デプロイされます。 これは、デプロイメントに欠陥がある場合のデプロイメントロールバックのユースケースをカバーしています。

index.htmlファイルに少し変更を加えることから始めます。

  1. GitLabで、プロジェクトの概要に移動し、index.htmlファイルを開きます。
  2. 編集ボタンをクリックしてオンラインエディタを開きます。
  3. ファイルの内容を次のように変更します。
index.html
<html>
<body>
<h1>My Enhanced Personal Website</h1>
</body>
</html>

ページの下部にある[変更をコミット]をクリックして、変更を保存します。

変更をデプロイするために、新しいパイプラインが作成されます。 GitLabで、 CI / CD>Pipelinesに移動します。 パイプラインが完了したら、ブラウザでhttp://your_server_IPを開いて、 My PersonalWebsiteの代わりにMyEnhanced PersonalWebsiteを表示する更新されたWebページを表示できます。

操作>環境>本番に移動すると、新しく作成されたデプロイメントが表示されます。 次に、最初の古い展開の再展開ボタンをクリックします。

A list of the deployments of the production environment in GitLab with emphasize on the re-deploy button of the first deployment

ロールバックボタンをクリックしてポップアップを確認します。

その古いパイプラインのデプロイジョブが再開され、ジョブの概要ページにリダイレクトされます。 ジョブが終了するのを待ってから、ブラウザでhttp://your_server_IPを開くと、最初の見出し個人用Webサイトが再び表示されます。

このチュートリアルで達成したことを要約してみましょう。

結論

このチュートリアルでは、GitLab CI/CDを使用して継続的デプロイパイプラインを構成しました。 HTMLファイルとDockerfileで構成される小さなWebプロジェクトを作成しました。 次に、.gitlab-ci.ymlパイプライン構成を次のように構成しました。

  1. Dockerイメージをビルドします。
  2. Dockerイメージをコンテナーレジストリにプッシュします。
  3. サーバーにログインし、最新のイメージをプルして、現在のコンテナーを停止し、新しいコンテナーを開始します。

これで、GitLabは、リポジトリにプッシュするたびにWebページをサーバーにデプロイします。

さらに、GitLabとサーバーでのデプロイを確認しました。 また、2番目のデプロイメントを作成し、GitLab環境を使用して最初のデプロイメントにロールバックしました。これは、欠陥のあるデプロイメントに対処する方法を示しています。

この時点で、デプロイメントチェーン全体が自動化されています。 コードの変更を世界中や顧客とより頻繁に共有できるようになりました。 その結果、フィードバックの収集とコード変更の公開に必要な時間が短縮されるため、開発サイクルが短くなる可能性があります。

次のステップとして、ドメイン名でサービスにアクセスできるようにし、HTTPSとの通信を保護することができます。DockerコンテナのリバースプロキシとしてTraefikを使用する方法は適切なフォローアップです。