コーディング未経験のPO/PdMのためのRails on Dockerハンズオン vol.1 -Introduction-

# このハンズオンは
超初心者向けのRuby on Rails on Dockerハンズオンです！
プログラミングなんてやったことない、別の言語でhello worldしたことならある、Dockerって名前は聞くけど...という感じの方に向けてお送りしたいと思います。

# 僕は誰か？
軽く自己紹介です。と思いましたが、自己紹介はリンク先で。

- [asatoさん (@at_946) / Twitter](https://twitter.com/at_946)
- [🅐🅢🅐🅣🅞さんのレジュメ | RESUME](https://www.resume.id/at)

注目してほしいところは、僕は社会人になって以来、常にサービス開発・システム開発に携わってきました。
しかしながら、僕は仕事でプログラミングをしたことがありません。このハンズオンにきてくれた人たちも同じような境遇の人が多いんじゃないかと思います。

僕がプログラミングを始めたきっかけは、そうやってサービス開発・システム開発をしていく中で、「<b>自分でちゃんと動くものを作れるようになったら自分がコードを書く書かないによらずもっといいプロダクトを創れるんじゃないか</b>」と思ったからです。あ、普通に面白そうとかもありましたけどね。

僕がプログラミングを始めたのが2017年の夏くらいです。まず「[Progate](https://prog-8.com/)」を有料会員になって全部やって、そこからは自分の考えたプロダクトを作ってました。やっぱり作ってみるのが一番学びが早いです。
それから今まで趣味程度でプロダクトを作ったりしてきましたが、その成果は確実に業務にも活かされていると思います。<b>Webサービスのプロダクトマネージャーをやるときはフロント・バック・インフラ問わず方針検討・デベロッパーとのディスカッションができている</b>と思いますし、Dockerをプライベートで触っていたからこそ、<b>CloudNativeのようなちょっとチャレンジングなプロジェクトにも参画</b>させてもらっています。
そして、やっぱりプログラミングは楽しいです。笑

まぁ楽しいのがなによりだと思いますので、みなさんもぜひこのハンズオンを楽しんでください！

# なんでハンズオンをやろうと思ったのか？
1. プログラミングは私には難しそう...と躊躇っている人を後押ししたかった
2. 技術楽しいじゃん！と盛り上がれる仲間を増やしたかった
3. そのくらい自分たちで作っちゃうか！って文化を作りたかった

# このハンズオンを通してどんなことが学べるのか（目標）
## 学べること
- Ruby on Rails
- Docker
- Docker Compose
- HTML
- CSS / SASS (SCSS)
- Bootstrap
- Javascript / jQuery
- AWS
- Heroku

## 学べないこと（今のところスコープ外）
- 学べることのふか〜いところ（僕も知らない）
- Ruby以外の言語（Java / C / Perl / Go / Python / ...)
- Rails以外のフレームワーク（Sinatra / Larabel / Django / ...)
- Git （これはやるかもしれない）
- CI/CD （これもやるかもしれない）
- API / SaaS / mBaaS / ...
- GCP / Azure / ...
- Kubernetes ...

# ハンズオンを受ける上でのお願い
Input for Output.
このハンズオンでインプットをするからには何かにアウトプットしてください。何かを作るでもいい。誰かに話すでもいい。他者に見えること・ものにすることを意識すると学習が一気に捗るはずです！

# 前置きながくてすみません！こっから本題です！
最初に言っておきますが、今日はコード書きません。

# プログラミングの3原則
実際にプログラミングを始める前に、プログラミングの3原則である『KISS』『YAGNI』『DRY』を心の中に刻み込んでください。

## KISS - Keep In Simple, Stupid.
意訳すると『<b>シンプルにしとけよ、バーカ</b>』です。
これはサービスを企画したりする段階から言えることだと思っているのですが、<b>シンプルであるほど『使う人』にとっても『創る人』にとってもいいプロダクト</b>のはずです。しかし実際には、様々なパターンを複雑に絡み合わせてシステムにしてしまっているケースも多く、そのせいでメンテナンス性が悪く、すぐには発展させられないような状況になってしまっているプロダクトは多いのではないでしょうか。
<b>シンプルに保つことはとても難しいこと</b>です。妥協せずにシンプルに保ちましょう。

## YAGNI - You Aren't Gonna Need It
意訳すると『<b>それいらねーべ</b>』です。
上のKISSを実現するための考え方な気がしますが、サービスやコード、システムが複雑になってしまう原因第一位はおそらくこのマインドがないからでしょう。
パレートの法則に従えば、<b>全体の80%のユーザーが利用する機能は全体の20%</b>です。今はこれが90:10、99:1になってきていると言われている時代。『多分いる』『きっといる』を排除する勇気を持ちましょう。

## DRY - Don't Repeat Yourself
意訳すると『<b>何度も同じことをするんじゃない！</b>』です。
似たようなコードを何回も書いたり、同じようなファイルをあっちゃこっちゃに作ったり。こう言う状況もKISSを阻害しますし、コードのメンテナンス性を大きく下げさらにはバグを引き起こします（あっちは直してたけどこっち直すの忘れた）。

ここまで3つの話をしてきましたが、総じて『シンプルさ』を求めていることがわかります。
直感的に考えてみると当然のことですが、シンプルであればあるほどバグは少なく、シンプルであればあるほど拡張性が高くなりそうですよね。

ここから先は、このハンズオンの中心となってくる『<b>Ruby</b>』『<b>Rails</b>』『<b>Docker</b>』『<b>Docker Compose</b>』を紹介していきます。

# Rubyって何？
[Ruby](https://www.ruby-lang.org/ja/)はまつもとゆきひろさんによって開発されたオブジェクト思考スクリプト言語です。1995年に公開されて以降今でも活発に開発が続けられてるプログラミング言語です。
Javaとかもちょっと触ったことがあるのですが、初めてRubyを触ったときは、シンプルに直感的にかけるなーという印象を持ちました。
`gem`と呼ばれるライブラリを追加することで、そりゃあもういろいろなことができます。

# Railsって何？
[Rails](https://rubyonrails.org/)は2004年に公開されたRuby用のWebアプリケーションフレームワークです。Rails自体も`gem`の1つとして配布されています。WebアプリケーションフレームワークはWebアプリケーションの開発するためのあれやこれやを最初から準備してくれていて開発をサポートしてくれるものです。
元々Twitter社が利用していたこともあり世界的に有名なのかなと思います。現在でもAirbnb, GitHub, Hulu, Shopifyなどで利用されています。日本だとCookpadや食べログなどで利用がなされているはずです。

RailsではDRYとConvention over Configuration (CoC)を基本理念としています。CoCは開発者が色々と設定を弄るのではなくWebアプリケーションフレームワークが決めた規約の中で開発を進めていくことで、開発者の決定しなくてはいけないことを極力減らしWebアプリケーションをシンプルに素早く実装することができるようにします。

# Dockerって何？
Dockerはコンテナ技術のデファクトスタンダードになっている技術です。コンテナ技術は仮想化技術の一種でVMと並ぶ技術と思ってください。VMに比べて軽量で共有しやすく、起動・停止も高速です。
詳しくは以下の記事がとても参考になります。

- [【図解】Dockerの全体像を理解する -前編- - Qiita](https://qiita.com/kotaro-dr/items/b1024c7d200a75b992fc)
- [【図解】Dockerの全体像を理解する -中編- - Qiita](https://qiita.com/kotaro-dr/items/88ec3a0e2d80d7cdf87a)
- [【図解】Dockerの全体像を理解する -後編- - Qiita](https://qiita.com/kotaro-dr/items/40106f13d47bfcbc2572)

DockerはDockerfileにミドルウェア以上の情報をInfrastructure as Code (IaC）できるのもいいところだと思っています。Dockerfileを共有したり、DockerHubなどのRegistryサービスを経由してDocker imageを共有することでmacのローカルマシーンと遠隔のLinuxマシーンのような異なるプラットフォーム上でも全く同じ環境でアプリケーションを動かすことができます。

詳しくは上の記事を参考にしてほしいのですが、まず大まかにDocker imageとDocker containerの関係性を理解しましょう。
わかりやすい方で、Docker containerを起動させることで仮想的にアプリケーションを動作させることができます。Docker imageはこのDocker containerの型になるものです。
例えば、とあるRailsアプリケーションのDocker imageがあるとします。このDocker imageからDocker container AとDocker container Bを別々のポート番号で起動させることができます。このDocker container A/Bは起動させたり停止させたり、はたまた中のファイルが更新されたりしますが、これらは型となるDocker imageには何の影響も与えません。そのため毎回同じ初期状態でDocker containerを起動させることができるのです。
Docker image自体を更新したい場合はbuildという行為を行います。これは起動中のDocker containerをベースにDocker imageを作成することもできますが、たいていの場合Dockerfileをbuildして行います。この辺りの詳しい話は、この後のハンズオンで実際に手を動かして理解していってもらえればと思います。

## Dockerを試してみる。
Dockerは様々なインストール方法があります。ここは各自のOSなどに合わせてインストールしてみてください。macを利用している場合はDocker Desktop for Macを使うのが一番楽かと思います。[Install Docker Desktop on Mac | Docker Documentation](https://docs.docker.com/docker-for-mac/install/)

では、試しにWebサーバーの一つであるnginxをDocker上で起動してみます。
あ、ちなみに今後↓のように`$`が書いてあったらターミナルでコマンドを入力するんだと思ってください。コマンドには`$`は不要です。

```
$ docker image pull nginx
$ docker run -d -p 8080:80 nginx
```
たったこれだけでローカル上にnginxが立ち、`http://localhost:8080`でアクセスできるようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254216/dd87225b-d6f6-df42-8c3e-e30ca0e8d0f8.png)

少しコマンドを説明します。

### docker pull
`docker pull`はDocker imageをレジストリ（Docker imageを保存するためのサービス）からローカルに取得するコマンドです。引数には`<image name>:<tag>`を指定します。今回は[DockerHub](https://hub.docker.com/)からnginxを取得しています。`<tag>`は省略可能で、省略されている場合は`latest`（最新バージョン）が選択されます。最新バージョンはどんどん更新されていくので、実際に開発を進めていく時にはタグを厳密に指定してあげないとバージョン差異の不具合を受けてしまう可能性があるので注意しましょう。

### docker run
`docker run`コマンドはDocker imageを元にDocker containerを起動させるコマンドです。オプションを除けば`docker run nginx`となりますが、先ほどローカルに取得した`nginx`イメージを設計書としてコンテナを起動させています。オプションの説明は以下の通り。

- `-d`: コンテナをバックグラウンドで実行する。これをつけないとターミナル上にコンテナのログが表示され続けて操作できなくなる（その場合はCtrl+Cでコンテナを停止させる）
- `-p <host port>:<container port>`: `container port`を`host port`で公開する。Dockerコンテナはホスト（ローカル）上のDocker networkと呼ばれる閉塞ネットワークで起動しており、外部（`http://localhost`など）からアクセスできない。このオプションをつけることで`<container port>`と`<host port>`を関連づけて`<host port>`からコンテナにアクセスできるようにしている。

なかなか理解が追いつかないのはわかります。いっぱい触って感覚掴みましょう。
DockerHubにはいろいろなDocker imageが公開されており、詳細ページには使い方も細かく書かれています。色々気になったものを触ってみましょう。

もう少しコンテナの様子をみていきます。

### docker ps
`docker ps`コマンドはDocker containerの情報を確認するためのコマンドです。

```
$ docker ps
CONTAINER ID  IMAGE  COMMAND                 CREATED         STATUS         PORTS                 NAMES
8230d7a776e1  nginx  "nginx -g 'daemon of…"  25 minutes ago  Up 25 minutes  0.0.0.0:8080->80/tcp  vigilant_johnson
```

- `CONTAINER ID`: コンテナのID。コンテナ初回起動時に振られる。
- `IMAGE`: コンテナの元になっているイメージ。
- `COMMAND`: コンテナ内で起動時に実行されているコマンド。アプリケーションの起動コマンドなどが元々Docker imageで定義されているのでそれが表示されている。
- `CREATED`: コンテナが作成された日時。
- `STATUS`: コンテナのステータス。起動しているときはいつから起動しているのかが表示される。
- `PORT`: コンテナのポートが表示されます。
- `NAMES`: コンテナのニックネームです。オプションで自分でつけることもできますが、デフォルトではランダムな名前が自動的につきます。

`docker ps`コマンドを使って、コンテナが起動中であることが確認できました。

次にコンテナを停止してみます。

### docker stop
`docker stop`コマンドはコンテナを停止させるコマンドです。引数として`CONTAINER ID`か`NAMES`を与えます。

```
$ docker stop vigilant_johnson
```
今回は`vigilant_johnson`の名前がついていたのでそれを停止させました。
ではまず`http://localhost:8080`に再度アクセスしてみましょう。リロードでも構いません。先ほどのnginxのwelcomeページが表示されなくなっているはずです。このことからコンテナが停止したことがわかると思います。

さらに先ほど使った`docker ps`コマンドでコンテナが停止したことを確認してみましょう。

```
$ docker ps
CONTAINER ID  IMAGE  COMMAND  CREATED  STATUS  PORTS  NAMES
```
おや、何も表示がされませんね。実は`docker ps`コマンドは起動中のコンテナのみが表示されるようになっています。停止中のコンテナの情報も確認したい場合は`-a`オプションをつける必要があります。

```
$ docker ps -a
CONTAINER ID  IMAGE  COMMAND                 CREATED         STATUS                    PORTS  NAMES
8230d7a776e1  nginx  "nginx -g 'daemon of…"  58 minutes ago  Exited (0) 2 minutes ago         vigilant_johnson
```
先ほどのコンテナが表示されましたね。`STATUS`が`Exited`になっていることがわかります。このことからコンテナが停止したことが読み取れます。また`PORT`も公開されなくなっているので何も表示されていません。

### docker start
このように停止状態になっているコンテナはすぐに再起動させることができます。

```
$ docker start vigilant_johnson
```
また、`http://localhost:8080`にアクセスするとnginxのwelcomeページが表示されていると思います。

```
$ docker ps
CONTAINER ID  IMAGE  COMMAND                 CREATED            STATUS             PORTS                 NAMES
8230d7a776e1  nginx  "nginx -g 'daemon of…"  About an hour ago  Up About a minute  0.0.0.0:8080->80/tcp  vigilant_johnson
```
`docker ps`コマンドでも起動を確認できました。

### docker rm
さて、ここまででコンテナを`docker stop`で停止しても、停止しただけでホストにはコンテナが残り続けているのがわかったと思います。再度起動する可能性がある場合はすぐに再起動ができるので便利なのですが、本当にもういらないやつは残り続けているとホストのボリュームを圧迫する原因にもなってしまうので完全削除したくなります。この場合`docker rm`コマンドを使うことでコンテナを完全に削除することができるので試してみましょう。

```
$ docker stop vigilant_johnson
$ docker rm vigilant_johnson
$ docker ps -a
CONTAINER ID  IMAGE  COMMAND  CREATED  STATUS  PORTS  NAMES
```
これでコンテナは完全に削除されました。

### docker images
コンテナのLifecycleは以上の形になります。ではイメージはどうでしょうか？
先ほど`docker pull`コマンドでnginxのDocker imageをローカル（ホスト）にダウンロードしていました。コンテナを削除しても設計書となるイメージは残り続けているのでまずはそれを確認してみましょう。
ダウンロード済のイメージを確認するためのコマンドが`docker images`です。

```
$ docker images
REPOSITORY  TAG     IMAGE ID      CREATED      SIZE
nginx       latest  5ad3bd0e67a9  10 days ago  127MB
```
いますね。再度このイメージを使ってコンテナを起動させる場合は残しておくとダウンロードが不要になるので便利なのですが、今回はもう使わないので削除します。

### docker rmi
`docker rmi`コマンドはホストに保存されているイメージを削除するコマンドです。`docker images`で調べた`REPOSITORY:TAG`の形式か`IMAGE ID`を引数に与えてイメージを削除します。

```
$ docker rmi nginx:latest
```
もう一度`docker images`などで確認していただくとイメージも削除されたことがわかります。

このような感じで、Dockerを使うことで本来であれば色々とミドルウェアやファイルを用意しないと動かないアプリケーションをDocker imageを元にDocker containerを起動させるだけで扱えるようになります。便利ですね。

# Docker Composeって何？
さて、Dockerはとても便利なのですが、イメージ名とかオプションとか毎回覚えてられないですよね。
また、今後Webアプリケーションを開発するとなると、WebアプリのコンテナとDBのコンテナなど複数のコンテナを相互に関連付けながら起動・停止させないといけなくなるわけで、その手順を覚えておくのも面倒です。

こんなことを解決してくれるのがDocker Composeです。Docker Composeを使えば、複数のコンテナのコンテナ起動時のオプションなどをyamlファイルで定義することができます。百聞は一見に如かずですので、試しに先ほどのnginxコンテナをDocker Composeを使って起動させてみましょう。
Docker ComposeはDocker Desktop for Macなどをインストールしている場合は一緒に使えるようになっています。そのほかの方法でDockerをインストールしている場合は、別途Docker Composeもインストールしないといけないので頑張ってください。笑

```docker-compose.yml
version: '3'

services:
  nginx:
    image: nginx:latest
    ports:
      - 8080:80
```
少し説明します。

- `version`: Docker Composeのファイルフォーマットのバージョンです。現在の最新のメジャーバージョンの`3`を指定しています。
- `services`: この下に管理したいコンテナの定義を書いていきます。
- `nginx`: これが一つコンテナの定義の塊です。名前は何でも自由につけられます。
- `image`: 起動するコンテナの元となるimageを指定します。`docker run`でコンテナを起動させたときと同じように`registry:tag`を指定します。
- `ports`: ホストとコンテナのポートの関連付けをしてます。`docker run`でいう`-p`オプションの役割。

これをつかってコンテナを起動させます。

```
$ docker-compose up -d
```
これで`docker-compose.yml`の定義に沿ってコンテナが起動します。`-d`オプションは`docker run`の時と一緒でバックグラウンドでコンテナを起動させるオプションです。同じように`http://localhost:8080`でnginxのwelcomeページが表示されるようになっていると思います。

`docker-compose`はデフォルトでコマンドが実行されたディレクトリの`docker-compose.yml`をターゲットに実行します。ファイルを指定する場合は`-f [file path]`をつけます。イメージは↓。

```
$ docker-compose -f ./docker-compose.test.yml up -d
```

`docker-compose ps`で`docker-compose.yml`で起動させたコンテナの情報だけをみるためには`docker ps`ではなく`docker-compose ps`を使うと便利です。

```
$ docker-compose ps
    Name              Command          State          Ports
-------------------------------------------------------------------
xxxxx_nginx_1   nginx -g daemon off;   Up      0.0.0.0:8080->80/tcp
```

コンテナを停止するときは以下のコマンドです。

```
$ docker-compose down
```

これで何回でも同じ条件で楽にコンテナを作ることができますね。

# まとめ
前置きがかなり長くなってしまいましたが、今日は実際に開発を始めていくにあたって、

- プログラミングの3原則を学んだ
- これからキーとなる『Ruby』『Rails』『Docker』『Docker Compose』を学んだ

ができたと思います。次回は<b>Ruby on Rails on Docker</b>でHello world（とりあえず立ち上げてみた）をやっていきたいと思います。乞うご期待！

Next: [コーディング未経験のPO/PdMのためのRails on Dockerハンズオン vol.2 -Hello, Rails on Docker- - Qiita](https://qiita.com/at-946/items/45ccca274b30268f68fc)

# Reference
- [Ruby on Rails チュートリアル：実例を使って Rails を学ぼう](https://railstutorial.jp/)
- [Ruby - Wikipedia](https://ja.wikipedia.org/wiki/Ruby)
- [Ruby on Rails - Wikipedia](https://ja.wikipedia.org/wiki/Ruby_on_Rails)
- [docker | Docker Documentation](https://docs.docker.com/engine/reference/commandline/docker/)
- [Overview of Docker Compose | Docker Documentation](https://docs.docker.com/compose/)

# Other Hands-on Links
- https://github.com/at946/rails_on_docker_handson/tree/master