とあるお兄さんの雑記

基本的に技術系の内容を書きますが、何を書くかは私の気分です。

統計学基礎vol.31~標本分散と不偏分散~

今回は標本分散と不偏分散です。

やはりエンジニア(といいつつ、最近はプログラムを書くことが多いのでプログラマーなのか?)の端くれなので、テック系の記事が受けますかね...?

標本分散

得られたデータの平均を \bar{x}、個々のデータを x_i (i = 1, 2, \dots, n)、サンプルサイズを n とすると、標本分散 \hat{\sigma} ^2

 \displaystyle \hat{\sigma} ^2  = \frac{1}{n} \sum_{i=1} ^{n} (x_i - \bar{x}) ^2

と表される。

ただし、標本分散は一致推定量ではあるものの、不偏推定量ではない。

コラム:一致推定量/不偏推定量とは

前回の記事で若干触れたといえば触れたのですが。
一言でいえば、サンプル数を増やしまくれば、ほぼ真の値を予測できるというものです。

反対に不偏推定量は、サンプルの数は一切関係なく、予測値の平均値は、真の値に一致するというものです。

不偏分散

標本分散の代わりに、標本分散の期待値が母分散に一致するように \displaystyle \frac{n}{n-1}をかけたもの。不偏分散 \displaystyle s ^2一致性と普遍性を持つ推定量である。

 \displaystyle s ^2  = \frac{n}{n-1} \hat{\sigma} ^2  = \frac{1}{n-1} \sum_{i=1} ^{n} (x_i - \bar{x}) ^2

ちなみに、標本分散と不偏分散の使い分けとしては、母集団すべてのデータを使って求めたものが標本分散となります。

一方、不偏分散は母集団のデータから無作為に取り出したデータから求めたものになります。

標準偏差

不偏分散の平方根のうち、正の数値

 \displaystyle s = \sqrt{\frac{1}{n-1} \sum_{i=1} ^{n} (x_i - \bar{x}) ^2 }

標準誤差

 standard errorとも書きます。
定量標準偏差で、標本から得られる推定量そのもののばらつき(精度)を表すもの。
一般的に標本平均の標準偏差を意味する。

 \displaystyle SE = \frac{s}{\sqrt{n}} = \frac{\sqrt{\frac{1}{n-1} \sum_{i=1} ^{n} (x_i - \bar{x}) ^2 }}{\sqrt{n}}

まとめ

用語 意味
標本分散  \displaystyle \hat{\sigma} ^2  = \frac{1}{n} \sum_{i=1} ^{n} (x_i - \bar{x}) ^2
不偏分散  \displaystyle \hat{s} ^2 = \frac{1}{n-1} \sum_{i=1} ^{n} (x_i - \bar{x}) ^2
標準偏差  \displaystyle s = \sqrt{\frac{1}{n-1} \sum_{i=1} ^{n} (x_i - \bar{x}) ^2 }
標準偏差  \displaystyle SE = \frac{s}{\sqrt{n}} = \frac{\sqrt{\frac{1}{n-1} \sum_{i=1} ^{n} (x_i - \bar{x}) ^2 }}{\sqrt{n}}

統計学基礎vol.30~推定と検定~

統計学の推定と検定です。

おおよその内容は統計Webさんの内容をかなり薄くしたものなので、しっかり学びたい方は統計Webさんの方でしっかり学びましょう!

bellcurve.jp

推定と検定

推測統計学の中には推定 検定が含まれる。ある母集団から標本を抽出した時に、

  • 推定:母集団を特徴づける母数(パラメータ)を統計学的に推測すること
  • 検定:母集団から抽出された標本の統計量に関する仮説が正しいかを統計学的に判定すること

を意味する。

点推定

平均値などの母数を1つの値で推定すること。

母数の値はこれだ!のようにたった一つに限定するイメージです。

区間推定

平均値などの母数をある区間で推定すること。

区間推定は点推定と異なり、母数の値はこの範囲からこの範囲くらいと多少余裕をもった推定をします。

定量

パラメータを推定するために利用する数値の計算方法や計算式

例.

 \displaystyle \overline{x} = \frac{1}{n} \sum_{i} x_iなど

