Dockerの基礎

# なぜdockerを使うのか
「環境構築を楽にするため」
パッケージ、ライブラリ、開発環境構築、実行環境構築などdockerがあると楽に環境構築ができる

ホストの上にpythonなどを入れると、アンインストールするだけで大変だったり、他のパッケージなどとコンフリクトを起こしたりする。
ホストではなく、コンテナに環境構築することで、エラーがあった場合コンテナを捨てるようにすることで、ホストには全く影響がでないところで構築できる。

dockerfileに環境構築方法さえ書けば、環境を何度も作ったり消したりできる。
また、dockerfileやイメージを他の人に渡せば、全く同じ環境を構築できる。

ホストとは全く関係のない場所で、環境構築するのでその人固有のエラーがでない。

###dockerなし
１.インストーラのダウンロード
2.インストーラ起動
3.エラーが出る
4.トラブルシュート
2.インストーラ起動　・　・　・

１回のトラブルシュートではうまくいくことはほとんどない

###dockerあり
1.コンテナを起動する

だけで終わり。


#Dockerの特徴


###コンテナの独立性
同じdocker imageから二つのコンテナを立てても、それぞれコンテナのファイルシステムは独立している。
コンテナの中で作業をしても、他のコンテナやホストに影響がない。

#Dockerを学ぶに当あたっての予備知識

### シェル
bash, zsh, sh・・・


シェルを動かすためのアプリケーションがターミナル。
シェルはカーネルに接続するためのツール

###環境変数
OSの上で動くプロセスが情報を共有するために使う変数

※プロセス
（ターミナル、Chromeのブラウザ・・・）

###シェルの環境変数確認
コマンド　「echo $SHELL」
＄・・・環境変数の目印のようなもの

###環境変数作り方
コマンド　「export AGE=20」


#基本的な用語

###Dockerfile
・Docker imageの設計図
・テキストファイル

Docker imageをどう作るかが書かれている。
どんなコンテナ、どんなDocker imageになるのかが書かれているのでわかりやすい

### Docker image
コンテナを作る元となるもの
タグと呼ばれるバージョンを管理する機能がある。

「docker image」 は複数の「image layer」 から構成されている。
dockerのコンテナが作られると、「docker image」 に新しい「image layer」 が追加される。

###コンテナ
「開発環境、実行環境などの集合」

パソコンやサーバーにコンテナを複数置ける。
コンテナの中に、開発環境や実行環境、必要なパッケージやライブラリをインストールしておく。
すると、コンテナの中で作業したり、コンテナを使って、スクリプトを動かしたり、コードを動かしたりできる。

一つコンテナを作れば、そのコンテナを他のメンバーや、開発環境、テスト環境、本番環境における。

コンテナは「Docker image」から作られる。
コンテナから「Docker image」も作れる。
「Docker image」は「Docker file」というテキストファイルから作られる。









### Docker hub
Dockerレジストリの一つ
色々なリポジトリ（箱）があり、リポジトリに対して１種類のDocker imageが管理されている。
（タグが異なるdocker imageが複数入っている）

### Dockerレジストリ
「Docker image」を保管している場所


#コマンド
###docker login
docker hubにアクセスできる状態になる。

###docker pull [image]
imageをdocker hubからローカル（ホスト、自分のパソコン）に持ってくる

###docker run -it [image] bash
Docker imageからコンテナを作る

例）
docker run -it ubuntu bash
「ubuntu」というイメージからコンテナにする。

※ bash・・・コンテナ起動時に実行するプログラム

#### -it　とは
-i と　-t　の二つのオプションが連なっている。
####-i  
inputのチャネルを開くオプション。
inputのチャネルとは、stdinというチャネル
stdinというチャネルを開くとキーボードから打った内容がlinuxに届いたりする。
ホストからコンテナのstdinのチャネルを開くというチャネル。
「-i」を省いてdocker run(bashを起動)すると、コンテナに打ったコマンドの情報が伝わらない。


####-t
出力した結果をきれいにする。(prettyにする)。
prettyな出力結果を表示するためのコマンド。

###docker run hello-world 
「hello-world」 というイメージからコンテナにする

###exit
コンテナから抜ける

###docker ps -a 
コンテナの状況を確認する
※ps・・・process status

###docker images
ホストにあるimageのリストを表示

