Ubuntu Weekly Recipe

第531回RStudio ServerのDockerイメージのカスタマイズ

第527回ではRStudioのインストール方法を、第529回ではDockerを使ったRStudio Serverについて紹介しました。今回はより便利にDockerイメージを使えるよう、カスタマイズする方法を紹介しましょう。

TinyTeXで日本語PDFを生成できるようにする

第529回でも紹介しているように、Rocker版のRStudio ServerではTeXの実行環境としてRStudioの開発者が作成したTinyTeXを採用しています。

これは一般的なLinuxで使われているTeX Liveではパッケージサイズが大きくなりがちで、結果としてDockerイメージのサイズも大きくなってしまうためです。TeX Liveベースで作られたTinyTeXはTeX Liveのうち最低限必要なものをコンテナイメージに取り込んでおき、残りは必要に応じてtlmgrコマンドでインストールする仕組みになっています。またRの実行環境からtlmgrコマンドを実行できる、tinytexライブラリーも同梱されています。

第529回同様、rocker/verseイメージを起動してみましょう。

$ sudo docker run --rm -d -p 8787:8787 --name verse rocker/verse

メニューからR Markdownファイルを作成し、PDFを選択、output: pdf_documentの部分を次のように変更します。

output:
  pdf_document:
    latex_engine: lualatex
documentclass: ltjsbook

