LoginSignup
180
292
記事投稿キャンペーン 「Rails強化月間」

今更聞けないDockerのしくみ(「Dockerとは?」から「docker-composeファイルを1人で作れるようになる」まで)

Last updated at Posted at 2023-10-19

はじめに

なんとな〜くdockerを使い始めてはや4年ほど。
既存のプロジェクトにアサインされた場合はdockerファイルに何が記載されているかなんて意識せずコマンドを実行するだけで、何か自分で一から作る時は、誰かが作ったものをどこからか持ってきて済ませていた。

こんな感じなのでdockerをなんとなく扱えてはいるが細かいところを全く理解できてない。
今回は人に説明できるくらい理解できるようになろうとした男の記事です。

ハンズオン形式でやっていきますので一緒に手を動かしながらやってみていただけると嬉しいです。

対象とする読者

  • これからdockerをは0から理解したい人
  • なんとなくdocker触っちゃってて理解していない俺みたいな人
    (でもLinux多少知っていないと少し大変かもです)
  • Docker, docker image, docker container, docker-composeといわれてもパッとイメージがつかない俺みたいなレベル感の人
    ※rubyとrailsをベースに解説を行いますがエッセンスとなる部分はrubyユーザーでなくても参考になる部分は多いと思います。

Dockerとは?

※もっと説明上手い人たくさんいるのでさらっといきます

Dockerとは?

Dockerはコンテナ仮想化プラットフォームのこと。
開発環境をコンテナにパッケージ化することで、開発者間で一貫性のある環境を確保でき、コンテナはどのプラットフォームでも動作するため、異なる環境での問題を最小限に抑えることができる。

Docker勉強.jpg

実際にdocker imageをbuild→docker containerを起動させてみよう

docker containerを起動するにはdocker imageが必要です。
docker imageとはdocker containerの雛形です。
docker imageを作成するためにはDockerfileを用意します。
Dockerfileはいわばdocker imageの「設計図」のようなものです。

①docker imageのビルド

では任意のディレクトリに作業ディレクトリを作りましょう。僕はdocker_sampleというディレクトリで作業しようと思います。

mkdir docker_sample

cd docker_sample

続いて必要なファイルを用意します。

$ touch Dockerfile

$ mkdir app

$ touch app/main.rb

ディレクトリが以下のようになっていればOKです。

$ find .                                                                     
.
./app
./app/main.rb
./Dockerfile

実際のファイルには以下のように記述します。

Dockerfile
FROM ruby:2.7

WORKDIR /var/www
# /var/wwwの部分は/myappなどなんでも良い
COPY ./app /var/www

以下のコマンドを実行します。
$ docker image build -t sample_app:latest .

すると以下のように表示されます。

[+] Building 3.3s (8/8) FINISHED                                      docker:desktop-linux
 => [internal] load build definition from Dockerfile                                  0.0s
 => => transferring dockerfile: 143B                                                  0.0s
 => [internal] load .dockerignore                                                     0.0s
 => => transferring context: 2B                                                       0.0s
 => [internal] load metadata for docker.io/library/ruby:2.7                           3.1s
 => [internal] load build context                                                     0.0s
 => => transferring context: 57B                                                      0.0s
 => [1/3] FROM docker.io/library/ruby:2.7@sha256:2347de892e419c7160fc21dec721d595273  0.0s
 => CACHED [2/3] WORKDIR /var/www                                                     0.0s
 => [3/3] COPY ./app /var/www                                                        0.0s
 => exporting to image                                                                0.0s
 => => exporting layers                                                               0.0s
 => => writing image sha256:4aff41a0a11e60c78693c2d4bb5b0e00cfdbe98747c725a03666e4df  0.0s
 => => naming to docker.io/library/sample_app:latest   

解説

Dockerfileの解説
スクリーンショット 2023-10-15 14.27.39.png

コマンドの解説
スクリーンショット 2023-10-17 21.23.21.png

ビルドしたdocker imageを確認するにはこのコマンドを実行します
$ docker image ls
実行すると以下のようなイメージがビルドされていることがわかります。
先ほど設定したsample_applatestという値が設定されていることがわかります。
スクリーンショット 2023-10-17 21.36.09.png

②docker containerの起動

Dockerにはライフサイクルがあります

docker containerの作成

docker containerの起動

docker container停止

docker container削除

Docker勉強 (1).jpg

docker imageを作成してからdocker containerを作成、起動する必要があるということです。

今回はdocker containerが起動した際にapp/main.rb"Hello World!"が実行されるようにしてみます。

main.rb
p "Hello World!"
Dockerfile
FROM ruby:2.7

WORKDIR /var/www
# /var/wwwの部分は/myappなどなんでも良い
COPY ./app /var/www

CMD ["ruby", "/var/www/main.rb"]