###docker restart
コンテナをupの状態にする

###docker exec -it [container] bash
コンテナに対して、指定したプログラム(bash)を実行する

###docker commit [container] [image]
コンテナからimageにする
（新しいimageを作る）

###docker tag [source] [target]
imageを新しい名前にタグ付けし直す。
古いimageは残ったままで、新しいimageの名前ができる。

[source]・・・今のimage名
[target]・・・新しいimage名

※docker imageの名前とリポジトリの名前は一致していなければいけない
 
###docker push [image]
docker hub にpushする


###docker rmi [image]
imageを削除する
rmi・・・remove image


###docker create
コンテナを作る

###docker start
コンテナを実行する

※run = create + start

###docker run  [image] [command]
デフォルトのコマンド上書き
「docker run」の後ろに追加でコマンドを打てば、そのデフォルトのコマンドが上書きされて、この[command]が実行される


###docker rm

コンテナの削除
コンテナが動いていたら削除できない。（docker stopで止める）

###docker system prune
exitedのコンテナを全て削除。
使っていないダングリングイメージ（まだ何も使っていないイメージ）や、キャッシュも消せる。

###docker run --name [container_name] [image]
コンテナ名の指定
コンテナに名前を付けて起動できる。
バックグラウンドでコンテナを動かしておきたい時や、共有サーバーを使っていて色々な人がそこにコンテナを立てる。という時に名前が必要になる。
他のプログラムからコンテナにアクセスする時やコンテナを使ったりする場合も、名前を付けておくことですぐ呼び出せる。


###docker run -d [image]
コンテナをバックグラウンドで常に動かしておくケースは「-d」。
開発環境で使う時などは裏でコンテナを常に起動しておく必要があるので、「-d」コマンドを使いdetachedモードにする。
-d ・・・ detached
###detachedモード
コンテナを起動してすぐdetachしてホストに戻る。裏でコンテナは動いている。

detachedモード　⇆　foregroundモード

###foregroundモード
「-d」を付けないで起動するコンテナ
直接コンテナに入ったり、一度コンテナを立てて何かプログラムを起動して、すぐにコンテナを消すケース

docker run --rm [image]
コンテナからexitするとすぐにそのコンテナを削除する。


###dokcer build .
DockerfileからDocker imageを作る
大抵はDockerfileがあるフォルダに移動してから、カレントディレクトリを指定して、Docker imageをビルドする。

###docker build -t [name] [directory]
-tオプションでDocker imageに名前を付けれる。

#Dockerfile

・Docker imageの設計図
・テキストファイル

Docker imageをどう作るかが書かれている。
どんなコンテナ、どんなDocker imageになるのかが書かれているのでわかりやすい

環境構築する時は、一度コンテナに入りコンテナ内で環境を構築し、必要なパッケージなどを知る。
次に、Dockerfileに書いて、docker buildする。
という流れになる。

⭐️Docker imageのlayerの数は最小限にする。
・docker layerを作るinstructionは、RUN, COPY, ADDの３つ

###Dockerfileの書き方
INSTRUCTION arguments

の形で書く。
arguments・・・引数


ex)

```
FROM ubuntu:latest
RUN touch test
```

ex)

```
FROM ubuntu:latest
RUN apt-get update && apt-get install -y \
  curl\
  cvs\
  nginx
CMD ["/bin/bash"]
```

##docker instruction
###FROM [image]
ベースとなるDocker imageを決めるもの（例では、ubuntuがベース）

必要最低限の機能を備えたimageを指定するのが良い。
Docker hubに置いてあるイメージである場合が多い。
ubuntuなどOSそのままのDocker imageを使う場合が多い。

###Run [command]

linuxコマンドを実行するためのdockerINSTRUCTION

imageを構成するために必要なlinuxコマンドを実行する。
コマンドを実行(image layerを作成)
基本的にはRUNのdocker instructionでdocker imageを作っていく。

基本的な書き方は、上記の例なので、覚えるとよい。

ubuntuではapt-get（またはapt）というコマンドでパッケージを管理する。
常に新しいリストを取得してからインストールするのが一般的。


apt-get update　・・・　パッケージのリストを最新にして取得
apt-get install [package]　・・・　[package]をインストール

「-y」　・・・　パッケージをインストールするときに「yes」で答える。
必要なパッケージをabc順に並べる。


