Dockerまとめ

プログラミングスクールでDockerを使用した経験はありましたが、しばらく使っていなかったので結構忘れていましたので、復習しました。

# dockerコマンド

* ログインする
docker login

* イメージを削除する
docker rmi <image>

* imageに名前をつける
docker tag IMAGE_ID IMAGE_NAME:TAG

* docker hubからイメージをpull
docker pull <image>

* docker hubにイメージをpush
docker push <image>

bundle できていない場合は、
* docker-compose down/ docker-compose build/ docker-compose up
でdownし直してから、再度buildとupをする

もしくは、以下のコマンドを実行後、バックエンドのコンテナに入り、`bundle install`を実行でも良い。

* コンテナの再起動ポリシーを上書きし、コンテナの実行後に削除
docker-compose run --rm image bash

:::note info
※イメージの名前とレポジトリの名前は一致する必要があるので、dockerhubにpushする際は、その点に注意する。※以下の名前の変更コマンドで、変更してからpushする。docker tag <source><target>
:::

* コンテナ作成＋スタート
docker run <image>　
#run = create + start

* プロセスを表示
docker ps -a

* コンテナに入る
docker run -it ubuntu bash
-i: インプット可能　-t: 表示を綺麗にする

* containerを止める
docker stop container

* 停止したコンテナをup状態にする。まだ、containerには入っていない。
docker restart container

* コンテナに入る
docker exec -it container bash

* コンテナに名前をつける
docker run —name <name> ubuntu

コンテナに名前をつける目的は、以下の3点。
1. 起動させ続けるコンテナを立てるとき　
2. 共有サーバを使うとき　
3. 他のプログラムで使用するとき

* containerを削除する
docker rm container

* containerを全削除する
docker system prune

* detached mode コンテナを起動後にdetachする（バックグラウンドで動かす）
docker run -d <image>

* foreground modeコンテナをexit後に削除する（１回きりのコンテナ）
docker run —rm <image>

# docker imageを更新する方法

１、containerからdocker imageを作成
* docker commit container <new image>
```
xxx ~ % docker commit 98hogehogeohe ubuntu:updated
#:でタグ付けしている
sha256:7hogehogeohoegoeohehoehoeogeohoeoheoohogeoheg644d7ab3c8690e8147e6
xxx ~ % docker images  
```
```
#imageが2個できていることを確認。
REPOSITORY             TAG       IMAGE ID       CREATED          SIZE
ubuntu                 updated   76sgsgasdgsd   18 seconds ago   77.8MB
ubuntu                 latest    d2hogehogeho   11 months ago    77.8MB
```
２、docker fileからdocker imageを作成
一般的には１よりも、docker fileからimageを作成する方が多い。
### Dockerファイル
Docker imageの設計図で、DockerファイルからDocker imageを作成する。

・Dockerファイルがあるディレクトリに移動して、以下のコマンドでイメージを作成
```
docker build .
```

・出来立てほやほやのイメージ（TAG がNONE）はdangling image
```
docker images -f dangling=true
#で検索できる。
```

* FROM
ベースとなるイメージを決定
DockerファイルはFROMから始める
基本的には、OSを指定する=>ubuntuやalpine
alpineは5MBなので軽量で使用可能

* RUN
Linuxコマンドを実行
RUNを使うことで好きなようにカスタマイズ
RUN毎にLayerが作られる

* CMD
コマンドのデフォルトのコマンド
CMD[“executable”, “param1”, ”param2”]　例えば、[“/bin/bash“]みたいな感じ。
原則Dockerファイルの最後に記述
Layerは作らない
CMDとENTRYPOINTは似ているので注意！

Ubuntuではapt-get(または、apt)というコマンドでパッケージ管理をする。
* apt-get update:　
新しいパッケージリストを取得
* apt-get install <package>
パッケージをインストール

__ベストプラクティス：Docker imageのLayer数は最小限にする！__
Layerを作るのはRUN, COPY, ADDの３つ対応は以下の２点
* コマンドを &&でつなげる
* バックスラッシュで改行する
:::note info
作成している途中はchacheを利用するために、RUNを追加してbuildする。
最後にうまくいくことを確認したら、RUNを一つにまとめる。
:::

dockerファイルを指定するのではなく、current directoryを指定するのか？
=> なぜなら、そのディレクトリをbuild contextとして利用できるから！！

dockerでは、buildされるとdocker daemonに渡される。
基本的にbuildに使わないファイルはbuild contextに入れない。
もし、利用したいファイルがある場合は、ADDやCOPYでbuild contextの中にあるファイルをimageに持っていける。

### COPY
```
COPY <src> <destination>
```
これを使えば、build contextに置いたファイルなどをcontainerに持っていける。

### ADD
COPYと同じようなことができる。

じゃあ、COPYとADDの違いはなんなの？

:::note info
* COPY
単純にファイルやフォルダをコピーする場合
* ADD
tarの圧縮ファイルをコピーして解凍したい場合
:::

### Dockerファイルという名前のファイルがビルドコンテキストに入っていない場合
以下のように実行すると、ビルドコンテキスト以外を指定できる。
```
docker build -f <docker file name><build context>
# パスでも指定可能 
docker build -f ../Dockerfile.dev
```