以下の一行を追加しdocker containerが起動した時に実行したいコマンドを記述しておきます。
CMD ["ruby", "/var/www/main.rb"]

あくまでdocker container上でコマンドを実行するためディレクトリはDockerfileで記述した/var/www内のmain.rbになっているということですね。

Dockerfileを編集したのでdocker imageを再度ビルドします。

$ docker image build -t sample_app:latest .

ビルドが完了したらいよいよdocker containerを起動します。

$ docker container run --name sample_app sample_app:latest
スクリーンショット 2023-10-17 22.29.23.png

runというコマンドはコンテナの作成+起動を意味するコマンドです。(のちのdocker-composeの話にも共通します)

$ docker container run --name sample_app_container sample_app:latest    

"Hello World!"

main.rbに記述してある"Hello World!"がコールされます。

docker containerの起動を確認するには以下のコマンドを実行します。

$ docker container ls

CONTAINER ID  IMAGE COMMAND  CREATED  STATUS PORTS NAMES

あれ、起動したはずなのに何も表示されていませんね。

これはp "Hello World!"という処理が完了したためdocker containerが自動的にストップしている状態です。

停止したコンテナも確認するには-aオプションをつける必要があります。

$ docker container ls -a

CONTAINER ID   IMAGE                           COMMAND                   CREATED         STATUS                     PORTS                               NAMES
084abfd63748   sample_app:latest               "ruby /var/www/main.…"   5 minutes ago   Exited (0) 4 minutes ago                                       sample_app_

Exitedとなっているので停止していることがわかりますね。

docker containerの説明をしたいのに我ながら例が悪かったと思いますのでファイルを修正して再度試してみましょう。

main.rb
p "Hello World!"

sleep(100)

このようにすれば100秒間はコンテナが起動し続けますのでこれで確認してみましょう。

一度docker containerを削除します。

$ docker container rm sample_app_container
sample_app_container

docker imageも再度ビルド

$ docker image build -t sample_app:latest .

再度コンテナを起動します。

$ docker container run --name sample_app_container sample_app:latest    

ここでdocker container lsを実行しようとすると...
あ、ターミナルが入力を受け付けてくれませんね。
docker containerのログが出力されるようになっているためです。

別タブを開いてもいいですが、バックグラウンドでコンテナを起動するモードがあるのでそれを試してみましょう。

docker containerコマンドを実行する際に-dオプションをつける必要があります。

$ docker container run -d --name sample_app_container sample_app:latest
0cc709282c933d71b424b0856eef0eb12d974ca4f3fb31604aeb7eec874a28d8

バックグラウンドでコンテナが起動しているか確認してみます。

$ docker container ls
CONTAINER ID   IMAGE                           COMMAND                   CREATED         STATUS                 PORTS                               NAMES
0cc709282c93   sample_app:latest               "ruby /var/www/main.…"   3 minutes ago   Up 8 seconds                                               sample_app_cont

起動が確認できました。

また起動中のコンテナに対してコマンドを実行するには以下のようにします

$ docker container exec sample_app_container ruby -v
ruby 2.7.8p225 (2023-03-30 revision 1f4d455848) [aarch64-linux]

docker container exec + コンテナ名 + 〇〇
とすることでコンテナの中のファイルにアクセスできます。

コンテナを停止→削除

コンテナを停止→削除するには以下の手順です。

停止

$ docker container stop sample_app_container
sample_app_container

削除

$ docker container rm sample_app_container
sample_app_container

gemを使ってみよう

もう少し実践的にしてみます。docker環境上でgemを使うとしましょう。

もうお気づきの方もいるかもしれませんが先ほどの挙げたdocker container exec + コンテナ名 + 〇〇というコマンドを応用して
docker container exec bundle installなどとすればgemが利用できそうですよね。
ただ、それだとdocker containerを起動するたびにそのコマンドを実行しなければならず、そんなことやってられないですよね。

そこでDockerfileに追記を行います。

Dockerfile
FROM ruby:2.7

WORKDIR /var/www
# /var/wwwの部分は/myappなどなんでも良い
COPY ./app /var/www
#gemをインストールする先のパスを指定する(あまり深く考えなくていいです)
RUN bundle config --local set path 'vendor/bundle'

RUN bundle install

CMD ["ruby", "/var/www/main.rb"]

RUNというコマンドを利用することでDockerコンテナイメージをビルドする際に実行するコマンドを指定できます。

Gemfileも作り忘れちゃいけませんね。今回はかの有名なnokogiriを例にとってgemが利用できることを確認していきます。

app/Gemfile
source "https://rubygems.org"

gem 'nokogiri'

main.rbを以下のように書き換えます。

app/main.rb
require 'nokogiri'
require 'open-uri'

# ウェブページを取得(今回はwikipediaを例に取ります)
doc = Nokogiri::HTML(open('https://ja.wikipedia.org/wiki/'))

# タイトルを表示
puts doc.title

