ログイン中のQiita Team
ログイン中のチームがありません

Qiita Team にログイン
コミュニティ
OrganizationイベントアドベントカレンダーQiitadon (β)
サービス
Qiita JobsQiita ZineQiita Blog
LaTeX
TeX
入門
Docker
入門編
74
どのような問題がありますか?

この記事は最終更新日から1年以上が経過しています。

投稿日

更新日

【Docker超入門】DockerでつくるLaTeX環境

本稿に関して動作がおかしいところや不明な点,ツッコミなどがあれば適宜コメントいただけるとありがたいです.

はじめに

大学の研究会で講義を1コマ持つことになったため,弊研究会では必須なLaTeX環境をDockerで作る方法を解説することにしました.

読者としてはDockerの知識が一切ない人を想定しています.

なお,本稿ではLaTeX自体の使い方に関してはあまり触れません.

LaTeXとは?

LateXとは,Donald E. Knuth氏が開発したレイアウトシステムのTexをLeslie Lamport氏が文書作成のために改良したものだそうです.主に学術論文を書く際に使用されており,数式を綺麗にレイアウトしてくれます.

例えば,世界一美しいとされるオイラーの等式でさえ,テキストで表現すると美しくないですが,

e^{iπ} + 1 = 0

TeXを用いてコンパイルすれば,以下のように美しくレイアウトされます.

eiπ+1=0

Dockerとは?

Dockerとはコンテナ仮想化技術のプラットフォームです.

image.png

従来の仮想化技術では,ホストOS上の仮想ハードウェアや,Hypervisorを利用してゲストOSを動かすことで環境を隔離していたのに対し,DockerではDocker EngineがホストOSのカーネルの利用を上手く管理することでプロセスとユーザを隔離しているため,VMに比べて軽量な仮想化環境を実現しています.

事前準備

本稿では,このDockerを用いてLaTeXのディストリビューションであるTeXLiveの環境を行うため,まずはDockerと生成されたPDFの閲覧用にSkimをインストールします,

Dockerのインストール

下記ページからDockerHubのアカウントを作成しdmgをダウンロードしてインストールします.
MAC=> https://docs.docker.com/docker-for-mac/install/
Windows=> https://docs.docker.com/docker-for-mac/install/

Skim(軽量PDFビューワー)のインストール

下記ページよりダウンロードしてインストール
https://skim-app.sourceforge.io/

全体像を把握する

今回は下記のような構成でDockerを用いてLaTeX環境を構築します.

image.png

とりあえずDockerでLaTex環境を建ててみる

DockerとSkimのインストールが完了したら,筆者が事前に用意したTeXLiveのDockerイメージ(nontan18/texlive)を用いて以下のコマンドでLaTeX環境を実際に構築してみましょう.

// 作業用のworkディレクトリを作成し移動
$ mkdir work && cd work

workディレクトリに入ったら下記の内容のファイルを作成します.

sample.tex
\documentclass[twocolumn, a4j]{article}
\usepackage{multirow}
\usepackage{amsmath,amssymb}
\usepackage[T1]{fontenc}

\title{サンプル用のTexファイル}
\renewcommand{\thefootnote}{\fnsymbol{footnote}}
\author{Nozomu Miyamoto\footnotemark[2] nontan@sfc.wide.ad.jp}
\renewcommand{\thefootnote}{\arabic{footnote}}
\date{\today}

\begin{document}

\twocolumn[
\begin{@twocolumnfalse}
  \maketitle
  \vspace{-6mm}
  \begin{abstract}
    ここに概要を書きましょう.
  \end{abstract}
  \vspace{2mm}
\end{@twocolumnfalse}
]

\renewcommand{\thefootnote}{\fnsymbol{footnote}}
\footnotetext[2]{慶應義塾大学 村井研}
\renewcommand{\thefootnote}{\arabic{footnote}}

\section{はじめに}

   hoge hoge hoge hoge