「\」・・・改行した物も１行とみなす。



RUNはdockerのimage layerを作っていくので、RUNを複数業かくと、その数だけdocker image layerができ、docker imageが大きくなってしまう。
それを回避するために、コマンドを「&&」で繋ぎ、なるべくRUNの行数を減らす。

###CMD["executable", "param1", "param2"]
Docker imageのデフォルトのコマンドを指定できる。

executable　・・・　実行可能なコマンド

大抵DockerfileはCMDで終わる。

なくても良いが、つけた方が良い。
というのも、Dockerfileをみた時に、デフォルトでこのコマンドが動くことが色々な人にわかる。

※エントリポイントという別のDocker instructionを使う場合は、CMDの形と意味が変わる。

###COPY [src][destination]
build時に、build contextの中にあるファイルをdocker imageに組み込める。
→ホストから何かファイルやフォルダをコンテナに渡せる。


src ・・・ ホストのファイルのpath
destination ・・・ コンテナ内で目的のファイルをおくファイルのpath 

「COPY」と「ADD」は似ているが、基本的には「COPY」で問題ない。



###ADD [src][destination]
build時に、build contextの中にあるファイルをdocker imageに組み込める。
→ホストから何かファイルやフォルダをコンテナに渡せる。

圧縮されたtarファイルをdocker buildした時に、docker imageに送り、さらにそれを解凍してくれる。


※「COPY」で、フォルダごとdocker imageにしても良いが、docker daemonに情報を渡す際に時間がかかる。
よって、重たいファイルがある時は、tarファイルで圧縮してから「ADD」でdocker imageに送る方が良い。



###ENTRYPOINT
docker runした時のデフォルトのコマンドを指定できる。（CMDと同じ）

「ENTRYPOINT」では、docker runした時に上書きできない。
「CMD」では、docker runした時に上書きできる。

「ENTRYPOINT」がDockerfileにある場合はCMDの書き方が変わる。
CMDの形は「ENTRYPOINT」に指定したこのコマンドの引数を指定する。

例）
・ ENTRYPOINTなしの場合のCMD
CMD["ls", "--help", "引数", "引数", ・・・]

・ ENTRYPOINTありの場合のCMD
ENTRYPOINT["ls"]
CMD["--help", "引数", "引数", ・・・]
※docker run [image] -laのように引数（オプション）は上書きできる

###ENV　[key] [value]
環境変数をDockerfileで指定できる。
pathを通す時によく使う。

環境変数 ・・・ OSの上で動くあらゆるプロセスが情報を共有するために使う変数。keyとvalueの組み合わせ

ENV [key]=[value]
とも書ける

###WORKDIR [絶対パス]
Dockerfile内に記載されているdocker instructionの実行をするディレクトリを変更できる。

Dockerfileでは各instructionが実行される場所はルート直下。
Dockerfile内で、RUNでcdコマンドで移動しても、RUNで次のコマンドが実行されるのはルート直下。
しかし、それだと不便な場合があるので、「WORKDIR」で絶対パスを指定して、それ以降のinstructionを指定されたパスのところで実行する。



##docker buildコマンドの詳細
###なぜdocker build実行時、dockerfileではなく、そのフォルダを指定するのか
docker build実行時、指定されたフォルダをdocker daemonに渡す。
docker damonがフォルダとDockerfileを元にdocker imageを作る。



なので、Dockerfileだけでなく、フォルダも必要。

buildするフォルダのことを「 build context 」と言う。
このcontextを使って、docker daemonがdockerfileからdocker imageを作る。

context ・・・ 環境や状況

###docker daemonとは

・　dockerのオブジェクトを管理している。（実際に、コンテナやdocker imageを操作する）
・　docker composeなど他のDockerのツールとコミュニケーションする。

dockerオブジェクト ・・・ コンテナ、イメージ、ネットワークなどのこと

docker CLIのコマンド（「 docker build 」 や　「 docker run 」など）で、dockerのオブジェクトを管理しているdocker daemonに命令を送っている。








###キャッシュを使いながらdocker buildをする
既にdocker layerがあった場合、改めてbuildする必要はない。
docker fileをメンテナンスしていく中では、RUNを複数業に分ける。

新たにパッケージを追加した場合に、RUNが一つにまとめている状態でbuildすると新たにimage layerが作られてしまう。

