読者です 読者をやめる 読者になる 読者になる

RからDigitalOceanにサーバを立てる

というのをやるのにはちゃんとパッケージがあります。羽鳥とかキャプテンアメリカとかもauthorに入っているということで使わない理由はありません。

が、人間は理由に隷属する生き物ですが、敢えてそれに抗う愚かさも兼ね備えています。ということで(?)、素のhttrでやったときのメモ。

トークンを発行

ドキュメントはこのへん: https://developers.digitalocean.com/documentation/v2/#authentication

Applications & APIからアクセストークンを発行します。今回はdropletを立てるのでWriteの権限も必要です。

f:id:yutannihilation:20160903010002p:plain:w450

これを、

Authorization: Bearer (トークン)

というヘッダにして投げればAPIを使えます。

リクエストを投げる

ドキュメントはこのへん:https://developers.digitalocean.com/documentation/v2/#create-a-new-droplet

サンプルリクエストによると、dropletをつくるには、こんな感じのJSONを投げないといけないみたいです。

{
  "name": "example.com",
  "region": "nyc3",
  "size": "512mb",
  "image": "ubuntu-14-04-x64",
  "ssh_keys": null,
  "backups": false,
  "ipv6": true,
  "user_data": null,
  "private_networking": null,
  "volumes": null
}

regionとかimageとかに指定する値がよくわかりません。この辺はRegionとかImageを列挙するAPIがあるみたいなのでそれを使うことにします。

/v2/regions

/v2/regionsに対してGETリクエストを投げます。

library(httr)
library(purrr)

token <- "(トークン)"

res <- GET("https://api.digitalocean.com/v2/regions",
    add_headers(`Authorization` = sprintf("Bearer %s", token)))

x <- content(res)

結果はこんな感じです。

names(x)
#> [1] "regions" "links"   "meta"   

str(x$regions[[1]])
#> List of 5
#> $ name     : chr "New York 1"
#> $ slug     : chr "nyc1"
#> $ sizes    :List of 9
#> ..$ : chr "512mb"
#> ..$ : chr "1gb"
#> ..$ : chr "2gb"
#> ..$ : chr "4gb"
#> ..$ : chr "8gb"
#> ..$ : chr "16gb"
#> ..$ : chr "32gb"
#> ..$ : chr "48gb"
#> ..$ : chr "64gb"
#> $ features :List of 5
#> ..$ : chr "private_networking"
#> ..$ : chr "backups"
#> ..$ : chr "ipv6"
#> ..$ : chr "metadata"
#> ..$ : chr "storage"
#> $ available: logi TRUE

sizesfeaturesはlistではなくてvectorになってほしいところです。content()は結果がJSONのときはjsonlite::fromJSON()を呼んでいて、その引数を渡すことができます。 simplifyVector=TRUEを指定すると結果を可能な限りdata.frameにしてくれます。

content(res, simplifyVector = TRUE)
#> $regions
#>               name slug                                             sizes                                             features available
#> 1       New York 1 nyc1 512mb, 1gb, 2gb, 4gb, 8gb, 16gb, 32gb, 48gb, 64gb private_networking, backups, ipv6, metadata, storage      TRUE
#> 2  San Francisco 1 sfo1 512mb, 1gb, 2gb, 4gb, 8gb, 16gb, 32gb, 48gb, 64gb          private_networking, backups, ipv6, metadata      TRUE
#> 3       New York 2 nyc2 512mb, 1gb, 2gb, 4gb, 8gb, 16gb, 32gb, 48gb, 64gb          private_networking, backups, ipv6, metadata      TRUE
#> 4      Amsterdam 2 ams2 512mb, 1gb, 2gb, 4gb, 8gb, 16gb, 32gb, 48gb, 64gb          private_networking, backups, ipv6, metadata      TRUE
#> 5      Singapore 1 sgp1 512mb, 1gb, 2gb, 4gb, 8gb, 16gb, 32gb, 48gb, 64gb          private_networking, backups, ipv6, metadata      TRUE
#> 6         London 1 lon1 512mb, 1gb, 2gb, 4gb, 8gb, 16gb, 32gb, 48gb, 64gb          private_networking, backups, ipv6, metadata      TRUE
#> 7       New York 3 nyc3 512mb, 1gb, 2gb, 4gb, 8gb, 16gb, 32gb, 48gb, 64gb          private_networking, backups, ipv6, metadata      TRUE
#> 8      Amsterdam 3 ams3 512mb, 1gb, 2gb, 4gb, 8gb, 16gb, 32gb, 48gb, 64gb          private_networking, backups, ipv6, metadata      TRUE
#> 9      Frankfurt 1 fra1 512mb, 1gb, 2gb, 4gb, 8gb, 16gb, 32gb, 48gb, 64gb private_networking, backups, ipv6, metadata, storage      TRUE
#> 10       Toronto 1 tor1 512mb, 1gb, 2gb, 4gb, 8gb, 16gb, 32gb, 48gb, 64gb          private_networking, backups, ipv6, metadata      TRUE
#> 11 San Francisco 2 sfo2 512mb, 1gb, 2gb, 4gb, 8gb, 16gb, 32gb, 48gb, 64gb private_networking, backups, ipv6, metadata, storage      TRUE
#> 12     Bangalore 1 blr1 512mb, 1gb, 2gb, 4gb, 8gb, 16gb, 32gb, 48gb, 64gb          private_networking, backups, ipv6, metadata      TRUE
#> 
#> $links
#> named list()
#> 
#> $meta
#> $meta$total
#> [1] 12