\renewcommand{\refname}{参考文献}
\begin{thebibliography}{数字}
  \bibitem[opt]{key} 文献情報
\end{thebibliography}

\end{document}

// 生成したdocument.texをdocument.pdfにコンパイル
$ docker run -v $(pwd):/root/work -it nontan18/texlive:stable latexmk --pvc ./sample.tex

これでworkディレクトリ内にsample.pdfが出力されますので,SkimなどのPDFビューワーを使って表示してみましょう.

表示できたら,お好きなエディタを用いて,sample.texを編集してみます.

$ vim sample.tex

編集が完了すると自動でコンパルが始まり,document.pdfが更新されるかと思います.

Dockerの基本的な使い方

このセクションでは上記のDockerコマンドで一体何が起きているのかを説明します.

docker run

docker runはDockerイメージを元にDockerコンテナを起動するコマンドです.
runコマンドの第1引数で指定したコマンドが起動したコンテナ内で実行されます.

上記の

$ docker run -v $(pwd):/root/work -it nontan18/texlive:1.0.0 latexmk --pvc ./sample.tex

においてはlatexmk --pvc ./sample.texコマンドが実行されています.
(latexmk --pvc ./sample.texコマンドは任意のTexファイルを監視して更新されるたびに自動コンパイルするコマンドです.)

-tオプションでDockerイメージを参照することができます.
上記の例ではDockerHubに登録されているnontan18/texliveというイメージ(より詳細には,その中でも1.0.0タグがついているイメージ)を参照して,それを元にDockerコンテナを作成しています.
(ちなみに,このDockerHubのようなDockerImageが登録されている場所をDockerRegistryと呼びます.)

-vオプションはボリュームのマウントオプションで,DockerホストのボリュームをDokcerコンテナ内の任意の場所にマウントすることができます.上記の例では,texの作業用に作った
(-v オプションでは相対リンクでボリュームを指定できないので,pwdコマンドで現在のディレクトリを取得してそれを引数に取っています.)

docker ps

docker runコマンドで動いているDockerコンテナはdocker psコマンドで確認することができます.

$ docker ps
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS                               NAMES
1349e83ba710        nontan18/texlive:1.0.0            "latexmk --pvc ./sam…"   5 seconds ago       Up 3 seconds                                            gallant_galileo

docker exec

また,docker exec -it [CONTAINER ID] [COMMAND]コマンドを使うと,起動中のコンテナ内でコマンドを実行することができます.

// $CONTAINER_IDはdocker psで取得したコンテナのIDに変更(example:1349e83ba710)
$ docker exec -it $CONTEAINER_ID sh
// ここからコンテナ内
~/work# ls
sample.aux  sample.dvi  sample.fdb_latexmk  sample.fls  sample.log  sample.pdf  sample.synctex.gz  sample.tex
// TeXLiveの絵文字パッケージが入っているか調べる
~/work# tlmgr search fontawesome
// コンテナ内にTeXLiveの絵文字パッケージをインストール
~/work# tlmgr install fontawesome
//...省略
tlmgr: package repository http://mirror.utexas.edu/ctan/systems/texlive/tlnet (verified)
[1/1, ??:??/??:??] install: fontawesome [498k]
running mktexlsr ...
done running mktexlsr.
running updmap-sys ...
done running updmap-sys.
tlmgr: package log updated: /usr/local/texlive/2019/texmf-var/web2c/tlmgr.log
// コンテナから出る
~/work# exit
exit
// ホストのシェルに戻った
$

上記の例では先程建てたコンテナ内でshコマンドを実行して,ターミナルからコンテナ内に入り,TeXLiveの絵文字パッケージ(fontawsome)が入っていないことを確認して,TeXLiveパッケージマネージャー(tlmgr)を用いて絵文字パッケージをインストールしました.

docker commit(※あんま使わない)

