Compose: Docker Composeは、名前付きボリュームを「root」として排他的にマウントします

作成日 2016年04月05日  ·  33コメント  ·  ソース: docker/compose

これは、名前付きボリューム(つまり、「データボリュームコンテナー」、「volumes-from」はありません)とdocker-compose.ymlに関するものです。

ここでの目標は、docker-composeを使用して2つのサービス「appserver」と「server-postgresql」を2つの別々のコンテナーで管理し、「volumes:」docker-compose.yml機能を使用してサービス「server-postgresql」からのデータを永続化することです。 。

'server-postgresql'のDockerfileは次のようになります。

FROM        ubuntu:14.04
MAINTAINER xxx

RUN apt-get update && apt-get install -y [pgsql-needed things here]
USER        postgres
RUN         /etc/init.d/postgresql start && \
            psql --command "CREATE USER myUser PASSWORD 'myPassword';" && \
            createdb -O diya diya
RUN         echo "host all  all    0.0.0.0/0  md5" >> /etc/postgresql/9.3/main/pg_hba.conf
RUN         echo "listen_addresses='*'" >> /etc/postgresql/9.3/main/postgresql.conf
CMD         ["/usr/lib/postgresql/9.3/bin/postgres", "-D", "/var/lib/postgresql/9.3/main", "-c", "config_file=/etc/postgresql/9.3/main/postgresql.conf"]

Docker-compose.ymlは次のようになります。

version: '2'
services:
    appserver:
        build: appserver
        depends_on:
            - server-postgresql
        links:
            - "server-postgresql:serverPostgreSQL"
        ports:
            - "1234"
            - "1235"
        restart: on-failure:10
    server-postgresql:
        build: serverPostgreSQL
        ports:
            - "5432"
        volumes:
            - db-data:/volume_data
        restart: on-failure:10
volumes:
    db-data:
        driver: local

次に、すべてをdocker-compose up -dで開始し、サーバーのpostgresqlコンテナーにdocker-compose exec server-postgresql bashを入力します。簡単なls /volume_data 、次にcdして、 touch testFileを試してみると、「許可が拒否されました。簡単なls -lは、 volume_dataroot:rootによって所有されていることを示しているため、これは正常です。