推定値

実際に事項を行った結果から計算した値。 \muなど

一致性

 nが大きくなれば、推定量 \hat{ \theta }がだんだんと真のパラメータ \thetaに近づく性質。

 \displaystyle \forall \epsilon > 0, \lim_{n \to \infty} P(| \hat{ \theta } - \theta | > \epsilon) = 0

上記式は、 0よりも大きい任意の \epsilonがある時、 nが大きくなれば推定量 \hat{ \theta }と真のパラメータ \thetaの差が \epsilonよりも大きくなる確率は 0ということを言っています。

 \epsilon 0よりも大きいけど、限りなく 0に近い正の値だよ♪っていうのは数学界での暗黙の了解なんでしょうか?)

不偏性

定量の期待値がパラメータに一致すること。

 E [ \hat{\theta} ] = \theta

 nの値に関係なく、 \hat{\theta}の期待値が \thetaであること。

コラム:標本平均の性質

標本平均は一致推定量であり、不偏推定量である。そのため、標本平均の値を母平均の推定量として使うことができる。

まとめ

推定と検定の記事でした。と言いいつ、用語の説明で終わっちゃたんですが。

用語 説明
推定 母集団を特徴づける母数(パラメータ)を統計学的に推測すること
検定 母集団から抽出された標本の統計量に関する仮説が正しいかを統計学的に判定すること
点推定 平均値などの母数を1つの値で推定すること
区間推定 平均値などの母数をある区間で推定すること
定量 パラメータを推定するために利用する数値の計算方法や計算式
推定値 実際に事項を行った結果から計算した値。 \muなど
一致性  nが大きくなれば、推定量 \hat{ \theta }がだんだんと真のパラメータ \thetaに近づく性質
不偏性 定量の期待値がパラメータに一致すること

統計学基礎vol.29~中心極限定理~

今回の記事は中心極限定理です。

前回と今回と統計学的な内容が入ってきてテンション上がりますね!

中心極限定理

標本を抽出する母集団が平均 \mu、分散 \sigma ^2正規分布従う、従わないにかかわらず抽出するサンプルサイズ nが大きくなるにつれて標本平均の分布は正規分布\displaystyle N(\mu, \frac{\sigma ^2}{n})に従う。

中心極限定理は言ってしまえば上の通りなのですが、一回読んだだけで理解するには結構難しい定理です。私はなかなか理解できなかったのですが、ある本で中心極限定理をプログラムしたコードが載っており、それをみて初めて中心極限定理を理解できたような気がします。

というわけで、実際にプログラムを組んで理解を目指しましょう。また、今回の目標としては、母集団の分布が正規分布であろうが無かろうが、サンプルサイズ nが大きくなるにつれて、標本平均の分布が正規分布のようになグラフになることを確かめることとします(厳密な証明はしません)。

中心極限定理を確かめる

条件・環境など

今回使うのは前回に引き続きGoogle Colaboratoryです。
加えて、今回は 1  6までの目をもつサイコロとします。ちなみに、このサイコロはどの目が出る確率も同様に確からしいとします(勘の良い人は、この時点で母集団の分布が正規分布ではなく一様分布であることに気づいたはずです)。

この時、上記のサイコロを10回投げてその時の平均を記録するまでを1セットとし、このセットを n回ほど行うとします。今回のプログラムではこの nを次第に大きくしていき、その結果、だんだん正規分布のグラフになるかを確かめます。

プログラムコード

下記がプログラムコードです。中心極限定理と読み合わせがならプログラムを読むと理解できるかなと思います。

import random
import matplotlib.pyplot as plt

random.seed(1) #乱数の固定

n = 50 # サンプルサイズ n
mu = [] # i回目の標本平均を記録するための配列


for i in range(1, n+1):
  total = 0

  # 1~6のサイコロを1回投げそれの合計を計算
  for j in range(10):
    total = total + random.randint(1, 6) 

  # i回目の標本平均を記録
  mu.append(total / 10)

# ヒストグラムで表示
plt.hist(mu)

出力〜サンプルサイズを変えながら

