Y.Hoshiをフォローする

【Docker】1たす1から始めるクラスタ環境構築 ①基礎編

開発環境・ツール

これまで、受託案件の開発環境としてDockerを使用する機会があったのですが、ここ最近社内でもDockerを本格的に導入するようになってきました。

新しいものを使いこなすのが苦手な人間なので、辟易しつつも1からDockerの使いかたを学んでみようということで、こんな記事を作成しました。

これからDockerで開発環境を構築したいと思っている方はぜひご一読ください!

Dockerチュートリアル連載記事

  • 【Docker】1たす1から始めるクラスタ環境構築 ①基礎編(←いまここ)
  • 【Docker】1たす1から始めるクラスタ環境構築 ②ボリューム編(作成中)
  • 【Docker】1たす1から始めるクラスタ環境構築 ③ネットワーク編(作成中)
  • 【Docker】1たす1から始めるクラスタ環境構築 ④Docker-Compose編(作成中)

Dockerチュートリアル 基礎編

下準備 ~ホストOS上に仮想環境を作成する~

① 仮想環境立ち上げ

さっそくDockerをインストールしてコンテナを立ち上げていきたいところですが、これから作成するコンテナのせいでホストOSの環境を汚してしまうことは避けたいので、手始めにいくらでも汚せて不要になったらさっさと捨てられる環境を作っておきましょう。(ひどい言いよう)

構成としてはこんな感じのイメージです。

事前にインストールが必要なのは下記のとおり。

  • Vagrant
  • VirtualBox
  • VSCode (仮想環境でソースコードを編集するのに使います)

今回はDockerのチュートリアルなのですが、Vagrantを使うとホストOSと切り離して仮想環境を作成することができるため、新しい環境を作ってシミュレーションをしたいときや、適当に実験をしてささっと削除することのできる気軽な環境としてはやはり重宝します。

必要なものをインストールしたら、適当なフォルダを作成し、そこで下記を実行します。(私は:C\Vagrant\ubuntu-20というフォルダを作成しました。)

C:\Vagrant\ubuntu-20> vagrant init

実行したらディレクトリ内にvagrantfileが作成されるはずなので、テキストエディタで開き、下記の内容でまるっと置き換えます。

# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "bento/ubuntu-20.04"
  config.vm.network "private_network", ip: "192.168.33.10"
  config.vm.provision "shell", inline: <<-SHELL
    apt update
    apt remove docker docker-engine docker.io
    apt install -y git \
    vim \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common

    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
    add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"
    apt install -y docker-ce
    systemctl enable docker

    curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    chmod +x /usr/local/bin/docker-compose
    usermod -a -G docker vagrant
  SHELL
end

こちらのvagrantfileを使えば、最初っからdocker, docker-compose, gitがインストールされた環境が立ち上げられるので、以降のチュートリアルはこちらの環境で実行してください。

vagrantfileを書き換えたら、以下を実行して仮想環境を立ち上げましょう。

C:\Vagrant\ubuntu-20> vagrant up

しばらく時間がかかります。 お茶でも飲みながらしばし待ちます。🍵
… 環境が立ち上がったら以下のコマンドで仮想環境にssh接続しましょう。

C:\Vagrant\ubuntu-20> vagrant ssh

② VSCodeで仮想環境のコードを編集できるようにする

VSCodeの拡張機能を使うと、仮想環境にssh接続してファイルの内容などをかんたんに編集することができます。

(仮想環境にVimがインストールされているので、仮想環境で直接編集するという方はこの手順はすっ飛ばしてしまっても大丈夫です。)

インストールするVSCodeの拡張機能はRemote – SSHです。

①インストールしたら、Vagrantを立ち上げているディレクトリで下記コマンドを実行。

C:\Vagrant\ubuntu-20> vagrant ssh-config

②vagrant環境のssh接続情報が表示されるので、これをまるっとコピーしておきます。

③VSCodeの一番左端にある”><“こんなボタンを押下します。

④プルダウンの中からConnect to Host -> Configure SSH Hostsの順番で押下します。

⑤ホストPCのssh接続を定義しているファイル(WindowsならC:\Users\~~~.ssh\config)に、コピーしておいたテキストをそのままペタっと貼り付けます。

これで準備は完了です。

“><“のボタンを押下し、プルダウンからConnect to Hostを選択し、続いてvagrant ssh-configで出てきたHost名を選択すればvagrant環境に接続できるはずです。

Apacheコンテナを作る

①コンテナの起動

まずは定番のApacheコンテナの立ち上げから。

下記のコマンドを実行してみましょう。

