Cloud BuildでrockerイメージにGitHub上のRパッケージをインストール

目的

GitHub上にRパッケージのソースリポジトリがあり、rocker/rstudio にこのRパッケージをインストールしたDockerイメージを作成する場面を考えます。このとき、ビルド中にGitHubと接続するため認証情報を使用する必要があります。

過去の記事 “Dockerイメージビルド時の秘密情報の扱い方に関するまとめ” で述べたように、ビルド時に秘密情報を使用する際はビルド後のイメージに残らないよう慎重に取り扱う必要があります。

別の記事 “rockerにGitHub上のRパッケージをインストールしたイメージの安全なビルド方法” では、認証情報として Personal Access Token を BuildKit や マルチステージビルド で取り扱う方法を紹介しました。ローカルマシン上でビルドを実行する場合は基本的に BuildKit を利用するのが良いでしょう。

本記事ではローカル環境ではなくGCPの Cloud Build でビルドパイプラインを構築することを考えます。 認証には Personal Access Token ではなくSSH秘密鍵を使用します。 この秘密鍵はビルド対象のコンテナには渡さず、docker build の前に GitHub からソースをダウンロードするようにします。 これは前の記事 “Cloud Build で シークレット マネージャー を使用して GitHub プライベートリポジトリに接続する” で紹介した方法の応用例になります。


概要

適当なGCPプロジェクト projectname とRパッケージソースのGitHubリポジトリ username/pkgname 、およびビルド構成管理用のGitHubリポジトリ username/imagename を用意します。

GitHub と Cloud Build および Container Registry (GCR) を組み合わせて、次のような流れのビルドパイプラインを構築します:

  • ビルド構成管理リポジトリ username/imagename へプッシュされると Cloud Build の自動トリガーが実行される
  • rocker/rstudio にRパッケージ username/pkgname をインストールしたイメージがビルドされる
  • ビルドされたイメージがコンテナレジストリ gcr.io/projectname にプッシュされる

認証に使用するSSH秘密鍵については次のように扱います:

  • ビルド後のイメージに秘密鍵が残らないよう、docker build では秘密鍵を直接扱わないようにします。そのため、Dockerfile 内で remotes::install_githubinstallGithub.r などは使用せず、Rパッケージはソースからインストールします。
  • Rパッケージのソースは docker build を行う前のステップでGitHubからダウンロードします。
  • Cloud Buildへの秘密鍵の受け渡しには シークレット マネージャー を利用します。

基本的な構成は前の記事 “Cloud Build で シークレット マネージャー を使用して GitHub プライベートリポジトリに接続する” と同一です。Cloud Buildやシークレット マネージャーの使用方法についてはそちらでより詳しく説明しています。

図1: シーケンス図


GCPのプロジェクト設定

このプロジェクト projectname では Cloud Build、シークレット マネージャー、Container Registryを有効化しておきます。

ローカルマシンに gcloud コマンドラインツールをインストールしておきます。

次のコマンドで gcloud の認証を実行します:

gcloud auth login

対象プロジェクトを projectname に設定します:

gcloud config set project projectname

Docker CLIでGCRに接続するため、認証ヘルパーの設定を行っておきます(参考):

gcloud auth configure-docker

GitHubマシンユーザー

GitHub リポジトリ username/pkgname にSSH鍵認証で接続するための準備を行います。

デプロイキーを使う方法もありますが、ここでは マシンユーザー を利用します(デプロイキーは同一の鍵を複数のリポジトリで使用できないため、この方法で複数のRパッケージをインストールしたい場合には不便です)。 マシンユーザーとは、GitHubを自動処理で利用するためのユーザーアカウントです。

マシンユーザーは通常のGitHubユーザーアカウントと同じように作成します。 ここではマシンユーザーの名前を machine-user-name とします。

このユーザー用のSSH公開鍵 github_machine_user_key.pub と秘密鍵 github_machine_user_key のペアを生成します:

ssh-keygen -f github_machine_user_key

GitHubの設定ページ を開き、github_machine_user_key.pub をマシンユーザーの公開鍵として登録します。

またソースリポジトリ username/pkgname 側ではユーザー machine-user-name を Collaborator に招待するなどしてアクセスを許可します。


シークレット マネージャー

次のようにしてGitHubマシンユーザーのSSH秘密鍵 github_machine_user_key をシークレット マネージャーに保存します:

gcloud secrets create github_machine_user_key --data-file=github_machine_user_key

この秘密鍵に Cloud Build からアクセスできるようにするため、 Cloud IAM でサービスアカウント [PROJECT_NUMBER]@cloudbuild.gserviceaccount.com に対して Secret Manager のシークレット アクセサー の役割を与えます。ここで [PROJECT_NUMBER] はプロジェクト projectname のプロジェクト番号です。

