cucumber flesh

Rを中心としたデータ分析・統計解析らへんの話題をしていくだけ

Rから離れたくない人向けのDocker環境の操作: RStudio Serverを分析・開発の基盤にするために

この記事はRStudioアドベントカレンダーの21日目の記事です。もうすぐこのアドベントカレンダーも終わりですね。ハヤイ!

今年のはじめにこんな記事を書きました。

uribo.hatenablog.com

皆さんはDockerを利用していますでしょうか。今年のデータ分析系のアドベントカレンダーでもぞうさんがdockerが取り上げられています。

qiita.com

Rユーザの自分にとっては、Dirk EddelbuettelCarl Boettigerなどが携わるrockerプロジェクトが整備されているのが嬉しいです。

notchained.hatenablog.com

rockerプロジェクトのdockerイメージの多くはRStudio Serverをイメージのベースとしており、お手軽にローカル環境とは別のRStudio環境が構築できます。また必要に応じて、rockerのdockerimageをベースに俺俺dockerimageへ拡張するのも簡単です。俺俺dockerimageの必要性についてはTokyo.RのLTで発表を行った過去の資料もあります。

speakerdeck.com

そんな便利なdockerですが、dockerコマンドを覚えられなかったりコマンドラインから実行するのが面倒だったりします。特に自分は常にRStudioの画面を開いていたい側の人間なのでRからdockerコンテナやイメージの操作ができると良いです。良いです(大切なことなので二回言いました)。

... というわけでdockerパッケージを使います。これにより、Rから直接dockerのあれこれが行えるようになります(実態はPythondockerモジュールreticulateパッケージでラップしているだけ)。大好きなRからできるので、dockerで何ができるかも理解しやすくて良いですね。

github.com

dockerパッケージ

以下の説明はdockerが利用できる環境で、dockerモジュールがインストールされていることを想定しています。あ、Rのdockerパッケージのインストールも忘れずに。CRANからインストールできます。まずはdockerを起動した状態で次のコードを実行します。Rスクリプトと一緒にコマンドラインで実行する際のdockerコマンドも示します。

# install.packages("docker")
library(magrittr)
library(docker)
client <- docker$from_env()

インストールされているイメージ一覧を出力します。

# docker images
client$images$list()
# [[1]]
# <Image: 'uribo/tiny_rocker_geospatial:latest'>
# 
# [[2]]
# <Image: 'rocker/tidyverse:latest'>
# 
# [[3]]
# <Image: 'kaggle/rstats:latest'>
# 
# [[4]]
# <Image: 'rocker/rstudio:latest'>
# 
# [[5]]
# <Image: 'rocker/r-base:latest'>

Dockerイメージをpullしてくるにはpull関数を使います。

# docker pull uribo/tiny_rocker_geospatial
client$images$pull("uribo/tiny_rocker_geospatial")

利用するdockerイメージの用意ができたらコンテナを起動しましょう。次のRスクリプトの実行は、docker run --rm -p 8787:8787 uribo/tiny_rocker_geospatial と同じです。

rss_instance <- client$containers$run(image = "uribo/tiny_rocker_minimum",
                                      remove = TRUE,
                                      # RStudio上で操作を続ける際はTRUEにする
                                      detach = TRUE,
                                      ports = list("8787/tcp" = "8787"))
rss_instance$start()

これでコンテナが起動しました。localhost:8787をブラウザで開きましょう。また次のコードでコンテナの一覧を確認してみると、きちんとコンテナが動いていることがわかります。

# docker ps -a
client$containers$list()
# [[1]]
# <Container: a94576e6a2>

コンテナを終了するにはstop関数を使います。現在のコンテナはremoveオプションを有効にしていたため、コンテナを停止すると自動的にコンテナが削除されます。

# docker stop <コンテナID>
rss_instance$stop()

# 停止とともにコンテナは削除される
# docker ps -a
client$containers$list()
# list

基本はこれでOKです。既存のコンテナを起動するには、まずコンテナのIDを知る必要があります。あるいはコンテナに名前をつけている場合にはそれを使うことも可能です。次はコンテナに名前をつけ、停止しても再起動できるように永続化させましょう。

rss_instance <- client$containers$run(image = "uribo/tiny_rocker_geospatial",
                                      remove = FALSE,
                                      detach = TRUE,
                                      name = "dev_1712",
                                      ports = list("8787/tcp" = "8787"))
# 再起動を行うため停止します
rss_instance$stop()