サンプルサイズ nを変更しながらプログラムを実行した結果です。サンプルサイズを増やすほどに正規分布のようなグラフに近づくことが分かりますね。

サンプルサイズn = 50の時

f:id:kurasher:20210120202643p:plain
サンプルサイズ n = 50の時

サンプルサイズ n = 100の時

f:id:kurasher:20210120202711p:plain
サンプルサイズ n = 100の時

サンプルサイズn = 1000の時

f:id:kurasher:20210120202734p:plain
サンプルサイズ n = 1000の時

サンプルサイズn = 10000の時

f:id:kurasher:20210120202748p:plain
サンプルサイズ n = 10000の時

まとめ

まとめです。大数の法則中心極限定理ときてやっと統計学らしくなってきましたね。

言葉 意味
中心極限定理 標本を抽出する母集団が平均 \mu、分散 \sigma ^2正規分布従う、従わないにかかわらず抽出するサンプルサイズ nが大きくなるにつれて標本平均の分布は正規分布\displaystyle N(\mu, \frac{\sigma ^2}{n})に従う

おまけ

私のノートになぜか統計Webさんの練習問題2が書いてありました(なんでや?)。ついでなので、書いておきます。

練習問題2

問題


平均 \mu、分散 \sigma ^2のある分布に従う、互いに独立な n個の確率変数 X_i(i = 1, 2, \dots, n)がある。 Y Zを次のように定義するとき、分散 V [ Y ] V [ Z ]を求めよ。

 \displaystyle Y = \sum_{i=1} ^{n} X_i

 \displaystyle Z = \frac{1}{n} \sum_{i=1} ^{n} X_i

解答

 Y = X_1 + X_2 + \dots + X_n

 V[ Y ] = V[ X_1 + X_2 + \dots + X_n ]

 X_iは互いに独立より

 V[ Y ] = V[ X_1 ] + V[ X_2 ]+ \dots + V[X_n ]

 = n \sigma ^2

 \displaystyle V[ Z ] = V[ \frac{1}{n} Y ]

 \displaystyle = \frac{1}{n ^2} V[ Y ]

 \displaystyle = \frac{\sigma ^2}{n}

なぜ、この問題だけノートに書いていたのか...?

統計学基礎vol.28~大数の法則~

最近、なんで人って写真なんか撮るんだろうか、インスタ目当てか?なんて考えていたんですが。

聞いた話であれば人って楽しい出来事よりも悲しい出来事の方が記憶に残りやすいそうで。

ということを考えると、楽しい思い出を残しておくのは自然だなぁと思うようになりました。はい。


あ、今回の記事は統計学です。

対数の法則

確率 pで起こる事象において、試行回数を増やすほどその事象が実際に起こる確率は pに近づく。

 1~6の数字が書いてある普通のサイコロがあるとします。このサイコロのそれぞれが出る目は同様に確からしいとします。
ここまで考えると 1の目が出る確率は \displaystyle  \frac{1}{6}になるはずです、理論上は。

ここで、仮にサイコロを10回投げて 1の目が出た回数が 1回だったとすると、\displaystyle \frac{1}{10}になります。
さらに回数を増やし、20回投げたときにtex: 1]の目が出た回数が 4回だと、\displaystyle \frac{4}{20} = \frac{1}{5}になります。
さらに回数を増やし、...としていくと、 1の目が出る確率は、理論上の\displaystyle \frac{1}{6}に近づくはず!

というのが大数の法則です。

というわけで実際にサイコロを投げてみましょう。

大数の法則を調べる

とはいえ、実際にサイコロを投げると途方もない時間がかかりますので、プログラムを組んでみましょう。言語はPythonです。今回はGoogle Colaboratoryを使ってみました。

Google Colaboratory

出た目が1の総数に対して、投げた回数 nで割った値を5回刻みで出力しています。理論値は\displaystyle  \frac{1}{6} = 0.16666666666666666です。

import random

n = 100

dice1 = 0
for i in range(1, n+1):
  if (random.randint(1, 6) == 1):
    dice1 = dice1 + 1

  if i % 5 == 0:
    print('{:>5}回目、p = {:}'.format(i, dice1 / i))


