Processing math: 100%

7.30.2014

Scala: How to Merge Two Maps with Summing Values

Scala: Map の値を加算しながらマージする

 

Scalaz を使うとシンプル。

sbt 設定例

libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.0.6"

実行例

1
2
3
4
5
scala> import scalaz.Scalaz._
import scalaz.Scalaz._
 
scala> Map('a -> 10, 'b -> 20, 'c -> 30) |+| Map('a -> 5, 'c -> 7, 'd -> 9)
res0: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 15, 'c -> 37, 'd -> 9, 'b -> 20)

引き算をするなら、こんな感じだろうか。

1
2
scala> Map('a -> 10, 'b -> 20, 'c -> 30) |+| Map('a -> 5, 'c -> 7, 'd -> 9).mapValues(-_)
res1: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 5, 'c -> 23, 'd -> -9, 'b -> 20)

 

 

References

7.21.2014

Scala: Getting Started with Akka-HTTP

Scala: Akka-HTTP を試してみる

 

Akka-HTTP とは、Spray++ という位置づけの新進気鋭の HTTPサーバ フレームワーク。
Reactive Stream を使って実装されているのが大きな特徴。

Spray は ベンチマークFinaglewai (Haskell) に圧勝している。
Akka-HTTP もその流れを引き継ぎ、ハイパフォーマンスサーバに適したフレームワークになるだろうと期待している。

これまで Spray すら使ったことがなかったのだが、少しだけ Akka-HTTP に触れてみる。

 

Hello World with Akka-HTTP


/ping にアクセスすると "PONG" を返すシンプルな Webサーバを作る。

project/Build.scala
Build.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import sbt._
import sbt.Keys._
 
object ApplicationBuild extends Build {
  lazy val buildSettings = Seq(
    organization := "com.github.mogproject",
    version      := "0.1.0-SNAPSHOT",
    scalaVersion := "2.11.1",
    scalacOptions ++= Seq(
      "-deprecation",
      "-unchecked",
      "-feature",
      "-encoding", "utf-8"
    ),
    javacOptions ++= Seq(
      "-source", "1.7"
    )
  )
 
  lazy val resolverSettings = Seq(
    resolvers ++= Seq(
      "Sonatype OSS Releases" at "http://oss.sonatype.org/content/repositories/releases",
      "Sonatype OSS Snapshots" at "http://oss.sonatype.org/content/repositories/snapshots",
      "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"
    ),
    libraryDependencies ++= Seq(
      "org.scalatest" %% "scalatest" % "2.2.0" % "test",
      "com.typesafe" % "config" % "1.2.1",
      "com.typesafe.akka" %% "akka-http-core-experimental" % "0.4",
      "com.typesafe.akka" %% "akka-stream-experimental" % "0.4"
    )
  )
 
  lazy val testSettings = Seq(
    parallelExecution in Test := true
  )
 
  lazy val root = Project(
    id = "akka-http-example",
    base = file("."),
    settings = super.settings ++ buildSettings ++ resolverSettings ++ testSettings
  )
}
src/main/resources/application.conf
application.conf
akka {
  loglevel = INFO
}
src/main/scala/Boot.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import akka.io.IO
import akka.http.Http
 
import akka.actor.ActorSystem
import akka.pattern.ask
import akka.util.Timeout
import akka.http.model._
import akka.http.model.HttpMethods._
import scala.concurrent.duration._
import akka.stream.scaladsl.Flow
 
object Boot extends App {
  import system.dispatcher
  implicit val system = ActorSystem()
  implicit val askTimeout: Timeout = 500.millis
 
  val bindingFuture = IO(Http) ? Http.Bind("localhost", 8080)
 
