📘

Dockerの基礎勉強メモ

2022/07/06に公開

M1チップのmac前提

概要

Dockerを使うメリット

  • 環境構築時間の削減
  • インフラをコード化することにより、誰でも同じ環境を構築できる
  • CI、CDなどの自動化ソフトとの相性が良い

コンテナとは?

貨物船などで使われる隔離され、パッケージ化された箱
インフラのコンテナも同じで、サーバー上で隔離されたアプリ空間を作れる技術のこと。

Docker以外にも

  • Linux Containers
  • Hyper-Vコンテナ
  • windowsコンテナ
    などがあるがLinuxでの使いやすさでDockerに人気が集まっているらしい。

仮想化とコンテンナ(Docker)の違い

Docker(コンテナ)

  • メモリ上に存在
  • ゲストOSは存在しない
  • メモリ上に存在するのでデータは一時的
  • Dockerfileでコード化できる

仮想化

  • ディスク上に存在
  • ホストOSとゲストOSに分かれる
  • 常に保存される
  • コード化はできない

この記事のシリーズが最高にわかりやすい
https://tech-lab.sios.jp/archives/18811

Dockerfile、Dockerイメージ、コンテナの関係性

コンテナが隔離されたアプリの箱
Dockerイメージが設計図(レイヤーになっている静的なテンプレートファイル)
Dockerfileがそれを言語化、file化したもの

Dockerイメージがクラスでコンテナがインスタンスと言う解釈が良さそう。

Docker Hubには例えばphpの公式が用意してくれたDockerイメージなどが
用意されていてそれを入手(pull)して使える。

https://and-engineer.com/articles/YaSPjRIAACAAkhMI

Dockerイメージのタグについて

タグ名 = バージョン名

ubuntu:18.04

指定しないとlatest(最新版)が入ってしまう。
配布した時期によってバージョンの違いがでるトラブルに繋がる。

docker hubについて

https://hub.docker.com/
アカウントを作成しないとpullに制限がかかるらしいので作っておこう。

作成しただけではダメでDocker Desktopからサインインしておく。
コマンドからもログインできる。

docker login

よく使うコマンド

まずは、練習用のディレクトリを作ってその中で実行していく。
基本的には公式のリファレンスを見る。
https://docs.docker.jp/engine/reference/index.html

docker run

起動したいイメージを指定して実行

docker run hello-world

イメージがローカルにない場合は自動的にdocker hubからpullして来てくれる。
https://docs.docker.jp/engine/reference/commandline/run.html

docker run コマンドは、まず指定されたイメージ上に書き込み可能なコンテナ・レイヤを create (作成)します。それから、指定されたコマンドを使って start (開始)します。

docker ps

コンテナのプロセス一覧を表示する

docker ps

さっき作ったhello-worldが表示されない。
これはhello-worldが以下の表示をしたらプロセスを閉じるイメージだから。

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (arm64v8)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

以下のオプションをつけることで停止中のプロセスも表示される。

docker ps -a

プロセスに別の名前をつけることもできる。
例えば、hello-worldをkonitiwaと言う名前で走らせる。

docker run --name konitiwa hello-world

--rmをつけると停止と同時にコンテナの削除もしてくれる。

docker run --name rmtest --rm  hello-world

IDとnameだけ出力したい場合

docker ps --format "{{.ID}}: {{.Names}}" -a

https://docs.docker.jp/engine/reference/commandline/ps.html#docker-ps-formatting

https://docs.docker.jp/engine/reference/commandline/ps.html

docker start,stop,restart

start,stop,restartを試すためにcentosイメージからコンテナを立ち上げる

# -i,-tオプションで起動しながらbashに指定してコンテナに入る
docker run -it --name mycentos centos:8 /bin/bash
# 繋がったら一旦でる
exit
# mycentosが停止していることを確認
docker ps -a

-itオプションについて
https://scrapbox.io/llminatoll/docker_runのオプションいろいろ
https://qiita.com/toshihirock/items/22de12f99b5c40365369