出力

    5回目、p = 0.2
   10回目、p = 0.2
   15回目、p = 0.2
   20回目、p = 0.2
   25回目、p = 0.16
   30回目、p = 0.16666666666666666
   35回目、p = 0.17142857142857143
   40回目、p = 0.175
   45回目、p = 0.15555555555555556
   50回目、p = 0.16
   55回目、p = 0.18181818181818182
   60回目、p = 0.18333333333333332
   65回目、p = 0.16923076923076924
   70回目、p = 0.17142857142857143
   75回目、p = 0.16
   80回目、p = 0.175
   85回目、p = 0.16470588235294117
   90回目、p = 0.17777777777777778
   95回目、p = 0.18947368421052632
  100回目、p = 0.18

出力を見ると、5回目よりも、100回目のほうが理論値に近いことが分かりますね(そこ、実は30回目がめっちゃ理論値に近くね?とか言わない)。

ちなみに統計Webさんでは

母平均が \muである集団から標本を抽出する場合、サンプルサイズが大きくなるにつれて、標本平均が母平均 \muに近づく

ということを言っています。

大数の法則1
大数の法則2

コラム:母平均と標本平均

母平均:母集団の平均のこと。一般的に \muで表されることが多い。
標本平均:標本の平均のこと。

母集団と標本についてはこちら
→ 統計学基礎vol.25~母集団と標本~

まとめ

用語 意味
対数の法則 確率 pで起こる事象において、試行回数を増やすほどその事象が実際に起こる確率は pに近づく
母平均 母集団の平均のこと
標本平均 標本の平均のこと

統計学番外編~研究デザイン~

この記事で紹介する内容は、常識的な生活を送っている限りは基本的にあまり出てきません。そのため、そこまで詳しい説明を行いません。

実験研究

研究対象に対して何らかの介入(投薬や治療)を行い、その結果を検証するための研究デザインのこと。

ランダム化比較実験(前向き研究)

実験群と対象群への割り付けをランダムに行い、介入の効果を調べる研究。バイアスの出現率が理論上では等確率になる。

クロスオーバー試験(前向き研究)

対象者を実験群、対象群の2群に分けて介入を行い、比較を行う。その後、一定の期間をあけてから実験群と対象群を入れ替えて、再度介入を行い比較を行う。

観察研究

研究対象に対して介入を行わず、観察によってデータを集め解析を行う研究デザイン。

横断研究

ある1時点において断面的調査を行い、要因と結果の関連を調べる研究。過去にさかのぼった調査や、未来に向かって調査を行うことはない。

例. 年齢と視力の関連に興味がある場合に、様々な年齢の人に対して視力検査を行う。

コホート研究(前向き/後ろ向き研究)

異なる特性を持つ対象集団(コホート)において、ある特性を持つ暴露群がある群と無い群にわけ、時間の流れに沿って疾患の発生や改善などを観察し、その特性/暴露と疾患との関係を調べる研究のこと。

ケースコントロール研究(症例対照研究(後ろ向き研究))

ある病気に罹患している群と対象群に対して、その病気に対して、病気の特性/暴露の有無との関係を調べる研究のこと。
発生頻度が稀な研究に対して広く使われる。

コラム:前向き研究と後ろ向き研究の違い

コホート:異なる特性を持つ複数の患者群

前向き研究:コホートを設定した後で、未来に向かって対象疾患がどのくらいの頻度で発生するかを観察する方法。(現在から未来にかけての研究)
後ろ向き研究:既に対象疾患が発生している患者集団について、過去のある時点でのコホートを同定し、現在に向かって各コホートでの罹患率を調べる方法。(過去から現在にかけての研究)

Dockerを学ぶ第5回~Docker Composeを使う~

HTC Advent Calendar 2020の25日目を担当します、kurasherです。

第5回はDocker Composeです。

環境

Windows 10 Pro バージョン 1903
Docker Toolbox
Dockerバージョン: 18.09.1
VirtualBox: 6.1.16

Docker Composeとは