ビルド構成管理リポジトリ username/imagename

このリポジトリでは cloudbuild.yamlDockerfile 、および known_hosts の3つのファイルを作成します。そして次のような内容のビルド構成を記述します:

  • シークレットマネージャーからGitHubマシンユーザーのSSH秘密鍵を取得する(ステップ1)
  • マシンユーザーとしてGitHubにSSHで接続し、Rパッケージのソースをクローンする(ステップ2)
  • docker build でRパッケージをソースからインストールする(ステップ3)
  • ビルドされたDockerイメージはGCRに保存する(成果物)

cloudbuild.yaml

これが主となるビルド構成設定ファイルです。 以下のような内容で保存します:

steps:
# シークレット マネージャからSSH秘密鍵をダウンロードする
- name: gcr.io/cloud-builders/gcloud
  entrypoint: bash
  args:
  - -c
  - gcloud secrets versions access --secret=github_machine_user_key latest > /root/.ssh/id_rsa
  volumes:
  - name: ssh
    path: /root/.ssh
# GitHubとのSSH接続のセットアップを行う
- name: gcr.io/cloud-builders/git
  entrypoint: bash
  args:
  - -c
  - |
    chmod 600 /root/.ssh/id_rsa
    cat <<EOF >/root/.ssh/config
    Hostname github.com
    IdentityFile /root/.ssh/id_rsa
    EOF
    cat <<EOF >/root/.gitconfig
    [user]
        name = machine-user-name
        email = github-machine-user@example.com
    EOF
    mv known_hosts /root/.ssh/known_hosts
  volumes:
  - name: ssh
    path: /root/.ssh
# GitHubからRパッケージのソースをダウンロードする
- name: gcr.io/cloud-builders/git
  args: ['clone', '--depth', '1', 'git@github.com:username/pkgname', '/src/pkgname']
  volumes:
  - name: ssh
    path: /root/.ssh
  - name: src
    path: /src
# docker build を実行する
- name: gcr.io/cloud-builders/docker
  args: ['build', '-t', 'gcr.io/projectname/imagename:tag', '.']
  volumes:
  - name: src
    path: /src
# ビルドされたイメージをGCRに保存する
images: ['gcr.io/projectname/imagename:tag']

Dockerfile

こちらは以下のような内容とします:

FROM rocker/rstudio:3.6.3

COPY /src /src

RUN install2.r -r NULL -- /src/pkgname && rm -rf /src

known_hosts

このファイルは github.com を既知のSSHホストとして確認するために使用されます。 次のコマンドで生成します:

ssh-keyscan -t rsa github.com > known_hosts

以上のファイルを作成したらコミットしてGitHubリポジトリ username/imagename にプッシュします。


ビルドトリガーの設定

GCPコンソールの “ビルドトリガー” ページ を開き、次のような設定でビルドトリガーを作成します:

  • GitHubリポジトリ username/imagename を Cloud Source Repositories にミラーリングしてビルドソースとする
  • master ブランチへのプッシュなど適当なトリガー条件を設定
  • ビルド構成ファイルとして cloudbuild.yaml を使用する

テスト

リポジトリ username/imagename に適当なコミットを push するか、手動でトリガーを実行するとビルドが開始されます。

ビルドの状態やログは “ビルド履歴” のページ から確認することができます。 ビルドが完了しGCRにDockerイメージ gcr.io/username/imagename:tag が保存されれば成功です。

このイメージをローカルに pull して rocker/rstudio と同じように起動すれば、RStduio Serverに接続してパッケージがインストール済みになっていることを確認できます。

# GCRからイメージを pull する
docker pull gcr.io/username/imagename:tag
# コンテナを起動する
docker run -d --rm -p 8787:8787 -e PASSWORD=password -e DISABLE_AUTH=true gcr.io/username/imagename:tag
# http://localhost:8787 から RStudio Server に接続する

まとめ

  • Google Cloud Buildrocker/rstudio をベースとした Docker イメージを作成するビルドパイプラインを構築しました。
  • このビルドの中でGitHub上のRパッケージをインストールするようにしました。
  • 前の記事 のように、シークレット マネージャー から認証用のSSH秘密鍵を取得してGitHubに接続し、Rパッケージのソースをダウンロードするような構成としました。
  • docker build の中では remotes::install_github などを使わず、ダウンロード済みのソースからRパッケージをインストールするので、認証情報がビルドされたイメージ内に残ってしまう心配はありません。

関連記事