ウェブスクレイピングは、サイトの利用規約に適合し、倫理的に行わなければならないことに注意して行う必要があります。違法行為や悪意のある活動は絶対に行わないようにしましょう。

再度イメージをビルドし、コンテナを起動してみましょう。

docker image build -t sample_app:latest .を実行してみるとさっきDockerfileに追記したコマンドが実行されていることがわかります。
スクリーンショット 2023-10-18 21.54.23.png

コンテナを起動します

$ docker container run --name sample_app_container sample_app:latest
/var/www/main.rb:5: warning: calling URI.open via Kernel#open is deprecated, call URI.open directly or use URI#open
Wikipedia

gemを利用してWikipediaというtitleが取得できていることがわかります。
/var/www/main.rb:5: warning: calling URI.open via Kernel#open is deprecated, call URI.open directly or use URI#open
という警告が出ていますがあくまでサンプルアプリなのでそこはご愛嬌でお願いします。(gemが使えることをおみせしたかっただけなのです:bow_tone1:)

docker composeとは?

ここまで長々と書いてきて「実務環境で使うdocker-composeいつ出てくるんだ????」と思われていると思います。。。

いよいよ本題のdocker-composeです

現在ではdocker-composeコマンドよりdocker composeコマンドが推奨されています。

従来の docker-compose コマンドと新しい docker compose コマンドのどちらを使用するかは、状況に依存します。ただし、Dockerは将来的には新しい docker compose コマンドを推奨する方向に進んでおり、新しいプロジェクトでは docker compose コマンドの使用を検討することが一般的です。

実務で使うアプリケーションだと

  • webサーバー
  • データベース

など複数のアプリケーションやミドルウェアが一つになって一つのシステムが出来上がっていると思います。
(ほかにも例を挙げるならバッチ処理やフロントエンドのアプリケーションなど...)
それらのアプリケーションごとにDockerfile用意してコンテナ起動して...なんてやっていられないですよね。相互の連携まで考えたら頭が痛くなります。

ここでdocker-composeが必要になってくるわけです。

docker composeはdockerコンテナの集合体

実際にdocker-composeを利用してみよう

ここからはrailsを題材にwebアプリケーションをdocker環境で構築できるようになってみたいと思います。

別のディレクトリを用意していただいた方がわかりやすいと思うので僕は今回はdocker_rails_sampleというディレクトリを用意しました。以下のファイルを用意してください。

Dockerfile
FROM ruby:2.7

WORKDIR /var/www

COPY ./app /var/www

RUN bundle config --local set path 'vendor/bundle'

RUN bundle install

Dockerfileはもう解説不要ですよね。

app/Gemfile
source "https://rubygems.org"

gem 'rails'
docker-compose.yml
version: '3'
services:
  db:
    image: mysql:8.0
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - ./app/db/mysql_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: password
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - ./app:/var/www
    ports:
      - "3000:3000"
    environment:
      RAILS_ENV: development
    depends_on:
      - db

解説

スクリーンショット 2023-10-19 1.55.38.png

rails newを実行

まだgemfileしかないのでrails newをdocker環境に対して行います
$ docker-compose run web rails new . --force --database=mysql
スクリーンショット 2023-10-19 8.29.23.png

ずらっとファイル群が生成されていますね。
(ここまでやってきて今更ですが自作したappディレクトリにrailsによって生成されたappディレクトリがあり、名前がややこしいですね...反省🙇)
スクリーンショット 2023-10-19 8.33.11.png

これによりDockerfileやGemfileの内容が更新されているので例によって再ビルドを行います。

$ docker-compose build

app/config/database.yml
# (中略)

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
# ↓ここに先ほどdocker-compose.ymlファイルで指定したMYSQL_ROOT_PASSWORDの値を入
  password: password
# ↓ここに先ほどdocker-compose.ymlファイルで指定したdbというコンテナ名を指定
  host: db

# (中略)

railsのdbを作成するコマンドを実行します
$ docker-compose run web rails db:create
スクリーンショット 2023-10-19 8.52.42.png

dbの作成ができました。

いよいよです。

$ docker-compose up
アクセスしてみると
http://localhost:3000/

スクリーンショット 2023-10-19 8.55.32.png

できました〜〜:clap:

終わりに

どうでしょう。ここまでくれば実務で見かけるDokcerfiledocker-compose.ymlファイルをみて「あ!これってこういうことしてるんだ!」と今までわからなかったことがわかるようになっているのではないでしょうか。
僕自身前までは「なんだかいろいろやってんだな...」くらいの理解で自分の中で完全にブラックボックス化していましたが、今回腰据えて一から勉強してみて非常に有意義だったのでぜひ駆け出し・新卒エンジニアの皆さんは僕のように4年も放置せずキャッチアップしてもらいたいです。

最後まで読んでいただきありがとうございました。

参考文献

180
292
1

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
180
292