Help us understand the problem. What is going on with this article?

超基礎からの 速習 Docker (1)

本稿は、Christffer Noring さん (@chris_noring) の Learn Docker, from the beginning を翻訳し、分かりやすいように少しだけ追記、サンプルコードの実行上の補足等を行ったものです。Docker について教科書的な使い方を実際に実行して確認しつつ、Docker Composeを使ったオーケストレーションの基礎までを導いてくれます。今、Docker で注目されるのは、コンテナ同士が連携する、オーケストレーションです。本稿ではオーケストレーションの最も基礎となる Dokcer Compose までですが、オーケストレーションを活用するための基礎的部分の考え方を実地を以ってしっかりと理解できます。その意味で、原題は「from the beginning」ですが、初心者向けと言うよりも、今現在進行形の技術トレンドをキャッチアップするための実践的な基礎作りと言うニュアンスとして「超基礎からの」と冠を付けてみました。初心者向けの Docker 本でなんとなく概要は分かったけど、どうも活用の仕方が分からない、オーケストレーションって何なのか分からない・・と言う皆様に、次のステップへの一助となれれば幸いです。

超基礎からの 速習 Docker シリーズ一覧

今や Docker に関するたくさんの記事が出ていますが、実際何が起きているのか、きちんと解説出来ているものがありません(なお、これは私の印象に過ぎないので、反論は自由ですよ:smiley:)。私は自身の理解のためにたくさんの記事を書いてきていますし、それ自体楽しんでいます:smiley:。あなたにとってもこれらが役に立てば良いなと思います。
本稿では、みなさんが他では得られないであろうぐらい比較的深く掘り下げることにしました。TLDR、これが「超基礎からの 速習 Docker」の一番最初で、基本的事項と、君が Docker を使うべき理由について説明します。

この記事に書かれていることは Docker のちょうど出発点です。前提知識も何も要りません。Enjoy:smiley:

Whale, Docker

本稿では、以下のトピックをカバーしています。

  • なぜ?なに? Docker これはもしかすると一番大事なパートかも知れません。なぜ Docker なのでしょう?なぜ他のテクノロジーや現状維持ではないのでしょう?Docker とは何か、Docker の構成についてご説明します。
  • Docker の実行 アプリケーションを実際に Docker 化して、僕らが理解して、Docker を作るコア・コンセプトを使えるようにするショーケースにします。
  • セットアップの改善 僕らはそのソリューションを Static な変数に依存するべきではありません。僕らのアプリの中から読みだすことのできる環境変数を使うことが可能です。
  • コンテナ―の管理 コンテナー化して実行することはまずまず簡単です。でも、どのように管理するかについても見てみましょう。僕らはコンテナーを永遠に動かしたいとは思わないでしょうね。非常に軽いことでも、作業が追加となり、他で使いたい Port をブロックしてしまいます。

これはシリーズ一番最初のパートだってことをお忘れなく。Volume や Link、マイクロサービス、そしてオーケストレーションといったことにも探求していくことになるだろうけど、それは将来のパートでカバーすることになります。

リソース

Docker を使う事、コンテナー化は、一枚岩をマイクロサービスに分解していくことです。このシリーズのいたるところで僕らは Docker やそのコマンド体系をマスターするために学ぶことになります。そうすれば、きっと君は自作のコンテナーをプロダクション環境で使いたくなるでしょう。その環境は大抵クラウド上にあります。十分な Docker 経験を積んだと思ったなら、次のリンクで Docker をクラウドでどのように活用できるか、ご確認してみると良いと思います。

  • Azure 無料アカウントのサインアップ プライベート レジストリのようなクラウドのコンテナーを使うには、無料 Azure アカウントが必要でしょう。
  • クラウドのコンテナー クラウドのコンテナーについて他に知っておくべきことについて網羅する概要ページです。
  • 自作コンテナーをクラウドにデプロイ 今の Docker スキルをレバレッジしてクラウド上でサービスを動かすことがいかに簡単かを示すチュートリアル。
  • コンテナー レジストリの作成 自作 Docker イメージを Docker Hub に入れられますが、クラウドのコンテナー レジストリも可能です。自作イメージをどこかにストアして、一瞬でレジストリから実際のサービスをできるようにすることは凄くないですか?