この状態でKnitを実行すると(Ctrl+Shift+Kを押すと⁠⁠、pandocがRmdファイルをTeXファイルに変換し、lualatexがPDFを生成しようとします。しかしながらdocumentclassで指定したクラスファイルltjsbook.clsが見つからないために、luatexjaパッケージのインストールを試みます。

実際、R Markdownのプリアンブルにlatex_engineとしてlualatexを記述した上でPDFを生成しようとすると、いろいろなTeXパッケージをインストールしようとします。documentclassに「ltjsbook」を書いておくと、ltjsbook.clsファイルを提供しているluatexjaパッケージのダウンロードを試みます。

tlmgr search --file --global '/ltjsbook.cls'
Trying to automatically install missing LaTeX packages...
tlmgr install luatexja
(中略)
tlmgr install luatexja
tlmgr install: package already present: luatexja
tlmgr path add
tlmgr search --file --global '/ltjsbook.cls'
! LaTeX Error: File `ltjsbook.cls' not found.

しかしながら、この方法ではなぜかうまくいきません。どうもインストールしたファイルの検索パスがらみの問題のようですが、Dockerイメージの権限が原因かもしれません。あらかじめ必要なパッケージがわかっている場合は、手動でインストールしたほうが良さそうです。

TinyTexにはR向けのtinytexライブラリーがあります。これを使えば、Rコマンドからtlmgrを実行できます。

> tinytex::tlmgr_search('/ltjsbook.cls')
tlmgr search --file --global '/ltjsbook.cls'
luatexja:
    texmf-dist/tex/luatex/luatexja/ltjsbook.cls

> tinytex::tlmgr_install('luatexja')
(中略)
copy /opt/TinyTeX/tlpkg/texlive.tlpdb.tmp to
/opt/TinyTeX/tlpkg/texlive.tlpdb failed:
Operation not permitted at
/opt/TinyTeX/tlpkg/TeXLive/TLPDB.pm line 628.
tlmgr path add

今度は「Operation not permitted」エラーが出てしまいました。rockerイメージのrstudioアカウントはプライマリグループはrstudioであり、サブグループとしてstaffグループに属しています。また、/opt/TinyTex以下の所有者はrootになっているものの、グループはstaffになっています。よってrstudioアカウントでも、/opt/TinyTex以下を変更できるのです。

しかしながらプライマリグループはrstudioであるため、Rコンソールから作成したファイル/opt/TinyTeX/tlpkg/texlive.tlpdb.tmpのグループはrstudioになってしまいます。グループの変更を伴うコピーが「Operation not permitted」と言われているようです[1]⁠。

さてRコンソールからのTeXパッケージのインストールはうまくいきませんでした。そこで泥臭い方法ではありますが、とりあえずはDockerのコンテナに入ってインストールしてしまいましょう。コンテナ起動時--name verseでコンテナに名前を付けておいたので、以下のようにコンテナ上で直接tlmgrを実行できます。

$ sudo docker exec -it verse tlmgr install luatexja \
  luatexbase ctablestack adobemapping ms filehook
running mktexlsr ...
done running mktexlsr.
tlmgr: package log updated: /opt/TinyTeX/texmf-var/web2c/tlmgr.log

luatexjaの依存関係から最低限必要なパッケージも列挙しています。

また日本語フォントもRockerイメージにはインストールされていませんので、それも合わせてインストールしておきます。

$ sudo docker exec -it verse apt update
$ sudo docker exec -it verse apt install fonts-ipaexfont

あとはKnitを実行すれば、無事に日本語PDFを生成できるはずです。

生成されたTeXファイルを残しておきたい

R Markdownから生成されるTeXファイルをPDFにコンパイルする際の問題に対応したい場合、TeXファイルが残っていると便利です。R Markdownのプリアンブルにkeep_tex: trueを追加すれば実現できます。

output:
  pdf_document:
    latex_engine: lualatex
    keep_tex: true
documentclass: ltjsbook

Knitでビルドすると、ソースファイルと同じディレクトリにTeXファイルができているはずです。あとはコンテナの中で、トライアンドエラーを繰り返せば良いでしょう。

$ sudo docker exec -it verse bash
# lualatex foo.tex

たとえば特定のスタイルファイルが見つからないエラーが発生したとします。

# lualatex foo.tex
(中略)
! LaTeX Error: File `filehook.sty' not found.

Type X to quit or <RETURN> to proceed,
or enter new name. (Default extension: sty)

Enter file name: x

tlmgrコマンドでそのファイルを提供しているパッケージを探してみましょう。

# tlmgr search --global --file '/filehook.sty'
(中略)
filehook:
        texmf-dist/tex/latex/filehook/filehook.sty
        texmf-dist/tex/latex/filehook/pgf-filehook.sty

filehookパッケージであることがわかったので、それをインストールします。

# tlmgr install filehook

コンテナに加えた変更は一通り記録しておいて、後ほどDockerfileを作る際の参考にすれば良いでしょう。

ちなみにUbuntuのTeX Liveパッケージでもtlmgrコマンドは提供されています。しかしながらDebianパッケージ版のTeXシステムとの整合性を取ることが難しいため、システムグローバルへのインストールは推奨されていません。あくまでホームディレクトリーのようなユーザーローカルな環境へインストールする仕組みだと考えましょう。

RStudioを日本語ロケールで動かす

Rocker版のRStudioはen_US.UTF-8ロケールで動作しています。また、タイムゾーンはUTCです。RユーザーならロケールはUTF-8にさせ設定されていれば英語でもほぼ問題ないものと思いますが、タイムゾーンはJSTのほうが何かと便利なことも多いでしょう。そこで両方の変更方法を紹介しておきます。

ロケールはコンテナ起動時に環境変数で指定できます。

$ sudo docker run --rm -d -p 8787:8787 --name verse \
  -e LANG=ja_JP.UTF-8 -e LC_ALL=ja_JP.UTF-8 rocker/verse

しかしながらコンテナ内部に日本語のロケール情報が生成されていないので、上記だけでは不十分です。さらにコンテナの中で次のコマンドを実行する必要があります。

$ sudo docker exec -it verse bash -c \
  'echo "ja_JP.UTF-8 UTF-8" > /etc/locale.gen && locale-gen && update-locale LANG=ja_JP.UTF-8'

ちなみにR本体が--disable-nlsオプション付きでビルドされているためRockerのイメージのRは日本語化されていません。このため表示されるメッセージはすべてオリジナルの英語です。

タイムゾーンについてはコンテナ起動時に環境変数TZを指定すれば変更できます。

$ sudo docker run --rm -d -p 8787:8787 --name verse \
  -e TZ=Asia/Tokyo rocker/verse

恒久化したい場合はやはりいずれもDockerfileに記述することになるでしょう。

Dockerfileで自分用イメージを作成する

TinyTeXの対応方法や日本語ロケールへの変更方法がわかれば、対応済みのイメージを作りたくなるでしょう。またTinyTeXに限らず、特定のRパッケージがインストール済みのイメージを作りたいこともよくあるはずです。これはDockerfileを書くことで実現できます。ベースイメージとしてRockerのイメージを指定しておけば、Rockerからの差分だけをDockerfileに記録できます。

具体的な例を見てみましょう。ここで行った設定は次のような内容で、⁠Dockerfile」という名前のファイルに記述します。

FROM rocker/verse

## フォントのインストールと余計なデータの削除
RUN apt-get update \
  && apt-get install -y fonts-ipaexfont \
  && apt-get remove --purge -y $BUILDDEPS \
  && apt-get autoremove -y \
  && apt-get autoclean -y \
  && rm -rf /var/lib/apt/lists/*

## 標準のロケールとタイムゾーンの変更
ENV LANG ja_JP.UTF-8
ENV LC_ALL ja_JP.UTF-8
ENV TZ Asia/Tokyo
RUN echo "ja_JP.UTF-8 UTF-8" >> /etc/locale.gen \
  && locale-gen ja_JP.UTF-8 \
  && update-locale LANG=ja_JP.UTF-8

## TeXパッケージのインストール
RUN tlmgr install \
  luatexja \
  luatexbase \
  ctablestack \
  adobemapping \
  ms \
  filehook

## Rパッケージのインストール
RUN install2.r --error \
  --deps TRUE \
  githubinstall

このDockerfileを元にDockerイメージを作ってみましょう。

$ sudo docker build -t myverse .
Sending build context to Docker daemon 111.3 MB
Step 1/8 : FROM rocker/verse
 ---> 49ba46f8dce6
(中略)
The downloaded source packages are in
        ‘/tmp/downloaded_packages’
 ---> e40cbe97aee2
Removing intermediate container 1febd5a9203c
Successfully built e40cbe97aee2

-t myverseで作成したイメージに対してタグ名「myverse」を付けています。

これでdocker run時にRockerイメージの代わりにこのmyverseイメージを指定すれば、最初からR Markdownで日本語PDFファイルを生成できます。

$ sudo docker run --rm -d -p 8787:8787 --name verse myverse

作成したDockerfileをGitHubの任意のリポジトリにアップロードしておき、Docker Hubとそのリポジトリを連携しておけば、Docker Hub上で自動的にイメージがビルドされます。あとは様々な環境でこのイメージをpullすれば良いだけで済むようになるため、環境構築が非常にかんたんになります。

おすすめ記事

記事・ニュース一覧