まず、Dockerの簡単な紹介を軽くしておこう。

- Dockerは、コンテナ型の仮想化技術を提供するプラットフォームである。
- これにより、アプリケーションを環境から切り離して実行できる。
- 新規の環境準備の手間も、既存の環境への汚染も、両方の懸念がなくなる。

- 移行や導入、カスタマイズが非常に容易になる。
- 単体のソフトだけではなく、複数のソフトを組み合わせた環境も簡単に構築できる。
- Dockerfileやdocker-compose.ymlというテキストファイルで管理できるので、環境のカスタマイズも容易で見通しがいい。管理や移行も簡単だ。
- 複雑なアプリケーションに限らず、さっと新しい言語に触れたり、異なる環境を試すのにも便利。
- AWSやAzure、GCPなどのクラウドサービスでもDockerをサポートしている。
Docker利用ケース
実際に筆者がDockerを利用している/していたケースを紹介する。
導入・運用の手間軽減
ボットおよびスクリプト関連
- 複数環境でのDiscord Botの安定運用
- 複数環境でのScript実行
開発およびコンパイル環境
- 簡単にLaTeXのコンパイル環境を構築
- Go, Rust, Juliaなどに触れるための環境
動作確認および検証
- 新規のUbuntuでの動作確認
- アプリケーションで不具合が生じた際、環境由来の影響を排除するため
Webアプリケーション導入
- GrowiやCodimdなどのWebアプリケーション導入
- もともとOnlineで動かしてるphpmyadminのlocal動作検証
一時的な利用環境
Docker Volume
実は、Dockerのコンテナ(VMみたいなもの)は、データの非保持が原則だ。
コンテナを閉じるまではデータが保持されるが、再度コンテナを立ち上げるとデータは消える (Imageの状態に初期化される)。
それに対する解決策は、以下の通り。

- DockerコンテナのImageを都度、再作成する
- 内部データ永続化のためのイメージ作成はおすすめしない。
- Dockerは処理ごとに差分バックアップをとっているので、この手法を続けると、容量が膨れ上がる。
- なにより、DockerのImageの利用方法として不適切だ。

- ホストOSのディレクトリをマウントする (Bind Mount)
- この方法は、コンテナ内のデータをホストOSのディレクトリと同期することで、データの永続化を実現する。
- 常にホストOS側でデータにアクセスできるので、管理も簡単。
- ただ、この処理はDockerコンテナの動作を非常に低速化する。
- 少数のファイルや簡単な処理の場合は問題ない
- LaTeXのコンパイルや、簡単なスクリプトの実行など

- Docker Volume (正確には名前付きVolume) を利用する
- 一般的なVMにおけるStorageとしてとらえるとよい
- コンテナにattachされる形で、コンテナのデータを保持する
G, GitHub Copilotくん、どうした……?

Docker Volume in script
docker runの際に、-v
または--mount
オプションを利用することで、Docker Volumeを利用できる。docker docsより引用。
1 2 3 4 5 6 7 8 9 10 11 12
| docker run -d \ --name devtest \ -v myvol2:/app \ nginx:latest
docker run -d \ --name devtest \ --mount source=myvol2,target=/app \ nginx:latest
|
Docker Volume in docker-compose.yml
今回はあまり触れていないが、Docker ComposeというDockerの拡張を利用すると、複数のDockerサービスや実行時のオプションを一つのYAMLファイルで管理できる。
以下はMincecraft Bedrock Serverを立ち上げるdocker-compose.ymlの例。
docker-compose.yml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| services: bds: image: itzg/minecraft-bedrock-server container_name: bedrock_server environment: EULA: "TRUE" SERVER_NAME: ServerName LEVEL_NAME: LevelName GAMEMODE: survival DIFFICULTY: normal TZ: Asia/Tokyo ALLOW_CHEATS: TRUE ONLINE_MODE: FALSE SERVER_PORT: 19132 PLAYER_IDLE_TIMEOUT: 0 ports: - 19132:19132/udp - 19133:19133/udp volumes: - type: volume source: dbs target: /data stdin_open: true tty: true volumes: dbs: external: true name: minecraft_data
|
Docker Composeの説明 by ChatGPT
Docker Composeの説明 by ChatGPT
Docker Composeは、複数のDockerコンテナを定義・管理し、まとめて起動・停止するためのツールです。docker-compose.yml
という設定ファイルを用いて、アプリケーションの各サービスを記述し、一括して管理します。これにより、複雑なアプリケーションの環境を簡単に設定し、再現することができます。
Docker Composeのメリット
簡単な設定管理:
docker-compose.yml
ファイルにより、複数のコンテナ設定を一つのファイルで管理可能。
一括操作:
- 複数のコンテナを一度に起動・停止・再起動できるため、操作が効率的。
開発環境の再現性:
- 同じ設定ファイルを使用することで、開発環境やテスト環境を簡単に再現可能。
依存関係の管理:
- サービス間の依存関係を明確に定義でき、起動順序の制御が容易。
ネットワーキングの簡素化:
- 複数のコンテナ間のネットワーク設定が自動で行われ、手動でのネットワーキング設定が不要。
これらのメリットにより、Docker Composeは開発者や運用担当者にとって非常に有用なツールとなっています。
不満点: 単体でCopyができない
で、DockerやDocker Volumeのすばらしさが伝わったところで、何が不満なのか。
Docker Volume単体へのCopyができない。
かみ砕いて説明しよう。
いかにもcopyをしそうなdocker cp
は、コンテナとホストOS間のファイルコピーを行うコマンドだ。通常のcp
と似た感覚で利用できる。
1 2
| docker cp worlds mcs:/data/worlds
|
これはコンテナとのファイルコピーであり、Docker Volumeとのファイルコピーではない。
当然、Docker Volumeがコンテナにマウントされている場合は、コンテナ内のファイルをDocker Volumeにコピーすることができるが、いつでもDocker Volumeがマウントされているわけではない。
たとえば、MinecraftサーバーのためのVolumeの場合、サーバー本体立ち上げ前にVolumeにワールドデータをコピーしておきたいものだろう。

