Dockerコンテナ上でNGSのソフトウェアを実行してみる(Windows10・MacOSXローカル環境編)

きっかけ

近年では、BiocondaのようなBiology関連ソフトウェアに対するパッケージマネージャが登場し、NGS関連のソフトウェアのインストールがグッと簡単になりました(Biocondaが登場する前は、いちいちソースコードからコンパイルする必要があったり、各ソフトウェア固有のインストール方法を理解する必要があったり、とにかく手間がかかっていました…。NGS関連のソフトは依存関係が複雑で本当にやっかい)。

ただ、いろんな人のNGS関連のブログを見ていると、環境によってはBiocondaでうまくインストールできないという事例もあるようです(Biocondaレシピで想定されていなかったパッケージへの依存によるもの?)。確かに、Biocondaは非常に便利なんですが、他の環境でも同じ解析環境を構築しようとすると、再現よく環境を構築できないケースがあることがわかってきました。

そこで、ソフトウェアごとに仮想環境を構築し、各ソフトウェアの動作が保証された環境を色々な解析に使いまわすことで、上記の問題を解決しようと考えました。

そこで、Dockerの出番です。Dockerはコンテナ型仮想化技術の1つであり、ホストOS上で単一のプロセスとして起動します。ホストOSと同じカーネルを共有するため、VMWare, VirtualBoxなどのホスト型仮想化技術と比較して、軽量であるとされています。

「Docker」を全く知らない人のために「Docker」の魅力を伝えるための「Docker」入門
https://qiita.com/bremen/items/4604f530fe25786240db

前置きが長くなりましたが、今回は、Dockerを土台としたシステム基盤をつくることを考えるために、まずはローカル環境でDockerコンテナを動かしてみた (FastQCを実行して見る)という内容になります。

Dockerをベースにした解析パイプラインの活用事例

パッと目についた事例をあげてみました。他にもたくさんあると思います。

High-Throughput Genomics on AWS - LFS309 - re:Invent 2017

AWSのカンファレンス re:Invent 2017で紹介されたNGS解析のパイプライン
https://www.slideshare.net/AmazonWebServices/highthroughput-genomics-on-aws-lfs309-reinvent-2017

Broad Institute: Genomes-in-the-cloud

Broad Instituteで利用されているゲノム解析用のパイプライン
https://hub.docker.com/r/broadinstitute/genomes-in-the-cloud/

BioContainers

バイオインフォマティクス関連のDockerイメージファイルをまとめたもの
https://biocontainers.pro/

ベースとなるDockerイメージの選択

NGSのデータ解析をする上では、必要最小限のLinuxディストリビューションのDockerイメージを使用した方がいいと思います。1つはイメージサイズが小さいほど、Dockerイメージの起動や破棄、ダウンロードなどの取り回しの面で有利だから。もう1つは、軽量コンテナの方が不要なパッケージが少なく、セキュリティ上、潜在的な攻撃を受けにくいからです。

また、ベースイメージを選定する上で、OSにインストールマネージャーがついているかどうかや、ITベンダーなどにより定期的にメンテナンスされているかどうかもポイントになります。

以下では、主要なLinuxディストリビューションについて簡単に比較をして見たいと思います。

Dockerイメージのサイズ

以下が主要なLinuxディストリビューションのDockerイメージのサイズになります。

Linux OS Compressed size
debian8.10-slim 30 MB
debian 8.10 53 MB
ubuntu 18.04 35 MB
centos 7.4.1708 73 MB

上記のデータは、2018-03-13時点のデータです。少し前までは、Debianが軽量なDockerイメージだったので、Debian一択なのかと思っていましたが、Ubuntuも直近ではかなりスリム化を果たしていますね(ちなみに、Anacondaやその他NGS関連のDockerイメージはDebianをベースに作られたものが多い気がします)。

脆弱性レポート

各Dockerイメージのレポジトリのtagタブから、各バージョンの脆弱性レポートを確認することができます。

image.png (328.0 kB)

スキャンされた各イメージをクリックすると、詳細を見ることができます。以下では、各LinuxディストリビューションのDockerイメージの脆弱性レポートを見ていくことにします。

debian8.10-slim

image.png (281.9 kB)

debian 8.10

image.png (283.9 kB)

ubuntu

image.png (281.8 kB)

centos 7.4.1708

image.png (308.9 kB)

脆弱性レポートまとめ

上記で示した脆弱性レポートの結果をまとめて見ました。いくつのコンポーネント脆弱性を抱えているかカウントした表が以下の通りになります。

Linux OS Vulnerability (Components)
debian8.10-slim 6
debian 8.10 6
ubuntu 18.04 0
centos 7.4.1708 16