name、container idのどちらで指定しても動作することがわかる。

docker start mycentos
docker stop mycentos
docker restart 3bbb6244c880

docker exec

コンテナに入ってバージョン表示

docker exec -it mycentos /bin/bash
[root@3bbb6244c880 /]# cat /etc/redhat-release
CentOS Linux release 8.4.2105

コンテナに入らずにcentosのバージョン情報を取得

docker exec mycentos cat /etc/redhat-release
↓
CentOS Linux release 8.4.2105

https://docs.docker.jp/engine/reference/commandline/exec.html

docker rm

コンテナを削除-fで強制で削除できるがストップしてからやろう。

docker rm コンテナ指定

https://docs.docker.jp/engine/reference/commandline/rm.html

docker images

ローカルにあるイメージをすべて表示

docker images

意外と容量がデカイので定期的に確認して削除しよう。
イメージもコンテナと同じくIDを持っている。
https://docs.docker.jp/engine/reference/commandline/images.html

docker rmi

Dockerイメージを削除
起動中、依存関係があるベースイメージは削除できない。

docker rmi イメージ指定

コンテナが起動中のイメージを削除しようとするとエラーがでる。

% docker rmi e6a0117ec169
Error response from daemon: conflict: unable to delete e6a0117ec169 (cannot be forced) - image is being used by running container 3bbb6244c880

https://docs.docker.jp/engine/reference/commandline/rmi.html

docker image prune

使用していないイメージを一括で削除もできる。

% docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:bad28a0252ef2a7490c95c11
deleted: sha256:845fc4a1c9954fd36895c11
deleted: sha256:e7579523d95c1195c11
deleted: sha256:e060cc6d9995c1195c11
deleted: sha256:63cca88695c1195c1195c11

https://docs.docker.jp/config/pruning.html#prune

docker build

Dockerfileからイメージを生成

docker build Dockerfileのパス

https://docs.docker.jp/engine/reference/commandline/build.html

docker cp

コンテナとホストマシンでファイルのやりとりを行うコピーコマンド

# 逆もできる
docker cp コンテナのファイルパス:ホストのファイルパス

https://docs.docker.jp/engine/reference/commandline/cp.html

docker logs

# 打ったコマンドのログが見れる
docker logs コンテナ名

# リアルタイム監視
docker logs -f コンテナ名

https://docs.docker.jp/engine/reference/commandline/logs.html

docker inspect

コンテナの詳細情報を確認できる。
主にトラブル時に使う模様。

docker inspect コンテナ名

https://docs.docker.jp/engine/reference/commandline/inspect.html

docker pull

イメージをダウンロードするコマンド。
プライベートイメージをダウンロードする時などにも使う。

docker pull イメージ名:タグ [レジストリURL]

https://docs.docker.jp/engine/reference/commandline/pull.html

docker commit

コンテナをイメージ化するコマンド。
pushする場合は以下の形にしないとできない。

docker commit コンテナ指定 DockerHubのID/イメージ名:タグ

https://docs.docker.jp/engine/reference/commandline/commit.html

docker push

レジストリにイメージやリポジトリを 送信pushl します。

docker push アカウント名/イメージ名

https://docs.docker.jp/engine/reference/commandline/push.html

docker history

イメージの履歴を確認。
他人が作ったイメージの中身を知りたい時に使う。
Dockerfileがない時に使う。

docker history イメージ指定

https://docs.docker.jp/engine/reference/commandline/history.html

コンテナのストレージ

コンテナの永続化

コンテナは削除してしまうと保存したデータも消えてしまう。
じゃあDBなどはどうするの?

ホストのボリューム(ストレージ)をオプションでコンテナにマウント
することができるのでそれで解決する。

-v フラグは現在の作業ディレクトリをコンテナ内にマウントします。

https://docs.docker.jp/engine/reference/commandline/run.html#v-read-only

共有されるので双方向に保存される

docker run -v ホストのパス:コンテナのパス イメージ名
↓
docker run -v /GitHub/dockertest/source:/var/www/html mycentos

