Dockerを学び直す(備忘録)
今回は、dockerについて学び直していきます。
基本的な知識はありますが、改めて細かい部分などで知らないことが出てきたら、備忘録として残していきます。
image
- docker image を pull するときは、コロンをつけて、バージョンを指定することができる
- 指定しない場合は、基本的に最新の安定バージョンが pull される
- 例:
docker image pull ubuntu
- 例:
- 指定しない場合は、基本的に最新の安定バージョンが pull される
- 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"]
- 例:
- コンテナ実行時のデフォルトのコマンド。Dockerfileで1度しか使えず、複数あった場合は、最後のCMDのみ有効
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のようなホスト側からは編集しなくても良いものは、ホストの環境に依存させたくないので、ボリュームを使う
ボリューム | バインドマウント | |
複数のコンテナで共有可能か? | YES | YES |
ホストからアクセス可能か? | NO(macOSの場合) | YES |
ホストの環境に依存するか? | NO | YES |
コンテナとの接続
ホストとコンテナのポートを紐づける
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"
]
}
}
}
以上です。