Docker なぜ?なに?

Docker は再生可能な環境を提供します。特定の OS、異なるライブラリーの正確なバージョン、異なる環境変数、その他変数をそこに特定することができます。重要なことは、その分離された環境の中でアプリケーションを実行できることです。

さて、なぜ僕らはそうしたいのでしょうか?

  • オンボーディング プロジェクトで新しいデベロッパーをオンボードするときは、SDK のインストール、開発ツール、データベース、パーミッションの設定、などなど、いつもセットアップすることがたくさんあります。このプロセスは丸一日から二週間にも及びます。
  • 環境を同じにする Docker を使うことで、DEV、STAGING、PRODUCTION に至るまで環境を同じにすることができます。Docker / コンテナー化 以前、似たような環境を持つことは出来ても、小さな違いがあり、君がバグを見つけると、バグの原因を追いかけることに大きな時間を費やす羽目になった。時にバグはソースコードの中にあり、時にバグは切り分けに大きな時間を必要とする環境の違いの中にあったりします。
  • 自分のマシンで動く 上記に似ていますが、Docker が分離したコンテナーを作るため、君がどれだけ細かく特定したとしても、君はそのコンテナーを客先に届けて、君の開発マシンで行ったことを正確に同じ方法で実行することができます。

それってなに?

OK、なぜ Docker を調査するべきか、僕らは大きな理由を先に書いたけど、Docker の実際何なのか、更にダイブして行こう。Docker は OS のような環境と、アプリとそれに必要な変数を指定できることを説明したけど、他に Docker について何か知ることはあるだろうか?

Docker は君のアプリケーションを動かすのに必要なすべてを持っているコンテナーと呼ばれるスタンドアロン パッケージを作ります。各コンテナーは自身の CPU、メモリー、ネットワーク リソースを取得し、特定の OS やカーネルに依存しません。こう言って最初に思いつくのは Virtual Machine ですが、Docker はリソースをシェアする方法が異なります。Docker はコンテナーに一般的なパーツをシェアすることを可能にする Layerd File System を利用し、結果として、Virtual Machine よりもリソースを消費しないコンテナーとなります。

要するに、Docker コンテナーは、君が書いたソースコードを含めて、アプリケーションを実行するのに必要なすべてを持っています。コンテナーはまた、分離されたセキュアなライト・ウェイト ユニットでもあります。このことが、同じ OS でも、異なるプログラミング言語で書かれたり、同じライブラリの異なるバージョンを利用している複数のマイクロサービス生成を可能にしています。

もし、Docker がどのように動くか正確に知りたい場合は、次のリンクを見てください。layerd file sytem、ライブラリ ranc、そして、wikipedia Docker 概要の個所。

Docker の実行

OK、Docker とは何で、どんな良いことがあるか説明してきました。最終的に自作のアプリケーションを実行するものはコンテナーと呼ばれることが分かりましたね。しかし、どうやって作るのでしょう?それには、まず Dockerfile と言うファイルを書くことから始めます。そこには、OS、環境変数、そしてアプリケーションをどのように取得するかと言った必要な情報すべてを記載します。

もうすぐ僕らは最も深いとこまで着きますよ。アプリをビルドして、Docker化し、外とは隔離され、開かれた Port によってのみ通信するコンテナーの中で、アプリを実行するのです。

それには以下のステップとなります。

  • アプリケーションの作成 今回、REST API として動く Node.js Express アプリケーション を作ります。
  • Dockerfile の作成 Docker が自作アプリケーションをどうビルドするかを記述したテキスト ファイルをつくります。
  • イメージをビルド アプリケーションを実行する前段として、最初に Docker イメージと呼ばれるものを作ります。
  • コンテナーの作成 アプリを実行する最終ステップとなります。Docker イメージからコンテナーを作成します。