では停止しているコンテナを再起動します。再起動の方法には色々ありますが、ここではコンテナ名からコンテナidを抽出し、それを利用する方法を用います。

httr::http_error("http://localhost:8787")
# Error in curl::curl_fetch_memory(url, handle = handle) : 
#   Failed to connect to localhost port 8787: Connection refused
container_id <- client$containers$list(all = TRUE, filters = list("name" = "dev_1712")) %>% 
  magrittr::extract2(1) %>% 
  magrittr::use_series(id)

cll <- docker$APIClient()
cll$start(resource_id = container_id)
httr::http_error("http://localhost:8787")
# [1] FALSE

APIClient関数からcllというオブジェクトを作成し、コンテナを起動しました。dockerパッケージは便利ですが、コンテナの再起動をできないとRStudioを終了できないので困りますが、これで安心です。

明示的にコンテナを削除するには次のようにします。

cll$stop(resource_id = container_id)
# docker rm <コンテナ ID>
cll$remove_container(resource_id = container_id)

前述の通りdockerパッケージはPythonのdockerモジュールをラップしているので、dockerパッケージで何ができるのかを知りたい時はドキュメントを読むのが早いです。

ローカル環境のRStudioの設定を適用する

ここからは応用編です。起動したRStudio Serverでは、当然ながらRStudioの初期設定が適用されています。これではコンテナを作る度にぽちぽちとマウス操作によって設定をしていく必要が生じるので、楽にやりたいです。

なので、ローカルの設定をRStudio Serverへコピーするという方法をとっています。

system(
  paste0('docker cp /Users/uri/.rstudio-desktop/monitored/user-settings/user-settings ',
        container_id,
        ':/home/rstudio/.rstudio/monitored/user-settings/'))

f:id:u_ribo:20171221211612g:plain

APIがあるかと思ったのですが、なかなかうまくいかず...まだ試行錯誤なのですがこの方法で一応ローカルで動かしているRStudioのテーマやパネルの配置等の設定をRStudio Serverへ持ってくることができます。

注意としては、一度対象のコンテナを起動していないとコピーが成功しない(.rstudioという隠しフォルダが作成されない)、パスは絶対パスでなければいけないの2点です。

マウント: ローカルのファイルと同期させる

docker環境で作業して、それをローカルに保存したり、ローカルのコードをdocker環境で試す、ということがあります。それにはvolumeオプションを利用します。これはコンテナの作成時にやっておきます。

まずはdockerコンテナに構築したいローカルのパスとdockerコンテナ内の関係をオブジェクトとして作成します。modeは書き込みと保存ができるようにrwとしましょう。

mount <- list("/Users/uri/Documents/projects2016/jpmesh" = 
            list("bind" = "/home/rstudio/jpmesh",
                 # rw: read and write (ro: read only)
                 "mode" = "rw"))

rss_instance <- client$containers$run(image = "uribo/tiny_rocker_geospatial",
                                      remove = TRUE,
                                      detach = TRUE, 
                                      volumes = mount,
                                      name = "dev_1712",
                                      ports = list("8787/tcp" = "8787"))

f:id:u_ribo:20171221211810p:plain

ユーザ、パスワードを変更する

RStudio Serverでは初期ユーザ名とパスワードがrstudioになっていますが、運用していくためには変更したいです。これもコンテナの起動時に設定しておきます。ここではenvironmentオプションを使います。USERPASSWORDという値をそれぞれ任意のユーザ、パスワードにすることが可能です。以下は、ユーザ、パスワードの変更とついでに管理者ユーザとなるオプションも有効にする例です(追加でシステムのインストールが必要となることがあるため)。

rss_instance <- client$containers$run(image = "uribo/tiny_rocker_geospatial",
                                      remove = TRUE,
                                      detach = TRUE,
                                      environment = list("USER" = "piyo",
                                                         "PASSWORD" = "hogepass", 
                                                         # sudo が有効になる
                                                         "ROOT" = "TRUE"),
                                      ports = list("8787/tcp" = "8787"))

f:id:u_ribo:20171221211501g:plain

俺俺dockerfileの作成方法についてはまたの機会に。ちなみに、この記事で使っているコンテナは俺俺イメージの一つです。地理空間データを扱う上で欠かせない存在となったsfパッケージのインストールが面倒なのでサクッとできるようにしています。

https://hub.docker.com/r/uribo/tiny_rocker_geospatial/

Enjoy 🐳