Dockerを学び直す(備忘録)

Dockerを学び直す(備忘録)

今回は、dockerについて学び直していきます。

基本的な知識はありますが、改めて細かい部分などで知らないことが出てきたら、備忘録として残していきます。

image

  • docker image を pull するときは、コロンをつけて、バージョンを指定することができる
    • 指定しない場合は、基本的に最新の安定バージョンが pull される
      • 例: docker image pull ubuntu
  • dockerイメージの詳細を確認する場合は、docker image inspect {イメージ名} を実行する
  • dockerイメージを作成するときに名前をつける場合は、docker image build -t {名前:タグ名} {ビルドコンテキストパス} のように -t オプションをつける
    • 例: docker image build -t my-image:v1 .
  • dockerイメージをビルドする際に、Dockerfileのディレクトリがビルドコンテキストパスにない場合、明示的にどのDockerfileを使うかを指定する必要がある
    • docker image build -f {対象のdockerfile} {ビルドコンテキストパス}
      • 例:docker image build -t my-image:v2 -f docker/Dockerfile .
ビルドコンテキストって何?

dockerデーモンに送りたいファイル群が置いてあるディレクトリのこと。COPYなどをDockerfileに設定している場合は、dockerデーモンは送られたファイルの中からコピーをしてイメージビルドを行う。そのため、実行したいファイルが存在するディレクトリをパスとして指定する必要がある。

  • .dockerignoreファイルに指定したファイルはビルドされない
  • dockerイメージのレイヤーを確認したい場合は、docker image history コマンドを実行する
イメージレイヤーとは

Dockerはレイヤー構造になっており、レイヤーが複数重なってできている。例えば、DockerfileにRUNが書いてあるとそれが1つのレイヤーになる。レイヤーの数が少ないほど、イメージの容量も小さくなるので、RUNを書く場合は、複数のコマンドを繋げて書いた方が良い。ただし、レイヤーはキャッシュされるので、流動的にコマンドを変更する可能性がある場合は、RUNを分けて書いた方が変更差分が少なく、キャッシュが多く使われるので、良い。

Dockerfileの作成

  • CMD ["実行ファイル(コマンド)", "パラメータ1", "パラメータ2"]
    • コンテナ実行時のデフォルトのコマンド。Dockerfileで1度しか使えず、複数あった場合は、最後のCMDのみ有効
      • 例: CMD ["ls", "-la"]
  • ENV
    • 環境変数を設定する
    • スペースが入る場合はダブルクオートを使う
      • 例:ENV hello="HELLO WORLD"
  • ARG
    • 変数を設定する
      • 例:ARG message
        • 設定した上で、docker image build --build-arg message="Hello Message" コマンドを実行
        • デフォルト引数を設定する場合は、Dockerfile上でARG message="Hello Message" などとしてやれば良い
  • WORKDIR
    • 作業ディレクトリを指定する
      • 例: WORKDIR /app/my_dir
FROM ubuntu:20.04

ENV hello="Hello World" 
ENV hoge=HOGEHOGE

ARG message
RUN echo $message > message.txt

RUN apt update && \
    apt install -y curl vim

RUN touch 1.txt
WORKDIR /app/my_dir
RUN touch 2.txt
WORKDIR /app
RUN touch 3.txt

COPY ./hello.txt /app/

CMD ["bash"]

ENVとARGをどう使い分けるのか

ENVはコンテナビルド時とコンテナ実行時に有効だが、ARGはビルド時のみ有効。そのため、ビルドの時のみ必要な変数はARGで、コンテナ起動後も必要な変数はENVで設定する。もし全てENVで設定した場合は、使わない環境変数が予期しない形でアプリケーションに作用してしまうことがあるので、ENVとARGをきちんと使い分けるのが良い。

マルチイメージビルドとは

1つのDockerfileでステージごとの環境を分離し、必要なイメージだけを作ることができる。下記のような場合に使うと良い。FROMを書くたびにステージが変わる。

  • 処理ごとにイメージを使い分けることで、イメージサイズを圧縮したい
  • 共通処理と環境依存処理を分離し、Dockerfileの管理を楽にしたい