  bindingFuture foreach { binding =>
    Flow(binding.connectionStream) foreach { connection =>
      Flow(connection.requestProducer).map {
        case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
          HttpResponse(entity = "PONG!")
        case _ => HttpResponse(404, entity = "Unknown resource!")
      }.produceTo(connection.responseOut)
    }
  }
}
[error] akka-http-example/src/main/scala/Boot.scala:20: value connectionStream is not a member of Any
[error]     Flow(binding.connectionStream) foreach { connection ⇒
[error]                  ^
[error] one error found
[error] (compile:compile) Compilation failed
  • 以下のように修正したら、動いた
Boot.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import akka.io.IO
import akka.http.Http
 
import akka.actor.ActorSystem
import akka.pattern.ask
import akka.util.Timeout
import akka.stream.{MaterializerSettings, FlowMaterializer}
import akka.http.model._
import akka.http.model.HttpMethods._
import scala.concurrent.duration._
import akka.stream.scaladsl.Flow
 
object Boot extends App {
  import system.dispatcher
  implicit val system = ActorSystem()
  implicit val askTimeout: Timeout = 500.millis
 
  val materializer = FlowMaterializer(MaterializerSettings())
  val bindingFuture = IO(Http) ? Http.Bind("localhost", 8080)
 
  bindingFuture foreach { case Http.ServerBinding(localAddress, connectionStream) =>
    Flow(connectionStream).foreach { case Http.IncomingConnection(remoteAddress, requestProducer, responseConsumer) =>
      Flow(requestProducer).map {
        case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
          HttpResponse(entity = "PONG!")
        case _ => HttpResponse(404, entity = "Unknown resource!")
      }.produceTo(materializer, responseConsumer)
    }.consume(materializer)
  }
}
PONG!
Unknown resource!

 

ルーティング

ロードマップによると、spray-routing に相当する DSLライブラリが akka-http (akka-http-core ではない) に組み込まれる予定とのこと。

現時点ではまだ無いのでパターンマッチで何とかするしかなさそう。

  • 公式ドキュメントにかかれていたサンプル
1
2
3
4
5
6
7
8
9
10
11
12
import HttpMethods._
 
val requestHandler: HttpRequest ⇒ HttpResponse = {
  case HttpRequest(GET, Uri.Path("/"), _, _, _) ⇒
    HttpResponse(
      entity = HttpEntity(MediaTypes.`text/html`,
        "<html><body>Hello world!</body></html>"))
 
  case HttpRequest(GET, Uri.Path("/ping"), _, _, _) ⇒ HttpResponse(entity = "PONG!")
  case HttpRequest(GET, Uri.Path("/crash"), _, _, _) ⇒ sys.error("BOOM!")
  case _: HttpRequest ⇒ HttpResponse(404, entity = "Unknown resource!")
}

 

References

7.20.2014

AWS: Creating New EC2 Instance in N.Virginia by Vagrant

AWS: 米国東部リージョンの EC2 インスタンスを Vagrant で作成する

 

AWS を日常的に使うなら、やはり単価の安い米国リージョンを選びたいもの。

しかし安易に日本のインスタンス作成で使った Vagrantfile を使い回すと、以下のような落とし穴にハマる。

 

エラーメッセージとその対応

 

アベイラビリティゾーンが無い
InvalidParameterValue => Invalid availability zone: [us-east-1a]

何も考えず、リージョン名に「a」を付けるといきなりハマる。

現在、存在するのは「us-east-1b」「us-east-1c」「us-east-1d」のみ。EC2 -> Dashboard で最新情報を確認できる。

 

セキュリティグループが無い
The security group 'vagrant' does not exist

リージョン単位で定義を作成する必要がある。一方、IAM のグループ・ユーザは全リージョン共通。

 

AMI が無い
The image id '[ami-29dc9228]' does not exist

これも、リージョンごとに異なるイメージIDを持っているので、調べ直す必要がある。

 

ネットワーク環境とセキュリティグループの設定が対応していない
InvalidParameterCombination => VPC security groups may not be used for a non-VPC launch

セキュリティグループを作るとき、適当な VPC を選ぶとこうなる。

米国東部リージョンの場合、「サブネットID」または「ネットワーク・インタフェースID」の指定がない場合、そのインスタンスは non-VPC、つまり EC2-Classic 環境に作成されるようだ。

これに対する正しい対応は、正しい「サブネットID」を Vagrantfile の中で指定すること。

AWS Management Console の Services -> Compute & Networking -> VPC -> Subnets の画面で確認できる。

目的の「VPC」および「アベイラビリティゾーン」に対応したサブネットの「Subnet ID」を控え、以下のような記述を Vagrantfile に追加する。

aws.subnet_id = "subnet-xxxxxxxx"

 

インスタンスタイプが Non-VPC に対応していない
VPCResourceNotSpecified => The specified instance type can only be used in a VPC. A subnet ID or network interface ID is required to carry out the request.

セキュリティグループを作るとき、No VPC を選んでしまうとこうなる。
t2.micro インスタンスを立ち上げるためには、VPC に所属させないといけない。

 

セキュリティグループのグループ名が認識されない
InvalidParameterCombination => The parameter groupName cannot be used with the parameter subnet

aws.security_groups には、セキュリティグループの「Group Name」ではなく「Group ID」(sg-xxxxxxxx) を指定する必要があるようだ。

 

キーペアが無い
The key pair 'vagrant' does not exist

キーペアもリージョン単位で作成する必要がある。これが最後の難関。

 

Public IP の永続化

デフォルトの VPC を使えば Public IP が自動的に割り振られるが、これはインスタンスを起動するたびに
変わる可能性がある。

Vagrantfile に次の1行を書けば、自動的に新規の Elastic IP を取得し、立ち上げたインスタンスと紐付けてくれる。
素晴らしい。

aws.elastic_ip = true

 

 

 

References
Related Posts

7.18.2014

Trade-Off Sliders Template

トレードオフ・スライダーのテンプレート

 

とりあえず自分が使いやすいようにカスタマイズ。

要件定義のお供に。

 

  • 英語

Trade off sliders

  • 日本語

Trade off sliders ja

 

 

References

How to Monitor Amazon EC2 Instances with NewRelic (Provisioned by Vagrant + Ansible)

New Relic で EC2 インスタンスを監視する方法

 

目的

Amazon EC2 の新規インスタンスを立ち上げ、New Relic でリソース監視をする。
これを vagrant up コマンド一発で実現したい。

プロビジョニングには Ansible を使う。

 

事前準備

 

1. New Relic アカウントの準備

 

2. AWS Management Console の作業
  • AWS アカウントの作成 (手順割愛)
  • ユーザ・グループ作成
    • Services -> Deployment & Management -> IAM
    • IAM -> Groups: vagrant グループ (Power User Access) を追加
    • IAM -> Users: vagrant ユーザを追加し、vagrant グループに所属。APIキーを確認
  • セキュリティグループ作成
    • Services -> Compute & Networking -> EC2 -> NETWORK & SECURITY -> Security Groups
    • vagrant グループを追加し、Inbound 許可, {SSH, HTTP, HTTPS}, Anywhere を設定
  • SSH鍵の作成
    • Services -> Compute & Networking -> EC2 -> NETWORK & SECURITY -> Key Pairs
    • キーペア vagrant を作成し、vagrant.pem を適当な場所に保存 (例: ~/.ssh/aws-vagrant.pem)

 

3. プロビジョニング実行環境の整備
  • Vagrant のインストール
  • Vagrant プラグインのインストール
    $ vagrant plugin install vagrant-aws
    $ vagrant plugin install dotenv
     
  • Ansible のインストール

 

プロビジョニング用ファイルの作成

 

Vagrantfile

 

.env ファイル

Vagrantfile と同じディレクトリに「.env」というファイルを作成する。

以下のように認証情報を適宜設定すること。

1
2
3
4
5
6
7
8
9
10
11
12
13
# default provider
VAGRANT_DEFAULT_PROVIDER="aws"
 
# credentials
AWS_SSH_USERNAME="ec2-user"
AWS_SSH_KEY="path/to/your/vagrant.pem"
AWS_ACCESS_KEY_ID="your access key id"
AWS_SECRET_ACCESS_KEY="your secret"
AWS_KEYPAIR_NAME="vagrant"
AWS_SECURITY_GROUP="vagrant"
 
# new relic
NEWRELIC_LICENSE_KEY="your license key"

 

Ansible 関連ファイル

 

プロビジョニングの実行

$ vagrant up
Bringing machine 'aws-newrelic-test' up with 'aws' provider...
[aws-newrelic-test] Warning! The AWS provider doesn't support any of the Vagrant
high-level network configurations (`config.vm.network`). They
will be silently ignored.
[aws-newrelic-test] Launching an instance with the following settings...
[aws-newrelic-test]  -- Type: t2.micro
[aws-newrelic-test]  -- AMI: ami-29dc9228
[aws-newrelic-test]  -- Region: ap-northeast-1
[aws-newrelic-test]  -- Availability Zone: ap-northeast-1a
[aws-newrelic-test]  -- Keypair: vagrant
[aws-newrelic-test]  -- Security Groups: ["vagrant"]
[aws-newrelic-test]  -- Block Device Mapping: []
[aws-newrelic-test]  -- Terminate On Shutdown: false
[aws-newrelic-test]  -- Monitoring: false
[aws-newrelic-test]  -- EBS optimized: false
[aws-newrelic-test]  -- Assigning a public IP address in a VPC: false
[aws-newrelic-test] Waiting for instance to become "ready"...
[aws-newrelic-test] Waiting for SSH to become available...
[aws-newrelic-test] Machine is booted and ready for use!
[aws-newrelic-test] Running provisioner: ansible...
 
PLAY [all] ********************************************************************
 
GATHERING FACTS ***************************************************************
ok: [aws-newrelic-test]
 
TASK: [newrelic | check if repository is installed] ***************************
 
*snip*
 
PLAY RECAP ********************************************************************
aws-newrelic-test          : ok=9    changed=6    unreachable=0    failed=0

AWSインスタンス作成、起動、Ansible によるプロビジョニングが一気通貫で実行される。

暫くすると、New Relic の画面で EC2 サーバの情報が見られるようになった。
AWS課金には注意。

 

 

References

7.17.2014

Faster Insertion into PostgreSQL with Python

Python: PostgreSQL への高速インサート

 

Python + Psycopg で PostgreSQL の操作。

テーブルに大量データを投入する場合、INSERT よりも TSV (or CSV) ファイルを作って COPY で読み込んだ方が速いというのは知られているが、StringIO を利用すれば実際にファイルを作成する必要すらない、という例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from StringIO import StringIO
import psycopg2
 
data = [[42, 'foo'], [74, 'bar']]
 
conn = psycopg2.connect(host = "localhost", port = 5432, database="dbname", user="user", password="pass")
cur = conn.cursor()
 
f = StringIO()
f.write('\n'.join('\t'.join(str(s) for s in r) for r in data))
f.seek(0)
cur.copy_from(f, 'tablename')
 
cur.close()
conn.close()

 

実行例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from StringIO import StringIO
import psycopg2
 
data = [[42, 'foo'], [74, 'bar']]
 
conn = psycopg2.connect(host = "localhost", port = 5432, database="dbname", user="user", password="pass")
cur = conn.cursor()
 
cur.execute('CREATE TABLE awesome_table (id integer NOT NULL, name character varying(40));')
 
f = StringIO()
f.write('\n'.join('\t'.join(str(s) for s in r) for r in data))
f.seek(0)
cur.copy_from(f, 'awesome_table')
 
cur.execute("select * from awesome_table;")
print(cur.fetchall())
 
cur.close()
conn.close()

 

出力
[(42, 'foo'), (74, 'bar')]

 

 

References

7.14.2014

IVA Reading: Chapter 02, Section 05 Exercises

IVA読書会 chap02-sect05 宿題

 

No.5

i) G={g1,,gt}II のグレブナー基底であるとき、
I の任意の元の先頭項が LT(gi) のいずれかで割り切れることを示す