訳注
以下、Docker で実際にコマンドを実行しながらの確認が出来ますが、筆者の環境は Mac のようです(ちゃんと書かれてないけど)。訳者によって Mac / Windows 両方で実行確認していますので、必要に応じて補足していきます。実行には、Node.js 及び Docker Desktop のインストールが必要です。Docker Desktop のインストールは少し追加の設定が必要ですので、こちらなどを参考にインストールください → Windowsの場合 / Macの場合
特に指定がなければ、Mac の場合はターミナル、Windows の場合は、PowerShell を使ってください。あ、PowerShell は PowerShell v7 以降 をご利用ください。default では v5 だと思います。

自作アプリの作成

Express Node.js プロジェクトを作ります。ファイル構成は以下の通りとなります:

  • app.js REST API コードになります。
  • package.json プロジェクトのマニフェスト ファイルです。ここでは、Express と言った依存関係だけでなく、スクリプトの開始といったものも宣言します。
  • Dockerfile 自作アプリをどのように Docker化するかを記述します。

package.json を作成するには、任意のプロジェクトフォルダに、以下のコマンドを打つだけです。

npm init -y

その上で、express ライブラリといった依存関係ファイルを、以下のように打ってインストールします。

npm install express --save

コードを追加しましょう

package.json の生成、依存関係ファイルもインストールしたら、自作アプリを実行するのに必要なコードの追加です。以下のように app.js へコードを追加してください:

app.js
const express = require('express')

const app = express()

const port = 3000

app.get('/', (req, res) => res.send('Hello World!'))

app.listen(port, () => console.log(`Example app listening on port ${port}!`))

自作アプリの実行には以下のようにコマンドをタイプします。

node app.js

ウェブ ブラウザ―で http://localhost:3000 とすれば、以下のように表示されます。

Hello World!

OK、動いてますね。よくできました:smiley:

Dockerfile の作成

次のステップは Dockerfile の作成です。このファイルはマニフェストとして動作しますが、どのように自作アプリがビルド、実行されるかと言うビルド指示でもあります。OK、ビルドと実行には何が必要でしょう?僕らが必要なのは:
- copy すべての app ファイルを docker container に入れます。
- install express ライブラリのような依存関係ファイル。
- open up a port 外からコンテナーの中にアクセスできるようにします。
- instuct 自作アプリの開始する方法

更に複雑なアプリケーションでは、環境変数の設定、データベースのクレデンシャル、データベースのシード データなども必要です。今回、僕らが必要な銃弾リストは上記リストにあるだけ。Dockerfile で express を試してみましょう。

Dockerfile
FROM node:latest

WORKDIR /app

COPY . .

RUN npm install

EXPOSE 3000

ENTRYPOINT ["node", "app.js"]

上記各コマンドについて説明していきましょう。

  • FROM Docker Hub からの OS イメージの選択。Docker Hub は、私たちが pull できるイメージを持っているグローバル リポジトリです。今回、僕らは node と言う名前の Node.js がインストールされている Ubuntu ベースのイメージを選択しています。:latest タグで最も新しいバージョンを指定しています。
  • WORKDIR ワーキング ディレクトリーの指定です。以降に示すコマンドのカレント ディレクトリに効果があります。
  • COPY ディレクトリーから、コンテナーの WORKDIR で指定したディレクトリーにファイルをコピーします。
  • RUN Node.js express アプリケーションを実行するのに必要なすべてのライブラリをインストールするため、ターミナルでコマンドを実行します。
  • EXPOSE 僕らが作るコンテナーと通信するための Port を指定します。
  • ENTRYPOINT どのようにアプリケーションを開始するかを示します。["node", "app.js"] のように配列で示すと、node app.js と解釈されターミナルで実行されます。

Quick Overview

OK、僕らのプロジェクトで必要なファイルはすべて揃いました。こんな感じになるはずです:

app.js // 自作 express app
Dockerfile // Docker が読み込む指示ファイル
/node_modules // run npm install を実行するとできるディレクトリー
package.json // npm init を実行すると生成されるファイル
package-lock.json // NPM からライブラリをインストールすると生成される

イメージのビルド

自作アプリケーションをビルドしてコンテナー内で実行するためには2つのステップがあります:
- イメージの作成 Dockerfile で指示された内容を docker build コマンドでイメージを作成します。
- コンテナーの開始 上記で作成されたイメージから、コンテナーを作る必要があります。

一番最初の最初に、以下のコマンドでイメージを作成しましょう。
docker build -t chrisnoring/node:latest .
以上の指示でイメージが生成されます。最後の . は重要です。僕らの Dockerfile がどこにあるか、今回はカレント ディレクトリー にあることを示しています。もし FROM コマンドで指定する OS イメージを僕らが持っていない場合、Docker Hub から pull して、イメージを作成します。

ターミナルにはこんな風に表示されることでしょう:

% docker build -t chrisnoring/node:latest .
Sending build context to Docker daemon  2.008MB
Step 1/6 : FROM node:latest
latest: Pulling from library/node
99760bc62448: Pull complete 
e3fa264a7a88: Pull complete 
a222a2af289f: Pull complete 
c1f89293f045: Pull complete 
115b6fc5ace1: Pull complete 
9eb516295c24: Pull complete 
d23358c1492a: Pull complete 
08d6736f797a: Pull complete 
3dcecd6cc67a: Pull complete 
Digest: sha256:101d1d7ba7562fcb36b23eeff46607107802f1a439a571d86cf490cf9fe2150e
Status: Downloaded newer image for node:latest
 ---> a511eb5c14ec
Step 2/6 : WORKDIR /app
 ---> Running in b2e78f622519
Removing intermediate container b2e78f622519
 ---> 2db1af3bb5bb
Step 3/6 : COPY . .
 ---> 1f59d1ad443f
Step 4/6 : RUN npm install
 ---> Running in 74c2a6423aad
npm WARN nodejs@1.0.0 No description
npm WARN nodejs@1.0.0 No repository field.

audited 126 packages in 1.06s
found 0 vulnerabilities

Removing intermediate container 74c2a6423aad
 ---> 46e3f497d0d9
Step 5/6 : EXPOSE 3000
 ---> Running in 5179b2faa286
Removing intermediate container 5179b2faa286
 ---> 9136884bf44b
Step 6/6 : ENTRYPOINT ["node", "app.js"]
 ---> Running in 21ae8fe96482
Removing intermediate container 21ae8fe96482
 ---> b709d5fe6392
Successfully built b709d5fe6392
Successfully tagged chrisnoring/node:latest

OS image node:latest が Docker Hub から pull され、WORKDIRRUN といったコマンドが実行されています。注目に値するのは、各ステップ後に中間コンテナーを削除するやり方です。Docker がスマートに異なるファイル レイヤーをキャッシュするので、より高速になっています。最後にすべての構築が成功したことを successfully build と示しています。それでは作成されたイメージを見てみましょう:
docker images

% docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
chrisnoring/node    latest              b709d5fe6392        35 minutes ago      943MB

イメージが出来ていますね。成功しました:smiley:

コンテナーの作成

次のステップは、イメージからコンテナーを構築することです。コンテナーは自作アプリを中で動かす分離されたピースです。docker run を使うことでコンテナーをビルドします。コマンドは以下のように行います:
docker run chrisnoring/node
これでは十分ではありません。 内部からの Port 番号と外部から(hostから)の Port 番号をマップする必要があります。このアプリは、ブラウザーでアクセスするアプリだったことを思い出してください。以下のようにして -p フラグでマッピングします。
-p [external port]:[internal port]
今回の場合、以下のようにコマンドを入力します。
docker run -p 8000:3000 chrisnoring/node