# compilerステージでhello.cをコンパイル
FROM gcc:12.2.0 AS compiler
WORKDIR /app
COPY ./hello.c .
RUN gcc hello.c

# ubuntuステージで、compilerステージのa.outだけをコピー
# コンパイル前のファイルがないので、イメージサイズの圧縮になる
FROM ubuntu:20.04
WORKDIR /app
COPY --from=compiler /app/a.out .
CMD ["./a.out"]

  • どのステージを使うかは、target オプションを使うことで指定できる
    • docker build --target {ステージ名} -t alexellis2/href-counter:latest .

container

  • コンテナが起動する( up の状態になる)と定義されているコマンドが実行され、その実行が完了すると、すぐにコンテナは終了する( exited の状態になる)
    • そのため、ずっと起動し続けるようなものでない限りは、コンテナはすぐに終了する
      • 例えば nginx コンテナでは、ずっと起動し続ける
  • docker container run -it ubuntu のようなコマンドの -i-t にはそれぞれ意味がある
    • -i はcontainerに対して標準入力を可能にするオプション。このオプションがないとターミナルからの入力が受け付けられない
    • -t は見た目を綺麗にするオプション
  • docker container run はイメージからコンテナを新規作成するコマンド
  • docker container exec は既存のコンテナに対して、コマンドを実行するためのコマンド
    • docker container exec -it {コンテナID or コンテナ名} {実行コマンド}
      • 例: docker container exec -it 8c610c5cb5b0 bash
  • コンテナに名前をつけたい場合は、docker container run --name {つけたい名前} {イメージ名}
    • 例: docker container run --name my_ubuntu ubuntu:20.04
  • docker container run --rm {イメージ名}のように --rm オプションをつけると、コンテナ実行後すぐにコンテナが削除される
  • -d オプションをつけるとデタッチドモードでコンテナを起動する
    • 例: docker container run -d {イメージ名} {実行したいコマンド}
    • デタッチドモードで起動したコンテナにアタッチ(入って何か作業を)するためには、docker container attach {コンテナ名}

データの永続化

データを永続化する場合、2種類の方法がある

ボリューム

dockerにはボリューム領域という領域があり、そこに作成したデータを保存しておくことができる。実際に保存されるのはDockerホスト(Docker Engineが稼働している仮想マシン)上の領域のため、macの場合、ローカルマシンから直接アクセスして、ファイルを編集するなどはできない。

  • ボリュームの作成
    • docker volume create {ボリューム名}
  • ボリューム一覧の確認
    • docker volume ls
  • ボリューム詳細の確認
    • docker volume inspect {ボリューム名}
  • ボリュームの削除
    • docker volume rm {ボリューム名}
  • コンテナをボリュームに接続しながら起動させる
    • docker container run -v {ボリューム名}:{コンテナ内絶対パス} {イメージ}
    • docker container run --mount type=volume,src={ボリューム名},dst={コンテナ内絶対パス} {イメージ}
      • 書き方が違うだけで、どちらのコマンドもやってることは基本的には同じ
        • ただ、--mount の方が何をやっているか明示的にわかりやすく、挙動も若干違うため、公式はこちらを推奨

バインドマウント

  • コンテナをバインドマウントしながら起動させる
    • docker container run -v {ホスト絶対パス}:{コンテナ内絶対パス} {イメージ}
    • docker container run --mount type=bind,src={ホスト絶対パス},dst={コンテナ内絶対パス} {イメージ}

TIPS

ホスト絶対パスを毎回入力するのは面倒なので、docker container run --mount type=bind,src=$(pwd)/sample,dst=/app のように $(pwd) とすると、現在のディレクトリを省略してかけるため、ちょっと楽になる