I の任意の元 fI をとる。

定義1より、LT(f)LT(I), LT(f)LT(I)

また、グレブナー基底の定義より LT(I)=LT(g1),,LT(gt)=LM(g1),,LM(gt)

命題3より、LT(I) は単項式イデアルであり、
§4補題2より、単項式イデアルの任意の元は、そのイデアルの生成元のいずれかで割り切れるから、
LM(gi)|LT(f) となる i(1it) が存在することが言える。

LM(gi)LT(gi)0 でない定数(先頭係数 LC(gi))の違いしかないので、LT(gi)|LT(f)

f の先頭項 LT(f)LT(gi) のいずれかで割り切れる。

 

ii) G={g1,,gt}I について、I の任意の元の先頭項が LT(gi) のいずれかで割り切れるとき、
GI のグレブナー基底であることを示す

 

I の任意の元 fI をとると、LT(gi)|LT(f)LM(gi)|LT(f)

LM(g1),,LM(gt) は単項式イデアルであり、
§4補題2より、ある単項式が単項式イデアルの生成元のいずれかで割り切れるならば、その単項式はそのイデアルに属すると言えるので、
LT(f)LM(g1),,LM(gt)=LT(g1),,LT(gt)

I の全ての元についてこれが成り立つため、LT(I)LT(g1),,LT(gt)LT(g1),,LT(gt)LT(I)