ex)

```
FROM ubuntu:latest
RUN apt-get update 
RUN apt-get install -y \　　　←RUNを分ける
  curl\
  cvs\
  git\ 　　　　　　　　　　　　←追加
  nginx
CMD ["/bin/bash"]
```

※apt-getのupdateは既にdocker image layerがある。

###docker build -f　[docker file] [build context]
build contextの中にdockerfileがない場合や、他のdockerfileを作り、devとtestと分けている場合は、 「-f」オプションを使い、docker build することで特定のdockerfileを指定して、docker build できる。

開発環境とテスト環境で異なるdockerfileを持っている場合などに「-f」を用いて、dockerfileを指定し、docker buildすることがある。

Dockerfileの名前は、
開発系ではDockerfile.dev
テストではDockerfiel.test
のような形のファイル名にすることが多い。

#ホストとコンテナの関係

###ファイルシステムの共有
コンテナのファイルシステムは、ホストのファイルシステムとは独立しているが、「-v」オプションでコンテナからホストのファイルシステムにアクセスできるようになる。
ホストのファイルシステムをコンテナにマウントする。
ファイルシステムをマウントすることによって、あたかもホストのファイルシステムがコンテナのファイルシステムにあるかのように振舞うことができる。
（実際にはコンテナ内にはない。コンテナにおくと大きくなってしまう。）

開発環境でコードはホストに置いて、コードを実行する時にコンテナを使うという使い方が多い。

基本的に開発を進める時は、コードのスクリプトやデータはホストに置き、コンテナには置かないのが一般的。

docker run -v [host/path]:[container/path]

###ファイルへのアクセス権限

ユーザーIDとグループIDを指定してコンテナをrunする

コンテナからホストのファイルシステムにアクセスできると、ファイルへのアクセス権限が問題になる。
「-u」オプションをつけることによって、ユーザーのIDやグループのIDを指定してコンテナを立てれる。
それによって、ホストのユーザーのIDやグループのIDの権限がそのままコンテナにも適用される。

※共有サーバーを立てる時は必須

docker run -u　$(id -u):$(id -g)
(id -u) : ユーザーID
(id -g) : グループID
$ : コマンドの返り値が「-u」に指定される

###ポートをつなげる

ホストのポートをコンテナのポートに繋げる
ポート ・・・ プロセスがデータ通信する時に使う物
IPアドレスやホスト名は建物の住所、ポートは部屋番号のようなイメージ

外からホストのwebサーバーやwebサービスにアクセスする時に、ホストの名前やIPアドレスに加えて、ポートを指定することによって、ホストのwebサーバー、webサービスにアクセスできる。
もしwebサーバーやwebサービスがコンテナの上で動いていた場合、ホストのポートとコンテナのポートを繋げる(パブリッシュする)ことによって、外からコンテナのポートにアクセスできる。

docker run -p [host_port]:[container_port]
docker run -p 8888:8888

###コンピューターリソースの上限
ホストの中に複数のコンテナがあった場合に一つのコンテナで、メモリを全て使ったりすると、システムずべてが落ちる可能性がある。
それを避けるために、各コンテナに対して「cpu」や「メモリ」の上限を指定できる。

※共有サーバーを立てる時やデータ解析をする時などは必須

docker run --cpus [#ofCPUs] --memory [byte]

[#ofCPUs] ・・・ コンテナがアクセスできる上限のCPUを設定
[byte] ・・・ コンテナがアクセスできる上限のメモリを設定


###コンテナの詳細を表示する
dockerのコンテナの情報が一覧で表示される。

docker inspect [container] | grep -i [something]

docker inspectで表示される内容は量が多く、すぐに必要な情報にアクセスできない
grep -i [something]で、「CPU」や「メモリ」「環境変数（env）」など指定した文字列に関係するdocker inspectのみ表示する。

-i ・・・ ignore、大文字小文字を無視する



#最後に
この記事は、かめさん（ https://twitter.com/usdatascientist?s=21 ）のudemyのdocker講座の（ https://www.udemy.com/share/103aTRAEMfeVhaTXoB/ ）を元に、作成しました。

※あくまで、自分のための書き残しとして投稿しているので、講座と異なる部分を含んでいる可能性があります。

かめさんのブログ( https://datawokagaku.com/docker_lecture/ )