通常、アプリケーションはデータベースやサーバーなど複数の組み合わせで出来ています。API単体で一つのアプリケーションは非常に珍しいです。
Dockerでは基本的に一つのコンテナに一つのアプリケーションを推奨しており、データベースならデータベースのコンテナ、サーバーならサーバーのコンテナのように分けられます。
これら複数のコンテナをDockerfileにまとめ一つずつ手動で起動していくのは非常に手間がかかり、途中人為的なミスも発生しかねません。

となると、複数のコンテナを同時に起動、停止、イメージの作成などを扱えないかという考えが浮かびます。この複数のコンテナを自動で同時に動かす方法が、Docker Composeです。

Docker-composeを試す

実際に複数のコンテナを立ててみましょう。
以下の記事を参考にします。参考にするというか、以下の記事の内容をそのままこちらに書いちゃってるんですが。

Docker Desktop で 開発用 Web アプリを起動してみよう

docker-compose.yml

docker-compose.ymlの説明は後にするとして、以下のように書きます。

version: "3.3"
services: 
  web:
    image: adminer
    container_name: adminer_con
    ports: 
      - 8080:8080

  db: 
    image: mysql
    container_name: mysql_con
    environment:
      MYSQL_ROOT_PASSWORD: password

Docker Toolbox使っている人向けのポートフォワーディング設定(該当者のみ)

Docker Toolbox使っている人向けのポートフォワーディング設定です。Docker Desktopを使っている人は関係ないため飛ばしていただいて大丈夫です。

今回もポートフォワーディングが必要なので、VirtualBoxを起動し、以下の図のようにRule2を設定します。設定方法は前回の記事を参考にしてください。

ポートフォワーディング設定方法

f:id:kurasher:20201219031439p:plain

docker compose起動

上記で用意したdocker-compose.ymlファイルがあるディレクトリで、下記のコマンドを実行します。

$ docker-compose up -d

-dはバックグラウンドで起動することを意味します。

今回はadminerというイメージとMySQLのイメージを取得して、コンテナを起動します。初めてイメージをpullしてくる場合、docker-compose up -dコマンドをたたくと以下のように出力されます。

$ docker-compose up -d
Creating network "test_default" with the default driver
Pulling web (adminer:)...
latest: Pulling from library/adminer
801bfaa63ef2: Pull complete                                                                                                                                                                          30e209609427: Pull complete                                                                                                                                                                          320f26ee9b1c: Pull complete                                                                                                                                                                          4612e05a72cf: Pull complete                                                                                                                                                                          a7d577b440bd: Pull complete                                                                                                                                                                          1f67ee0e7b40: Pull complete                                                                                                                                                                          b4c84ce97d3b: Pull complete                                                                                                                                                                          2e1a66d6657a: Pull complete                                                                                                                                                                          ea3ec22f9c6f: Pull complete                                                                                                                                                                          0ee36b42e0ff: Pull complete                                                                                                                                                                          418366333583: Pull complete                                                                                                                                                                          4f911d97b55e: Pull complete                                                                                                                                                                          0aa14ee37402: Pull complete                                                                                                                                                                          d34c5a5f3112: Pull complete                                                                                                                                                                          384e9dd44a6b: Pull complete                                                                                                                                                                          Pulling db (mysql:)...
latest: Pulling from library/mysql
6ec7b7d162b2: Pull complete                                                                                                                                                                          fedd960d3481: Pull complete                                                                                                                                                                          7ab947313861: Pull complete                                                                                                                                                                          64f92f19e638: Pull complete                                                                                                                                                                          3e80b17bff96: Pull complete                                                                                                                                                                          014e976799f9: Pull complete                                                                                                                                                                          59ae84fee1b3: Pull complete                                                                                                                                                                          ffe10de703ea: Pull complete                                                                                                                                                                          657af6d90c83: Pull complete                                                                                                                                                                          98bfb480322c: Pull complete                                                                                                                                                                          9f2c4202ac29: Pull complete                                                                                                                                                                          a369b92bfc99: Pull complete                                                                                                                                                                          Creating adminer_con ... done                                                                                                                                                                        Creating mysql_con   ... done  
$ 