ところで、Windowsのペイントにレイヤー機能が復活してる!
Windows 7の頃の記憶以来だなぁ。

まじめに編集する場合はPhotoshop, Gimp / Illustrator, Inkscapeを使うとはいえ、
一瞬編集したいときのペイントでのレイヤー復活は素直にうれしい。

レイヤー複製ができたのはいいが、レイヤーごとの透過度の設定もしたかった……。
ていうか、Windowsペイントで透過度の概念自体ない……?
解決策
情報元
さて、すべてを簡単に解決してしまう情報元はGitHub Issueである。
1 2 3
| docker container create --name dummy -v myvolume:/root hello-world docker cp c:\myfolder\myfile.txt dummy:/root/myfile.txt docker rm dummy
|
あるいは、日本語のこちらのBlogを閲覧した人も多いだろう。(情報元は同じ)
つまり一度コンテナを作ってしまえばdocker cpが使えるので解決できるわけです。
コンテナを作って削除すれば、volumeだけ残るので結果的にやりたいことが実現できるわけですね。
またホストからコピーしたvolumeを作るだけなのでdocker runでわざわざ起動せずとも、docker container createで済みます。
作成されたScript
さて、すでに問題は解決しているのだが、これをScript化することで、より簡単にファイルのコピーを行うことができる。
~/.bash_aliases1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| alias dockerVolCp="_DockerVolume_Copy" function _DockerVolume_Copy(){ container_name="tmp_volcp"$(date +%s)
if [[ "$1" == *":"* ]]; then mode="from" virtual_path="$1" host_path="$2" else mode="to" host_path="$1" virtual_path="$2" fi volume_name=$(echo $virtual_path | cut -d: -f1) virtual_dest=$(echo $virtual_path | cut -d: -f2) host_path="$1" virtual_path="$2" docker container create --name $container_name -v $volume_name:/root hello-world if [[ $mode == "from" ]]; then docker cp $host_path $container_name:/root/$virtual_dest else docker cp $container_name:/root/$virtual_dest $host_path fi docker rm $container_name }
|
実際の使用例
1 2 3 4 5 6 7 8
| docker volume create test
dockerVolCp tmp.dat test:tmp.dat
dockerVolCp test:tmp.dat tmp.dat
|
今回のScriptの技術的な解説 by ChatGPT
今回のScriptの技術的な解説 by ChatGPT
以下のスクリプトは、Dockerボリュームとホスト間でファイルをコピーするためのものです。新しいスクリプトでは、ファイルのコピー方向(ホストからボリューム、またはボリュームからホスト)を判断するためにmode
変数を追加しています。これにより、どちらの方向にもファイルをコピーできるようになります。
スクリプトの解説
エイリアスの設定
1
| alias dockerVolCp="_DockerVolume_Copy"
|
dockerVolCp
というエイリアスを設定し、このエイリアスが呼ばれると_DockerVolume_Copy
関数が実行されます。
関数の定義
1
| function _DockerVolume_Copy(){
|
dockerVolCp
コマンドが実行されると、この関数が動作します。
一時的なコンテナ名の作成
1
| container_name="tmp_volcp"$(date +%s)
|
- 現在の時刻を使ってユニークなコンテナ名を作成します。
引数の解析とコピー方向の判断
1 2 3 4 5 6 7 8 9
| if [[ "$1" == *":"* ]]; then mode="from" virtual_path="$1" host_path="$2" else mode="to" host_path="$1" virtual_path="$2" fi
|
- 最初の引数に「:(コロン)」が含まれている場合、Dockerボリュームからホストへコピーする(
mode="from"
)。 - コロンが含まれていない場合、ホストからDockerボリュームへコピーする(
mode="to"
)。
ボリューム名とパスの設定
1 2
| volume_name=$(echo $virtual_path | cut -d: -f1) virtual_dest=$(echo $virtual_path | cut -d: -f2)
|
virtual_path
からボリューム名と、ボリューム内のパスを分割して取得します。
一時的なコンテナの作成
1
| docker container create --name $container_name -v $volume_name:/root hello-world
|
- Dockerボリュームをマウントした一時的なコンテナを作成します。
ファイルのコピー
1 2 3 4 5
| if [[ $mode == "from" ]]; then docker cp $host_path $container_name:/root/$virtual_dest else docker cp $container_name:/root/$virtual_dest $host_path fi
|
mode
変数に基づいて、適切な方向にファイルをコピーします。mode="from"
の場合、ホストからコンテナへコピーします。mode="to"
の場合、コンテナからホストへコピーします。
一時的なコンテナの削除
1
| docker rm $container_name
|
- コピーが終わったら、一時的なコンテナを削除します。
使い方の例
ホストからDockerボリュームへコピー
1
| dockerVolCp /path/to/local/file volume_name:/path/in/volume
|
Dockerボリュームからホストへコピー
1
| dockerVolCp volume_name:/path/in/volume /path/to/local/file
|
このスクリプトを使うことで、Dockerボリュームとホスト間のファイルコピーが簡単に行えるようになります。
まとめ
Docker Volumeは、DockerコンテナとホストOS間でデータを共有するための便利な機能である。しかし、DockerコマンドにはDocker Volume単体へのファイルコピー機能がないため、スクリプトを作成することでこの問題を解決した。
なんとなく放置してたdocker volume cpの関数化をようやく行った。まだ改善の余地はあるが、手間がかかるのでまたそのうち……。