一方、GI であるから {LT(g1),,LT(gt)}LT(I)LT(g1),,LT(gt)LT(I)

従って、LT(g1),,LT(gt)=LT(I) となり、
これは定義5と同じであるから、GI のグレブナー基底である。

 

i), ii) より、題意は満たされた。

 

No.11

f=a1x1++anxn+c(aik[x1,,xn],ck) と表す。

fx1,,xn という条件から、f0 でない定数項 cを持つ。

x1,,xn,f=x1,,xn,c(ni=1aixi+1f=c,ni=1aixi+1c=f)=1(ni=10xi+1cc=1,xi1=xi,c1=c)=k[x1,,xn]

 

No.17

a.

イデアルの基底から左辺右辺の互いの多項式が生成できることを示す。(参考: 第1章§4演習問題2)

1(x2y)+0(y+x24)=x2y

12(x2y)+12(y+x24)=x22

より、I=x2y,y+x24x2y,x22

 

1(x2y)+0(x22)=x2y

(1)(x2y)+2(x22)=y+x24

より、I=x2y,y+x24x2y,x22

 

従って、I=x2y,x22

b.

{x2y=0x22=0

を解くと、(x,y)=(±2,2)

よって、V(I)={(±2,2)}

 

7.07.2014

IVA Reading: Chapter 02, Section 04 Exercises

IVA読書会 chap02-sect04 宿題

 

No.5

問題:
単項イデアル I=xα:αA について、発生しうる全ての指数の組の集合を S とする。
どのような単項式順序 > に対しても、S の最小の要素が A に含まれることを示せ。

 

単項式順序は、その定義(2章§2 定義1)から整列順序であるから、任意の空でない単項式の集合は最小元を持つ。

αS から S は空集合でないので、ただ一つの最小元を持つ。これを γS とする。

このとき、S の定義から xγI であるため、補題2 より xγxα で割り切れ、適当な βZn0 を取って xαxβ=xγ が成立する。

従って、α+β=γS

γS の最小元であるため、β=0,γ=αA

 

No.11

a)