vagrant@vagrant:~$ mkdir apache
vagrant@vagrant:~$ cd apache
vagrant@vagrant:~/apache$ docker run -d -p 8080:80 httpd

上記Dockerコマンドの内容は下記のとおりです。

コマンドコマンドの概要
docker runDockerコンテナ起動のコマンド
-ddetached modeでコンテナを起動します。 これをしないとターミナルの制御がコンテナの標準入力にバインドされてしまう。
-p 8080:80コンテナの80番portをホストOS(ここではUbuntu)の8080番ポートにバインドする。
httpddocker run で起動するコンテナのイメージ

つまり、httpdというイメージのコンテナをホストOSの8080番ポートにバインドして立ちあげるコマンドです。(コンテナ側の80番ポートはhttpdデフォルトのポートです。)

この状態で、ブラウザから192.168.33.10:8080にアクセスすると、“It works!”という文言が出ているはず。

②コンテナの状態を確認する

続いて、docker ps -aコマンドでDockerのプロセスを確認しましょう。

(-aは停止中のプロセスも表示するためのオプションです。)

CONTAINER ID   IMAGE                     COMMAND                   CREATED          STATUS                      PORTS                                   NAMES
8d746d462d73   httpd                     "httpd-foreground"        35 minutes ago   Up 35 minutes               0.0.0.0:8080->80/tcp, :::8080->80/tcp   tender_keldysh

httpdのコンテナがUpになっていますね。

これで、Apacheのコンテナを立ち上げることができました🎉

ところで、コマンドを実行すると、下記のような文言が表示されたのに気づきましたでしょうか。

Unable to find image 'httpd:latest' locally
   ...

Dockerはdocker run時にローカルOSに該当のイメージがない場合、自動的にダウンロードしてきてくれます。 そのため、docker pullコマンドなどを使用して明示的にイメージを取得せずとも、コンテナを立ち上げることができたのですね。

下記コマンドで確認してみましょう。

vagrant@vagrant:~/apache$ docker images
REPOSITORY              TAG          IMAGE ID       CREATED         SIZE
httpd                   latest       c8ca530172a8   6 days ago      138MB

一度コマンドをたたいてイメージをローカルにダウンロードすれば、2回目以降はローカルのイメージを使用してコンテナを立ち上げるため、コンテナの立ち上げが早くなります。

③コンテナを停止する

続いて、作成したコンテナをストップさせます。

下記コマンドを実行しましょう。

vagrant@vagrant:~/apache$ docker stop tender_keldysh

上記で「tender_keldysh」となっているdocker stopの最後の引数にはストップするコンテナの名前(docker psした際の一番右端の文字列)を指定します。

コンテナ名は、デフォルトではDockerがランダムに生成してくれます。各環境ごとに異なりますのでご注意ください。

改めて、プロセスを確認しましょう。

vagrant@vagrant:~/apache$ docker ps -a
CONTAINER ID   IMAGE                     COMMAND                   CREATED          STATUS                      PORTS     NAMES
8d746d462d73   httpd                     "httpd-foreground"        42 minutes ago   Exited (0) 3 seconds ago              tender_keldysh

プロセスが終了していることが確認出来たら、コンテナを削除しましょう。 こちらも、最後の引数にはコンテナの名前を指定してあげます。

vagrant@vagrant:~/apache$ docker rm tender_keldysh

Rubyコンテナを作る

①イメージを作成する

続いては、プログラムを実行することのできる環境を立ち上げてみます。

下記のコマンドを実行してください。

vagrant@vagrant:~/apache$ cd
vagrant@vagrant:~$ mkdir ruby
vagrant@vagrant:~$ cd ruby
vagrant@vagrant:~/ruby$ touch Dockerfile

作成したDockerfileの中身は下記のように記述します。

Dockerfile

FROM ruby:3.0
WORKDIR /app/src
COPY . .

上記Dockerfileは、いわば作成するRubyコンテナの設計図です。

各コマンドについては下記のような役割になっています。

キーワードキーワードの概要
FROM使用するイメージ名
WORKDIRコンテナ立ち上げ時のワーキングディレクトリ
COPYホストのディレクトリ・ファイルをコンテナにコピーする

COPYはイメージ作成時のホストのディレクトリ・ファイルを丸ごとコンテナにコピーするキーワードです。

今回の場合”COPY . .”なので、ホストの~/ruby/以下のファイルをすべてコンテナの/app/src/以下にコピーすることになります。

続いてコンテナ内にコピーするrubyファイルを作成します。

vagrant@vagrant:~/ruby$ touch hello.rb
vagrant@vagrant:~/ruby$ echo puts \"hello\" >> test.rb

