環境は Docker で作ろうじゃないか

# Scala on docker (ubuntu)

### 動機
`scala` の動く環境が欲しかった。

localに`dmg`ファイルとかをダウンロードしてもよかったのだが、
localの環境を汚すのが好ましくないと思ったので、`docker`で作ろうと思った。

<font color='gray'>「人類、dockerという技術がありながら、なぜlocalの環境を汚してしまうのか」というツイートをどこかで見たような... </font>

状況として、「新しい言語が動く環境を`docker`を用いて作る」というケースとして、応用できるのではないかと思ったので、エントリとして書いておく。


## ubuntu containers 作る

```
$ docker run -it --name {container_name} -v /abs/path/to/mount/directory:/root/ ubuntu:latest /bin/bash
```

docker run の option に関して


| option | 意味 |
|--- | --- |
| -i | intaractive (attach しなくても標準入力を受け付ける状態に保つ) |
|-t | tty (超雑に言うと`terminal`) を割り当てる |
|-v | ホストPCのディレクトリを、`container`内のディレクトリにマウントする。 |

option に関する追記:

* `-it` は `-i` と `-t` の合わせ技。
* `-v` オプションでは、`[host_dir]:[container_dir]` で指定する。  
<font color='red'>注意: どちらも絶対パスで指定する必要がある。</font>   
今回、dockerでは `~/` が `/root/` になるので、そこにマウントをしている。


**以下、コンテナ内での履歴**

## apt update など

```
# apt update; apt upgrade -y;
```

## scala をインストールするだけ

```
# apt install scala
```

## とりあえず  REPL を起動

```
# scala

scala>
```

## 動作確認 (ちょっと遊んでみる)

### val (value): 定数

```scala
scala> val x = 10
x: Int = 10

scala> print(x)
10
```

val (value) はイミュータブル (再代入/変更不可能)

```scala
scala> val x = 10
x: Int = 10

scala> x = 20
<console>:12: error: reassignment to val
       x = 20
         ^
```


ただし、新しい定数として定義し直すことは可能
コンパイル時はエラーになるらしい。

```scala
scala> val x = 10
x: Int = 10

scala> val x = 20
x: Int = 20

scala> print(x)
20
```


### var (variable): 変数

```scala
scala> var a = 10
a: Int = 10

scala> print(a)
10
```


var (variable) はミュータブル (再代入/変更可能)

```scala
scala> var a = 10
a: Int = 10

scala> a = 20
a: Int = 20

scala> print(a)
20
```

もちろん、定義し直しも OK

```scala
scala> var a = 10
a: Int = 10

scala> var a = 20
a: Int = 20

scala> print(a)
20
```


ただし、変数の型を変えての再代入は不可

```scala
scala> var a = 10
a: Int = 10

scala> print(a)
10

scala> a = "hoge"
<console>:12: error: type mismatch;
 found   : String("hoge")
 required: Int
       a = "hoge"
           ^

// 例2
scala> var s = "this is string"
s: String = this is string

scala> print(s)
this is string

scala> s = 0.1
<console>:12: error: type mismatch;
 found   : Double(0.1)
 required: String
       s = 0.1
           ^

```

### 最後に動作確認のお決まり

```scala
scala> print("Hello World")
Hello World

scala> print("Hello World", "!")
(Hello World,!)
```

### 終了

```scala
scala> :quit // もしくは [Ctrl-c] でも抜けられる。
```

## Play FrameWork

`Scala` には `Play Framework` という web アプリケーション用のフレームワークがある。
(Scala に限らず、Java や Python, Go, Rust などを使って web アプリケーションを作りたい利用ケースはあるだろう)

Scala 環境を Docker container 内で立てて、container でアプリケーションを実行したら、サービスは container で動いており、「container のポートで」サービスの待ち受けがされている。

コンテナと `ip address` を用いた通信ができる ( network に docker とのブリッジを持っている ) 場合は問題ないのだが、私の Mac 環境では Host と Docker container の間で (なぜか) ip address でやりとりできない ... 超不便である。

こういったときは下記の様に、ホストの空いているポートと、Docker container (アプリケーション側) の待ち受けポートをマッピングすれば良い。


```
$ docker run -it -p 8081:9000 --rm hseeberger/scala-sbt:scala-2.12.2-sbt-0.13.15 bash
```

`-p [Host のポート番号]:[container の待ち受けポート番号]`

すると、Host の 8081 番ポートにアクセス ( http://localhost:8081 ) することで、
Docker の 9000 番ポートにアクセスしたことになり、サービスを受けることができる。

ここで `hseeberger/scala-sbt` は hseeberger さんが公開してくださっている Docker image である。 scala と sbt の両方が使える状態になっており、便利である。

[Docker Hub: hseeberger/scala-sbt](https://hub.docker.com/r/hseeberger/scala-sbt)

もしすでに Host にソースファイルがあったり、Host とソースファイルを同期したい場合は
container のどこか適切なディレクトリを、ホストとマッピングすれば良い。

```
$ docker run -it -p 8081:9000 -v $PWD/src:/root/src --rm hseeberger/scala-sbt:scala-2.12.2-sbt-0.13.15 bash
```

sbt を使って Play Framework の雛形を得るには次の様にすれば良い。

```
sbt new playframework/play-scala-seed.g8
```

他の言語に関しても、 `{言語} docker` で検索したりすると、公開 image などが出てくるので、同様の方法により web サービスを container 環境で実行、利用できるだろう。

### LINK

* [DockerでScala/sbt環境をお手軽に使う](https://qiita.com/yotsak/items/43cee726d44536208358)
* [Dockerでコンテナの内部IPを調べる](https://qiita.com/miyatahirotaka/items/a44e2ae0bac53a4a5feb)
* [Docker入門 Nginxを動かしてみる](https://qiita.com/mtakehara21/items/d7be42cf12772f3ada25)
* [Play frameworkことはじめ](https://qiita.com/Takashi_Kasuya/items/250752ff284c1986b5c7)