PT3 地デジ・ＢＳ録画環境を Docker コンテナ内に押し込める


ご存知 [PT3](http://earthsoft.jp/PT3/) による地デジ・ＢＳの録画環境を私も重宝しているのですが、いかんせん環境設定が面倒で、OS の入れ替えを躊躇しがちに。その一因として、関連ツール類がパッケージ化されておらず、ソースを一つ一つ取得してきてインストールせざるを得ないことが挙げられると思います。

それって Docker イメージに押し込んじゃえばいいんじゃね？ というわけで始めてみました。なお、録画サーバである EPGREC（[トップページ - 録画予約システムepgrec](http://www.mda.or.jp/epgrec/)）とそのバックで動く MySQL の設定については、Docker の設定としては極々オーソドックスですので、ここでは触れません（気が向いたら追記するかも）。

なお、下記の手順で、ホストでの PT3 録画環境 → Docker の PT3 録画環境への移行期間中は、ホスト側と Docker 側とで、一枚の B-CAS カードを共用して、同時に録画ができています（ホストが地デジ 3 チューナ、ＢＳ 3 チューナで、Docker コンテナは地デジ 1 チューナ・ＢＳ 1 チューナを使用）。

# 全体の構成

ホスト OS 側では PT3 ドライバと PC カードリーダのサーバを動かし、特権モードの Docker コンテナ側からは、PT3 へはデバイスファイルを通じて、カードリーダへは UNIX ドメインソケットを通じて利用するようにします。

# Docker ホスト側のセットアップ

そのようなわけで、Docker ホスト環境の側では、以下のように、仮想環境のホストと、ハードウェアの管理だけをしてもらいましょう。とっても今風ですね、ステキ。

* Docker のホスト
* PT3 ドライバ
* PC カードリーダのサーバ
* （各 web サービスへのリバースプロキシとして nginx）

## Docker のホスト

ホストの OS としては Ubuntu 14.04 ですので、Docker は「docker.io」の 1.0 系なのですが、このところ Docker は開発が活発すぎるので、下記で「lxc-docker」の最新版（今日時点で 1.3.1）を入れます。右記も参照。 ∥ [Ubuntu - Docker Documentation](https://docs.docker.com/installation/ubuntulinux/)

~~~~console:
$ curl --silent --show-error --location https://get.docker.io/ | sh
~~~~

それでも `docker exec` の動作とか、若干変な気もします。後で見てみるか。

> 追記（Nov 21 2014）: どうやら Ubuntu 14.04 の標準 Docker のストレージエンジンのデフォルトは devicemapper（設定ファイルで指定）なのですが、lxc-docker の標準が aufs（設定ファイルに無指定なので）になっているようです。aufs はマイナーな機能が足りていないので、後々痛い目にあわないように、最初に、/etc/default/docker にストレージエンジンを指定してから docker サービスを再起動しておいた方が良いと思われます。多くのケースでは、CentOS で yum(8) を動かす際にハマると思われます。
> 
> ~~~~:/etc/default/docker
> DOCKER_OPTS="-s devicemapper"
> ~~~~
> 
> 参考:
> 
> * [Comprehensive Overview of Storage Scalability in Docker | Red Hat Developer Blog](http://developerblog.redhat.com/2014/09/30/overview-storage-scalability-docker/)
> * [centos Repository | Docker Hub Registry - Repositories of Docker Images](https://registry.hub.docker.com/_/centos/)
> * [cap_set_file not permitted on aufs storage driver only · Issue #6980 · docker/docker](https://github.com/docker/docker/issues/6980)
> * [Why use AUFS as the default Docker storage backend instead of devicemapper? - Stack Overflow](http://stackoverflow.com/questions/24764908/why-use-aufs-as-the-default-docker-storage-backend-instead-of-devicemapper)
> * [CentOS, Docker, and Systemd](http://jperrin.github.io/centos/2014/09/25/centos-docker-and-systemd/)
> 
> 早く btrfs にしたーい！

## PT3 ドライバ

Git でソースをいただいてきて、DKMS（[Dell - Dell | Linux - Projects - DKMS](http://linux.dell.com/dkms/)）でドライバをインストールします。今どき、DKMS が必要な局面というのも減りましたけれどね。下記のような Chef レシピにまとめて実行しています。

~~~~ruby:pt3/recipies/default.rb
package "dkms"

git "#{Chef::Config[:file_cache_path]}/pt3" do
  repository "git://github.com/m-tsudo/pt3.git"
  reference "master"
  action :sync
  notifies :run, "bash[pt3 installation]"
end

bash "pt3 installation" do
  only_if ". #{Chef::Config[:file_cache_path]}/pt3/dkms.conf; test -z \"$(dkms status -m $PACKAGE_NAME -v $PACKAGE_VERSION)\""
  cwd "#{Chef::Config[:file_cache_path]}/pt3"
  code <<-EOH
. dkms.conf;
rm -fr /usr/src/$PACKAGE_NAME-$PACKAGE_VERSION
mkdir -p /usr/src/$PACKAGE_NAME-$PACKAGE_VERSION
git archive master | sudo tar -x -C /usr/src/$PACKAGE_NAME-$PACKAGE_VERSION
dkms add -m $PACKAGE_NAME -v $PACKAGE_VERSION
dkms build -m $PACKAGE_NAME -v $PACKAGE_VERSION
dkms install -m $PACKAGE_NAME -v $PACKAGE_VERSION
lsmod | grep pt3_drv || modprobe pt3_drv
  EOH
end

bash "pt3 boot time module loading" do
  not_if "grep pt3_drv /etc/modules"
  code <<-EOC
    echo pt3_drv >> /etc/modules
  EOC
end
~~~~

追記（Tue May 26 2015）: 最近は Ansible（[Ansible Documentation — Ansible Documentation](http://docs.ansible.com/)）に乗り換えました。決め手は、ノードをまたいでオーケストレーションが出来ることと、設定の可読性が高いことでした。

~~~~yaml:roles/pt3/tasks/main.yml
---

- name: DKMS がインストールされていること
  apt: name=dkms state=present

- name: pt3 ドライバのソースが git clone されていること
  git:
    repo: git://github.com/m-tsudo/pt3.git
    dest: /root/pt3
    accept_hostkey: true

- name: pt3 ドライバの DKMS ステータスを取得する
  args:
    executable: /bin/bash
    chdir: /root/pt3
  shell: |
    . dkms.conf
    dkms status -m $PACKAGE_NAME -v $PACKAGE_VERSION
  register: pt3_driver_status
  always_run: true
  changed_when: false

# - debug: var=pt3_driver_status

- name: pt3 ビルドの要否を取得する
  set_fact:
    pt3_build_required: force_build or (not pt3_driver_status.stdout)

- name: pt3 ドライバをインストールする
  when: pt3_build_required
  args:
    executable: /bin/bash
    chdir: /root/pt3
  shell: |
    . dkms.conf
    rm -fr /usr/src/$PACKAGE_NAME-$PACKAGE_VERSION
    mkdir -p /usr/src/$PACKAGE_NAME-$PACKAGE_VERSION
    git archive master | tar -x -C /usr/src/$PACKAGE_NAME-$PACKAGE_VERSION
    dkms add -m $PACKAGE_NAME -v $PACKAGE_VERSION
    dkms build -m $PACKAGE_NAME -v $PACKAGE_VERSION
    dkms install -m $PACKAGE_NAME -v $PACKAGE_VERSION
    lsmod | grep pt3_drv || modprobe pt3_drv

# modules-load.d http://www.freedesktop.org/software/systemd/man/modules-load.d.html
- name: 起動時の pt3 ドライバロード設定が存在すること
  copy:
    content: |
      pt3_drv
    dest: /etc/modules-load.d/pt3.conf

- name: 標準のドライバを入れないようになっていること
  copy:
  content: |
    blacklist earth-pt1
    blacklist earth-pt3
  dest: /etc/modprobe.d/blacklist-ath_pci.conf

# To remove:
#   # dkms remove $PACKAGE_NAME/$PACKAGE_VERSION --all

...
~~~~

## PC カードリーダのサーバ

B-CAS カードを差すカードリーダードライバ（[PCSClite project](http://pcsclite.alioth.debian.org/pcsclite.html)）の、サーバ側（「pcscd」）だけを動かします。クライアント（「pcsc-tools」）は、動作確認のために入れても構いませんが、ホスト側では必須ではありません。

~~~~console:
$ sudo aptitude install pcscd
~~~~

# Docker コンテナ

とりあえずは Dockerfile をさらします。

~~~~:Dockerfile
# Use phusion/baseimage as base image. To make your builds reproducible, make
# sure you lock down to a specific version, not to `latest`!
# See https://github.com/phusion/baseimage-docker/blob/master/Changelog.md for
# a list of version numbers.
FROM phusion/baseimage:latest

MAINTAINER Kiichiro NAKA <knaka@ayutaya.com>

# Set correct environment variables.
ENV HOME /root

# Regenerate SSH host keys. baseimage-docker does not contain any, so you
# have to do that yourself. You may also comment out this instruction; the
# init system will auto-generate one during boot.
RUN /etc/my_init.d/00_regen_ssh_host_keys.sh

# Use baseimage-docker's init system.
CMD ["/sbin/my_init"]

# --------------------------------------------------------------------

RUN echo "Asia/Tokyo" > /etc/timezone && dpkg-reconfigure --frontend noninteractive tzdata

RUN apt-get update
RUN apt-get install -y software-properties-common

RUN apt-get install -y wget unzip git
RUN apt-get install -y pkg-config autoconf build-essential

# Card reader
RUN apt-get install -y pcsc-tools

# ARIB library
RUN wget http://hg.honeyplanet.jp/pt1/archive/ec7c87854f2f.zip
RUN unzip ec7c87854f2f.zip
RUN apt-get install -y libpcsclite-dev
RUN cd pt1-ec7c87854f2f/arib25/src/ && make && make install

# recpt1
RUN git clone https://github.com/stz2012/recpt1
RUN cd recpt1/recpt1/ && ./autogen.sh && ./configure --enable-b25 && make && make install

# --------------------------------------------------------------------

# Clean up APT when done.
RUN apt-get clean && rm -fr /var/lib/apt/lists/* /tmp/* /var/tmp/*
~~~~

頭の部分と尻の部分は Docker baseimage（[phusion/baseimage-docker](https://github.com/phusion/baseimage-docker)）の定形ですのでどうでも良いとして、肝心なのは中間部分の、各種ツールをインストールしているところです。

要点としては、pcsc-tools は入れているが pcscd は入れていないところでしょうか。この理由については後述します。

その他のツールについては、私のところではこのツール＆バージョンの組み合わせが良好だったというところですので、何とも言えません。肝心なのは、Docker イメージとしては OS のバージョンも含めて固定＆塩漬けにできるため、ホスト OS のバージョンアップに影響されないというメリットが得られることです。この安心感は大きい。

## コンテナからの、PT3 ドライバや PC カードリーダーサーバへのアクセス

問題は、Docker コンテナ側からどうやって PT3 と PC カードリーダーサーバへアクセスするかです。下記は、最終的な Docker コンテナの起動コマンドです。

~~~~sh:
docker run -d --name epgrec \
 --privileged \
 --volume /dev/:/dev/ \
 --volume /var/run/pcscd/pcscd.comm:/var/run/pcscd/pcscd.comm \
 --volume $PWD/config.xml:/var/www/html/settings/config.xml \
 -p 127.0.0.1:1026:80 --link mysql-epgrec:mysql \
 knaka/epgrec:latest
~~~~

EPGREC のための設定ファイルや DB 設定も盛り込まれているのでゴチャゴチャしていますが、ここでの要点は、まずは `--privileged` オプションです。これによって Docker コンテナは特権モードで動作するので、デバイスファイル（ここでは `/dev/pt3video*`）に自由にアクセスすることができるようになります。 

さて、そのデバイスファイルはホスト OS 上で udev によって動的に管理されていますので、`--volume /dev/:/dev/` としてデバイスファイル群をゴッソリと共有してやります。

次に PC カードリーダーなのですが、PCSC は UNIX ドメインソケットを用いたクライアント・サーバモデルのソフトウェアですので、`--volume /var/run/pcscd/pcscd.comm:/var/run/pcscd/pcscd.comm` のようにソケットファイルだけを共有してやれば、あっさり pcsc-tools が、Docker コンテナ上で動作します。

なお、最初の予定では USB のデバイスファイルを共有して pcscd をコンテナ内でも動かそうとしたのですが、そうするとホストの pcscd と Docker コンテナ内の pcscd が先勝ちにリーダーデバイスを取り合ってしまって、ホストと Docker での同時動作ができませんでした。

# EPG 抽出ツールと EPGREC の設定

EPGREC のバックエンドである MySQL は別コンテナにしました。一サービス一 DB にすると、分かりやすくて良いですね。特に MySQL は、PostgreSQL などと違って、DB インスタンスを複数ポコスカ起動するように作られていないので。

# さて

何か見落としはありませんでしょうか。コメント請う