上記のデータは、2018-03-13時点のデータです。Ubuntu脆弱性が0なのが際立っている気がします(本当かなって結果になりました)。

現状では、Debian-slimかUbuntuを選択するのが良いのかなと思いました。

Dockerのインストール

自身のパソコンにDockerをインストールします。 以下のURLから、Docker (Community Edition)をダウンロードできます。
https://www.docker.com/community-edition#/download

MacOSXにDockerをインストールして使う場合は以上でインストール完了です。
Windows10環境の場合、既存のコマンドプロンプトPowershellがどうも使いにくい(個人的な感想ですが)ので、Windows Subsystem for Linux (WSL)を導入して、BashシェルからDockerを扱えるようにしたいと思います。

Windows Subsystem for Linux (WSL)経由でホストOS (Windows10)のDockerを動かす

1. WSLをインストールする

注意: Dockerを使用するにはHyper-Vを利用する必要があるため、OSがWindows10 Proであることが必須です。

Miscrosoft storeからWSLをダウンロード・インストールできるようになったので、ストアからインストールしましょう。今回はUbuntuを導入しました。

image.png (22.4 kB)

WSLを使用するためには、「プログラムと機能」から「Windowsの機能の有効化または無効化」を開き、Windows Subsystem for Linuxにチェックをつけます。

image.png (73.0 kB)

2. DockerをWSLにインストールする

以下のUbuntuへのDockerのインストール方法は、Dockerの公式ドキュメントを参考にしました。詳しくは、そちらを参照のこと。

Get Docker CE for Ubuntu
https://docs.docker.com/install/linux/docker-ce/ubuntu/#install-using-the-repository

WSLのBash shellを起動し、まずaptパッケージをアップグレードしておきます。

$ sudo apt-get update

次のDockerを動かすために必要な各種パッケージをインストールします。

$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common

Dockerの公式のGPGキーを加え、キー情報の確認を行っています。

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo apt-key fingerprint 0EBFCD88

pub   4096R/0EBFCD88 2017-02-22
      Key fingerprint = 9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88
uid                  Docker Release (CE deb) <docker@docker.com>
sub   4096R/F273FCD8 2017-02-22

Dockerをインストールします。

$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
$ sudo apt-get update
$ sudo apt-get install docker-ce

3. WSL側とホストOS (Windows10)側のDockerを連携させる

Docker for Windowsのデーモンに接続するための設定を行います。今の状態では、あくまでホストOS側(Windows10)とWSL側で別々にDockerをインストールしただけなので、WSL側から、ホストOS側のDockerのデーモンプロセスを操作できるのように連携させる必要があります。

以下のように、WSL側のDockerクライアントからホストOS側のデーモンを見れるように、環境変数DOCKER_HOSTを設定します。

$ echo "export DOCKER_HOST='tcp://0.0.0.0:2375'" >> ~/.bashrc
$ source ~/.bashrc

最後に、Windows10側のDockerの設定画面を開き、Expose daemon on tcp://localhost:2375 without TLSにチェックを付けます。これで、WSL側からホストOS側のDockerのデーモンを操作できるようになりました。

image.png (142.5 kB)

参照

Docker for Windowsで快適な環境を得るまでの そこそこ長い闘い
https://qiita.com/YukiMiyatake/items/73c7d6c4f2c9739ebe60
WSL(Bash on Windows)でDockerを使用する
https://qiita.com/yoichiwo7/items/0b2aaa3a8c26ce8e87fe
Docker for WindowsをWSLから使う時のVolumeの扱い方
https://qiita.com/gentaro/items/7dec88e663f59b472de6

VSCodeの総合ターミナルでWSLを使う

VSCodeでは、エディタ内でターミナルを開くことができるので、こちらの標準のターミナルをWSLに変更しておくと便利です。

まず。設定画面から、terminal.integrated.shell.windowsで検索します。

対象の設定に対して "terminal.integrated.shell.windows": "C:\\Windows\\System32\\bash.exe"のようにWSLのBash.exeのパスを指定します。

image.png (111.0 kB)

総合ターミナルを開いて確認すると、めでたくBashシェルが使えるようになっているはずです!

image.png (30.8 kB)

Dockerファイルの作成

まず、Linux OSのDockerイメージをベースイメージにして、その上にNGS解析のソフトウェアをインストールしたDockerイメージを作成します。

1. Debian9-slimにBiocondaをインストールしたDockerイメージを作成する

Linux OSのDebina9-slimをベースイメージにして、BiocondaをインストールしたDockerイメージを作成します。