Exercise2-4-12.pdf を参考に解いた。

問題:
系6 を使って、ウェイト順序 >u が単項式順序であることを示せ
(ヒント: u1,,un が線形独立であるという条件をどこで利用するか?)

単項式順序の定義に従って示す。

i) 全順序性について
uRn>0, α,βZn0 より、 uα,uβRn0

従って、


  • uα>uβ
  • uα=uβ
  • uα<uβ

のいずれか 1つのみが成り立つ。
これらはそれぞれ α>uβ, α=uβ, α<uβ に対応するので、>u は全順序である。

ii) 和による順序の保存について
α>uβ,γZn0 とする。

u(α+γ)=uα+uγ より、uα>uβu(α+γ)>u(β+γ)
となるので α+γ>uβ+γ が示せた。

iii) 整列順序性について
u は正、かつ Q 上で線形独立であるため、uα=0 となる αZn00 だけである。
従って
α=0uα=0α0uα>0
が成り立つ。

これにより αZn0,αu0 が示せたので、系6 より、>u は整列順序である。

b)

(1,2) が有理数体上で線形独立であることを示す。

a1+b2=0 を満たす a,bQ を考える。

この場合、a=b2 と変形可能で、これは a=b=0 の場合にのみ成立する。

従って (1,2) は有理数体上で線形独立。

 

c)

(1,2,3) が有理数体上で線形独立であることを示す。

a1+b2+c3=0 を満たす a,b,cQ を考える。

a+b2+c3=0a+b2=c3a2+2ab2+2b2=3c222ab=3c2a22b2ab=0a=0 or b=0

a=0 の場合、b2+c3=0b=c32b=c=0

b=0 の場合、a+c3=0a=c3a=c=0

以上より、a1+b2+c3=0a=b=c=0

従って (1,2,3) は有理数体上で線形独立。

 