ボリュームとバインドマウントの使い分け

  • 開発途中のソースコードなど、ローカルマシンで編集したものを即座にコンテナに反映させたい場合は、バインドマウント
  • DBのようなホスト側からは編集しなくても良いものは、ホストの環境に依存させたくないので、ボリュームを使う
ボリュームバインドマウント
複数のコンテナで共有可能か?YESYES
ホストからアクセス可能か?NO(macOSの場合)YES
ホストの環境に依存するか?NOYES

コンテナとの接続

ホストとコンテナのポートを紐づける

  • docker container run -p {ホスト側のポート}:{コンテナ側のポート} {イメージ} で紐付けが可能。これを実行した状態で、ブラウザから対象のホスト側のポートにアクセスすると、コンテナに接続できる。

Dockerネットワーク

  • コンテナ同士の通信を簡単にしたり、不要なコンテナ同士の通信を防ぐために隔離する
  • ネットワーク一覧の表示
    • docker network ls
  • ブリッジネットワークは、コンテナ間の橋渡しをするためのネットワーク
    • 同じブリッジネットワークに接続しているコンテナ同士は疎通ができる
    • 異なるブリッジネットワークに接続しているコンテナからは隔離される
    • デフォルトでもブリッジネットワークは存在するが、名前をつけて別のネットワークを作成することもできる
  • ネットワークの詳細を確認
    • docker network inspect {ネットワーク}
  • ネットワークの作成
    • docker network create {ネットワーク}
  • ネットーワークを指定してコンテナを起動させる
    • docker container run --network {ネットワーク} {イメージ}
  • 同じネットワーク内の通信では、コンテナ名を指定したら自動的に名前解決してくれる
    • 例:curl http://{コンテナ名}
      • ただし、bridge(デフォルトネットワーク)は名前解決してくれないため、IPアドレスを指定する必要がある
  • ネットワークの削除
    • docker network rm {ネットワーク}

Docker Compose

docker compose とは通信が必要な複数のコンテナを立ち上げる際に、簡単に起動することができるコマンド。やっていることは、Dockerコマンドを簡単に書いているだけ。

# docker-compose.yml
version: '3.9'
services:
  api:
    container_name: api
    build: # イメージをビルドする必要があるときはbuild
      context: ./api # ビルドコンテキスト
      target: build # targetのイメージ
    ports:
      - 8080:8080
    tty: true # dockerコマンドの -t と同じで、コンテナとホストマシンの標準出力をつなぐ
    depends_on: # DBに依存させる=先にDBを起動する
      - db
  db:
    image: postgres:15 # DockerHubにある既存のイメージを使う場合はimage
    ports:
      - 5432:5432
    environment:
      - POSTGRES_PASSWORD=mypassword
      - POSTGRES_USER=postgres
      - POSTGRES_DB=appdb
    volumes:
      - db-storage:/var/lib/postgresql/data
      - ./db/initdb:/docker-entrypoint-initdb.d # 相対パスでもOK

volumes:
  db-storage:

  • ビルドし直す
    • docker compose up --build
  • コンテナに入る
    • docker compose exec {コンテナ} bash
  • コンテナ一覧を表示する
    • docker compose ps

コンテナの中に入って開発する

  • vscodeを使っている場合、Dev Containersという拡張機能を使うと、コンテナの中に入ってソースを変更するという作業をvscode上で行うことができるようになる
  • Dev Containersを使う場合は、そのコンテナ内のみで使う拡張機能を設定することができる
    • .devcontainer というディレクトリを作成し、その中に devcontainer.json を作成する
      • devcontainer.json に使いたい拡張機能を書く
{
  "name": "api-container",
  "service": "api",
  "workspaceFolder": "/workspace",
  "dockerComposeFile": "../../docker-compose.yml",
  "customizations": {
    "vscode": {
      "extensions": [
        "vscjava.vscode-spring-initializr",
        "pivotal.vscode-spring-boot",
        "pivotal.vscode-boot-dev-pack",
        "vscjava.vscode-spring-boot-dashboard",
        "vscjava.vscode-java-pack"
      ]
    }
  }
}

以上です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です