/* terashim.com */

システム開発・データエンジニアリング・データ分析についての個人的なノート

rockerにGitHub上のRパッケージをインストールしたイメージの安全なビルド方法

Rパッケージは CRAN からインストールするのが普通ですが、 まだ CRAN に登録されていない開発版のRパッケージや、独自に開発したRパッケージを GitHub からインストールして使いたいこともあります。

この記事では Docker イメージ rocker/rstudiorocker/tidyverse をベースに、GitHub上にあるRパッケージをインストールして新しいDockerイメージをビルドしたいという場面を考えます。

GitHub上のリポジトリからRパッケージをインストールするには、 remotes パッケージの install_github 関数 を使用するのが便利です。その際、プライベートリポジトリに接続するため、あるいはGitHub APIの呼び出し頻度制限を緩和するため、Personal Access Token による認証が必要になります。

関数 install_github を実行する際、あらかじめ環境変数 GITHUB_PAT に Personal Access Token の値が入れてあれば自動的に認証が行われます。通常の利用時(Desktop版RStudioやRStudio Server上の操作でインストールする場合)はそれで良いのですが、Dockerイメージのビルド中に Personal Access Token を使用する場合は、ビルドされたイメージ内に Personal Access Token が残らないよう安全に取り扱う必要があります。

Dockerイメージのビルドにおける秘密情報の扱いについては 前の記事 にまとめました。

この記事では Personal Access Token の取り扱いについて

  • Buildkit の RUN --mount=type=secret を利用する
  • マルチステージビルドを利用する

の2つの方法を適用する例を示します。

なおこの記事のサンプルコードは GitHub でも公開しています


事前準備

インストールしたいRパッケージが GitHub のリポジトリ https://github.com/username/pkgname にあるものとします。 username/pkgname は適当なユーザー名やリポジトリ名に読み換えてください(例: tidyverse/tidyr など)。

このリポジトリにアクセスできるGitHubユーザーアカウント(つまり自分のGitHubアカウント)で Personal Access Token を作成しておきます。


Buildkit の RUN --mount=type=secret を利用する方法

この方法は秘密情報を受け渡すための専用機能を使うため、可読性が高く安全です。 その反面、BuildKit対応の環境でしか使えず、Docker Compose との併用ができないという難点もあります。

手順

作業ディレクトリに .env ファイルと Dockerfile を作成します。

.env ファイルは次のような内容とします:

GITHUB_PAT=[Personal Access Token の値]

Dockerfile の内容は以下のようにします:

# syntax = docker/dockerfile:1.0-experimental
FROM rocker/tidyverse:3.6.3

RUN --mount=type=secret,id=dotenv,dst=/run/secrets/.env \
  set -a && . /run/secrets/.env && set +a \
  && install2.r remotes \
  && installGithub.r username/pkgname

この状態で次のコマンドを実行すると、Rパッケージ username/pkgname をインストールした Docker イメージ myrstudio:latest がビルドされます:

DOCKER_BUILDKIT=1 \
  docker build -t myrstudio:latest \
  --secret id=dotenv,src=.env \
  .

解説

上のビルドコマンドでは秘密情報の書き込まれたファイル .env をマウントするため

  • 環境変数 DOCKER_BUILDKIT=1 の設定によって BuildKit を有効にする
  • --secret id=dotenv,src=.env のオプションによって .env ファイルをID dotenv のシークレットとして渡す

という対応を行っています。

一方 Dockerfile では次のようにして .env ファイルを受け取り環境変数 GITHUB_PAT を設定しています:

  • 1行目のコメント # syntax = docker/dockerfile:1.0-experimental によって RUN --mount=type=secret の文法を有効化
  • RUN --mount=type=secret,id=dotenv,dst=/run/secrets/.env ....env ファイルをコンテナ内のファイルパス /run/secrets/.env にマウント
  • コマンド set -a && . /run/secrets/.env && set +a によって .env ファイルの内容で環境変数を設定

