「Dockerベースの研究室クラウド基盤」の作成方法

JPA2022 TWS-004 「心理学研究における研究室インフラの整備」

Author

国里愛彦(専修大学

Dockerベースの研究室クラウド基盤

サーバー設定

研究室クラウド基盤を整備するにあたって,以下のようにサーバーを設定する。

  • さくらインターネットやGCPなどのサーバー上に,Dockerを使ってできるだけ楽にサービスを展開する(楽に設定したい)。
  • traefikを通して,SSL,負荷分散などを行って,1つのサーバー上で複数のサービスを展開する(セキュリティは担保しつつサーバーは1つに抑えたい)。

注意事項

以降では各種サービスをサーバー上で展開して,研究室で活用していく方法について説明するが,以下の注意事項がある。

  • 国里は臨床心理学者であり,情報系の専門家ではないサーバーの利用については,各自がサーバーの基礎知識や基本的なセキュリティ管理をしっかり身につけていただくようにお願いします。
  • 以降の内容について,誤った操作・設定による損害・損失について国里は責任を持てませんので,各自の判断で行ってください。

サーバーの選択

クラウドコンピューティングサービスは,GCP(Google Cloud Platform),AWS(Amazon Web Service),Microsoft Azure,さくらインターネット(専用サーバー)など様々ある。

VPSなどのサービスは安価だが,CPUの負荷がかかるRStudioやJupyterを使う場合はマシンパワーが足りないかもしれない(CPUに過負荷をかけてはいけないサービスもある)。国里は,2019年くらいからGCPを使っているが,2022年度からはゼミはさくらインターネットを使っている(円安の影響を受けない,固定金額)。

GCP(Google Cloud Platform)を用いたクラウド基盤の構築法

本日のデモの構成

GCPを使えるように設定し,GCE(Compute Engine)にて「インスタンスの作成」をクリックする。なお,Googleアカウントの2 段階認証を設定していない場合は,この機会に設定して,セキュリティを高める(パスワードも長く複雑性の高いものにしておく)。

今回は,月額の利用料金を低く抑えられるGCPのe2-medium(2vCPU, 4GB)を使う。複数人で使う場合は,n2-standard-2(2vCPU, 8GBメモリ,約75ドル/月),n2-standard-4(4vCPU, 8GBメモリ,約150ドル/月)が良いかもしれない(大体15名程度が在籍する国里研究室では,昨年度はn2-standard-4くらいを使っていた)。

ブートディスクを変更する

インスタンスを選んだら,「ブートディスク」で「変更」をクリックする。

OSやディスクの設定

OSはUbuntu 20.04 LTS,ディスクは50GBにした。

ファイヤーウォールを設定して,インスタンス起動

「HTTP トラフィックを許可する」と「HTTPS トラフィックを許可する」にチェックをいれて,「作成」をクリックする。インスタンスが作成されて起動される。

SSH接続してポートの変更をする

デフォルトの22番ポートには外部からの攻撃もあるのでポートを変更する。

ファイヤーウォールルールの追加

GCPの「VPCネットワーク」→「ファイヤーウォールルール」で「ファイアウォール ルールの作成」をクリックして,以下のように新しいSSHポートを設定する。

  • 名前やターゲットタグ:任意(私は”ssh-allow-port”としています)
  • 優先度: 1000
  • トラフィックの方向: 上り
  • 一致した時のアクション: 許可
  • ソースのIP範囲: 0.0.0.0/0
  • プロトコル: tcp
  • ポート: 好きなポート番号

GCP上でVMインスタンスをクリックし,「編集」ボタンを押して,ネットワークタグに上記で決めたターゲットタグを入れて保存する。

SSH接続してポートを変更する

インスタンスの「SSH」をクリックして,サーバーに接続(22番ポート仕様)する。

コンソールが出てきたら,以下のコマンドでsshdの設定ファイルを開く。

sudo vi /etc/ssh/sshd_config

画面上で# Port 22 を探して, i をタイプして(文字が入可能に),#を削除して,22の代わりに使いたいポート番号を入力し,escキーをタイプしてから, :wq とタイプする(保存&閉じる)。

sshdの設定を反映させるために,以下のコマンドでsshdを再起動させます。

sudo systemctl restart sshd

22ポートを閉じる

22番portで開いた画面は閉じずにそのままにし(もしここで閉じちゃうと,ポート設定がうまくできてない場合に自分がサーバーにアクセスできなくなる),VMインスタンスの「SSH」の下矢印を押して,「ブラウザウィンドウでカスタムポートを開く」をクリックする。そして,入力欄に先程設定した新しいSSHポート番号を入力する。

新しいSSHポートでも開けたら,「VPCネットワーク」→「ファイヤーウォールルール」で「ファイアウォール ルールの作成」を押して,以下を設定する。

  • 名前やターゲットタグ: 任意(私は”disallow-ssh22”とした)
  • 優先度: 1000
  • トラフィックの方向: 上り
  • 一致した時のアクション: 拒否
  • ソースのIP範囲: 0.0.0.0/0
  • プロトコル: tcp
  • ポート: 22

VMインスタンスの「編集」を押し,ネットワークタグに上記で決めたターゲットタグを入れて保存する(これで,httpのタグ,httpsのタグ,新しいsshポートのタグ,22番ポートを閉じるタグの4つになる)。

通常のSSH接続(22番ポート)をしても接続できず,「ブラウザウィンドウでカスタムポートを開く」で自分で設定したポートを使ってSSH接続できたら成功になる。

SSH接続後に以下を入力するとアクセス状況が見れる( f で次の画面に進んで, q で閉じます)

sudo less /var/log/auth.log

その他のセキュリティ設定としては,(1)請求アラートの設定(不正利用にも気付ける),(2)サーバー稼働状況の監視,(3)GCPのスマホアプリをいれていつでもVMをシャットダウンできるようにしておくなどがある。

Domainの入手とサブドメインの設定

Google Domainなどで研究室サーバー用のドメインを入手する(年1400円程度,無料でも入手できるサービスもあるが,国里は利用したことがない)。

Google Domainなどで,DNS設定をする(ドメイン名とIPアドレスを紐付ける)。ホスト名にサービス名をいれるとサブドメイン化できる(データにはインスタンスで表示される「外部IP」を入れる)。

Dockerのインストール

サーバー自体の設定ができたので,サービスを動かすためのDockerをDocker社のドキュメントに従ってインストールする。

まず,以下のように,Dockerのインストールに必要なソフトをインストールする。

sudo apt-get update
sudo apt-get install ca-certificates curl gnupg lsb-release

GPGキーの追加とリポジトリの用意をする。

sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Docker Engineをインストールする。これでDockerの準備は完了になる。

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

Traefikの準備

各種サービスの暗号通信化(SSL)などをするTraefikをDockerを使って準備する。

Traefikはダッシュボードを出してくれるが,外部から丸見えなので,パスワードをつけるBASIC認証で使うと良い。その際に,htpasswdというソフトを使うので,それが入ったapache2-utilsをインストールする。

sudo apt install apache2-utils

htpasswdを,ユーザー名とパスワードを変更して実行する。アカウント名とハッシュ値が出てくるので保存しておく。

htpasswd -nb ユーザー名 パスワード

dockerでネットワークの準備をする(ここでは,traefik-networkという名前にして,後でも使う)。

sudo docker network create traefik-network

Traefikの作業フォルダを作って,移動する。

mkdir traefik
cd traefik

以下を実行して,traefik.ymlを用意する。

sudo vi traefik.yml

以下をコピペして,メールアドレス@example.comはご自身のメールアドレスに変更する。変更できたら, esc を押して,:wqをタイプすることで保存して閉じる。

なお,以降でも,このymlが出てくるが,ymlはインデントが重要なので,インデントがズレていると正常に動作しない。ymlでのインデントは,半角スペース2つになる。

entryPoints:
  web:
    address: :80
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: :443
api:
  insecure: false
  dashboard: true

providers:
  docker:
    exposedByDefault: false

certificatesresolvers:
  myresolver:
    acme:
      httpChallenge:
        entryPoint: web
      email: "メールアドレス@example.com"
      storage: "/letsencrypt/acme_myresolver.json"

log:
  filePath: "/logs/traefik.log"
  format: json
  level: INFO

accessLog:
  filePath: "/logs/access.log"
  format: json

以下を実行して,compose.yml を用意する。

sudo vi compose.yml

以下をコピペする。your.domainは,自分の使っているドメイン名に変更し, アカウント名とハッシュ値を貼り付けは,上のhtpasswdで出たIDのハッシュ値に変更する(その際に, $は全て$$にしないと動かない落とし穴があるので注意する。これでミスると動かない)。できたら,escを押して,:wqをタイプすることで保存して閉じる。

services:
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: always
    ports:
      - "80:80"
      - "443:443"
    networks:
      - traefik-network
    environment:
      TZ: Asia/Tokyo
    labels:
      traefik.enable: true
      traefik.http.routers.dashboard.rule: Host(`traefik.your.domain`)
      traefik.http.routers.dashboard.entrypoints: websecure
      traefik.http.routers.dashboard.tls.certresolver: myresolver
      traefik.http.routers.dashboard.service: api@internal
      traefik.http.routers.dashboard.middlewares: auth
      traefik.http.middlewares.auth.basicauth.users: アカウント名とハッシュ値を貼り付け
    volumes:
      - letsencrypt_data:/letsencrypt 
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      - /var/log:/logs

volumes:
  letsencrypt_data:

networks:
  traefik-network:
    external: true

compose.ymlを以下のように実行する。これで,compose.ymlで設定したようにdockerが設定され,コンテナが起動する。

sudo docker compose up -d

https://traefik.ご自身のドメイン にアクセスすると,アカウント認証があるが,設定したものを入力して,以下のようなダッシュボードがでてきたら,成功している。

問題がなさそうなら,一度ホームディレクトリに戻る。

cd ..

RStudio serverを用意する

ホームディレクトリ内にrstudio_composeディレクトリを作って,そちらに移動し,compose.ymlを作る。

sudo mkdir rstudio_compose
cd rstudio_compose
sudo vi compose.yml

開いたdocker-compose.ymlに,以下のコードをコピペして,以下の変更を加える。

  • rstudio.your.domainyour.domainを自分のドメイン名に変更
  • environmentのユーザーIDパスワードを自分のものに変更

変更できたら,escキーをタイプして, :wq とタイプする(保存して閉じる)。

services:
  rstudio:
    image: ykunisato/paper-r:latest
    container_name: rstudio
    networks:
      - traefik-network
    container_name: rstudio
    restart: always
    environment:
      - USER=ユーザー名
      - PASSWORD=パスワード
    volumes:
      - ./rstudio:/home/rstudio
    labels:
      traefik.enable: true
      traefik.http.routers.rstudio.rule: Host(`rstudio.your.domain`)
      traefik.http.routers.rstudio.entrypoints: websecure
      traefik.http.routers.rstudio.tls.certresolver: myresolver

networks:
  traefik-network:
    external: true

以下を実行して,compose.ymlに従ってコンテナを起動する。イメージのダウンロードなどにしばらく時間がかかる。doneとでてきたら終了。

sudo docker compose up -d

https://rstudio.ご自身のドメイン にアクセスして,RStudioのログイン画面が出てきて,ログインができたらうまくいっている。

一度,ホームディレクトリに戻る。

cd ..

ここでうまくいかない場合,volumesの左側の./rstudioは,.のところをGCP上でpwdをした時にでてくるカレントディレクトにする必要があるかもしれない。また,RStduioの方の修正でうまくいかない場合は,上のTraefikの設定にミスがないかチェックください。

RStudioへのユーザー追加

compose.ymlで設定した以外のユーザーを追加する場合は,Dockerコンテナに接続してユーザーアカウントを作る。まず,以下のコマンドで,rstudioコンテナにssh接続する。

sudo docker exec -it rstudio bash

次に,以下のコマンドで新規ユーザー登録する。パスワードを聞かれるので打ち込んで,あとはエンターキーやYをタイプする。

sudo adduser 新規ユーザー名

このままだと,各ユーザーが自由にRパッケージをいれられないので,以下のコマンドで,ユーザーをstaffグループにする。

sudo usermod -a -G staff ユーザー名

exitでコンテナから出る。

exit

ユーザーの一括追加

上記の方法は1名ずつ追加する方法だが,人数が多くなると大変になる。その場合は,newusersを使った一括登録が良い。まずは,以下のような感じの情報を表計算ソフトに入力する。ユーザーごとに変更が必要なのは,ユーザー名,ユーザーID(1001から連番にする),パスワード,ホームディレクトリ名(home/rstudioの下にユーザー名をいれる)である。残りの,グループID(50), フルネーム(空白),ログインシェル(/bin/sh)は全員同じで問題ない。

ユーザー名 パスワード ユーザーID グループID フルネーム ホームディレクトリ ログインシェル
ユーザー1 ユーザー1のパスワード 1001 50 /home/rstudio/ユーザー1 /bin/sh
ユーザー2 ユーザー2のパスワード 1002 50 /home/rstudio/ユーザー2 /bin/sh
ユーザー3 ユーザー3のパスワード 1003 50 /home/rstudio/ユーザー3 /bin/sh

なお,上記の情報は,最終的に区切りに:を使って保存する。つまりuser1については,ユーザー1:ユーザー1のパスワード:1001:50::/home/rstudio/ユーザー1:/bin/shになる。上記の情報を表計算ソフトにいれて,それをcsvファイルで出力して,テキストエディタで開いて,カンマを:に変換して,一番上のヘッダー情報は削除して,最後に.txtの拡張子で保存するのが楽かと思う。ファイル名は,user.txtとしておく(なんでもいい)。

user.txtが作成できたら,VMにSSH接続して,user.txtをアップロードする(ウィンドウの右上の設定っぽいボタンをクリックするとアップロードの選択肢がでてくる)。アップロードしたuser.txtをrstudioのフォルダに一時的に移動する。

sudo mv user.txt rstudio_compose/rstudio

rsutdioのコンテナに入る。

sudo docker exec -it rstudio bash

以下を実行して,一括でユーザー登録する。

newusers /home/rstudio/user.txt

一応,user.txtを削除してから,コンテナからでる。

rm /home/rstudio/user.txt
exit

JupyterHubを用意する

ホームディレクトリ内にjupyterhub_composeディレクトリを作って,そちらに移動し,compose.ymlを作る。

mkdir jupyterhub_compose
cd jupyterhub_compose
sudo vi compose.yml

以下をコピペして,jupyterhub.your.domainyour.domainを自分のドメイン名に変更する。変更できたら,escキーをタイプして, :wq とタイプする(保存して閉じる)。

services:
  jupyterhub:
    image: ykunisato/ccp-lab-hub
    container_name: jupyterhub
    networks:
      - traefik-network
    restart: always
    volumes:
      - /home/ubuntu/jupyterhub:/home
    labels:
      traefik.enable: true
      traefik.http.routers.jupyterhub.rule: Host(`jupyterhub.your.domain`)
      traefik.http.routers.jupyterhub.entrypoints: websecure
      traefik.http.routers.jupyterhub.tls.certresolver: myresolver

networks:
  traefik-network:
    external: true

以下を実行して,compose.ymlに従ってコンテナを起動する

sudo docker compose up -d

特にエラーが出てないなら,一度ホーム画面に戻る

cd ..

Jupyter Hubのユーザー追加と設定

利用開始にあたり,コンテナ内に入ってユーザーを追加する(XXXはユーザー名,途中でパスワードが聞かれる。あとはYを打ったりエンターを押す)。

sudo docker exec -it jupyterhub bash
adduser XXX

作成したアカウントをsuにして,juliaを起動する。

su XXX
julia

以下をコピペして,juliaを設定する(パッケージを一気にインストールする)。

ENV["PYTHON"] = "";using Pkg;Pkg.update(); Pkg.add(["IJulia","PyCall"]); Pkg.build(["IJulia","PyCall"]); Pkg.add(["DataFrames","PyPlot","Distributions","Statistics","JuliaFormatter","CPUTime","Gadfly","GLM","Optim","Plots","Query","RDatasets","SpecialFunctions","StatisticalRethinking","StatsBase","StatsFuns","StatsPlots","AdvancedHMC","BAT","Bijectors","CmdStan","DiffEqBayes","DistributionsAD","ForwardDiff","MCMCChains","MeasureTheory","ParameterizedFunctions","Turing","LinearAlgebra","DifferentialEquations","Roots","SymPy","ForneyLab"]);Pkg.precompile()

exitでコンテナから出る。

exit

https://jupyterhub.ご自身のドメイン にアクセスして,Jupyterhubのログイン画面が出てきて,ログインができたらうまくいっている。

JATOSを用意する

ホームディレクトリ内にjatos_composeディレクトリを作って,そちらに移動し,compose.ymlを作る。

sudo mkdir jatos_compose
cd jatos_compose
sudo vi compose.yml

以下をコピペして,jatos.your.domainyour.domainを自分のドメイン名に変更する。変更できたら,escキーをタイプして, :wq とタイプする(保存して閉じる)。

services:
  jatos:
    image: jatos/jatos:latest
    container_name: jatos
    networks:
      - traefik-network
    restart: always
    labels:
      traefik.enable: true
      traefik.http.routers.jatos.rule: Host(`jatos.your.domain`)
      traefik.http.routers.jatos.entrypoints: websecure
      traefik.http.routers.jatos.tls.certresolver: myresolver

networks:
  traefik-network:
    external: true

以下を実行して,compose.ymlに従ってコンテナを起動する。

sudo docker compose up -d

エラーが無ければ,一度ホーム画面に戻る

cd ..

JATOSは立ち上がったらすぐに,https://jatos.ご自身のドメイン にアクセスして,admin/adminでログインして,アドミンのパスワードを変更する(最初は誰でもadmin/adminで入れるので,立ち上がったらすぐに設定する)。あとの設定はJATOS上でできる。

WordPressを用意する

ホームディレクトリ内にwordpress_composeディレクトリを作って,そちらに移動し,compose.ymlを作る。

sudo mkdir wordpress_compose
cd wordpress_compose
sudo vi compose.yml

以下をコピペして,以下を変更する。

  • wordpress.your.domainyour.domainを自分のドメイン名に変更
  • ルートのパスワードをご自身のものに変更
  • DBのパスワードをご自身のものに変更(2箇所)

変更できたら,escキーをタイプして, :wq とタイプする(保存して閉じる)。

services:
  db:
    image: mysql:5.7
    container_name: wp-db
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - default
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ルートのパスワード
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: DBのパスワード

  wordpress:
    depends_on:
      - db
    image: wordpress:latest
    container_name: wordpress
    networks:
      - traefik-network
      - default
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: DBのパスワード
    labels:
      traefik.enable: true
      traefik.http.routers.wordpress.rule: Host(`wordpress.your.domain`)
      traefik.http.routers.wordpress.entrypoints: websecure
      traefik.http.routers.wordpress.tls.certresolver: myresolver

volumes:
    db_data:

networks:
  traefik-network:
    external: true

以下を実行して,compose.ymlに従ってコンテナを起動する

sudo docker compose up -d

エラーが無ければ,ホーム画面に戻る

cd ..

https://wordpress.ご自身のドメイン にアクセスして, WordPressが立ち上がったのを確認したら,WordPressのインストールとアドミンアカウントの設定を行う。

プラグインのSensei-LMSをインストールして,有効化する。

サービスの稼働状況の確認

これで,自分のサーバーで4サービス(RStudio server, JupyterHub, JATOS, WordPress)が展開できた。以下で稼働状況が確認できる。

sudo docker ps -a

各サービスの使用状況(CPUやメモリ)は,以下で確認できる。使用状況が流れるので,不要になったら,Ctrl + cで停止する。使ってないのに異常にCPUが稼働している場合は,なんらかのトラブルを想定する。

sudo docker stats -a

Enjoy!