複数(今回は2つ)のコンテナが起動しているかを確認してみます。以下のように出てくればコンテナが起動されているはずです。

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
b33dd61bca46        mysql               "docker-entrypoint.s…"   9 minutes ago       Up 9 minutes        3306/tcp, 33060/tcp      mysql_con
a0d847359c82        adminer             "entrypoint.sh docke…"   9 minutes ago       Up 9 minutes        0.0.0.0:8080->8080/tcp   adminer_con



localhost:8080にアクセスすると、以下の画面があらわれるかと思います。 f:id:kurasher:20201219032015p:plain

各項目には、

データベース種類:MySQL
サーバ:db
ユーザ名root
パスワード:docker-compose.ymlMYSQL_ROOT_PASSWORDで設定したパスワード
データベース:(空白)

と設定すればログインできます。が、今回の趣旨と離れるため、説明はスキップします。

docker compose停止

以下のコマンドで停止します。

$ docker-compose down
Stopping mysql_con   ... done                                                                                                                                                                        Stopping adminer_con ... done                                                                                                                                                                        Removing mysql_con   ... done                                                                                                                                                                        Removing adminer_con ... done                                                                                                                                                                        Removing network test_default
$ 

docker-compose.yml解説

version: "3.3"
services: 
  web:
    image: adminer
    container_name: adminer_con
    ports: 
      - 8080:8080

  db: 
    image: mysql
    container_name: mysql_con
    environment:
      MYSQL_ROOT_PASSWORD: password

versiondocker-composeのバージョンを指定します。特にこだわりが無い場合は最新のバージョンを記述すればよいです。

servicesは起動する各コンテナについての定義を書くところです。今回はwebと、dbという2つのserviceが定義されています。このwebdbが今回で言うコンテナにあたります。分かりやすい名前であれば、無理にwebdbと書く必要はありません。

webimageでベースとなるイメージを、webcontainer_nameで起動するコンテナ名を、webportsでポートフォワーディングの設定を、dbenvironment環境変数を定義しています。

これらをdocker runする場合のコマンドのオプションと比較すると

設定内容 docker runコマンド docker-compose.yml
ベースイメージ コマンドの最後に指定 image:
コンテナ名 --name コンテナ名 container_name:
ポートフォワーディング -p ホストのポート:ゲストのポート ports:
環境変数 -e 環境変数設定 environment:

となります。

docker-composeの起動と停止の解説

解説するほどのものではないですが、一応書いておきます。

起動

$ docker-compose up -d

停止

$ docker-compose down

これだけです。起動のところで-dをつけていますが、必須ではありません。ただ、つけていた方があとあと楽かと思います。

まとめ

今回はDocker Composeについて学びました。コンテナが2つだけだったため、ありがたみが分かりにくかったかもしれませんが、複数のものを一回のコマンドだけで操作できるのは魅力的ですよね!

以下に、これまでのコマンド一覧を載せています。今まで学んだコマンドを思い出しながら、Dockerに慣れ親しんでいきましょう。

やること Dockerコマンド
イメージの取得 docker pull イメージ名
イメージの一覧表示 docker images
イメージの一覧表示 docker image ls
コンテナの起動 docker run イメージ名
コンテナ名を指定してコンテナを起動 docker run --name コンテナ名 イメージ名
起動中のコンテナの一覧表示 docke ps
起動中のコンテナの一覧表示 docke container ls
停止中のコンテナも含めてコンテナの一覧表示 docker ps -a
停止中のコンテナも含めてコンテナの一覧表示 docker container ls -a
停止中のコンテナの削除 docker container rm コンテナID
停止中のコンテナの削除 docker container rm コンテナ名
イメージの削除 docker rmi イメージID
イメージの削除 docker rmi イメージ名
イメージを探す docker search イメージ名
イメージの一覧表示 docker images
起動していないコンテナのコンテナ操作 docker run -it コンテナ名 イメージ名 コンテナ内で実行するコマンド
起動中のコンテナのコンテナ操作 docker exec -it コンテナ名 イメージ名 コンテナ内で実行するコマンド
コンテナをバックグラウンドで起動 docker run -d イメージ名
コンテナを停止後自動的にコンテナを削除するオプション docker run --rm イメージ名
起動中コンテナの停止 docker stop コンテナ名
イメージの作成 docker build -t イメージ名:タグ名 .
docker-compose起動 docker-compose up -d
docker-compose停止 docker-compose down

