
ゲノムブラウザ JBrowse2 を CloudFront + S3 で Web 公開してみた
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
ゲノムブラウザの JBrowse を Web 公開するときに Web サーバー(Apache, Nginx)が必要なのか調べていました。 すると、静的サイトなので Amazon S3 でも動作すると公式ガイドに書いてあります。ということで試してみました。
JBrowse doesn't strictly need Apache or nginx, it is "static site compatible" meaning it uses no server code and can run on any static website hosting. For example, you can upload the jbrowse folder that we prepared here in /var/www/html/jbrowse2 to Amazon S3, and it will work there too. See the FAQ for what webserver do I need for more info.
引用: JBrowse 2 combined guide | JBrowse
ところで S3 で公開するとなにが嬉しいの?と言われますと、サーバーに比べると格段に維持費が安いのも嬉しいですけど、なにより Web サーバーの運用・管理をしなくて済むのが嬉しいのではないでしょうか。今回は定番の構成で CDN のサービスとセットで環境構築します。
構成図
CloudFront(CDN)+ S3 構成にカスタムドメインでアクセスできる環境を Cloudformation で作成します。
CloudFront と S3 の準備
ゲノムブラウザを Web 公開するための静的サイトを構築します。あとで中身(ファイル)を入れて静的サイトが完成します。
前提
- Route53 にドメイン名の登録済み
- CloudFront で使用する証明書をバージニア北部(us-east-1)リージョンに登録済み
テンプレート
CloudFront のログは180日保存で自動削除のライフサイクルルールが設定してあります。ログ保持期限ポリシーがある場合はお気をつけください。
bigmuramura/cfn-cloudfront-s3にも置いてあります。
折りたたみ
AWSTemplateFormatVersion: "2010-09-09"
Description: Cloudfront and S3
Parameters:
  ProjectName:
    Description: Project Name
    Type: String
    Default: unnamed
  Environment:
    Description: Environment
    Type: String
    Default: dev
    AllowedValues:
      - prod
      - dev
  S3BucketName:
    Description: Bucket name for static site
    Type: String
  AliasName:
    Description: Alias for CloudFront
    Type: String
  HostedZoneId:
    Description: Route53 Host Zone ID
    Type: String
  CertificateId:
    Description: ACM Certificate ID must be us-east-1 region
    Type: String
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Common Settings
        Parameters:
          - ProjectName
          - Environment
      - Label:
          default: SSL Settings
        Parameters:
          - AliasName
          - HostedZoneId
          - CertificateId