References

7.03.2014

Docker: docker-attach - Helper Script for Entering a Running Container

Docker: 起動しているコンテナの中に入るための便利スクリプト

 

Dockerコンテナ (libcontainer) に入るときは、ssh でも nsinit でもなく nsenter が一番いいらしい。

そのラッパーシェルを docker-attach という名前で書いていたが、自分が使いやすいようにバージョンアップしてみた。

 

リポジトリ

 

備考
  • Mac上でも boot2docker VM上でも同じシンタックスで利用可能
  • 事前に boot2docker VM上で セットアップ が必要
  • READMEはそのうち書く。リポジトリ構成も整理する予定

 

使い方

引数なしで実行
$ docker-attach
 NO.  CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
 [1]  77c4f673e22d        centos:latest       /bin/sleep 1000     6 seconds ago       Up 5 seconds                            insane_poincar
 [2]  bf55f24e0231        centos:latest       /bin/sleep 1000     7 seconds ago       Up 6 seconds                            goofy_darwin
 [3]  ae214f93be56        centos:latest       /bin/sleep 1000     8 seconds ago       Up 8 seconds                            furious_laland
 [4]  2af5192ee5a6        centos:latest       /bin/sleep 1000     10 seconds ago      Up 9 seconds                            mad_einstein
 [5]  ff2b4987e717        centos:latest       /bin/sleep 1000     11 seconds ago      Up 11 seconds                           agitated_meitn
 [6]  0b2168db31db        centos:latest       /bin/sleep 1000     13 seconds ago      Up 12 seconds                           focused_bell
 [7]  89bce218d973        centos:latest       /bin/sleep 1000     14 seconds ago      Up 14 seconds                           stoic_fermi
 [8]  d9767d3d8551        centos:latest       /bin/sleep 1000     16 seconds ago      Up 15 seconds                           insane_tesla
 [9]  ddd3b0e67a1c        centos:latest       /bin/sleep 1000     17 seconds ago      Up 17 seconds                           furious_meitne
 [10] d4eea7f883d6        centos:latest       /bin/sleep 1000     19 seconds ago      Up 18 seconds                           condescending_
 
Enter number (1-10): 5
-sh-4.1# hostname
ff2b4987e717
-sh-4.1# exit
logout

docker ps の結果とプロンプトが出るので、入りたいコンテナを数字で指定する。

コンテナIDを引数に指定して実行
$ docker-attach 77c4
-sh-4.1# hostname
77c4f673e22d
-sh-4.1# exit
logout

コンテナID(識別可能な先頭の数文字でよい)がわかっている場合は、直接そのコンテナの中に入れる。

コンテナIDとコマンドを引数に与えて実行
$ docker-attach bf55 cat /etc/redhat-release
CentOS release 6.5 (Final)

コンテナの中で指定したコマンドが実行される。

ヘルプ
$ docker-attach --help
Usage: /usr/local/bin/docker-attach [CONTAINER [COMMAND [CMD_ARGS...]]]

 

 

 

Related Posts

7.02.2014

Docker: How to Dive Into a Container on the boot2docker VM, Improved

Docker: boot2docker VM で起動中のコンテナの中に入ってシェルを立ち上げる方法, 改良版

 

boot2docker 上で起動したコンテナに入ってシェル操作を行う方法。

nsinit を使う よりも、nsenter の方が少ない手順とコストで実現できた。

 

環境

  • OS: Mac OS X 10.9 (Mavericks)
  • boot2docker: v1.0.1

 

リポジトリ

 

事前準備

  • VirtualBox のインストール
  • boot2docker 最新版のインストール (OS X インストーラ の利用を推奨: 詳細手順)
    • インストール前に VirtualBox の全ての VM、GUI を停止すること
    • 既に boot2docker VM が存在している場合は以下のコマンドを実行 (元の環境は削除される)
      $ boot2docker halt
      $ boot2docker delete
      $ boot2docker download

 

セットアップ

 

1. boot2docker VM の作成・ログイン
Mac$ boot2docker init
Mac$ boot2docker up
Mac$ boot2docker ssh

以下の手順は boot2docker VM 上で実行。

 

