QIIME2をJupyterHub上に環境構築してみた

Qiitaの皆さん、初めまして。縁あって16Sリボソーム遺伝子配列に基づいた細菌叢解析を行っています。直近一年くらい、QIIME2に触れてきましたので、少しでも普及の支援になればと思い、作業環境の構築方法を記録しておきます。

#はじめに
QIIME2をお使いの皆さんはどのようなマシン環境でお使いでしょうか。[公式サイト](https://docs.qiime2.org/2020.11/install/)ではWindows, MacOS, Linux上でvirtualbox, Dockerなどを使って簡単に利用できる事になっています[^1]。SSHでリモート接続して[q2cli](https://docs.qiime2.org/2020.11/interfaces/q2cli/)で解析をしても良いのですが、生物系の皆さんはあまり馴染めない操作法だったようなのでjupyterhubを導入して複数人で扱える環境を目指しました[^2]。

[^1]: もしくはテンプレートのあるオレゴンのAWSにインスタンスを借りるようなケースも想定されていますね。自分の場合はハイスペックなEC2を維持したり、マシンパワーとメモリの潤沢なノートPCを購入する財力もなかったので、使い古しのデスクトップマシンをサーバにして、手元にChromebook Ideapad Duetからアクセスすることにしました。
[^2]: 研究所内ローカルネットワーク内にx86_64サーバを設置して、メンバーで共有するイメージです。セキュリティ上の問題があるので、このまま外部に公開しないようにご注意ください。

#システム構成
以下が本投稿の前提になります。AWSのEC2等のVPSで最小構成から初めてデータ量に合わせてスケールしてゆくのは良い考えかもしれませんが、セキュリティ対策が必要になります[^3]。

* CPU:　AMD64アーキテクチャのCPU[^4]
* Memory:　16GB(目安です[^5])
* Storage:　ハンドリングするデータ量に合わせます。おすすめは
    * 8GB以上のSSDを起動用
    * データストレージ用にHDD。/homeにマウントします。
    * HDDと同サイズ以上のHDD。USB接続でデータバックアップの際に適当な位置にマウントします。
* OS:　Ubuntu-server 20.04　(他のディストリビューションでも構いません)
* その他：　以下は必須ではありませんが本投稿の前提になります。
    * 24時間稼働することもあるため、電源は信頼のできるものがおすすめです。
    * ケースは給排気のしっかりしたものなら何でもOKです。ホコリの堆積による火災に注意。
    * port番号を変更した秘密鍵方式のSSHサービスを設定しました。
    * ファイアウォールで必要のないportは閉じておきました。
    * あとは常識の範囲で。

[^3]: このあたりはネット上に色々と情報がありますので、設定方法はそちらを御覧ください。気が向いたら投稿するかもしれません。
[^4]: 今(2021年1月)の所、Raspberry Pi等のARM系マシンは避けたほうが無難ですが、M1 Macの登場で状況が変わりそうです。今回は趣旨と異なるので深堀りはしません。
[^5]: メモリ不足でエラーが出たときはSwapファイルの拡大で対応可能です

## minicondaの導入とアップデート
大事なことはQIIME2から先にインストールすることです[^6]。まず[公式サイト](https://docs.qiime2.org/2020.11/install/native/)のNatively installingに従ってQIIME2をインストールしました。

```
$ sudo su -
# cd 
# apt update && apt -y upgrade 
# wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
# /bin/bash ./Miniconda3-latest-Linux-x86_64.sh
```
途中で訊かれますのでインストール先は`/opt/miniconda3`に`conda init`は実行を選択して`/root/.bashrc`にcondaの初期化設定を追加しました。

[^6]: [The littlest Jupyterhub](https://tljh.jupyter.org/en/latest/)などを利用してjupyterhubの環境構築してから、その上にQIIME2環境を作ろうとするとハマります。

## QIIME2のYMLファイルで仮想環境を構築、活性化
一旦、rootから抜けてして再度rootに戻ってくるのが楽かと思います。まずcondaのアップデートをおこない、QIIME2をインストールしました。[公式サイト](https://docs.qiime2.org/2020.11/install/native/)を参考にymlファイルを取り寄せて、仮想環境を作りました。

```
# exit
$ sudo su -
(base) # conda update conda
(base) # wget https://data.qiime2.org/distro/core/qiime2-2020.11-py36-linux-conda.yml
(base) # conda env create -n qiime2-2020.11 --file ./qiime2-2020.11-py36-linux-conda.yml
(base) # conda activate qiime2-2020.11
```
## jupyterhubの構築
もしq2cliで運用する場合はここまでで終了ですが、[Artifact API](https://docs.qiime2.org/2020.11/interfaces/artifact-api/)を何人かで共有する環境を目指します。

```
(qiime2-2020.11) # conda install -c conda-forge jupyterhub jupyterlab
(qiime2-2020.11) # jupyterhub --generate-config
(qiime2-2020.11) # mkdir -p /etc/jupyterhub
(qiime2-2020.11) # mv ./jupyterhub_config.py /etc/jupyterhub
(qiime2-2020.11) # vim /etc/jupyterhub/jupyterhub_config.py
```
必要に応じて設定ファイルを編集します。今回はファイルの最終行に以下を追記しました。

```
c.JupyterHub.ip = '0.0.0.0'
c.JupyterHub.port = 8000
c.Authenticator.admin_users = {'foo'}
c.Authenticator.whitelist = {'foo', 'baa'}
c.Spawner.default_url = '/lab'
```
一行目の`c.JupyterHub.ip`はすべての接続を許可しています。アクセス元のIPアドレスが限定できる場合はCIDR記法などで絞り込みを行ったほうが安全です。二行目で待受けするportを指定しています。他のサービスと重複しない場合は変更可能です。三行目はjupyterhubを管理するユーザを、四行目は利用するユーザを指定します。もちろん`foo`と`baa`は適切なユーザ名に変更してください。五行目は作業環境をjupyterlabにする設定です。不要であれば省略可能です。`foo`と`baa`がシステムに登録されていなければ、以下のようにサーバのユーザとして登録しておきます。

```
(qiime2-2020.11) # adduser foo
(qiime2-2020.11) # adduser baa
```
## 自動起動の設定
jupyterhubが稼働するディレクトリを`/var/local/jupyterhub`に作成し、`/usr/local/sbin`に次のファイルをrootで作成します。

```
(qiime2-2020.11) # mkdir -p /var/local/jupyterhub
(qiime2-2020.11) # vim /usr/local/sbin/jupyterhub-startup.sh
(qiime2-2020.11) # chmod +x /usr/local/sbin/jupyterhub-startup.sh
```
`/usr/local/sbin/jupyterhub-startup.sh`は以下のような内容にしました。`# >>> conda initialize >>>`から`# <<< conda initialize <<<`まではcondaの仮想環境へ切り替えるための初期化スクリプトで`tail -n 15 ~/.bashrc`で切り出すことができます。

```/usr/local/sbin/jupyterhub-startup.sh

#!/bin/bash

QIIME=qiime2-2020.11

# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$('/opt/miniconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
    eval "$__conda_setup"
else
    if [ -f "/opt/miniconda3/etc/profile.d/conda.sh" ]; then
        . "/opt/miniconda3/etc/profile.d/conda.sh"
    else
        export PATH="/opt/miniconda3/bin:$PATH"
    fi
fi
unset __conda_setup
# <<< conda initialize <<<

cd /var/local/jupyterhub
conda activate $QIIME
/opt/miniconda3/envs/$QIIME/bin/jupyterhub -f /etc/jupyterhub/jupyterhub_config.py
```

次にsystemdに自動起動のスクリプトを設置します。

```
(qiime2-2020.11) # vim /etc/systemd/system/jupyterhub.service
```
必要最小限の内容だけ記述しました。必要に応じて項目を追加してください。

```/etc/systemd/system/jupyterhub.service
[Unit]
Description=JupyterHub
After=syslog.target network.target

[Service]
User=root
ExecStart=/usr/local/sbin/jupyterhub-startup.sh

[Install]
WantedBy=multi-user.target
```
systemdに登録して、稼働するか確認してみます。

```
(qiime2-2020.11) # systemctl daemon-reload
(qiime2-2020.11) # systemctl start jupyterhub.service
(qiime2-2020.11) # systemctl status jupyterhub.service
```

エラーがないことを確認しました。

#動作確認とまとめ
webブラウザでIPアドレスとport番号を指定してアクセスして、ログインできてjupyterlabが表示されました。python3で新しいノートを作成し、`from qiime2 import Artifact`のようなコードを実行して、なんのエラーも返さなければ[Artifact API](https://docs.qiime2.org/2020.11/interfaces/artifact-api/)が利用可能です。`No module named 'qiime2'`というエラーが戻ったときはqiime2仮想環境で実行されていないので、上記内容を確認してください[^7]。
以上で、QIIME2のArtifact APIをjupyterhubで共有できるようになります。この記事をきっかけにQIIME2のユーザーが増えますよう、祈って終わります。

[^7]: QIIME2-2020.11の導入時に、`from qiime2 import Artifact`は通るのに`from qiime2.plugins import feature_table`に「`No module named 'qiime2'`では無いエラー」を返すことがありました。その時はSSHなどで接続して`(base) # conda activate qiime2-2020.11`で仮想環境に入った状態で`(qiime2-2020.11) # conda update --all`することで解消しました。