webサーバを冗長化している様な本番環境では、DBはコンテナ化せずに一箇所に集約
して運用することもあるらしい。

volumeはすごい分かりづらいので以下の記事を参照。

https://qiita.com/aki_55p/items/63c47214cab7bcb027e0
https://matsuand.github.io/docs.docker.jp.onthefly/storage/
https://note.com/w0o0ps/n/n9bc1bcd9fa59
https://qiita.com/Hirochon/items/121f172fbbae8c8ad749

Dockerfile

https://docs.docker.jp/develop/develop-images/dockerfile_best-practices.html

RUNとCMDの違い

どちらもコマンドを実行するがタイミングが違う

RUN apt-get install -y nginx
CMD ["nginx","-g","daemon off;"]
  • RUN : Dockerfile → イメージ
  • CMD : イメージ → コンテナ

https://docs.docker.jp/develop/develop-images/dockerfile_best-practices.html#run
https://docs.docker.jp/develop/develop-images/dockerfile_best-practices.html#cmd

COPYとADDの違い

どちらファイルをイメージに追加するコマンド

  • ADDはネット経由でも追加可能
  • COPYはローカルからの追加

ADD と COPY の機能は似ていますが、一般的には COPY を優先します。それは ADD よりも機能が明確だからです。

https://docs.docker.jp/develop/develop-images/dockerfile_best-practices.html#env

ENV

環境変数を設定

  • DBのユーザ名など
  • アプリのバージョンの指定など

https://docs.docker.jp/develop/develop-images/dockerfile_best-practices.html#env

このセクションのまとめ的にMariaDBを作ってみる

mariadbと言うディレクトリを作る。
配下に以下のファイルを作成。

  • Dockerfile
  • my.cnf
  • create-table.sql
Dockerfile
# OSからやるより最小の公式パッケージの方がいろいろよしなにやってくれている
FROM mariadb:10.4
RUN apt-get update -y
COPY my.conf /etc/mysql/conf.d
# 初期データを配置
COPY create-table.sql /docker-entrypoint-initdb.d
ENV MYSQL_USER=root
# 最初からこのDB名で作成してくれる
ENV MYSQL_DATABASE=docker
ENV MYSQL_ROOT_PASSWORD=root

参考記事
https://jongz.hatenablog.com/entry/2020/12/13/064949

my.conf
# clientセクション: mysqlクライアントツールへの設定
[client]
port=3306
socket=/tmp/mysql.sock

# mysqldセクション: mysqlサーバーへの設定
[mysqld]
port=3306
socket=/tmp/mysql.sock
key_buffer_size=16M
max_allowed_packet=8M

# mysqldumpセクション: バックアップコマンドへの設定
[mysqldump]
quick

# mysqld_safeセクション: 起動ファイル設定
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
create-table.sql
CREATE TABLE persons (
    id int,
    lastname varchar(255),
    firstname varchar(255),
    address varchar(255),
    city varchar(255)
);

ビルドしていく。
-t でタグ名をつけて、その後にDockerfileの場所を指定。

docker build -t mymariadb mariadb

mymariadbイメージがあることを確認

docker images

mariaと言う名前で作成したmymariadbイメージを起動する。

docker run -d --name maria mymariadb

コンテナのプロセスを確認して思惑通りにコンテナが出来たことを確認。

docker ps

-----------
CONTAINER ID   IMAGE       COMMAND                  CREATED         STATUS         PORTS      NAMES
6135ab07a702   mymariadb   "docker-entrypoint.s…"   8 seconds ago   Up 6 seconds   3306/tcp   maria

コンテナに入って中身を確認していく

docker exec -it maria /bin/bash

Dockerfileに記載した通りにDBとテーブルが作成されてことを確認できる。

root@6135ab07a702:/# mysql -u root -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 10.4.25-MariaDB-1:10.4.25+maria~focal mariadb.org binary distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| docker             |
| information_schema |
| mysql              |
| performance_schema |
+--------------------+
4 rows in set (0.006 sec)