2. util-linux 拡張機能のダウンロード
docker@boot2docker:~$ tce-load -w util-linux
ncurses.tcz.dep OK
Downloading: udev-lib.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
udev-lib.tcz         100% |*********************************************************************************************| 45056   0:00:00 ETA
udev-lib.tcz: OK
Downloading: ncurses-common.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
ncurses-common.tcz   100% |*********************************************************************************************| 12288   0:00:00 ETA
ncurses-common.tcz: OK
Downloading: ncurses.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
ncurses.tcz          100% |*********************************************************************************************|   148k  0:00:00 ETA
ncurses.tcz: OK
Downloading: util-linux.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
util-linux.tcz       100% |*********************************************************************************************|   992k  0:00:00 ETA
util-linux.tcz: OK

/tmp/tce/optional (/mnt/sda1/tmp/tce/optional) 配下にファイルがダウンロードされる。

 

3. util-linux 拡張機能 ファイルの永続化

先ほどダウンロードしたファイル群のある optional ディレクトリごと、永続ディスクである /var/lib/boot2docker 配下に移動する。

docker@boot2docker:~$ sudo mv /tmp/tce/optional /var/lib/boot2docker/

 

4. docker-attach スクリプトのダウンロード

コンテナにアタッチするための便利シェル、docker-attach を /var/lib/boot2docker/bin 配下にダウンロードする。

docker@boot2docker:~$ sudo mkdir -p /var/lib/boot2docker/bin
docker@boot2docker:~$ sudo curl -o /var/lib/boot2docker/bin/docker-attach \
docker@boot2docker:~$ sudo chmod +x /var/lib/boot2docker/bin/docker-attach
  • スクリプトの中身は、コマンドライン引数として渡されたコンテナIDからプロセスIDを検索して
    そこにオプション付きの nsenter を実行しているのが主処理。
    CONTAINER_ID=$1
    PID=$(docker inspect --format '{{.State.Pid}}' $CONTAINER_ID)
    sudo nsenter --mount --uts --ipc --net --pid --target $PID

 

5. bootlocal.sh スクリプトのダウンロードと実行

boot2docker VM は起動時に /var/lib/boot2docker/bootlocal.sh が存在すれば、それを実行する。

このシェルの中で util-linux のインストールとコマンドのリンクを行い、boot2docker 再起動後も元の状態を復元できるようにしている。

docker@boot2docker:~$ sudo curl -o /var/lib/boot2docker/bootlocal.sh \
docker@boot2docker:~$ sudo chmod +x /var/lib/boot2docker/bootlocal.sh
docker@boot2docker:~$ sudo /var/lib/boot2docker/bootlocal.sh
boot2docker: 1.0.1
             master : cad5ece - Fri Jun 20 02:03:40 UTC 2014
udev-lib.tcz: OK
ncurses-common.tcz: OK
ncurses.tcz: OK
util-linux.tcz: OK

 

実行例

  • 適当なコンテナを起動し、コンテナIDを確認 (boot2docker上でも、Macからでも可)
    $ docker run -d -t centos /bin/sleep 1000
    $ docker run -d -t centos /bin/sleep 1000
    $ docker ps
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
    f8c149734c7a        centos:latest       /bin/sleep 1000     5 minutes ago       Up 2 minutes                            naughty_sinoussi
    701810969c35        centos:latest       /bin/sleep 1000     5 minutes ago       Up 2 minutes                            sleepy_bell
  • boot2docker上で docker-attach を実行してコンテナにアタッチ
    docker@boot2docker:~$ docker-attach 701810969c35
    root@701810969c35:/# cat /etc/redhat-release
    CentOS release 6.5 (Final)
    root@701810969c35:/# exit
    logout
    docker@boot2docker:~$
  • Macからも、同じ docker-attach スクリプトを使って直接アタッチ可能
    Mac$ sudo curl -o /usr/local/bin/docker-attach \
    Mac$ sudo chmod +x /usr/local/bin/docker-attach
    Mac$ docker-attach f8c149734c7a
    -sh-4.1# cat /etc/redhat-release
    CentOS release 6.5 (Final)
    -sh-4.1# exit
    logout
    Mac$

 

ワンライナーでのセットアップ

セットアップ用のシェルも作った。

docker-attach/setup_boot2docker.sh at master · mogproject/docker-attach

 

References