Docker その他の Q&A

最終更新: 2022-04-27 17:02:27 +0800 +0800
tags: Docker
原文は英語で書かれています。あなたはAI翻訳版を読んでいます。

Docker に関する面白い事柄と備忘録。

コンテナからホストマシンの IP アドレスを参照する #

  • MacOS ホスト + Docker Desktop の場合

    docker.for.mac.host.internal という名前は、MacOS にインストールされた仮想 NIC の IP アドレスに自動的に解決され、設定は不要です。もちろん VM 層(https://github.com/linuxkit/linuxkit/blob/master/examples/docker-for-mac.md)は依然として存在しますが、少なくとも実際の TCP を配送する IPv4 ルーティングは機能します。

  • Linux ホスト + Docker engine の場合

    docker run および create コマンドは、--add-host=name:<ip> フラグを受け取り、コンテナの /etc/hosts に <ip> name という行を追加します。<ip> には特殊なリテラル値 host-gateway を指定でき、ホストの Linux カーネルにある仮想 NIC の IP アドレスを参照できます(Docker engine が設定します)。最終的には、172.0.0.1 name のような行が得られます。

コンテナ内の GUI からホストの X11 サーバーソケットに接続する例:

sh
docker run --name gugu -it --entrypoint bash --user jose --add-host=hostgg:host-gateway -v $AA:$AA -v $BB:$CC -e DISPLAY=:0 -e XAUTHORITY=$CC kk

# in the container
getent hosts hostgg
172.17.0.1      hostgg

docker compose の yaml でコンポーネントを再利用する #

docker compose の yaml ファイルで DRY 原則を実践する方法は 2 つあります。

  1. extends キーワードを使用する
  2. docker compose yaml で再利用可能な設定部分に YAML のアンカー/マージ構文を使用する
yaml
networks:
  gugunet:
    driver: bridge
    enable_ipv6: false
    ipam:
      driver: default
      config:
        - subnet: 99.99.99.0/24
          gateway: 99.99.99.1

x-hdfs-commons: &hdfs-commons
  # hadoop launcher scripts requires bash, don't use alpine.
  image: ubuntu:latest
  volumes:
    - ../rofiles/zulu17.58.21-ca-jdk17.0.15-linux_aarch64:/opt/jdk:ro
    - ../rofiles/hadoop-3.4.1:/opt/hadoop:ro
    - ./core-site.xml:/opt/hadoop/etc/hadoop/core-site.xml:ro
#    - ./hdfs-site.xml:/opt/hadoop/etc/hadoop/hdfs-site.xml:ro
    - ./hadoop-logs:/tmp/hadoop-logs
  environment:
    - JAVA_HOME=/opt/jdk
    - PATH=$PATH:/opt/jdk/bin:/opt/hadoop/bin
    - HADOOP_LOG_DIR=/tmp/hadoop-logs


services:
  hdfs-name-node:
    <<: *hdfs-commons
    networks:
      gugunet:
        ipv4_address: 99.99.99.100
    command: bash -c "hdfs namenode -format && hdfs --daemon start namenode && sleep infinity"
  hdfs-data-node-1:
    <<: *hdfs-commons
    command: bash -c "hdfs --daemon start datanode && sleep infinity"
    networks:
      gugunet:
        ipv4_address: 99.99.99.101

Dockerfile の変数置換規則 #

  • Dockerfile は $VAR と ${VAR} の両方の構文を使用します。
  • VAR は、以前の ENV(自身またはベースイメージの Dockerfile で定義されたもの)または ARG でなければなりません。
  • シェル変数を参照することはできません。ゲストのシェルからもホストのシェルからも不可能です(設計上の分離によるものです)。

$PATH を実際に参照できる場合があるのは、ゲストのシェルの init rc が評価されるからではなく、ベースイメージの Dockerfile に ENV PATH が記述されているためです。

ゲストのシェルでのシェル評価を必要とする動的な処理は実行できません。つまり、環境変数の値をゲスト内のコマンドで計算する必要がある場合、ENV では対応できません。以下を参照してください。

環境変数の設定 #

原則:

  • 「Dockerfile の変数置換規則」を読み、それで問題なければ (1) と (2) を使用します。
  • それでは不十分で、エントリポイントプロセスとその子プロセスから環境変数を参照できるようにしたい場合は、(3) と (4) に進みます。
  • それでは不十分で、Docker コンテナを永続的に実行し、docker exec で接続して操作したい場合は、(5) を使用します。
  1. Dockerfile の ENV コマンド

    この方法では、コンテナの一部として実行される任意のプロセスを fork-exec する際に、containerd が指定された環境変数を追加します。そのため、エントリポイントプロセスと実行中のコンテナに対する docker exec の両方で機能し、シェルやシェル rc とは無関係です。

    ENV がサポートする変数置換は ARG と他の ENV のみです。そのため $PATH は機能しません(その他のよく知られた Unix 環境変数も同様です)。

  2. docker run -e または compose.yaml の environment

    仕組みは Dockerfile の ENV コマンドと同じです。非シェルのエントリポイントは環境変数を参照でき、非シェルの exec でも同様です。ただし、次の点が異なります。

    • 使い捨てコンテナと、後からそのコンテナに対して実行するすべての docker exec に有効です。
    • すべての展開はホストのシェルで行われます。ホストのシェル変数以外の変数は使用できません。
  3. Dockerfile の RUN コマンドでシェル rc を変更する

長所:

  • 実際のゲストマシンのシェル環境にいるため、環境変数の値をプロセスの出力にできます。たとえば rc に export foo="$(...)" の行を追加できます。これは 1 と 2 の ENV ではできません。(例はこちら)。
  • docker exec コマンドとコンテナのエントリポイントが両方ともシェルであれば、同じ環境変数を参照できます。

短所:

  • 新しいイメージをビルドする必要があります。
  • エントリポイントまたは exec コマンドがシェルでない場合は機能しません。

ほとんどの Linux ディストリビューションの Docker イメージは非ログイン bash を使用します。profile ではなく、適切なシステムレベルまたはユーザーレベルの rc を変更してください。

  1. エントリポイントの init スクリプトで export する

これは 3 の機能を弱めたものです。export された環境変数を参照できるのは、その init スクリプトの子プロセスだけであり、docker exec からは何も見えません。

ただし、変数の可視性を制限することは常に良い設計です。

  1. docker exec -it ... script.sh 用に用意した独立スクリプトで export する

    この構成が意味を持つのは、エントリポイントが sleep infinity のみを実行し、分離された環境でシェルコマンドを入力するために docker exec でコンテナへ接続する場合です。init スクリプトのすべての子プロセスは環境変数を参照できません(自分で source した場合を除きます)が、sleep infinity には適しています。

Docker ボリュームに関する誤解 #

Docker ボリュームは dockerd が管理する永続ストレージです。

  1. どのコンテナを終了または削除してもボリュームは削除されません。両者は独立しています。
  2. コンテナ作成時には、任意のボリュームを任意のコンテナにアタッチできます。ただし設計上、コンテナ作成後にボリュームを追加または削除することはできません。

ボリュームに関する混乱の多くは、docker run|create コマンドの -v フラグに由来します。実際、これは非常に扱いにくい設計です。構文は -v [LHS:]RHS です。

  1. LHS: を省略すると、このフラグは匿名ボリュームを作成し、コンテナ内のパス RHS にマウントします。
  2. それ以外で、LHS が / または ./ で始まる文字列の場合、docker run コマンドを入力した時点のホストマシン上の絶対パスまたは PWD からの相対パスとして解釈され、そのディレクトリまたはファイルがコンテナ内の RHS パスにバインドマウントされます。LHS がホスト上に存在しないパスに解決される場合、空のディレクトリが作成されます。これは新しい Docker ボリュームを作成しません。
  3. それ以外で、LHS が既存の Docker ボリュームの名前(docker volume ls の結果として確認できるもの)に一致する場合、そのボリュームがコンテナ内の RHS にマウントされます。
  4. それ以外の場合、新しい Docker ボリュームが作成され、LHS という名前が付けられた後、コンテナ内の RHS にマウントされます。

新しいボリュームを作成するのは 1 と 4 のみです。3 は既存のボリュームを再利用し、2 は Docker ボリュームとは無関係です。それでも、これらはすべて -v フラグで実行されます。より明示的な指定になるため、現代の Docker では -v を -mount type=..., フラグに置き換えることが推奨されます。

以下はボリュームではなく、ボリュームを作成しません。

  1. LHS が / または ./ で始まる場合の docker run -v LHS:RHS
  2. docker run --mount type=bind,...
  3. 新しいコンテナを作成すると、Docker 内部に書き込みレイヤーが作成されますが、これはボリュームではありません。ホストマシン上の場所は docker inspect <container> で確認でき、Linux では通常 /var/lib/docker/overlay2/ 配下にあります。このストレージはボリュームではなく、Docker の実装詳細です。

以下はボリュームに関係します。

  1. 明示的に新しいボリュームを作成する:docker volume create
  2. LHS が / または ./ で始まらない場合、または LHS: を省略した場合の docker run -v LHS:RHS
  3. docker run --mount type=volume,src=...,dest=...

/tmp の tmpfs #

Docker はデフォルトでは、コンテナ内の /tmp にメモリ上の tmpfs を割り当てません。必要な場合は明示的に指定してください。

  • compose ファイルの場合は、サービス下の tmpfs キーワードを使用します。
  • docker run / create コマンドの場合は、繰り返し指定可能な --tmpfs /MOUNTING/POINT フラグを使用します。

読み取り専用コンテナ rootfs #

rootfs が読み取り専用のコンテナを作成するには、次のいずれかを使用します。

  • docker run / create コマンドの --read-only フラグ
  • compose ファイルのサービスにおける read_only: true

特定のソフトウェアの挙動を慎重に観察したい場合や、コンテナがリモート SSH を提供する場合にユーザーを制限したい場合に役立ちます。

これは rootfs のみを読み取り専用にします。/proc 上の tmpfs や /sys 上の sysfs など、他のマウント済みファイルシステムは通常どおり読み書き可能です。カーネルの動作には変更可能である必要があるため、これは理にかなっています。ただし、これで十分な場合が多いでしょう。特に /tmp も読み取り専用になります。書き込み可能な /tmp が必要な場合は、明示的に /tmp に tmpfs をマウントしてください。

--read-only コンテナ内で特定のパスを読み書き可能にしたい場合は、自分で Docker ボリュームをマウントしてください。

コメント

GitHub で閲覧・コメントする(投稿にはログインが必要です)