シンガポールがアジア圏なのでsgp1を指定すればよさそうです。

/v2/images

イメージも同じ感じでsimplifyVector = TRUEを指定します。

res <- GET("https://api.digitalocean.com/v2/images",
           add_headers(`Authorization` = sprintf("Bearer %s", token)),
           query = list(type = "distribution"))
x <- content(res, simplifyVector = TRUE)

# FreeBSDのイメージに絞り込み
dplyr::filter(x$images, distribution == "FreeBSD")
#>         id     name distribution                 slug public                                                                      regions
#> 1 10144573     10.1      FreeBSD     freebsd-10-1-x64   TRUE nyc1, ams1, sfo1, nyc2, ams2, sgp1, lon1, nyc3, ams3, fra1, tor1, sfo2, blr1
#> 2 18818640 10.3 zfs      FreeBSD freebsd-10-3-x64-zfs   TRUE       nyc1, sfo1, nyc2, ams2, sgp1, lon1, nyc3, ams3, fra1, tor1, sfo2, blr1
#> 3 18848244     10.2      FreeBSD     freebsd-10-2-x64   TRUE       nyc1, sfo1, nyc2, ams2, sgp1, lon1, nyc3, ams3, fra1, tor1, sfo2, blr1
#> 4 19103923     10.3      FreeBSD     freebsd-10-3-x64   TRUE       nyc1, sfo1, nyc2, ams2, sgp1, lon1, nyc3, ams3, fra1, tor1, sfo2, blr1
#>             created_at min_disk_size     type size_gigabytes
#> 1 2015-01-20T20:04:34Z            20 snapshot           2.44
#> 2 2016-08-02T02:42:12Z            20 snapshot           0.81
#> 3 2016-08-03T19:11:01Z            20 snapshot           1.05
#> 4 2016-08-16T19:11:09Z            20 snapshot           1.05

/v2/account/keys

登録しているSSH鍵の一覧を表示します。dropletをつくるときは、このSSH鍵のidfingerprintを指定するとのことなのでメモっておきます。

res <- GET("https://api.digitalocean.com/v2/account/keys",
    add_headers(`Authorization` = sprintf("Bearer %s", token)))
x <- content(res, simplifyVector = TRUE)

x$ssh_keys$id
#> [1] 123456  789012

/v2/droplets

リージョン名とイメージ名がわかったところでいよいよdropletsをつくります。I()を指定している理由が気になる方はメモ:httrでJSON形式のリクエストを送るときのコツ - Technically, technophobic.を読んでください。

res <- POST(
  "https://api.digitalocean.com/v2/droplets",
  add_headers(`Authorization` = sprintf("Bearer %s", token)),
     encode = "json",
     body = list(
         name = "r-test-server",
         region = "sgp1",
         size = "4gb",
         image =  "freebsd-10-3-x64",
         ssh_keys = I(123456),         # ssh_keysはarrayで指定するのでI()をつける
         backups = FALSE,
         ipv6 = FALSE,
         user_data = NULL,
         private_networking = NULL,
         volumes = NULL
     )
)

res
#>  Response [https://api.digitalocean.com/v2/droplets]
#>    Date: 2016-09-03 02:26
#>    Status: 202
#>    Content-Type: application/json; charset=utf-8
#>    Size: 1.16 kB

できました。resの中身にはできたdropletの詳細が入っています。

同じURLにGETリクエストを送ると、dropletsの一覧を取得することができます。先ほど指定した名前のdropletができていることがわかります。

res <- GET("https://api.digitalocean.com/v2/droplets",
           add_headers(`Authorization` = sprintf("Bearer %s", token)))

content(res, simplifyVector = TRUE, simplifyDataFrame = FALSE)
#> $droplets
#> $droplets[[1]]
#> $droplets[[1]]$id
#> [1] 12345678
#> 
#> $droplets[[1]]$name
#> [1] "r-test-server"
#> 
#> $droplets[[1]]$memory
#> [1] 4096
#> 
#> $droplets[[1]]$vcpus
#> [1] 2
#> 
#> $droplets[[1]]$disk
#> [1] 60
#> 
#> $droplets[[1]]$locked
#> [1] FALSE
#> 
#> $droplets[[1]]$status
#> [1] "active"
#> 
#> $droplets[[1]]$kernel
#> NULL
#> 
#> $droplets[[1]]$created_at
#> [1] "2016-09-03T02:26:04Z"
#> ...

試しに作っただけのやつなので、消します。dropletの削除には、先ほどのURLの最後にdropletのIDをつけます。

DELETE(sprintf("https://api.digitalocean.com/v2/droplets/%d", 12345678),
       add_headers(`Authorization` = sprintf("Bearer %s", token)))
#> Response [https://api.digitalocean.com/v2/droplets/24586153]
#>   Date: 2016-09-03 02:36
#>   Status: 204
#>   Content-Type: <unknown>
#> <EMPTY BODY>

あっさり消えました。

まとめ

これでカッとなってRからサーバを立てたいときとかも安心ですね。