今起こっていると思うのは、DockerfileにUSER postgresがあるので、 docker-compose execを実行すると、ユーザー 'postgres'としてログインします(そしてpostgresqlデーモンはユーザー 'postgres'として実行されます同様に、 /volume_dataに書き込むことはできません)。
代わりにこれを実行すると、 docker-compose exec --user root server-postgresql bashcd /volume_datatouch testFileを再試行すると、機能するため、これは確認されてます(ホストとコンテナー間のアクセス許可エラーではありません。コンテナがホストフォルダをマウントする場合があります。これは、ユーザー「postgres」が書き込もうとしているときに/volume_dataが「root:root」としてマウントされるため、一般的なUNIXパーミッションエラーです。

したがって、docker-compose.ymlには、namedvolumesを特定のユーザーとしてマウントする方法が必要です。

version: '2'
services:
    appserver:
        [...]
    server-postgresql:
        [...]
        volumes:
            - db-data:/volume_data:myUser:myGroup
        [...]
volumes:
    db-data:
        driver: local

私が考えることができる唯一の_dirty_回避策は、DockerfileからUSER posgresディレクティブを削除し、カスタム「init_script.sh」を指すようにENTRYPOINTを変更することです(私はUSER postgres削除しました)、このスクリプトは/volume_data権限を変更して、 'postgres'が書き込み、次にsu postgresを実行し、postgresqlデーモンを(フォアグラウンドで)実行できるようにします。 しかし、これはDockerfileとdocker-compose.ymlを非標準的な方法でリンクするため、実際には非常に汚いです(ランタイムENTRYPOINTは、マウントされたボリュームがdocker-compose.ymlによって利用可能になるという事実に依存します)。

arevolumes statu0-triage

最も参考になるコメント

実はここにニュースを持って来て、達成しようとしていることは実行可能のようですが、これが機能なのかバグなのかわかりません。 これが私が変更した理由です:

私のDockerfileで、_ユーザー 'postgres'に変更する

# ...
RUN    mkdir /volume_data
RUN   chown postgres:postgres /volume_data

USER postgres
# ...

これは、ディレクトリ/ volume_dataを作成し、その権限を変更して、ユーザー 'postgres'が書き込みできるようにします。
これはDockerfileの部分です。

今、私はdocker-compose.ymlで何も変更していません:したがって、docker-composeはまだ名前付きボリュームdirectory_name_db-dataを作成し、それを/volume_dataマウントし、権限は保持されています!
つまり、名前付きボリュームが_pre-existing_ディレクトリ/volume_dataマウントされ、アクセス許可が保持されているため、「postgres」で書き込むことができます。

それで、これが意図された動作またはセキュリティの違反である場合はどうなりますか? (ただし、この場合は役に立ちます!)

全てのコメント33件

これはDockerエンジンでサポートされているとは思わないため、APIに追加されるまで、Composeでサポートする方法はありません。 ただし、この機能を追加する必要はないと思います。 ファイルはいつでも正しいユーザーにchownできます。

version: '2'
services:
  web:
    image: alpine:3.3
    volumes: ['random:/path']

volumes:
  random:
$ docker-compose run web sh
/ # touch /path/foo
/ # ls -l /path
total 0
-rw-r--r--    1 root     root             0 Apr  5 16:11 foo
/ # chown postgres:postgres /path/foo
/ # ls -l /path
total 0
-rw-r--r--    1 postgres postgres         0 Apr  5 16:11 foo
/ # 
$ docker-compose run web sh
/ # ls -l /path
total 0
-rw-r--r--    1 postgres postgres         0 Apr  5 16:11 foo

あなたが直面している問題は、名前付きボリュームの初期化に関するものです。 これは確かにComposeによって処理されるものではありませんが(スコープ外であるため)、 docker-compose up実行する前に、dockercliを使用して名前付きボリュームを簡単に初期化できます。

これがDockerの問題なのか、作成の問題なのかは確かにわかりませんでした。ファイルを間違えた場合は申し訳ありません。
これはDockerAPIの計画ですか、そこで問題を報告する必要がありますか?

コンテナに手動でログインし、ボリュームを「postgres」ユーザーにchownする可能性があることを理解しています。 しかし、私の場合、Composeを使用しているので、新しいクライアント( docker-compose -p client_name up )の新しいコンテナーをすぐにインスタンス化でき、Composeはカスタムネットワークclient_name_defaultを作成し、コンテナーを作成します。 client_name _appserver_1client_name _server-postgresql_1 、そして_もっと重要なこと__という名前で、ボリュームclient_name_db-dataを作成します。 これらはすべて手動で行う必要がないため、クライアント登録を処理するスクリプトで実行できます。

あなたが説明した解決策(ボリュームラベルにshchownしてコンテナに_手動で_ "ロギング"する)では、新しいクライアントを追加する簡単な手順がありません。これは必須です。手で世話をします。

これが、この機能を実装する必要があると思う理由です。 Docker APIでは、ボリュームをマウントするときにroまたはrw (読み取り専用または読み取り/書き込み用)のアクセス許可を指定できます。 user:groupを指定できるはずです。

あなたはどう思いますか、私の要求は理にかなっていますか?

実はここにニュースを持って来て、達成しようとしていることは実行可能のようですが、これが機能なのかバグなのかわかりません。 これが私が変更した理由です:

私のDockerfileで、_ユーザー 'postgres'に変更する

# ...
RUN    mkdir /volume_data
RUN   chown postgres:postgres /volume_data

USER postgres
# ...

これは、ディレクトリ/ volume_dataを作成し、その権限を変更して、ユーザー 'postgres'が書き込みできるようにします。
これはDockerfileの部分です。

今、私はdocker-compose.ymlで何も変更していません:したがって、docker-composeはまだ名前付きボリュームdirectory_name_db-dataを作成し、それを/volume_dataマウントし、権限は保持されています!
つまり、名前付きボリュームが_pre-existing_ディレクトリ/volume_dataマウントされ、アクセス許可が保持されているため、「postgres」で書き込むことができます。

それで、これが意図された動作またはセキュリティの違反である場合はどうなりますか? (ただし、この場合は役に立ちます!)

これはDocker1.10.xで追加されたため、名前付きボリュームは、それらを使用した最初のコンテナーから初期化されると思います。 期待される動作だと思います。

また、Dockerfileで所有権が設定されている名前付きボリュームを実行しており、composeではuser: postgres追加して、PID1でさえroot以外のユーザーが所有するようにしています。

Docker-composeについてvolumes持っているdriver_optsオプションを提供します。
ドライバーlocal場合でも、 chmodchownようなオプションがあるのは非常に良いことです。
特に、起動時に存在しない場合は、ローカルで作成されたホストディレクトリにも適用されるようにしたいと思います。

関連(ある程度) https://github.com/moby/moby/pull/28499

誰かがMobyプロジェクトですでに問題を開いていましたか?

@dnephinからの回答はchownまたはchmodコマンドが失敗し、ボリュームがrootによって所有されているため、標準ユーザーはアクセス許可を変更できません。

@jcberthon推奨される方法は、rootを開始ユーザーとしてコンテナーを実行し、chown / chmodの後にUSERコマンドを使用して、基本的に「特権の削除」を行うことです。

Dockerイメージを制御している場合は問題ありませんが、既存のイメージを使用している場合は、実際にはオプションではありません。

@ dragon788@micheljung 、問題を解決しました。

実際の本当の問題は、Dockerfileで、 VOLUMEを宣言してVOLUME宣言をDockerfileの最後に移動する(またはオプションの場合は削除する)だけで、私の問題は解決します。 権限は正しいです。

したがって、間違いは次のとおりです。

FROM blabla
RUN do stuff
VOLUME /vol
RUN useradd foo && chown -R foo /vol
USER foo
CMD ["blabla.sh"]

上記のDockerfileの例のchownは、ビルド中にVOLUMEを宣言しているため、ビルド中に失われます。 コンテナーを実行すると、dockerdは、指定されたボリューム内で、 VOLUME宣言の前に/volのコンテンツをコピーします(したがって、root権限を使用します)。 したがって、実行中のプロセスはアクセス許可を変更または変更できないため、blabla.shスクリプトで強制的にchownを実行しても機能しません。

ファイルを次のように変更する。

FROM blabla
RUN do stuff
RUN useradd foo && chown -R foo /vol
USER foo
VOLUME /vol
CMD ["blabla.sh"]

問題は解決しました。

@jcberthon docker -compose.ymlでボリューム/ volをホストシステムにバインドする方法を教えてください。

私はFedoraでDockerを使用しています(つまり、SELinuxが有効になっています)が、上記の方法はどれも機能しませんでした。 理想的には、ユーザー(rootなし)のコンテキストでコンテナー内のアプリケーションを実行したいのですが、このボリュームの問題はそれを妨げるものです。

私にとって有効な唯一の回避策は、アプリケーションユーザーを排除し、すべてをrootユーザーとして実行/所有することです。

こんにちは@renanwilliamと@ egee-irl

私はいくつかのOSを含むいくつかのOSで上記のソリューションを使用しています。 Fedora26とCentOS7は両方ともSELinuxが適用され、Ubuntu 16.04、17.10とRaspbian 9はすべてAppArmorがアクティブ化されています(amd64プラットフォームとarmhfプラットフォームが混在しています)。

先ほど言ったように、Dockerfileの最後にVOLUME ...宣言を移動しましたが、すべて削除できます。必須ではありません。 また、私が通常行うことは、Dockerfileでユーザーを作成するときにユーザーIDを修正することです(例: useradd -u 8002 -o foo )。 次に、ホストでそのUIDを再利用して、フォルダーに適切なアクセス許可を与えることができます。

したがって、次のステップは、ホスト上に/volディレクトリの「ペンダント」を作成することです。たとえば、 /opt/mycontainer1/volであるとすると、次のようになります。

$ sudo mkdir -p /opt/mycontainer1/vol
$ sudo chown -R 8002 /opt/mycontainer1/vol
$ sudo chmod 0750 /opt/mycontainer1/vol

次に、コンテナをユーザーfooとして実行すると、 /opt/mycontainer1/volディレクトリに書き込むことができます。 何かのようなもの:

$ sudo -u docker-adm docker run --name mycontainer1 -v /opt/mycontainer1/vol:/vol mycontainer1-img

SELinuxベースのホストでは、Dockerがフォルダーに適切にタグを付けるように、ボリュームに:z :Zオプションを追加することをお勧めします。 zZの違いは、小文字のzがボリュームにタグを付けるため、このホストのすべてのコンテナーがSELinuxによってそのディレクトリへのアクセスを許可される可能性があることです(ただし、明らかに別のコンテナにバインドマウントする場合のみ)、大文字のZはタグを付けて、その特定のコンテナのみがディレクトリにアクセスできるようにします。 したがって、SELinuxを使用するFedoraでは、次のことを試してみてください。

$ sudo -u docker-adm docker run --name mycontainer1 -v /opt/mycontainer1/vol:/vol:Z mycontainer1-img

更新:ここで私のリポジトリを確認できますhttps://github.com/jcberthon/unifi-dockerこの方法を使用して、ホストを構成してコンテナーを実行する方法を説明しています。 これがあなたの問題をさらに解決するのに役立つことを願っています。

ところで、 @ renanwilliamへの返信が大幅に遅れたことをお詫び申し上げます。 今年の終わりには余暇があまりありません...

だから、せっかちな人の短い長い話:

RUN mkdir /volume_data
RUN chown postgres:postgres /volume_data

ボリュームは既存のディレクトリの権限を保持するため、事前にボリュームディレクトリを作成し、 chown解決します。

これは自明ではないため、回避策としては不十分です(Dockerfileでchownを実行し、マウント中にその所有権を継承します)。 docker-composeおよびdockerCLIで所有者とグループの制御を公開することは、UNIXスタイルのコマンドにとって最も驚きのないパスです。

@villasv

小さなヒント:2つのRUN ...を1つにマージします。これにより、余分なレイヤーが作成されるのを防ぎ、ベストプラクティスになります。 だからあなたの2行は

RUN mkdir /volume_data && chown postgres:postgres /volume_data

ただし、(上記のコメントで述べたように) VOLUME ...を使用してボリュームを宣言する(または実際にはボリュームを宣言しない)前に、上記のRUN ...コマンドを実行する必要があることに注意してください。 ボリュームを宣言した後に所有権の変更を行った場合(私が行ったように)、それらの変更は記録されず、失われます。

@colbygkそれは確かに便利ですが、それはLinuxがどのように機能するかではありません。 DockerはLinuxマウント名前空間を使用して、さまざまな単一ディレクトリ階層( /およびサブフォルダー)を作成しますが、現在Linuxマウント名前空間でオーバーライドされているユーザー/グループマッピングまたはアクセス許可はありません。 コンテナー内の(およびバインドマウントボリュームを含む)これらの「マウント」は、ホスト上のファイルシステム上にあり(もちろん他のDockerボリュームプラグインを使用しない限り)、このファイルシステムはすべてを実行するLinuxVFSレイヤーを尊重していますファイルのアクセス許可を確認します。 ホスト上に、コンテナーがコンテナー内のファイルにアクセスするのを妨げる可能性のあるMAC(SELinux、AppArmorなど)が存在する可能性もあります。 実際、 chrootを実行すると、chroot内のマウントフォルダーをバインドできるのと同様の問題が発生する可能性があります。また、chroot環境内で実行されているプロセスの有効なUID / GIDが間違ってファイルにアクセスする可能性があるという問題もあります。バインドマウント。

単純なLinux(そして実際にはUnixも)ルールがコンテナーに適用されます。 秘訣は、今日のLinux名前空間の可能性と限界を見て理解することです。そうすれば、この問題などの問題を解決する方法が明らかになりつつあります。 私は古典的なUnixコマンドを使用してそれを完全に解決しました。

@jcberthonあなたの思慮深い応答をありがとう:

これはあなたが提案するようにプラグインレイヤーにプッシュされる問題であり、したがってDockerに付属する汎用ボリュームハンドラープラグインの一部になる可能性があると私は主張します。 コンテナの派生元のイメージで定義されている本質的に静的な関係に外部リソース(特定のコンテナの外部)を強制的に適用することは、私のように非常にアンクラウド/コンテナのようです。

「unix」の他の同様の領域で発生する、この正確な種類のuid / gidマッピングの他の例があります。

私が間違っている場合は訂正してください。 https://github.com/zfsonlinux/zfs/issues/4177は、Linux上のZFSがUID / GID情報を正しく提供していないため、「LXC / LXDのリーダー」が原因であるようです。ここで説明しているほぼ_正確な_方法でコンテナを作成します。 https://github.com/zfsonlinux/zfs/issues/4177をよく見ると、zfsボリュームタイプは実際には名前空間間のこのuid / gidマッピングをすでにサポートしているようですが、そうするためのコントロールは公開されていません。

Dockerを使用するほとんどの人は開発/ CI用であり、php / nginx(ランナー)やgradle / python(ビルダー)などの「一般的な」イメージを使用するため、適切なソリューションは次のようになります。

  1. イメージをオーバーライドするためにDockerfileを作成/編集する必要はありません
  2. docker-composeymlファイルで簡単な構文を使用する

ボリュームの書き込み権限(X:X:OPTION_READ_WRITE)は簡単に決定できるので、同じ方法で所有者を追加するのはどうでしょうか。

SOURCE:TARGET:RW:OWNER

私は同じ問題を抱えています。 おそらくベストプラクティスがあり、私の方法はそれではありません。

version: '3.5'
services:
    something:
        image: someimage
        user: '1000'
        expose:
            - 8080
        volumes:
            - dev:/app

volumes:
    dev:

これにより、 EACCES: permission denied, access '/app'ます。

これをどのように行う必要がありますか? つまり、新しいボリュームを定義し、root以外のユーザーでそれにアクセスできるようにしますか?

こんにちは@Redsandro

someimage Dockerイメージで/appのUIDを1000に設定することをお勧めします。 または、それを制御できない場合は、作成ファイルのuser: ...に、画像の作成者が意図したUIDまたはGIDを使用する必要があります。

もちろん、イメージの作成者がUID 0を使用していて、サービスをrootとして実行したくない場合(および非特権ユーザーとして実行できる場合)、Dockerイメージの作成者に問題を提起します。

これはdockerが管理するものではないため、関連するコンテナーが開始する直前に、ボリュームをプロビジョニングするための別のコンテナーを作成できます。たとえば、 https://hub.docker.com/r/hasnat/volumes-provisionerを使用し

version: '2'
services:
  volumes-provisioner:
    image: hasnat/volumes-provisioner
    environment:
      PROVISION_DIRECTORIES: "65534:65534:0755:/var/data/prometheus/data"
    volumes:
      - "/var/data:/var/data"

  prometheus:
    image: prom/prometheus:v2.3.2
    ports:
      - "9090:9090"
    depends_on:
      - volumes-provisioner
    volumes:
      - "/var/data/prometheus/data:/prometheus/data"

Dockerがこれを修正しない理由がわかりません。開発目的でハッキングできることに同意しますが、本番環境では、方法はありません。

IMHO podmanは(非特権)ユーザーとして実行できここが作成ソリューションにも取り組んでおり、PodmanAPIはほとんどの部分で意図的にDockerと互換性があります。

[podman]は、大部分がDockerと互換性があるため、ここで一部の人々を助けるかもしれません。

完全に同意しますが、残念ながらpodmanはMacでは動作しません

完全に同意しますが、残念ながらpodmanはMacでは動作しません


そうですね、私見では、Dockerもpodmanもネイティブに動作することはありません。 しかし、OS / XへのDockerのインストールは、仮想マシンのものを非常にうまく隠しています。
ただし、適切な開発システムを構築するためにVMを手動でセットアップするのは面倒な場合があることに同意します。
ただし、ここでは少し話題から外れています。

私はもうOS / Xユーザーではありませんが、_experimental_ podmandmgがあることを確認しました

podmanプログラマティックまたはpodman-composeアクセスできるようになっているため、近い将来、同様のエコシステムが開発される可能性があると思い

これは、共有クラスターでsudo権限を持たないユーザーが、docker-composeを介してrootが所有するフォルダーを誤って作成し、それらのフォルダーを自分で削除できない場合に特に問題になります。

私もこの問題に遭遇しました。 jpetazzoの投稿でガイドされているように、 dockerを使用しようとしています。

このコンテナをdocker-composeファイルから起動し、dockerソケットをホストマシンからdockergroupの下のコンテナマシンにマウントできるようにします。 これは、root以外の別のユーザーを使用して、コンテナー内からdockerを実行できるようにするためです。

現在、バインドマウントファイルの所有権と権限を指定できないため、これは達成できません。

ここに私の2セントを入れてください:

docker-composeの「ネイティブ」ソリューションの場合、ほとんどの人がすでにイメージライブラリに高山を持っているので、これを行いました。

volumes:
  media:
services:
  mediainit:
    image: alpine
    entrypoint: /bin/sh -c "chown -v nobody:nogroup /mnt/media && chmod -v 777 /mnt/media"
    container_name: mediainit
    restart: "no"
    volumes: 
      - "media:/mnt/media"

もちろん、最も安全な方法ではありませんが、コンテナーへの特権が付与されているコンテナーのみがそれを見るので、それほど大したことではありませんが、カーネルがサポートしている場合は、 user:userをchown

編集:少なくとも私のテストでは、chmodの前に最初にフォルダをchownする必要があるようです。そうしないと、「くっつきません」。

ボリュームの所有権はdocker-composeの制御下にないため、このディスカッションはmobyプロジェクトリポジトリの下で行う必要があります。

回避策を探している人のためのいくつかのメモ:
Dockerボリュームは、コンテナーによって最初に使用されるときに、コンテナーから継承された初期コンテンツとアクセス許可を取得します。 つまり、次のようにイメージを構成できます。

#Dockerfile
FROM alpine
RUN addgroup -S nicolas && adduser -S nicolas -G nicolas
RUN mkdir /foo && chown nicolas:nicolas /foo  
# empty, but owned by `nicolas`. Could also have some initial content
VOLUME /foo  
USER nicolas

このようなイメージは、明示的なボリューム(ランダムなIDを使用して意図的に作成されます)なしで実行された場合、またはまだ存在しない名前付きボリュームを使用して実行された場合、ボリュームへのアクセス許可を「伝播」します。

➜  docker run --rm -it -v some_new_volume:/foo myimage
/ $ ls -al /foo
total 8
drwxr-xr-x    2 nicolas  nicolas       4096 Oct 18 08:30 .
drwxr-xr-x    1 root     root          4096 Oct 18 08:30 ..

作成ファイルで宣言されたボリュームを使用する場合も同じことが当てはまります。

#docker-compose.yml
version: "3"
services:
  web:
    image: myimage
    command: ls -al /foo
    volumes:
      - db-data:/foo
volumes:
    db-data:

➜  docker-compose up
Creating volume "toto_db-data" with default driver
Creating toto_web_1 ... done
Attaching to toto_web_1
web_1  | total 8
web_1  | drwxr-xr-x    2 nicolas  nicolas       4096 Oct 18 08:30 .
web_1  | drwxr-xr-x    1 root     root          4096 Oct 18 08:37 ..
toto_web_1 exited with code 0

別のコンテナですでに使用されているボリュームを再接続した場合、これは機能しません。 ボリュームの所有権の変更、または作成時にこれを制御することは、エンジンによって、またはいくつかの特定のオプションを備えたボリュームドライバーによって実装する必要があります。 それ以外の場合は、提案されているように、いくつかのchownトリックに依存する必要があります^。

お役に立てれば。
composeはボリュームの作成を制御できませんが、公開されたエンジンAPIを制御できるため、この問題を解決します。

このページは役に立ちましたか?
0 / 5 - 0 評価