LoginSignup
47
46

More than 5 years have passed since last update.

クジラに乗って海に出た

Last updated at Posted at 2016-06-02

久しぶりの投稿になりました, かどわき(ゲートサイド)です
今までとは状況が変わったので, 気持ち新たに記事を書いていこうと思います!

はじめに

この記事では, Dockerを使ってインフラ, サーバサイド, フロントエンドを全て自分で用意し, Webサービス開発を始める環境を整備した経緯についてご紹介します.
タイトルは
   クジラに乗って(Dockerを使って)
   海に出た(Webサービス開発を始めた)
的な意味でつけました!!!!
変なタイトルつければ記事の流入増えるってばあちゃんが言ってたんですね!!!!

主な記載事項はDockerに偏っちゃうかもですが, Webサービス開発を始める上での題材として, 以下の様な人に役立つ記事にしたいと思います.

  • Dockerの基本を知りたい人
  • Webサービスの開発環境および動作環境を用意したい人
  • フルスタックに興味がある人

多岐にわたるレイヤーの話が出てくるので, 上記以外の人にもお役に立てればと思います.

この記事のゴール

  1. インフラ, サーバサイド, フロントエンドの構成を大体理解できる
  2. コマンド一発でサービス(今回は動作環境)をデプロイできる

使用した言語など

インフラ

  • OS: Linux2.6
  • コンテナ管理: Docker
  • DB: MySQL
  • Cacheサーバ: Redis
  • HTTPサーバ & リバースプロキシ: Apache

サーバサイド

  • 言語: Java
  • フレームワーク: SpringBoot
  • Webサーバ(組込): Tomcat

フロントエンド

  • 言語: Javascript (ES6)
  • モジュールバンドラ: Webpack
  • UIライブラリ: React
  • 状態管理フレームワーク: Redux

ローカル開発環境

Mac OS X 10.11.4 (El Capitan)

サービスディレクトリ構成

MyApp
├── MyApp-client ・・・ フロントエンド
│   ├── node_modules
│   ├── package.json
│   ├── src
│   └── webpack.config.js
├── MyApp-server ・・・ サーバサイド
│   ├── Dockerfile
│   ├── build
│   ├── build.gradle
│   ├── build.sh
│   ├── gradle
│   ├── gradlew
│   ├── gradlew.bat
│   ├── run.sh
│   └── src
├── README.md
├── apache ・・・ HTTPサーバ
│   ├── Dockerfile
│   ├── bash.sh
│   ├── bundle.js
│   ├── httpd.conf
│   ├── index.html
│   └── run.sh
├── datastore ・・・ データストア
│   ├── Dockerfile
│   ├── build.sh
│   └── run.sh
├── db ・・・ データベース
│   ├── Dockerfile
│   ├── build.sh
│   ├── charset.cnf
│   └── run.sh
├── docker-compose.yml
├── redis ・・・ キャッシュサーバ
│   └── Dockerfile
├── remove_all_images.sh
├── remove_containers.sh
└── stop_containers.sh

ソースコード

準備

インストール

homebrew-caskを利用する方法

(homebre-caskは便利なので入れとくといいかも)

$ brew install caskroom/cask/brew-cask
$ brew cask install dockertoolbox

# docker, docker-machine, docker-compose, virtualbox, kitematic(GUI tool)が入ります

homebrewのみでやる方法

$ brew install Caskroom/cask/dockertoolbox

# 同様に docker, docker-machine, docker-compose, virtualboxが入ります
# もしvirtualboxが入っていた場合は以下を実行

$ brew install docker docker-machine docker-compose

パッケージを利用する方法

インフラ

Docker

まずはインフラ構築として, Dockerについて説明していきます.
ただし, 今更1から説明するのは冗長的なので, この方の記事
が参考になるかと思います.
このQiitaにも既に2600を超える記事に「Docker」タグがついていて(2016/06/02時点), それなりに利用されていることが伺えます.

メリット

自分が感じている主なメリットはこんな感じです.
* 用意したい環境をすぐに構築できる(例: Nginxを利用したHTTPサーバ, Rails + unicorn, キャッシュサーバなど)
* インフラ構築をコードで自動化できる → 環境によってアプリケーションが動かないということがなくなる
* コンピュータのリソースを隔離・制限できる → 少ないリソースでアプリケーションを動かす事ができる
* オーバーヘッドが少なく, インスタンスの作成や起動が早い