Resources:
  # ------------------------------------------------------------------------------------ #
  # S3
  # ------------------------------------------------------------------------------------ #
  # Static site bucket
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${ProjectName}-${Environment}-${S3BucketName}-${AWS::AccountId}
      OwnershipControls:
        Rules:
          - ObjectOwnership: "BucketOwnerEnforced"
      PublicAccessBlockConfiguration:
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True
      VersioningConfiguration:
        Status: Enabled
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: "AES256"
            BucketKeyEnabled: false
      LifecycleConfiguration:
        Rules:
          - Id: AbortIncompleteMultipartUpload
            AbortIncompleteMultipartUpload:
              DaysAfterInitiation: 7
            Status: "Enabled"
          - Id: NoncurrentVersionExpiration
            NoncurrentVersionExpiration:
              NewerNoncurrentVersions: 3
              NoncurrentDays: 1
            Status: Enabled
  # Bucket Policy for CloudFront
  BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref S3Bucket
      PolicyDocument:
        Statement:
          - Action: s3:GetObject
            Effect: Allow
            Resource: !Sub arn:aws:s3:::${ProjectName}-${Environment}-${S3BucketName}-${AWS::AccountId}/*
            Principal:
              AWS: !Sub arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${CloudFrontOriginAccessIdentity}
  # ------------------------------------------------------------------------------------ #
  # CloudFront
  # ------------------------------------------------------------------------------------ #
  CloudFrontDistribution:
    Type: "AWS::CloudFront::Distribution"
    Properties:
      DistributionConfig:
        PriceClass: PriceClass_All
        Origins:
          - DomainName: !Sub ${ProjectName}-${Environment}-${S3BucketName}-${AWS::AccountId}.s3.${AWS::Region}.amazonaws.com
            Id: !Sub "S3origin-${ProjectName}-${Environment}-${S3BucketName}-${AWS::AccountId}"
            S3OriginConfig:
              OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}"
        DefaultRootObject: index.html
        DefaultCacheBehavior:
          TargetOriginId: !Sub "S3origin-${ProjectName}-${Environment}-${S3BucketName}-${AWS::AccountId}"
          Compress: true
          ViewerProtocolPolicy: redirect-to-https
          CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 # CachingOptimized
          AllowedMethods:
            - GET
            - HEAD
          CachedMethods:
            - GET
            - HEAD
          ForwardedValues:
            Cookies:
              Forward: none
            QueryString: false
        Logging:
          Bucket: !GetAtt S3BucketLogs.DomainName
          IncludeCookies: false
        Aliases:
          - !Ref AliasName
        ViewerCertificate:
          SslSupportMethod: sni-only
          MinimumProtocolVersion: TLSv1.2_2021
          AcmCertificateArn: !Sub "arn:aws:acm:us-east-1:${AWS::AccountId}:certificate/${CertificateId}"
        HttpVersion: http2
        IPV6Enabled: true
        Enabled: true
  # OAI
  CloudFrontOriginAccessIdentity:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: Unique Domain Hosting Environment
  # CloudFront log bucket
  S3BucketLogs:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Properties:
      BucketName: !Sub ${ProjectName}-${Environment}-${S3BucketName}-cloudfrontlogs-${AWS::AccountId}
      OwnershipControls:
        Rules:
          - ObjectOwnership: "ObjectWriter"
      PublicAccessBlockConfiguration:
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True
      VersioningConfiguration:
        Status: Enabled
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: "AES256"
            BucketKeyEnabled: false
      LifecycleConfiguration:
        Rules:
          - Id: AbortIncompleteMultipartUpload
            AbortIncompleteMultipartUpload:
              DaysAfterInitiation: 7
            Status: "Enabled"
          - Id: CurrentVersionExpiration
            ExpirationInDays: 180
            Status: "Enabled"
          - Id: NoncurrentVersionExpiration
            NoncurrentVersionExpiration:
              NoncurrentDays: 30
            Status: "Enabled"
  # Alias Record
  Route53RecordSet:
    Type: AWS::Route53::RecordSet
    Properties:
      Name: !Sub ${AliasName}
      HostedZoneId: !Sub ${HostedZoneId}
      Type: A
      AliasTarget:
        DNSName: !GetAtt CloudFrontDistribution.DomainName
        HostedZoneId: Z2FDTNDATAQYW2 # fixed
パラメータで前提事項であった事前作成済みリソースのIDを入力します。
- ホストゾーンID
- Route53よりドメインを確認
 
- 証明書ID
- バージニア北部(us-east-1)で発行した証明書を確認
 
CloudFront の他、S3 バケットが2つ作成されます。上が静的サイトとして CloudFront を通して Web 公開されるバケットです。下は CloudFront のアクセスログ保存用のバケットです。後ほど上のバケットに Jbrowse2 のファイルをアップロードします。
JBrowse のファイル準備
前回 JBrowse ローカル実行環境を作成しましたので、以下のリンクを参考にjbrowse2フォルダを作成し静的サイトに必要ファイルを生成します。
最終的に /var/www/html配下に作成されるjbrowse2フォルダを S3 にアップロードして Web 公開します。
jbrowse2 フォルダ作成
/var/www/html配下にjbrowseフォルダ作成する予定のためローカル PC のディレクトリをマウントします。これでコンテナ内の JBrowse CLI でセットアップする初期ファイルを永続化できます。ここではマウントするディレクトリは/Users/ohmura.yasutaka/work/jbrowse/volを例にすすめていきます。
$ docker run -it -p 3000:3000 -v /Users/ohmura.yasutaka/work/jbrowse/vol:/var/www/html jbrowse2:v1 bash
前回のローカル環境構築手順と同様にショウジョウバエのリファレンスゲノムをサンプルに使用します。
# jbrowse create /var/www/html/jbrowse2 # cd /tmp # wget https://ftp.ncbi.nlm.nih.gov/genomes/all/GCF/000/001/215/GCF_000001215.4_Release_6_plus_ISO1_MT/GCF_000001215.4_Release_6_plus_ISO1_MT_genomic.fna.gz # gunzip GCF_000001215.4_Release_6_plus_ISO1_MT_genomic.fna.gz # samtools faidx GCF_000001215.4_Release_6_plus_ISO1_MT_genomic.fna # jbrowse add-assembly GCF_000001215.4_Release_6_plus_ISO1_MT_genomic.fna --out /var/www/html/jbrowse2 --load copy
コンテナにマウントしたローカルディレクトリ(vol配下)に必要なjbrowse2フォルダが作成・保存されました。
$ pwd
/Users/ohmura.yasutaka/work/jbrowse/vol
 $ tree -L 2
.
└── jbrowse2
    ├── GCF_000001215.4_Release_6_plus_ISO1_MT_genomic.fna
    ├── GCF_000001215.4_Release_6_plus_ISO1_MT_genomic.fna.fai
    ├── asset-manifest.json
    ├── config.json
    ├── favicon.ico
    ├── index.html
    ├── manifest.json
    ├── robots.txt
    ├── static
    ├── test_data
    └── version.txt
ファイルをアップロード
Cloudformation で作成した静的サイト用の S3 バケットに jbrowse2 配下のファイルをコピーします。オリジナルの TOP ページを作成する場合は index.html を自作してアップロードし、jbrowse2のファイル類はパスを切ってアップロードしてください。ここでは Jbrowse2 で必要なファイルをシンプルにアップロードします。
.fnaファイルの容量はそこそこのサイズがあると思いますので aws s3 sync で転送すると効率が良いです。
$ aws s3 sync ~/work/jbrowse/vol/jbrowse2/ s3://sample-dev-jbrowse-123456789012
S3 バケットを確認するとファイルがアップロードされています。
index.html について触れたのは CloudFront の設定にあるデフォルトルートオブジェクトで指定済みだったためです。CloudFront のテンプレートに書き込んでいるため、必要に応じてファイル名に変更してください。
Web アクセスしてみる
https://独自ドメイン でアクセスできます。
CloudFront + S3 バケットで構成した環境でもゲノムブラウザを操作できますね。
リロードするとキャッシュにヒットしていることから CloudFront からの配信であることも確認できます。
補足 アクセス制限
S3 へ直接アクセスはできない構成にしてあります。CloudFront でアクセス制限をかければ公開範囲を制限することもできます。
グローバル IP 制限
Basic 認証
おわりに
バイオインフォマティクスで使われるツールのドキュメントにも AWS の用語が登場する時代なんだという発見がありました。と同時に第一線の研究者が S3 で静的サイトを構築できるかというとまた話は別かなとも思いました。研究者とクラウドのインフラをいい感じに取り持てる人は研究現場で重宝されそうですね。