訳注
上記コマンドを入力する前に、すでに docker run chrisnoring/node を走らせてしまっていると、余計なコンテナーが動いてしまっているので、コンテナーの管理で後述する通り docker ps -a で コンテナーの ID を取得の上、docker stop docker rm でコンテナーを削除します。

OK、このコマンドを走らせると、http://localhost:8000 でコンテナーに訪問できるということです。8000 はコンテナー内部 3000 番を 8000 番にマップした外部 Port です。さて、ブラウザーで開いてみましょう:

Containerized app in the browser

皆さん、コンテナーが動いてますよ:smile:

High five, we did it

環境変数を使ってセットアップを改善

OK、Docker イメージをどうやってビルドするか、どうやってコンテナーを実行するか、それによってその中のアプリをどうやって実行するかについて学びました。ですが、PORT の扱いはもう少しだけナイスにできます。現段階では、Dockerfile に書かれた Port 番号と express サーバー を起動する Port 番号が一致することを app.js のソースで確認する必要があります。

これを修正するために、環境変数を紹介したいと思います。二つやることがあります。
- 追加 Dockerfile の環境変数を追加します。
- 読む app.js から環境変数を読み込みます。

環境変数を追加する

環境変数を追加するために、ENV コマンドを以下のように使います:
ENV PORT=3000
Dockerfile を次に示すように追加しましょう。

Dockerfile
FROM node:latest

WORKDIR /app

COPY . .

ENV PORT=3000

RUN npm install

EXPOSE 3000

ENTRYPOINT ["node", "app.js"]

もう一つ、固定値を変数化するよう EXPOSE を以下のように更新します:

Dockerfile
FROM node:latest

WORKDIR /app

COPY . .

ENV PORT=3000

RUN npm install

EXPOSE $PORT

ENTRYPOINT ["node", "app.js"]

EXPOSE コマンドのパラメータを \$PORT に変更している点に注目してください。変数は \$ を付ける必要があります。
EXPOSE $PORT

App.js から環境変数を読み込む

Node.js で環境変数を読み込む場合、以下のように行います:

app.js
const express = require('express')

const app = express()

const port = process.env.PORT

app.get('/', (req, res) => res.send('Hello World!'))

app.listen(port, () => console.log(`Example app listening on port ${port}!`))


app.js または Dockerfile を変更するとき、イメージをリビルドする必要があります。これは docker build をもう一度実行し、docker stopdocker rm を使って、コンテナーを落とさなければならないことを意味します。より詳細は次のセクションでお伝えします。

コンテナーの管理

OK、docker run を始めようとして、コンテナーがシャットダウンできないことに気が付いたですか?パニックになるね:wink: 新しいターミナル(訳注:Windows の場合は PowerShell)を開いて、以下のようにします:
docker ps
これで、実行中のコンテナーの名前と ID が一覧できます:

% docker ps   
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
cceed5dcd898        chrisnoring/node    "node app.js"       About an hour ago   Up About an hour    0.0.0.0:8000->3000/tcp   beautiful_curie

CONTAINER_ID または NAME カラムの値を使うことで、以下のようにコンテナーのストップできます:
docker stop cce
CONTAINER_ID を使ってますが、最初の3数字だけ(この例では cce)で実行できます。

訳注
docker stop では、コンテナーが「停止」した状態ですので、削除はされていません。完全に削除するためには 、これに続けて docker rm cce などとします。
また、この節で解説した Dockerfile と app.js の変数化を試すには、もう一度イメージの作成 docker build -t chrisnoring/node:latest . を行った上で、docker run(以下省略)する必要があります。

Daemon モード

以上で示した別々のターミナル(訳注:Windowsの場合は PoserShell)を開くわけですが、Daemon モードで実行した方が良いでしょう。このモードはコンテナーをバックグラウンドで実行し、出力を見えなくするモードのことです。このモードにするには、単純に -d フラグを付けます。以下試してみましょう。

% docker run -d -p 8000:3000 chrisnoring/node
6a43d94dfa397793438d6187b7d8e83c70fad3203548159265954eb0724fe409