ただ,これだけではDockerコンテナ内での変更でしか無いため,一度Dockerコンテナを削除し,docker runコマンドで再度コンテナを建てると,絵文字パッケージはインストールされていない状態に戻ってしまいます.

このDockerコンテナの変更をDockerイメージに反映させるためにはdocker commit [CONTAINER ID] [IMAGE NAME]コマンドを用います.

$ docker commit 1349 nontan18/texlive:stable
sha256:a64dcff68e35d56082c52817f9432b9a2c1142d32a631f461474188912207341

これでローカルのイメージに変更が反映され,新しいイメージのSHA256ハッシュ値が出力されます.

docker images

ローカルのDockerイメージはdocker imagesコマンドで確認することができ,以下の用に,変更したDockerイメージ(nontan18/texlive)のIMAGE IDが先程のハッシュ値になっていることが確認できます.

$ docker images
REPOSITORY                                  TAG                 IMAGE ID            CREATED             SIZE
nontan18/texlive                            stable              a64dcff68e35        3 minutes ago       1.48GB

実際に,docker run -it [IMAGE_NAME] [COMMAND]コマンドを用いて,更新したDockerイメージからDockerコンテナを作成すると,今度は絵文字パッケージ(fontawesome)がはじめからインストールされていることがわかります.

// コンテナを立てるとともにshを起動してコンテナ内に入る
$ docker run -it nontan18/texlive:stable sh
// fontawesomeがインストールされているか調べる
~/work# tlmgr search fontawesome
fontawesome - Font containing web-related icons
~/work# exit
exit

※ただ,上記の方法でイメージを更新すると,イメージの中身が不透明になりメンテナンスが難しくなるのでdocker commitコマンドの仕様はあまり推奨しません.

そこで次にDockerイメージの元となるDockerfileを変更する方法を解説します.

Dockerfile

DockerfileはDockerイメージの作り方が書かれたファイルです.
今回使っているnontan18/texliveのDockerfileはgithubに公開されているので確認してみましょう.

// githubからローカルにレポジトリをクローン
$ git clone https://github.com/nontan18/texlive.git
// クローンしてきたディレクトリに移動
$ cd texlive
// ディレクトリ内のファイル一覧を出力
$ ls
Dockerfile  README.md  app  docker-compose.yml  sample
// Dockerfileの中身を出力
$ cat Dockerfile

今回使ったDockerイメージ(nontan18/texlive)の元となった以下のようなDockerfileが出力されるかと思います.

# Alpine Linux v3.9.4をベースのイメージとして指定
FROM alpine:3.9.4

# MAINTAINER nontan <nontan@sfc.wide.ad.jp>
LABEL maintainer="nontan@sfc.wide.ad.jp"

WORKDIR /root/work

# パッケージリストの更新
RUN apk update
# TeXLiveのインストールに必要なパッケージを取得
RUN apk add perl wget fontconfig-dev
# TeXLiveのインストーラーの解凍に必要なパッケージのインストール
RUN apk add xz tar

# 圧縮されたTeXLiveのインストーラーをダウンロードし,tmpディレクトリに保存
ADD http://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz /tmp/install-tl-unx.tar.gz
# インストーラーを解凍した際に配置するディレクトリを作成
RUN mkdir /tmp/install-tl-unx
# ダウンロードしたinstall-tl-unx.tar.gzを解凍
RUN tar -xvf /tmp/install-tl-unx.tar.gz -C /tmp/install-tl-unx --strip-components=1
# TeXLiveのインストール用の設定ファイルを作成
RUN echo "selected_scheme scheme-basic" >> /tmp/install-tl-unx/texlive.profile
# TeXLiveのインストール
RUN /tmp/install-tl-unx/install-tl -profile /tmp/install-tl-unx/texlive.profile
# TeXLiveのバージョンを取得しインストールディレクトリを特定し,latestの名称でシンボリックリンクを作成
RUN TEX_LIVE_VERSION=$(/tmp/install-tl-unx/install-tl --version | tail -n +2 | awk '{print $5}'); \
    ln -s "/usr/local/texlive/${TEX_LIVE_VERSION}" /usr/local/texlive/latest
