第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すれば良いだけで済むようになるため、環境構築が非常にかんたんになります。