MariaDB [(none)]> use docker;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [docker]> show tables;
+------------------+
| Tables_in_docker |
+------------------+
| persons          |
+------------------+
1 row in set (0.001 sec)

docker-compose

Compose とは、複数のコンテナを定義し実行する Docker アプリケーションのためのツールです。

https://docs.docker.jp/compose/overview.html

以前は、コンテナをひとつずつ docker run --link で立ち上げて繋ぐと言う
方法もあったが非推奨になっている模様。
https://docs.docker.jp/engine/userguide/networking/default_network/dockerlinks.html

docker-composeはコンテナ間をリンクして一括で管理できる便利なやつ。
(ネットワーク機能は難しそうなので別の機会に勉強する)

docker-compose.ymlファイルとは?

アプリケーションを構成するサービスをファイル内に定義する。
インデントに意味がある書き方で、スペースで親子関係になる。
全体の設計図がyamlでDockerfileが個別の設計図みたいなイメージかな?

docker-compose.yml
version: '3'
services:
  app:
    # buildはDockerfileのパスをymlファイルがある場所からの相対パスで指定
    build: ./php
    # docker run --nameと同じでコンテナ名を自由に付けられる
    container_name:"php8"
    # docker run -pと同じポートフォワーディング
    ports:
     "8080:80"
  db:
    # imageはDockerHubから指定
    image:mariadb:10.4
    # docker run -vと同じでデータの永続化
    volume:
      ./data:/var/lib/mysql

docker-composeでコンテナ間通信できるのはなぜか?

デフォルトでネットワークに参加しているからみたい。

https://docs.docker.jp/engine/userguide/networking/dockernetworks.html
https://amateur-engineer-blog.com/docker-compose-network/

wordpressを試しにdocker-composeで作ってみよう

公式の内容が良いので書き写す必要も無いので参照。
https://docs.docker.jp/compose/wordpress.html

my_wordpressと言うディレクトリを作成。
公式から引用して改変してwordpressのファイルも永続化している。
wordpressがlatestになっていた部分も指定した。
公式でも実運用の部分では微妙な記述もあるみたいだから注意しよう。

my_wordpress/docker-compose.yml
version: '3'

services:
   db:
     image: mysql:5.7
     # M1macの場合に指定
     platform: linux/amd64
     volumes:
       # 名前付きボリューム
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     depends_on:
       - db
     image: wordpress:6
     volumes:
       # バインドマウント
       - ./wp-data:/var/www/html
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
volumes:
    # 名前付きボリュームを定義
    db_data:

色々調べているとcontainer_nameより以下のリンクの方法が良いらしいので
実践してみた。
実運用経験が乏しいのでメリットがピンと来ないがやっておこう。

コンテナ名を決める要素を .env に切り出せる、名前が機械的に決定される、という意味でもこの方法が好ましそう。

https://qiita.com/okashoi/items/1ddf3724ad5c166e417b

my_wordpress/.env
COMPOSE_PROJECT_NAME=test_project

作業ディレクトリに移動して、docker-composeを立ち上げるコマンドを実行。

cd my_wordpress
docker-compose up -d

http://localhost:8000/

無事wordpressが動いた。

https://prograshi.com/platform/docker/difference-among-docker-compose-run-up-build-and-create/

# ymlのmysqlのところに platform: linux/amd64 を記載しないとエラーがでる
no matching manifest for linux/arm64/v8 in the manifest list entries

docker-composeのコマンド

# コンテナをストップ&削除
docker-compose down

# コンテナ全てを再起動
docker-compose restart

# コンテナ一覧を表示
docker-compose ps

# docker-compose.ymlで管理されているサービスを1つ指定してコマンドを実行
docker-compose run サービス名 実行したいコマンド

参考記事
https://tech-lab.sios.jp/archives/20051
https://note.com/seki19/n/nfb12519b792c
https://docs.docker.jp/compose/environment-variables.html#env
https://zenn.dev/ajapa/articles/1369a3c0e8085d
https://qiita.com/Hirochon/items/121f172fbbae8c8ad749

Discussion