ファイルを用意したら下記のコマンドでコンテナのイメージを作成します。

vagrant@vagrant:~/ruby$ docker build -t ruby_local .

docker buildは、Dockerfileの内容をもとにイメージを作成するコマンド。 また、-tオプションを付与することでイメージにタグ付けをすることができます。

docker imagesコマンドで確認すると、以下のようなイメージが作成されているはず。

REPOSITORY              TAG          IMAGE ID       CREATED          SIZE
ruby_local              latest       64c489f0a10c   3 minutes ago    881MB

②コンテナを立ち上げる

作成したイメージは、タグ名を指定して、公式のイメージと同様にコンテナ化することができます。

vagrant@vagrant:~/ruby$ docker run -dit --rm --name ruby ruby_local

少し長いコマンドになりましたね。

新出のオプションをそれぞれ確認します。

コマンドコマンドの概要
-dit-i, -d, -tオプションの複合 
–rmコンテナストップ時に自動で削除
–name <任意のコンテナ名>コンテナ名を割り当てる

-ditオプションは個人的にはセットでよく使うオプションです。

-d, -i, -tオプションの複合で、コンテナをdetachedモードで起動しつつも、標準入力を起動したままにしてくれます。

-itをつけずに-dのみでこのコンテナを立ち上げると、立ち上げたとたんに標準入力のストリームが閉じて、コンテナがストップしてしまいます。

-rmオプションはdocker stop でコンテナをストップした際に自動でコンテナの削除まで行ってくれるオプションです。

–nameオプションは文字どおりコンテナ名を任意に付与するオプションです。

では、立ち上げたコンテナにアクセスします。

vagrant@vagrant:~/ruby$ docker exec -it ruby bash

docker execコマンドは起動中のコンテナでコマンドを実行するために使用します。

上記コマンドでは、「rubyという名前のコンテナでシェルを起動する」ことができます。

起動したコンテナ内にはすでにrubyがインストール済み。また、Dockerfileの設定でホストのファイルをコピーしているので、下記コマンドでrubyのスクリプトを実行することができます。

root@ad771699324e:/app/src$ ruby hello.rb

“hello”という文字列が表示されましたね!

このように、Dockerを使用するとホストOSにrubyをインストールしなくても、コンテナ内でインストールしたrubyを使用することができるので、ホストの環境を汚さずに済みます。

③コンテナ内のファイルを編集する

ただし、現状のままでは問題点がひとつ。

VSCodeでhello.rbを開き、先ほどのコードを変更します。

hello.rb
puts "successfully built a docker container"

この状態で再びruby hello.rbを実行してみましょう。 どうなるでしょうか??

hello.rb
hello

..

編集前の”hello”が表示されてしまいましたね…。

Dockerでは、ホストOSのファイルシステムからコンテナ内のファイルシステムは独立しています

そのため、イメージを作成した段階でコピーしたhello.rbは、(ホストOSから独立したファイルシステムに存在しているため、)ホストOSからは編集できません。

コンテナ内で以下のコマンドを実行すると、ちゃんと変更されます。

root@ad771699324e:/app/src$ rm hello.rb
root@ad771699324e:/app/src$ touch hello.rb
root@ad771699324e:/app/src$ echo puts \"successfully built a docker container\" >> hello.rb
root@ad771699324e:/app/src$ ruby hello.rb

ちゃんと“successfully built a docker container”が表示されましたか?

④コンテナを削除する

コンテナ内のファイルの変更ができましたが、まだ問題は解決していません。

コマンドを実行しコンテナを離脱&停止しましょう。 –rmオプションを付けているので、コンテナは停止後は自動的に破棄されます。

root@ad771699324e:/app/src$ exit
vagrant@vagrant:~/ruby$ docker stop ruby

そして、再度コンテナを立ち上げ、bashを起動します。

vagrant@vagrant:~/ruby$ docker run -dit --rm --name ruby ruby_local
vagrant@vagrant:~/ruby$ docker exec -it ruby bash

ruby hello.rbを実行するとどうでしょうか…?

表示されたのは、一番最初に作成した際の“hello”のままだったはずです…。

すでに記載したように、Dockerのコンテナ起動時にコンテナ内に存在するのファイルはイメージ作成時のファイルのみです。

そのため、たとえコンテナ内で編集した内容であったとしても、一度コンテナを削除してしまうとその内容は失われてしまうのです…

このままだと不便ですね。

ということで、この問題を解決する手段が「Volume」というシステムです。

次回はこの「Volume」に着目した内容でお届けしますので、ぜひご覧ください!(サンプルのアプリケーションも用意しています🎉)