コンテナー ID が戻ってきます。コンテナー ID はこれまでも出てきたものと同一です。止めたい時はこの ID を使って docker stop 6a4 と、最初の3数字を指定すれば良いので簡単です。

Interactive モード

Interactive モードは、面白いです。これは実行中のコンテナーに入り、ファイルをリストしたり、追加・削除したり、例えば bash でできることを実行できます。今回、以下のようにして docker exec コマンドを以下のように行います:

% docker exec -it 6a4 bash
root@6a43d94dfa39:/app# ls
Dockerfile  app.js  node_modules  package-lock.json  package.json
root@6a43d94dfa39:/app# 

上記コマンドは:
docker exec -it 6a4 bash
コンテナーが実行中でなければなりません。すでにストップしている場合は、docker start 6a4 としてスタートしてください。6a4 には docker run で戻ってきた値に置き換えてください。

訳注
docker rm を用いて削除までしている場合は、docker run(以下略) からやり直してください。なお、コンテナー ID は、前述の通り、別ターミナル(またはPowerShell)から docker ps -a としても取得できますね。

6a4 は、コンテナー ID の最初の3数字、-it は Interactive モード、最後の bash は bash shell を実行することを意味します。

ls のようなコマンドにすることもできます。一度 bash shell を起動すれば、コンテナー内をリストしたりできます。ビルドが上手くいっているかどうか確認したり、デバッグしたりするにも良い方法です。

もし、node コマンドのように、コンテナー内で何か実行したいのであれば、例えば、以下のようにタイプします。
docker exec 6a4 node app.js
これは、コンテナー内で、node app.js が実行されます。

Docker kill vs Docker stop

ここまで、コンテナーを止めるために docker stop を使ってきましたが、止めるには docker kill を使う方法もあります。違いは何でしょうか?

  • docker stop このコマンドでは、SIGTERM シグナルを SIGKILL の然るべき前に送っています。要するに、コンテナーを落とす際にリソースの開放、状態の保存を行う、上品なやり方です。
  • docker kill 直ちに SIGKILL を送ります。このことは、意図的に、リソースの開放や状態の保存を行わないことを意味します。開発では、この二つのコマンドのどちらかを使うことは大きな問題ではありませんが、プロダクション環境下では、docker stop に頼る方が賢いかも知れません。

クリーンアップ

このコースで開発中に作成されたたくさんのコンテナーをクリーンアップするには以下のようにタイプします。
docker rm <id-of-container>

サマリー

OK、Docker を最初から体験しました。Docker を利用する基本的なコンセプトもカバーしています。更に、アプリを Docker 化するやり方を調査しつつ、使いやすい Docker コマンドをカバーしました。データベースや Volume、Link の仕方、複数コンテナーの起動、オーケストレーションといった、Docker には知るべきことがさらにあります。

しかし、この記事はシリーズになっています。どこかで止めないと記事が大きくなってしまいます。Volume とデータベースについて解説する次回パートにご期待ください。

謝辞

Docker に関する驚くべきコースを提供してくれた、Dan Wahlin Twitter に感謝します。あなたのコースのお陰で、Docker のたくさんのことについて私をクリックしてくれました。

Twitter をフォローして、トピックへのあなたの問い合わせやご質問、提案を頂けるとハッピーです。


超基礎からの 速習 Docker (2) に進む

Gucchiy
20年弱ソニーでソフトウェアエンジニアとして従事の後、セキュリティ系ベンチャー、日本IBM、アカマイを経て現在日本マイクロソフト。ソニーの古き良き組織を明らかにしたいと考え、組織論を研究。2011年慶應義塾大学修士号。エンジニアが良いアウトプットを出すスタイルをコミュニティ論やOSS、ハッカーなどから研究中
https://www.gucchiy.com
microsoft
マイクロソフトのメンバーが最新の技術情報をお届けします。Twitterアカウント(@msdevjp)やYouTubeチャンネル「クラウドデベロッパーちゃんねる」も運用中です。
https://aka.ms/MSFT-Docs-JPN
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした