実行中の Docker コンテナにボリュームを動的に追加する方法

実行中の Docker コンテナにボリュームを動的に追加する方法

以前、Docker コンテナの起動後にボリュームをマウントできるかどうか尋ねられたことがあります。mnt 名前空間の動作を考えると、最初はこれを実現するのは難しいだろうと思いました。しかし、今ではそれが達成されたと思います。

  • つまり、実行中のコンテナにディスク ボリュームをマウントするには、次の操作が必要です。
  • nsenter を使用して、このディスク ボリュームを含むファイル システム全体を一時マウント ポイントにマウントします。
  • ディスク ボリュームとして使用する特定のフォルダーからこのディスク ボリュームの場所へのバインド マウントを作成します。

最初の手順で作成した一時マウント ポイントをアンマウントします。

予防

次の例では、入力する必要がある内容とマシンの応答を区別できるように、これがシェル コマンド ライン プロンプトであることを示すために、意図的に $ 記号を含めています。一部の複数行コマンドでは、引き続き > を使用します。これにより、例のコマンドを簡単にコピーして貼り付けることができなくなることに気付きました。コードをコピーして貼り付ける場合は、記事の最後にあるサンプル スクリプトを確認してください。

詳細な手順

次の例では、次のコマンドを使用して charlie という名前の単純なコンテナを起動したことを前提としています。

$ docker run --name charlie -ti ubuntu bash

必要なのは、ホスト フォルダー/home/jpetazzo/Work/DOCKER/dockerコンテナー内の/srcディレクトリにマウントすることです。では、始めましょう。

センター

まず、nsenter と docker-enter ヘルパー スクリプトが必要です。なぜ?コンテナからファイルシステムをマウントしたいからです。セキュリティ上の理由により、コンテナではこれを行うことはできません。 nsenter を使用すると、上記のセキュリティ制限を突破し、コンテナのコンテキスト (厳密には名前空間) 内で任意のコマンドを実行できます。もちろん、これには Docker ホスト上のルート権限が必要です。

nsenter をインストールする最も簡単な方法は、docker-enter スクリプトと組み合わせて実行することです。

$ docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter

詳細については、nsenter プロジェクトのホームページを参照してください。

ファイルシステムを見つける

ホストフォルダ (/home/jpetazzo/Work/DOCKER/docker) を含むファイルシステムをコンテナにマウントします。次に、どのファイル システムにこのディレクトリが含まれているかを調べる必要があります。

まず、ファイルがシンボリック リンクである場合、またはそのパスにシンボリック リンクが含まれている場合は、ファイルを正規化 (または逆参照) する必要があります。

$ readlink --canonicalize /home/jpetazzo/Work/DOCKER/docker
github.com の docker ディレクトリに docker というファイルを作成します。

はは、確かにシンボリックリンクですね!これを環境変数に設定してみましょう:

$ HOSTPATH=/home/jpetazzo/Work/DOCKER/docker
$REALPATH=$(readlink --canonicalize $HOSTPATH)

次に、どのファイルシステムにこのパスが含まれているかを調べる必要があります。私たちは、やや意外なツールである df を使用してこれを行います。

$ df $リアルパス
ファイルシステム 1Kブロック 使用済み 使用可能 使用率 マウント済み
/sda2 245115308 156692700 86157700 65% /home/jpetazzo

-P フラグを使用します (Solaris または BSD に Docker をインストールするときに、特殊な df または他の人の df がある場合に備えて、POSIX 形式を強制します)。また、結果も変数に格納します。

$ FILESYS=$(df -P $REALPATH | tail -n 1 | awk '{print $6}')

ファイルシステムのデバイス(およびサブルート)を見つける

現在、システムにはバインド マウントや BTRFS サブボリュームは存在しないため、/proc/mounts を調べて、/home/jpetazzo ファイル システムに対応するデバイスを見つける必要があります。しかし、私のシステムでは、/home/jpetazzo は BTRFS プールのサブボリュームです。サブボリューム情報 (またはバインド マウント情報) を取得するには、/proc/self/moutinfo を確認する必要があります。

mountinfo について聞いたことがない場合は、proc.txt のカーネルドキュメントを確認してください。

まず、ファイル システム デバイス情報を取得します。

$ 読み取り中 DEV マウント ジャンク
> [ $MOUNT = $FILESYS ] を実行する && break
> 完了 </proc/mounts
$ エコー $DEV
/dev/sda2

次に、サブルート情報 (マウントされたファイル システムへのパス) を取得します。

$ ABC SUBROOT MOUNT JUNK を読み取り中
> [ $MOUNT = $FILESYS ] を実行する && break
> 完了 < /proc/self/mountinfo 
$ echo $サブルート
/jpetazzo

とても良い。これで、 /dev/sda2をマウントする必要があることがわかりました。ファイルシステム内で、/jpetazzo に移動します。ここから、必要なファイル (この例では/go/src/github.com/docker/docker ) への残りのパスを取得できます。
残りのパスを計算してみましょう:

$ SUBPATH=$(echo $REALPATH | sed s,^$FILESYS,,)

注意: この方法は、パスに「,」が含まれていない場合にのみ機能します。パスに「,」があり、この記事の方法でディレクトリをマウントしたい場合は、お知らせください。 (この問題を解決するには、シェル トライアドを呼び出す必要があります: ジェシー、ソウルシェイク、ティアノン?)

コンテナに入る前に最後に行うことは、ブロック デバイスのメジャー番号とマイナー番号を見つけることです。 stat を使用できます:

$ stat --format "%t %T" $DEV
8 2

これら 2 つの数値は 16 進数であることに注意してください。後で 2 進数が必要になります。これは次のように変換できます。

$ DEVDEC=$(printf "%d %d" $(stat --format "0x%t 0x%T" $DEV))

要約する

最後にもう1つのステップがあります。説明できない理由で、一部のファイルシステム (BTRFS を含む) は、複数回マウントされた後に /proc/mounts のデバイス フィールドを更新します。つまり、コンテナ内に /tmpblkdev という一時ブロック デバイスを作成し、それを使用して独自のファイルシステムをマウントすると、ファイルシステムは (ホスト マシン上で!) /dev/sda2 ではなく /tmpblkdev として表示されます。これは無害に思えるかもしれませんが、実際には、ファイル システム ブロック デバイスを取得しようとする後続の試行が失敗する原因になります。

簡単に言うと、コンテナ内のブロック デバイス ノードがホスト マシンと同じパスに配置されていることを確認する必要があります。

次の操作を行う必要があります:

$ docker-enter charlie --sh -c \
> "[ -b $DEV ] || mknod --mode 0600 $DEV b $DEVDEC"

ファイル システムをマウントするための一時マウント ポイントを作成します。

$ docker-enter charlie --mkdir /tmpmnt
$ docker-enter charlie --mount $DEV /tmpmnt

ボリューム マウント ポイントが存在することを確認し、ボリュームをバインド マウントします。

$ docker-enter charlie --mkdir -p /src
$ docker-enter charlie --mount -o bind /tmpmnt/$SUBROOT/$SUBPATH /src

一時マウントポイントを削除します。

$ docker-enter charlie --umount /tmpmnt
$ docker-enter charlie --rmdir /tmpmnt

(デバイス ノードはクリアしません。そもそもデバイスが存在するかどうかを確認するのは冗長かもしれませんが、すでに十分複雑です。)

ミッション完了!

すべてを自動化

次の段落はそのままコピーして貼り付けることができます。

#!/bin/sh
セット-e
コンテナ=チャーリー
HOSTPATH=/home/jpetazzo/Work/DOCKER/docker
CONTPATH=/src

REALPATH=$(readlink --canonicalize $HOSTPATH)
FILESYS=$(df -P $REALPATH | tail -n 1 | awk '{print $6}')

DEV MOUNT JUNKを読みながら
[ $MOUNT = $FILESYS ] を実行し、break する 
完了 </proc/mounts
[ $MOUNT = $FILESYS ] # 正常性チェック!

\ABC SUBROOT MOUNT JUNKを読みながら
\do [ $MOUNT = $FILESYS ] && break
\done < /proc/self/mountinfo 
[ $MOUNT = $FILESYS ] # マウントの健全性チェック!

SUBPATH=$(echo $REALPATH | sed s,^$FILESYS,,)
DEVDEC=$(printf "%d %d" $(stat --format "0x%t 0x%T" $DEV))

docker-enter $コンテナ --sh -c \
   "[ -b $DEV ] || mknod --mode 0600 $DEV b $DEVDEC"
docker-enter $コンテナ --mkdir /tmpmnt
docker-enter $CONTAINER --mount $DEV /tmpmnt
docker-enter $コンテナ --mkdir -p $CONTPATH
docker-enter $CONTAINER --mount -o bind /tmpmnt/$SUBROOT/$SUBPATH $CONTPATH
docker-enter $コンテナ --umount /tmpmnt
docker-enter $コンテナ --rmdir /tmpmnt

ステータスと制限

上記の方法は、ブロック デバイスをベースとしないファイル システムには適用されません。/proc/mounts がブロック デバイス ノードを正しく取得できる場合にのみ機能します (前述のように、常に正しく取得できるとは限りません)。また、私はこれをクラウド インスタンスなどではなく、自分の環境でのみテストしましたが、そこでも当てはまるかどうか興味があります。

以上がこの記事の全内容です。皆様の勉強のお役に立てれば幸いです。また、123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • Docker 学習に関する簡単な説明: Docker データ量 (ボリューム)
  • Docker におけるコンテナデータボリュームとデータ管理の詳細な説明
  • Dockerデータボリューム管理の詳細な説明
  • Dockerボリュームマウントの実装方法
  • Dockerfile命令VOLUMEの簡単な紹介
  • Dockerでボリュームを管理する2つの方法

<<:  cocoscreatorプレハブの詳しい説明

>>:  MySQL ベースのシーケンス実装方法

推薦する

MySQL マスタースレーブ同期における server-id の例の詳細な説明

序文MySQL クラスターを構築する場合、当然のことながら、データの一貫性を確保するために、データベ...

レスポンシブウェブデザインを実現するためにIEでCSS3メディアクエリをサポートする

今日の画面解像度は、320 ピクセル (iPhone) ほど小さいものから、2560 ピクセル以上 ...

Dockerを使用してシンプルなJava開発およびコンパイル環境を構築する方法の詳細な説明

Java 言語には多くのバージョンがあります。一般的に使用されている Java 8 に加えて、一部の...

MySQLのクラスタモードでのgalera-clusterのデプロイメントの詳細説明

目次1: galera-clusterの紹介2. galera-clusterの仕組み3: Mari...

Vueはプログレスバーの変更効果を実現します

この記事ではVueを使ってプログレスバーの変更を簡単に実装してみましたので参考にしてください。具体的...

Docker を使用した nGrinder パフォーマンス テスト プラットフォームの導入プロセスの分析

nGrinderとは何ですか? nGrinder は、スクリプトの作成、テストの実行、監視、結果レポ...

CSS で適応型ディバイダーを巧みに実装する N 通りの方法

分割線はウェブページでよく使われるデザインです。例えば、Zhihuのその他の回答をご覧ください。 こ...

MySQLクエリが遅い理由

目次1. 遅いところはどこですか? 2. 不要なデータをクエリしましたか? 1. 不要なレコードをク...

Linuxにおけるselinuxの基本設定チュートリアルの詳細な説明

selinux ( Security-Enhanced Linux)は、Linux カーネル モジュ...

rpm を使用して指定されたバージョンの docker (1.12.6) をインストールする詳細な手順

1. 理由システムが Centos7.3 の場合、yum install docker を使用して直...

LinuxにRocketMQインスタンスをインストールする手順

1. JDKをインストールする1.1 現在の仮想マシン環境にJDKがあるかどうかを確認する rpm ...

Centos7にTenda U12ワイヤレスネットワークカードドライバーをインストールする際の問題を解決する

解決プロセス:方法1: CentOS7.3 のデフォルトのカーネル バージョンは低く、3.10.0-...

Linux での MySQL 8.0.11 のインストールに関するチュートリアル

1. 公式サイトにアクセスしてインストールパッケージをダウンロードしますダウンロードリンク: クリッ...

Linux MySQL ルートパスワードを忘れた場合の解決方法

MySQL データベースを使用する際、何らかの理由で長期間 MySQL にログインしていない場合、ま...

MySQL ページングクエリ最適化テクニック

ページング クエリを使用するアプリケーションでは、LIMIT と OFFSET を含むクエリが非常に...