最後に

HTC Advent Calendar 2020の5回にわたってDockerについて学んできました。
しかし、たった5回で完全に理解できるほどDockerは簡単なものではありません。
しかし、Dockerを全く知らず、ひとまず動かしてみたいと思った方にとって、これら5回は有益なものになってくれればなと思っています。

Dockerに限った話ではありませんが、読むだけではなく、実際に手を動かして自分の目で確かめてみることで習得の一歩を踏み出せると思います。
私の記事だけでなく他の人の記事も参考にしながらDockerについて学び、より高みを目指していただければと思います。

また、HTC Advent Calendar 2020では、私含め、5人が持ち回りでカレンダーを担当しました。
社会人歴も短く、記事としてはまだまだ未熟な部分もあったとは思いますが、これらの記事がご覧いただいた方々の少しでもお役に立てれば幸いです。

参考

HTC Advent Calendar 2020

Docker Desktop で 開発用 Web アプリを起動してみよう

Docker入門(第六回)〜Docker Compose〜

Dockerを学ぶ第4回~Dockerfileを作成する~

HTC Advent Calendar 202020日目を担当します、kurasherです。

第4回はDockerfileです。

環境

Windows 10 Pro バージョン 1903
Docker Toolbox
Dockerバージョン: 18.09.1
VirtualBox: 6.1.16

Dockerfileとは

Docker Hubにはたくさんの既存イメージがあります。これらをベースイメージとしたとき、ベースイメージに対して実行する操作を記述する設定ファイルのことをDockerfileと言います。

一言で言ってしまえば、カスタマイズみたいなもんです。
今まではDocker Hubにある既存イメージを取得してコンテナを起動してきました。これは非常に便利ではあるのですが、一方で細かい設定が出来ませんでした。そこで、Docker Hubにある既存イメージをベースにして、我々ユーザーが自由にカスタマイズすることで新しいイメージを作り出す道具がDockerfileというファイルです。

ちなみに、ベースとなるイメージに対し、何らかの機能を加えて、ユーザイメージ(自分独自のイメージ)を作成することをDocker イメージビルドというらしいです。

コンテナ内でFlaskを起動するDockerfileを作成する

実際に動かしてみましょう。以下、3つのファイルを用意します。

Dockerfile

拡張子はつきません。気を付けましょう。

FROM python:latest

COPY requirements.txt /usr/src/app/

RUN pip install --upgrade pip

RUN pip install -r /usr/src/app/requirements.txt

COPY hello.py /usr/src/app/

CMD ["python", "/usr/src/app/hello.py"]

requirements.txt

click==6.6
Flask==0.11.1
itsdangerous==0.24
Jinja2==2.8
MarkupSafe==0.23
Werkzeug==0.11.10

hello.py

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World! from Docker"

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8000, debug=True)

実行方法:イメージ作成

先ほどのDockerfileに書いた内容を基に、オリジナルイメージを作成します。イメージ名はflaskimgとしています。最後の.も忘れずに!

$ docker build -t flaskimg .

これで、オリジナルのイメージを作成することが出来ました。確認してみましょう。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
flaskimg            latest              42fb6c5bcc58        9 seconds ago       896MB
python              latest              0611cf846c85        22 hours ago        885MB

出来ていれば、上記のようにflaskimgという名前があるはずです。

実行方法:コンテナ起動

先ほど作ったイメージのコンテナを起動してみます。

Docker Desktopの場合

実行は以下のようにします。

$  docker run -d --name flaskcon -p 5000:8000 flaskimg

これで、localhost:5000からアクセスできるはずです。Docker Desktopを持っていないため、確かめられていませんが...。

Play with Dockerの場合

Play with Dockerからも確かめることが出来ます。

Play with Dockerの画面を開いたら、先ほどの3ファイルすべてをPlay with Dockerのターミナル上にドラッグ&ドロップし、アップロードします。

アップロードし終わったらイメージを作成します。

$ docker build -t flaskimg .


イメージの作成が終わったらdocker runでコンテナを起動します。