### ENV：環境変数を設定する
```
ENV <key> <value>
ENV <key>=<value>
```

### 環境変数の一覧を表示
```
$ env
```

### WORKDIR
Docker instructionの実行ディレクトリを変更する。

RUNはルート直下で実行されるため、例えば、
```
RUN mkdir sample_folder
RUN cd sample_folder
RUN touch sample_file
```
のように記述してもsampleフォルダの中にファイルは作成されない。


じゃあ、どうやってフォルダの中にファイルを作成するのか？

__結論、WORKDIRを使う。__

```
FROM ubuntu:latest
RUN mkdir sample_folder

#絶対パスで指定する
WORKDIR /sample_folder
RUN touch sample_file
```

* WORKDIRを使用する場合は、勝手にフォルダを作ってくれるのでRUN mkdirする必要はない。

# ファイルシステムをコンテナにマウントする（超重要な気がする）
```
-v host:container
```

コンテナに実際にホストのファイルシステムがあるかのように振る舞う。
コードはホストに置いておいて、実行環境や解析環境としてcontainerを使う。

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

created_in_Dockerfileに書き込みしようとするとpermission denyされる。
ls -la で権限を確認すると以下のようになっている。

### Dockerimageをtarファイルに圧縮
```
docker save myimage > mydocker.tar
ssh -i mydocker.pem ubuntu@<hostname>
# tarファイルをDockerimageに変換
docker load < mydocker.tar
docker run -it myimage bash
```

### ec2のpemkeyを使用してsftp
```
sftp -i mydocker.pem ubuntu@<hostname>  
＃ec2にデータを転送する
put local/path
# ec2からデータを受信する
get remote/path
```

# docker-compose

dockerコマンドでコンテナを起動するときに、オプションを指定する。
例えば、-it / -v / -p などいくつかある。

それを毎回記述していたら長すぎて面倒なので、事前にその設定をしてdocker-composeコマンドを使って楽にしようというもの。

また、もう一つの理由としてはdockerコンテナを複数利用したい場合なんかに使うらしい。

### docker-composeコマンド

* docker-compose.ymlを元にコンテナ群を作成し起動
  docker-compose up

* Dockerイメージのビルド
  –build

* Dockerコンテナをバックグラウンドで起動
  -d

*　コンテナを停止
docker-compose stop

*　コンテナを削除
docker-compose rm -f

* docker-compose.ymlを元にコンテナ群を停止して削除
docker-compose down

* Dockerコンテナの再起動
docker-compose restart

* Dockerコンテナが起動しているか確認
docker-compose ps

* コンテナでコマンド実行コマンド
docker-compose run [コンテナ名] [コンテナで実行したいコマンド]
docker-compose exec [コンテナ名] [コンテナで実行したいコマンド]

* Gemfileに記述したGemをインストール
docker-compose build

# run と execの違い

run
* コンテナを新しく作って実行する。
* 「docker-compose up」等でコンテナが起動していなくても使える。
* 新しいコンテナに接続するのでコマンド履歴が残らない。
* 低速用途コンソール用のコンテナ等、普段は立ち上げもしないが、いざコマンド作業の際にのみ使うコンテナで作業をする場合はこれを使う。

exec
* docker-compose up などで起動しているコンテナを利用する。
* 起動中の docker コンテナがないと実行できない。
* 同じコンテナに接続するのでコマンド履歴が残っている。
* 高速用途DBコンテナ等、普段から当たり前に立ち上げているコンテナに接続する場合はこちらを使う。

# RAILSの環境を構築

```docker-compose.yml
version: '3'
volumes:
  db-data:
services:
  web:
    build: .
    ports:
      - 3000:3000
    volumes:
    - ".:/product-register"
    environment:
      - "DATABASE_PASSWORD=postgres"
    tty: true
    stdin_open: true
    depends_on:
      - db
    links:
      - db
  db:
    image: postgres
    volumes:
      - "db-data:/var/lib/postgresql/data"
    environment:
      - 'POSTGRES_USER=postgres'
      - 'POSTGRES_PASSWORD=postgres'

dockerfile

FROM ruby:2.5
RUN apt-get update && apt-get install -y \
        build-essential\
        libpq-dev\
        nodejs\
        postgresql-client\
        yarn
WORKDIR /product-register
COPY Gemfile Gemfile.lock /product-register/
RUN bundle install
```

# Railsプロジェクトの作成
```
docker-compose run web rails new . --force --no-deps --database=postgresql
```

```
docker-compose build
```

DB接続設定
```database.yml
default: &default
  adapter: postgresql
  encoding: utf8
  # For details on connection pooling, see Rails configuration guide
  # https://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  host: db
  username: <%= ENV['POSTGRES_USER'] %>
  password: <%= ENV['POSTGRES_PASSWORD'] %>

development:
  <<: *default
  database: app_development

test:
  <<: *default
  database: app_test
.
.
.
```

## DB作成
```
docker-compose run web rails db:create
```

### イメージの起動
docker-compose up
http://localhost:3000/ にアクセスしてページが表示されたら成功です。
ちなみに、localではdocker-compose.ymlのportsの3000:3000の左側を使用するため、こちらを違うポートにしている場合はそちらのポートを使用する。右側はdockerのポートとなる。