など

コマンド要約

今回の記事で使うのは以下3つのコマンドのみです
$ docker : コンテナを操作するコマンド
$ docker-machine : コンテナを動かすための仮想マシン(VM)を操作するコマンド
$ docker-compose : 複数のコンテナをまとめて操作するコマンド

Docker Machine

まずはこれでVM作りましょう!
$ docker-machine create --driver virtualbox MyAppVM
はい終わり!!!

というと不安かと思いますので,
$ docker-machine ssh MyAppVM
とか実行してみてください.
普段サーバにsshするのと同じようにVMで操作できるでしょう!(ログアウトするときは同様に ctrl + dで)

今後のコンテナ操作のために以下を実行しておいてください.

  • $ eval $(docker-machine env MyAppVM)
    dockerコマンドで利用する環境変数が設定され, コンテナの管理先VMが自動的に認識されます.

  • $ docker create network myapp-net
    あらかじめコンテナ同士を繋ぐネットワークを作っておきます.

Dockerfile

Dockerfileはコンテナの構成内容を記述するファイルであり, この内容を元にdocker buildをおこなうと, 思い通りのコンテナイメージが出来上がります.

今回は, 各コンテナの設定ファイルで簡単に説明します.

serverコンテナイメージのDockerfile

/myapp/myapp-server/Dockerfile
FROM java:8

ADD build/libs/myapp-0.0.1-SNAPSHOT.jar /opt/tomcat8/webapps/myapp-0.0.1-SNAPSHOT.jar

WORKDIR /opt/tomcat8/webapps

EXPOSE 8080

CMD ["java", "-jar", "myapp-0.0.1-SNAPSHOT.jar"]
命令 引数
FROM 元となるイメージの指定
ADD ファイル・ディレクトリの追加
WORKDIR 作業ディレクトリの指定
EXPOSE 他コンテナに晒すポートの指定
CMD コンテナの作成後に実行するコマンド

上記の設定をするようなケースを, ストーリーで説明してみます.

  • SpringBootのWebアプリが動く環境が欲しいなぁ
  • javaさえ入ってれば動くんだけどなぁ
  • どうやらjavaのイメージがあればjavaが動くらしいぞ
  • じゃあjava:8というイメージを元にコンテナを作成して, この中にSpringBootのWebアプリ(jarファイル)を置くぞ
  • コンテナの◯◯ディレクトリにjarファイルを追加することにしよう
  • Webアプリの起動コマンドを実行する場所はjarファイルの置いてある場所にしておこう
  • 後でapacheから繋げるように, 8080ポートは晒しとくのだ
  • $ docker build -t spring-boot . ッターン

という感じで, そのWebアプリが動く環境のイメージを元にしてファイル追加や起動コマンドなどの命令をDockerfileに書き込んでおくと, 思い通りのコンテナが出来るわけです.

java:8イメージって何や!(# ゚Д゚)」って人には申し訳ない説明になりましたが, イメージは自分で作成する他, DockerHubというDocker版Githubみたいなサービスからpullしてくることができます.
今回使ったイメージは, DockerHubのjava公式イメージになります. FROMで指定したイメージは, 手元になければDockerHubから探して勝手にとってきます. それでもなければエラーになるでしょう.

他に以下4つのコンテナを作成するためのDockerfileを記載します
* HTTPサーバ(Apache)
* DBサーバ(MySQL)
* キャッシュサーバ(Redis)
* データストア

HTTPサーバ

/myapp/apache/Dockerfile
FROM httpd

RUN mkdir -p /usr/local/apache2/htdocs/myapp/build

COPY ./httpd.conf /usr/local/apache2/conf/httpd.conf
COPY ./index.html /usr/local/apache2/htdocs/myapp/index.html
COPY ./bundle.js /usr/local/apache2/htdocs/myapp/build/bundle.js

EXPOSE 80

少しだけ解説すると, RUNはコンテナの作成前に実行される命令で, いくつでも実行できる模様. CMDと似ているが, CMDはコンテナ作成後に実行される命令で, 1回のみ実行できる模様.
この変ややこしいのですが, 基本RUNでディレクトリを作成したりライブラリをインストールしたりしてから, CMDでコンテナ内部で動かしたいツールやソフトの実行命令を指定するのが良さそうです.
他にもENTRYPOINTとかあるけど今回はいっかなぁ

COPYはその通り, 指定元ファイルを指定先ファイルへコピーする命令です.
ADDはファイルを追加しつつzipファイルとか展開してくれるのと, リモートからファイルも追加できるらしいので, ADDの方が多機能っぽいです. でもCOPYですむならCOPYを使えばいいと思います.

ちなみに, index.htmlbundle.jsはフロントエンドの開発で生成したファイルです.
Webpackを使うとjscssを, よしなに処理してひとまとめにしてくれます.

DBサーバ

/myapp/db/Dockerfile
FROM mysql

COPY charset.cnf /etc/mysql/conf.d/charset.cnf

EXPOSE 3306

公式のMySQLイメージ利用したのですが, 日本語環境がアレなのでcharset.cnfを追加しました.
こんな感じ.

charset.cnf
[mysqld]
character-set-server=utf8
[client]
default-character-set=utf8

キャッシュサーバ

/myapp/redis/Dockerfile
FROM redis

EXPOSE 6379

特に使わなかったので公式イメージそのままコンテナにするという暴挙

データストア

/myapp/datastore/Dockerfile
FROM busybox

VOLUME /var/lib/mysql

MySQLのデータを永続化するために, 必要最低限のLinuxコマンドだけ入ってるコンテナを作って, ホストマシンの特定のディレクトリをボリュームとしてマウントする役割を与えました.
そうしておけば, サービスのコンテナを全部潰してもデータは消えずにすむはずです.
ログファイルなどもこのデータストアコンテナを利用して保存するのが良いようです.

以上5つのコンテナでWebサービスを構成します.

Docker Compose

今回の中心になりうるツールの1つで, これがあることでコマンド1発デプロイが実現できるのです.
何故かと言うと, この設定ファイルで上記のDockerfileを元にコンテナの一斉ビルドと起動をおこなえるようになるからです.
自分が説明するより 'docker compose' と検索して一番上に出てくるこの記事が非常にわかりやすそうです.

docker composeでは, docker-compose.ymlというファイルを作成してコンテナの構成管理を設定します.
以下, 今回の記事で利用するdocker-compose.ymlの内容とざっくりとした解説をおこないます.

/myapp/docker-compose.yml
version: '2'   # ① 
services:   # ②
  server:   # ③
    container_name: spring-boot   # ④
    build: ./myapp-server   # ⑤
    ports:   # ⑥
      - '8080:8080'
    environment:   # ⑦
      REDIS_URL: redis://redis:6379
    links:   # ⑧
      - db
      - redis
    network_mode: 'myapp-net'   # ⑧
  db:
    container_name: mysql
    build: ./db
    environment: # ⑨
      MYSQL_DATABASE: myapp_db
      MYSQL_ROOT_PASSWORD: root
      MYSQL_USER: myapp
      MYSQL_PASSWORD: password
    ports:
      - '3306:3306'
    volumes_from:   # ⑩
      - datastore
    network_mode: 'myapp-net'
  datastore:
    container_name: datastore
    build: ./datastore
    network_mode: 'myapp-net'
  redis:
    container_name: redis
    build: ./redis
    ports:
      - '6379:6379'
    network_mode: 'myapp-net'
  apache:
    container_name: apache
    build: ./apache
    ports:
      - '80:80'
    links:
      - server
    network_mode: 'myapp-net'
番号 key 解説
version ymlのバージョン(サービスのバージョンじゃないです)
services トップレベルオブジェクトの1つで, この階層に各コンテナの設定情報を書いていきます.
トップレベルオブジェクトには他にvolumesnetworksが定義できます(今回は省略).
server ここではserverとしていますがkeyは何でもよいです.
ただし, Webサービスとして何の役割を持つコンテナなのかが分かるkeyにしたほうが良いでしょう.

上記の例で言えば, docker-compose.yml が置いてあるディレクトリの名前が MyApp , keyが server なので, 作成されるコンテナのイメージ名が myapp_server となります.
これなら, MyAppサービスのサーバサイドコンテナだと一目で分かるかと思います.
container_name そのままの意味でコンテナ名となります. 人にもよりますが, 主にそのコンテナで作動しているアプリやソフト, ツールの名前にしてます.
(spring-boot, apache, mysqlなど)
build Dockerfileを元に, コンテナをビルドするための設定です. Dockerfileが置いてあるディレクトリを指定します.
ports 公開するポートを指定します. ハッシュのvalueとしてシーケンス(配列形式)を指定するため, 以下のように複数のポートを公開することが可能です.
ports:
- "80":"80"
- "8080" : "8080"
environment コンテナ内部の環境変数を追加する設定です.
links 他のコンテナとのリンク設定です. これを設定しておくと, コンテナ名がホストネームとなり, 名前解決が出来るようになります.
上の例で言うと, spring-bootコンテナとdbコンテナにはIPアドレスが自動的に振られているので(172.20.0.4172.20.0.3), コンテナが起動した後にしかDB接続先がわからず, わざわざspring-bootコンテナでDB接続設定を書き換えなければなりません.
ダルい上に今回はspring-bootをjarファイルとして動かしていることもあり, ソースコードは書き換えられません.
そこでlinksを使うと, mysqlという名前のホストネームをspring-bootのDB接続設定に書いておくことで, 自動的にDBの接続先は172.20.0.3であると判断してくれます.
environment コンテナ内の環境変数を設定します.
今回のケースではMySQLの環境変数を正しく設定すると, 自動的にDBとユーザを作成してくれます.
この辺の環境変数設定はコンテナによりけりな印象なので, DockerHubの公式イメージを見てみると良いかもしれません.
network_mode 作成したコンテナ間ネットワークの設定です.
基本的にサービス全体を1つのネットワークで動かす想定なので, コンテナを同一ネットワークで繋ぐために全てのコンテナ設定でこの記述を追加します.
複数追加できるので, 別のネットワークを作って繋ぎたいコンテナ同士を繋ぐのも良いでしょう. ※1

そしてついに! ここであのコマンドを打つのです
$ docker-compose up

必要なイメージをすべてpullしてきて, Dockerfileの設定を元に全コンテナをビルド&起動します.
これでサーバサイド・フロントエンド・HTTPサーバ・DBサーバ・キャッシュサーバがDockerで作られたインフラ上で動作し, 同一ネットワークで繋がれたサービスが出来上がるのです.

↑の方にGithubのリポジトリへのリンクを貼っているので, dockerやらdocker-machineやらをいれたなら, READMEGet Started通りにやってみてください!

多分動くと思います(動かなかったらコメントにお願いします!!)

※1
docker-composeでコンテナを立ち上げる時に, 今回のようにこだわりなく全て同じネットワークで繋ぎたい場合は, わざわざ$ docker create network hogehogeでネットワークを作ってnetwork_modeを全てに指定しなくても, デフォルトでネットワークが作成されます.
リポジトリに上がっているものに関してはnetwork_modeを削除しています.

おわりに

ちょっと文章が多くなってしまったのと, 省略してしまったところは申し訳ないですorz

とにかく, Dockerはどんなエンジニアにとっても便利なツールになるはずです.
モバイルエンジニアも簡単なAPI作って連携してみたり, 学生エンジニアで一つのレイヤーを専門にしてしまっている人でもWebサービス全体を学べる環境が作れたり, 幅広く役に立ちます.
また, 会社で業務をおこなっている人でも新規サービスを提案するにあたり, モックアプリなどを何人かで爆速開発したいと思った時に, この環境さえあればレイヤーの切り分けを簡単にできて, デプロイも簡単にできるはずです.

使って損はないと思うので是非試してみてください! ♪〜(´ε` )

また, この後数週間にわたってこの記事の中で実装したフロントエンドやらサーバサイドやらについて書くつもりです〜

それでは長文失礼致しました!


p.s. ここが分からない!といったことがあれば質問受け付けてます!!

子記事

  1. Spring Bootで作ったRESTful APIをコンテナにして動かす
47
46
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
47
46