# インストールしたTeXLiveへパスを通す
ENV PATH="/usr/local/texlive/latest/bin/x86_64-linuxmusl:${PATH}"

# TeXLive Package Managerを使用して必要なパッケージをインストール
# texファイルの自動コンパイルパッケージをインストール
RUN tlmgr install latexmk
# latexmkの設定ファイルをホストからイメージにコピー
COPY ./app/config/.latexmkrc /root/.latexmkrc
# 2カラムの設定に必要なパッケージのインストール
RUN tlmgr install multirow
# 日本語対応パッケージのインストール
RUN tlmgr install collection-langjapanese
# フォントパッケージのインストール
RUN tlmgr install collection-fontsrecommended
RUN tlmgr install collection-fontutils

# 不要なパッケージなどの削除(イメージの容量削減のため)
RUN apk del xz tar
RUN rm -rf /var/cache/apk/*
RUN rm -rf /tmp/*


# References
# - https://github.com/blang/latex-docker
# - https://github.com/Paperist/docker-alpine-texlive-ja/blob/master/Dockerfile

ここで,先程のようにfontawesomeをインストールしておくには,RUN tlmgr install fontawesomeを追加します.

# フォントパッケージのインストール
RUN tlmgr install collection-fontsrecommended
RUN tlmgr install collection-fontutils
# 下の行を追加
RUN tlmgr install fontawesome

docker build

DockerfileからDockerイメージをビルドするには,docker build -t [IMAGE NAME] .コマンドを用います.

$ docker build -t mylatexlive . 

Dockerイメージのビルドが終わったら,docker imagesコマンドでDockerイメージが追加されているのがわかります.

$ docker images
REPOSITORY                                  TAG                 IMAGE ID            CREATED             SIZE
mylatexlive                                 latest              0bbb5bed8ce1        7 minutes ago       1.45GB

当然,このDockerイメージからDockerコンテナを作成すると,fontawesomeのパッケージがインストールされていることがわかります.

// コンテナを立てるとともにshを起動してコンテナ内に入る
$ docker run -it mylatexlive sh
// fontawesomeがインストールされているか調べる
~/work# tlmgr search fontawesome
fontawesome - Font containing web-related icons
~/work# exit
exit

docker-composeの基本的な使い方

ここまででDockerの基本的な使い方を学びましたが,実際にDockerを用いて環境構築を行おうと考えると,複数のコンテナで様々なDockerイメージやオプションを追加しなければならなく非効率です.

そこで,複数のコンテナを効率的に管理するためにdokcer-composeを紹介します.

docker-compose.yml

docker-composeはdocker-compose.ymlに記述したDockerコンテナの構成をdocker-compose upコマンドやdocker-compose downコマンドなどで手軽に展開できるツールです.

ここでは,上の過程でgit cloneしたdocker-compose.ymlを見てみましょう.

docker-compose.yml
version: "3.1"
services:
  texlive:
    build: .
    # image: nontan18/texlive
    volumes:
      - ./sample:/root/work
    command: latexmk --pvc /root/work/sample.tex

  nginx:
    # /root/public以下のファイルをホスティングするnginxベースのイメージ
    image: nontan18/stable-file-host
    ports:
      # "ホストのポート:コンテナのポート"
      - "8080:80"
    volumes:
      # 生成されるsample.pdfを公開ディレクトリにマウント
      - ./sample:/root/public

上記のdocker-compose.ymlではtexliveコンテナでコンパイルされたsample.pdfファイルが入っているsampleディレクトリがnginxコンテナの/root/public/にマウントされるようになっています.nginxコンテナのイメージであるnontan18/stable-file-host/root/public/以下のファイルをホスティングしているため,nginxコンテナの80番ポートにアクセスすることができれば,ブラウザ上でsample.pdfを閲覧することができます.

ここではportsでホストの8080番ポートをnginxコンテナ内の80番ポートに転送しているので,このコンテナがたつと,ブラウザからhttp://localhost:8080/sample.pdfにアクセスすることで出力されたPDFを閲覧することが可能になります.

docker-compose up

上記のdocker-compose.ymlのあるディレクトリでdocker-compose upコマンドを実行すると下記のようにdocker-compose.ymlに記述されたすべてのコンテナが起動します.

$ docker-compose up
Starting texlive_nginx_1 ...
Starting texlive_nginx_1 ... done
Attaching to texlive_texlive_1, texlive_nginx_1

上記の通り,http://localhost:8080/sample.pdfにアクセスすると出力されたPDFが確認できるはずです.

docker-compose ps

docker-compose upした状態でdocker-compose psコマンドを実行すると,docker-compose.ymlで定義された各コンテナが確認できます.

$ docker-compose ps
      Name                     Command               State          Ports
---------------------------------------------------------------------------------
texlive_nginx_1     nginx -g daemon off;             Up      0.0.0.0:8080->80/tcp
texlive_texlive_1   latexmk --pvc /root/work/s ...   Up

docker-compose down

docker-compose upをした状態でCtrl + Cで各コンテナを停止させることもできますが,別のターミナルのセッションからdocker-compose downを用いることで起動したすべてのDockerコンテナを停止することができます.

$ docker-compose down

docker-compose downには,マウントしたDockerボリューム(本稿では解説していませんが,ホストの特定ディレクトリをマウントする以外のコンテナのデータを永続化させる方法)をすべて削除する-vオプションや使用しているイメージを削除する--rmiオプションなどが存在します.

MySQL+Django+Angularのようなサーバークライアントモデルの開発環境を構築する際には,このdocker-composeは非常に便利です.

おわりに

最後に,今回,無造作に建てたDockerコンテナとイメージを削除してクリーンな状態に戻しましょう.
docker container rm [CONTAINER ID]コマンドやdocker image rm [IMAGE NAME]コマンドでもDockerコンテナやDockerイメージの削除は可能ですが,ここではdocker container prunedocker image pruneを紹介します.

docker container prune

docker container pruneコマンドは停止しているDockerコンテナをすべて削除するコマンドです.
docker psコマンドで起動中のコンテナを確認し,不必要なコンテナをdocker stop [CONTAINER ID]で停止させたのち,docker container pruneコマンドを実行しましょう.

$ docker container prune

docker image prune

docker container pruneと同様にdocker image pruneは起動中のコンテナに使用されていないDockerイメージを全て削除します.(なぜだかは知りませんが削除するイメージの数が多いとだいぶ長く時間がかかることがあります)

$ docker image prune

これで本稿で作成したコンテナもイメージも綺麗さっぱりホストから削除することができました.

従来の仮想化技術より軽量なコンテナ仮想化技術は,複雑な環境ごとパッケージ化したアプリケーションを気軽に展開したり削除したりできて非常に便利です.

本稿で紹介したのはDockerコマンドの一部に過ぎず,DockerはCIツールやクラスターにコンテナによるオーケストレーションを展開するkubernetes,アプリケーションコンテナの依存関係を整理しパッケージ管理するHelm,Docker環境にkubernetesクラスターを瞬時に展開しHelmを用いてアプリケーションを展開するRancherなどDockerの世界は非常に広大です.

本稿が皆様のHello Docker Worldのきっかけになれば幸いです.

注意事項

上記では何の躊躇もせずに筆者がビルドしたDockerイメージを使用していますが,信頼できない他人の作ったDockerイメージを安易に使用するのには注意が必要です.
たとえDockerfileが公開されていたとしても,Dockerレジストリに公開されているDockerイメージが悪意の無いものとは限りません.
Dockerfileを精査したのち,自身でdocker buildを行って使用することを推奨します.
特にホストのボリュームをマウントする場合や,--privilegedオプションを必要とするものには注意しましょう.

参考

ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
この記事は以下の記事からリンクされています
kaizen_nagoyaLaTeX Ieeeに投稿する準備からリンク

コメント

リンクをコピー
このコメントを報告

Dockerfile のベストプラクティスにも書かれてますが、
http://docs.docker.jp/engine/articles/dockerfile_best-practice.html

RUN apt-get -q update
RUN apt-get -qy install wget build-essential libfontconfig1

この書き方だと、別レイヤとしてキャッシュされるから、意図した動きになってない可能性が・・・
RUNは可能な限りまとめて書いた方が良いかと。

1
リンクをコピー
このコメントを報告

@TKR

ご指摘ありがとうございます.

リンク先読ませていただきました.
正直,当方はDockerのキャッシュの仕様を詳細に理解していなかったので勉強になりました.

当方の理解ですとapt-get updateのレイヤがビルドされる際にキャッシュが使われると,apt-get installの際に過去の「最新版」のパッケージがインストールされてしまうという問題かと思います.

リンク先では,

RUN apt-get update && apt-get install -y を使うことで、最新バージョンのパッケージを、追加の記述や手動作業なく利用できます。

というような記述があるかと思いますが,これに関しても,結局は同様のレイヤが過去に存在していればそのキャッシュが使用されるため,必ずしも「最新バージョンのパッケージを、追加の記述や手動作業なく利用でき」る訳ではないかと思います.

そのため,確実にbuild時に最新のパッケージをインストールしたいのであれば,--no-cacheオプションをつけてdocker buildを行うか,キャッシュが存在しないクリーンな環境でビルドを行うしか無いかと思います.

また,リンク先に以下の用にある通り,RUNをまとめてレイヤー数を減らすかどうかは,可読性とイメージのクオリティのトレードオフの関係にあります.

Dockerfile の読みやすさと、使用するイメージレイヤーの数を最小化。両者のバランスを見つける必要があります。戦略的に注意深くレイヤー数を使います。

本稿では初学者向けということで,できる限りレイヤを分割してキャッシュの利用を増やし,Dockerfile自体をいじりやすくすることを意図して,上記のような構成にさせていただきました.

(もちろん,@TKRさんの仰ると通りRUNをまとめたほうがイメージサイズもコンパクトになり,実用上はそちらのほうが良いです.)

Dockerfileの構成に関してあまり記載をしておりませんし,軽量化のためにalpineベースのものにイメージを変更しようかと思いますので,ご指摘と上記内容を踏まえて記事を更新しようかと思います.

ありがとうございました.

2
リンクをコピー
このコメントを報告

お世話になります。Docker初学者です。buildを実行したくて、よい例がないかなぁと探してこの記事にたどり着きました。楽しく実践しています。

確認させてください。

「当然,このDockerイメージからDockerコンテナを作成すると,fontawesomeのパッケージがインストールされていることがわかります.

// コンテナを立てるとともにshを起動してコンテナ内に入る
$ docker run -it mytexlive sh」

このmytexliveはmylatexliveですよね。

1
リンクをコピー
このコメントを報告

@cgai1209

ご指摘ありがとうございます!!

mylatexliveが正しいです。修正させていただきました!

1
どのような問題がありますか?
あなたもコメントしてみませんか :)
ユーザー登録
すでにアカウントを持っている方はログイン
74
どのような問題がありますか?
ユーザー登録して、Qiitaをもっと便利に使ってみませんか

この機能を利用するにはログインする必要があります。ログインするとさらに下記の機能が使えます。

  1. ユーザーやタグのフォロー機能であなたにマッチした記事をお届け
  2. ストック機能で便利な情報を後から効率的に読み返せる
ユーザー登録ログイン
ストックするカテゴリー