以下のDockerfileは、condaの公式Dockerfileを参考しました。
https://hub.docker.com/r/conda/miniconda2/~/dockerfile/

&&で処理を連続して行っているのは、レイヤーを減らすためです。また、中間ファイルを削除することでDockerのイメージファイルのサイズを小さくしています。

Dockerfile

# Base image
FROM debian:9-slim

# Install Miniconda2 & Bioconda
RUN apt-get -qq update && apt-get -qq -y install curl bzip2 \
  && curl -sSL https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -o /tmp/miniconda.sh \
  && bash /tmp/miniconda.sh -bfp /usr/local \
  && rm -rf /tmp/miniconda.sh \
  && conda install -y python=2 \
  && conda update conda \
  && conda config --add channels r \
  && conda config --add channels defaults \
  && conda config --add channels conda-forge \
  && conda config --add channels bioconda \
  && apt-get -qq -y remove curl bzip2 \
  && apt-get -qq -y autoremove \
  && apt-get autoclean \
  && rm -rf /var/lib/apt/lists/* /var/log/dpkg.log \
  && conda clean --all --yes

ENV PATH /opt/conda/bin:$PATH

次に、DockerfileからDockerイメージを作成します。

$ docker build -t debian9-slim-bioconda2:latest -f Dockerfile .

2. FastQCをインストールしたDockerイメージを作成する

先程作ったDockerイメージをベースにして、FastQCのインストールしたイメージファイルを作成します。

Dockerfile

# Base image
FROM debian9-slim-bioconda2:latest

# Application entry point
# Dejavu fonts are not included with default::openjdk(fastqc recipe).
# They are already included with conda-forge::openjdk.
# https://github.com/conda-forge/staged-recipes/issues/3164 
RUN conda update conda \
  && conda install fastqc=0.11.6 \
  && conda install -c conda-forge openjdk \
  && conda clean --all --yes
COPY ./fastqc/run_fastqc_local.py /run_fastqc_local.py

ENTRYPOINT ["python", "/run_fastqc_local.py"]

Pythonのコードをエントリポイントに設定します。以下が、そのソースコードになります。

from __future__ import print_function

import os
import shlex
import subprocess
from argparse import ArgumentParser


def run_fastqc(fastq_path, cmd_args, working_dir):
    """
    Runs fastqc  
   :param fastq_path: Local path to fastq file  
   :param cmd_args: Additional command-line arguments to pass in  
   :param working_dir: Working directory  
   :return: Path to the result folder
    """
    # Prepare fastqc result path
    fastqc_result_dir = os.path.join(working_dir, '')

    # Prepare fastqc log path
    fastqc_log_path = os.path.join(fastqc_result_dir, "log_fastqc.txt")

    # Prepare fastqc command
    cmd = "fastqc -o {0} {1} {2}".format(
        fastqc_result_dir, cmd_args, fastq_path)
    print(cmd)

    # Execute fastqc
    with open(fastqc_log_path, 'w') as log_file:
        subprocess.check_call(shlex.split(cmd), stdout=log_file)

    return fastqc_result_dir


def main():
    argparser = ArgumentParser()

    argparser.add_argument('--fastq_path', type=str,
                           help="fastq sequence file", required=True)
    argparser.add_argument('--cmd_args', type=str,
                           help="arguments/options for fastqc", default="")
    argparser.add_argument('--working_dir', type=str, default="data/")

    args = argparser.parse_args()

    print("Creating working directory...")
    if not os.path.exists(args.working_dir):
        os.mkdir(args.working_dir)

    print("Running fastqc...")
    local_result_dir = run_fastqc(
        args.fastq_path, args.cmd_args, args.working_dir)

    print('Save the result in {0}.'.format(local_result_dir))

    print("successfully Completed !!")


if __name__ == '__main__':
    main()

次に、DockerfileからDockerイメージを作成します。Pythonのコードも同じディレクトリに置いておきます。

$ docker build -t debian9-slim-bioconda2-fastqc:latest -f Dockerfile .

以下のコマンドで作成したDockerイメージをチェックできます。

$ docker images

image.png (53.4 kB)

Dockerイメージの実行

1. FASTQファイルの準備

適当なFASTQファイルを持っていない場合、以下の記事を参考にしてテストに使うFASTQファイルを用意してください。

imamachi-n.hatenablog.com

2. Dockerコンテナを起動する

FASTQ_FILEにFASTQファイル、FASTQファイルが置いてあるLOCAL_WORKING_DIRに作業ディレクトリを指定します。また、DOCKER_IMAGEに先ほど作成したDockerイメージを、DOCKER_WORKING_DIRにDockerコンテナ内の適当な作業ディレクトリを指定します。

#!/bin/bash
#LOCAL_WORKING_DIR="/Users/imamachinaoto/Desktop/NGS/data" # MacOS
LOCAL_WORKING_DIR="/c/Users/imama/Desktop/NGS/fastqc/data"    # Windows10(WSL)
DOCKER_WORKING_DIR="/data"
DOCKER_IMAGE="debian9-slim-bioconda2-fastqc:latest"
FASTQ_FILE="RefSeq_hg38_2015-08-19_NM_ultraSlim.fq"

docker run --rm -it \
-v ${LOCAL_WORKING_DIR}:${DOCKER_WORKING_DIR} \
${DOCKER_IMAGE} \
--fastq_path ${DOCKER_WORKING_DIR}/${FASTQ_FILE} \
--working_dir ${DOCKER_WORKING_DIR}

ちなみに、DockerでWSL環境からホストOS(Windows10)のフォルダを見るとき、Cドライブは/mnt/cではなく、/cです。Bashシェルからアクセスするときは、/mnt/c(WSLにマウントされているCドライブを見る形)なのでパスの違いに注意が必要です。Windowsだと、これで詰まりました…。

コンテナでデータを管理する
http://docs.docker.jp/engine/userguide/dockervolumes.html

上記シェルスクリプト(もしくは以下で説明するmakeコマンド)を実行することで、FastQCが実行されます。

image.png (191.3 kB)

ホストOSのディレクトリを、Docker上のディレクトリに紐づけておくこと(ボリュームのマウント)で、以下のようにDockerコンテナが破棄されたあとでも、データを永続化させることができます。

image.png (36.4 kB)

Dockerコマンドをまとめたmakefileの作成

Dockerコマンドはイメージファイルの作成やコンテナ起動時など、繰り返し使用します。なので、今回はmakefileにコマンド類をまとめて、make buildmake startなどを用意することで、コマンド操作が簡単になります。

Windows10やWSLには、makeコマンドがインストールされていないため、あらかじめインストールしておきます。

Windows10でmakeファイルを実行する

WSLを使わない場合のmakeコマンドのインストール方法を示します。Windows10にはコマンドプロンプトからmakeコマンドを使えないので、まずmakeコマンドをインストールします。

1. makeコマンドをコマンドプロンプトに導入する(参考)

以下のURLから、makeをダウンロードします。
http://gnuwin32.sourceforge.net/packages/make.htm

image.png (225.1 kB)

C:\Program Files (x86)\GnuWin32\binにmake.exeが保存されているので、パスを通します。

image.png (56.5 kB)

2. WSL環境にmakeコマンドを導入する

WSLのコンソールから、以下のコマンドを実行するだけです。

$ sudo apt-get install make

Windowsでmakeコマンドを使う
https://qiita.com/tokikaze0604/items/e13c04192762f8d4ec85

makefileの例

NAME=debian9-slim-bioconda2-fastqc
TAG=0.11.6.local
UNDERSCORE_TAG=0_11_6_local

LOCAL_WORKING_DIR=/c/dev/NGS/fastqc/data
DOCKER_WORKING_DIR=/data
DOCKER_IMAGE="fastqc-aws-debian9-slim-bioconda2:0.11.6.local"
FASTQ_FILE=RefSeq_hg38_2015-08-19_NM_ultraSlim.fq

all: build push

build:
      docker build -t $(NAME):$(TAG) -t $(NAME):latest -f Dockerfile .
build-no-cache:
      docker build --no-cache -t $(NAME):$(TAG) -t $(NAME):latest -f Dockerfile .

push:
      docker push $(NAME):$(TAG)
      docker push $(NAME):latest

start:
      docker run --rm -it -v ${LOCAL_WORKING_DIR}:${DOCKER_WORKING_DIR} ${DOCKER_IMAGE} --fastq_path ${DOCKER_WORKING_DIR}/${FASTQ_FILE} --working_dir ${DOCKER_WORKING_DIR}

rm:
      docker rm `docker ps -aq`

rmi:
      docker rmi $(NAME):$(TAG) $(NAME):latest

つまづいたポイント

makefileの作成

インデントはスペースではなく、タブにしないとエラーになる。 Visual Studio Codeの場合、makefileと認識されたファイルでは、インデントをタブにするように設定されている。しかし、ファイル名がMakefileだと下記の設定が適応されない。makefileと小文字のmにする必要がある。

image.png (76.5 kB)

Docker関連の参考

docker コマンド チートシート
https://qiita.com/voluntas/items/68c1fd04dd3d507d4083
dockerでなrepositoryを消す
https://qiita.com/lirispp/items/06fc74c5bbc64fddf9ab