$ docker run --name flaskcon -p 5000:8000 flaskimg
 * Running on http://0.0.0.0:8000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger pin code: 326-429-724

コンテナの起動が終わったら、下記の図の5000の部分をクリックします。
f:id:kurasher:20201214190837p:plain

クリック後は以下の画面が表示されればOKです。
f:id:kurasher:20201214191013p:plain

Docker Toolboxの場合

Docker Toolboxの場合、ポートフォワーディングがうまくいきません。実際に私が以下の記事で四苦八苦していることが分かります。

Docker Toolboxでポートフォワーディングできない!場合の対処法

Docker Toolboxでポートフォワーディングする場合は他の記事も参考にしながら行うようにしてください。

設定方法ですが、VirtualBoxを起動し、
default > 設定 > ネットワーク > アダプター1 > 高度(D)
まで順番にクリックしてください。すると、以下の画面まで行きつくかと思います。
f:id:kurasher:20201213211432p:plain

上図のポートフォワーディング(P)をクリックし、「ポートフォワーディングルール」という新しい画面が出てきたら以下のように設定してください。
f:id:kurasher:20201213211630p:plain

これで一応準備完了です。

(途中のポートフォワーディングルールの部分で、ホストポートを8000、ゲストポートを5000と別々にしていますが、一緒の値にしても問題ありません。ただし、hello.pyで設定したポート番号に合わせる必要があります。今回の場合、8000にしているので、ホストポートとゲストポート、どちらも8000にします。)

実行は以下のようにします。

$  docker run -d --name flaskcon -p 5000:8000 flaskimg



これで、localhost:8000からアクセスすると、以下の画面が見れると思います。
f:id:kurasher:20201213212501p:plain

Dockerfile解説

FROM python:latest

COPY requirements.txt /usr/src/app/

RUN pip install --upgrade pip

RUN pip install -r /usr/src/app/requirements.txt

COPY hello.py /usr/src/app/

CMD ["python", "/usr/src/app/hello.py"]

ここではFROMCOPYRUNCMDに焦点を当てて解説します。他のコマンドもあるので、公式等で確認するようにしてください。

コマンド 意味
FROM ベースとなるイメージを指定。必須です。
COPY [A] [B] ディレクトリパスAにあるものをコンテナ内部Bのディレクトリにコピー
RUN イメージビルド時に実行するコマンド
CMD コンテナ作成時に実行するコマンド

ここで気になるのはRUNCMDの違いですが、
RUNは、イメージビルド中
CMDは、コンテナを作成するとき
という違いがあります。下記の記事が分かりやすいです。

DockerのRUNとCMDの違い

また、Dockerfileでよく使うコマンドの一覧については下記を参照してください。
Docker超入門⑦〜よく使うDockerfileコマンド一覧〜【初心者向け】

Dockerコマンド解説

とは言っても今回は一つしかないんですが...。

$ docker build -t flaskimg .

-t イメージ名:タグ名で、イメージを作成しています。タグもつけることができ、

$ docker build -t flaskimg:v1.0 .

とすれば、TAGv1.0flaskimgのイメージが出来上がります。タグをつけた際はdocker imagesで確認してみるといいでしょう。
省略した場合、TAGは自動でlatestとなります。

また、一番最後に書かれている.ですが、これは作成したDockerfileのパスを指定しています。今回はbuildするディレクトリと同じ配下にあるため、.だけで済みます。Dockerfileを別のパスに置く必要が無ければ、buildするディレクトリに置いておいた方が間違いは少ないと思われます。

まとめ

今回学んだことです。次回、docker-composeを学びます。

やること Dockerコマンド
イメージの作成 docker build -t イメージ名:タグ名 .
Dockerfileコマンド 意味
FROM ベースとなるイメージを指定。必須です。
COPY A B ディレクトリパスAにあるものをコンテナ内部Bの位置にコピー
RUN イメージビルド時に実行するコマンド
CMD コンテナ作成時に実行するコマンド

参考

Docker Toolboxでポートフォワーディングできない!

DockerのRUNとCMDの違い

Docker超入門⑦〜よく使うDockerfileコマンド一覧〜【初心者向け】