そして、RUN の残りのコマンド install2.r remotes && installGithub.r username/pkgname で GitHubリポジトリ username/pkgname からRパッケージをインストールしています。このとき remotes パッケージの install_github 関数が呼び出され、環境変数 GITHUB_PAT に設定された Personal Access Token が認証に使用されます。

RUN --mount=type=secret でマウントされたファイルはビルドされたイメージに残らないことが保証されているため、Personal Access Token を安全に渡すことができています。


マルチステージビルドを使う方法

BuildKitRUN --mount=type=secret と異なり、マルチステージビルドは本来秘密情報を扱うための仕組みではないので可読性の面で劣ります。中間イメージと最終イメージの違いを意識し、最終イメージに秘密情報が残らないよう注意して運用する必要があります。 個人的には Docker Compose を使いたい場合に限ってこの方法を採用することにしています。

手順

作業ディレクトリに Dockerfile を以下の内容で作成します:

FROM rocker/rstudio:3.6.3 AS builder

ARG GITHUB_PAT
RUN install2.r remotes && installGithub.r username/pkgname

FROM rocker/rstudio:3.6.3
COPY --from=builder /usr/local/lib/R/site-library /usr/local/lib/R/site-library

次のコマンドを実行すると、Rパッケージ username/pkgname をインストールした Docker イメージ myrstudio:latest がビルドされます:

docker build \
  -t myrstudio:latest \
  --no-cache \
  --build-arg GITHUB_PAT=[Personal Access Token の値] \
  .

解説

上のビルドコマンドでは、オプション --build-arg GITHUB_PAT=[Personal Access Token の値] で環境変数 GITHUB_PAT に Personal Access Token のを渡しています。 前の記事 でも述べた通り、本来はこのように --build-arg で秘密情報を渡すのは非推奨とされています(参考: Docker 公式ドキュメント)。

docker build が実行されると、まず Dockerfile の前半部分

FROM rocker/rstudio:3.6.3 AS builder

ARG GITHUB_PAT
RUN install2.r remotes && installGithub.r username/pkgname

で中間イメージ builder のビルドが行われます。 このとき --build-arg で渡された変数 GITHUB_PAT を使用し、GitHubから username/pkgname パッケージをインストールしています。 これで中間イメージ builder のライブラリ /usr/local/lib/R/site-libraryusername/pkgname パッケージがインストールされた状態になります。

続けて Dockerfile の後半部分

FROM rocker/rstudio:3.6.3
COPY --from=builder /usr/local/lib/R/site-library /usr/local/lib/R/site-library

で中間イメージ builder のライブラリ /usr/local/lib/R/site-library が最終イメージにコピーされます。 これで最終イメージのライブラリに username/pkgname パッケージがインストールされた状態になります。

中間イメージ builder のレイヤーは最終イメージに保存されないので、--build-arg で渡された Personal Access Token の値は最終イメージに残らないようになっています。

マルチステージビルド + Docker Compose

マルチステージビルドは Docker Compose と併用できるので、 docker build コマンドのオプション --build-arg GITHUB_PAT=[Personal Access Token の値] で与える変数 GITHUB_PAT を設定ファイル docker-compose.yml で管理することができます。

まず以下のような内容の .env ファイルを作成し、Personal Access Token の値を保存します:

GITHUB_PAT=[Personal Access Token の値]

次に設定ファイル docker-compose.yml を以下の内容で作成します:

version: "3.7"
services:
  myrstudio:
    image: myrstudio:latest
    build:
      context: .
      args:
        GITHUB_PAT: ${GITHUB_PAT}
    ports:
      - "8787:8787"
    environment:
      - PASSWORD=password
      - DISABLE_AUTH=true

Dockerfile の内容は上と変わりません。

この状態で次のコマンドを実行すると Docker イメージ myrstudio:latest のビルドが行われます:

docker-compose build --no-cache

解説

Docker Compose では .env ファイルの内容が反映されます(参考)。

設定ファイル docker-compose.yml でビルドに関する記述はこの部分です:

    build:
      context: .
      args:
        GITHUB_PAT: ${GITHUB_PAT}

ここで .env に書き込まれた GITHUB_PAT の値がビルド変数 GITHUB_PAT として渡されるよう設定しています。


参考