Docker configs を利用した設定データの保存

読む時間の目安: 12 分

configs について

スウォームサービスでは configs が導入され、設定ファイルのようにそれほど重要ではない情報を、サービスイメージや稼働中のコンテナーの外部に保存できます。 これがあれば、ビルドイメージをできるだけ汎用的なものとして維持できます。 また設定ファイルをコンテナーにバインドマウントしたり、環境変数を利用したりすることも不要になります。

configs は secrets と同じように機能します。 ただし configs は保存の際に暗号化はされません。 またコンテナーのファイルシステム内に直接マウントされますが、RAM ディスクは消費しません。 configs はサービスに対して、どのタイミングであっても追加および削除ができます。 またサービス間で 1 つの config を共有することもできます。 さらに configs と環境変数や Docker labels を組み合わせて利用できるので、最大限に柔軟性を持たせることができます。 configs の値には、通常の文字列やバイナリ(500 KB まで)を指定します。

メモ: Docker configs はスウォームサービスにおいて利用可能であり、スタンドアロンのコンテナーでは利用できません。 この機能を利用するには、コンテナーをサービスとして稼動させ、スケールは 1 としてください。

configs は Linux と Windows においてサポートされます。

Windows サポート

Docker には Windows コンテナーに対する configs サポートが含まれます。 ただし実装には違いがあるため、以降の利用例において示しています。 重要な違いとして以下があることを覚えておいてください。

  • カスタムターゲットを利用する config ファイルは、Windows コンテナーに対して直接バインドマウントされません。 Windows では、ディレクトリではないファイルのバインドマウントがサポートされないためです。 そのかわり、コンテナーに対する configs は、コンテナー内の C:\ProgramData\Docker\internal\configs (アプリケーションに依存しない実装場所)にすべてマウントされます。 ここを示すシンボリックリンクが利用され、コンテナー内に必要となる config ターゲットが設定されます。 デフォルトターゲットは C:\ProgramData\Docker\configs です。

  • Windows コンテナーを利用するサービスが生成されるとき、UID、GID を指定するオプションやモードは configs においてサポートされません。 configs は現在のところ、コンテナー内の administrators か system アクセス可能なユーザーのみがアクセス可能であるからです。

  • On Windows, create or update a service using --credential-spec with the config://<config-name> format. This passes the gMSA credentials file directly to nodes before a container starts. No gMSA credentials are written to disk on worker nodes. For more information, refer to Deploy services to a swarm.

Docker は configs をどう管理しているか

スウォームに対して config を追加すると、Docker は TLS 相互接続によりスウォームマネージャーに対して config を送信します。 この config は Raft ログとして暗号化され保存されます。 Raft ログ全体は、他のマネージャーに向けて複製されますが、スウォームが管理するデータとともに configs の高可用性は確保されます。

新規生成したサービス、あるいは既存のサービスに対して config へのアクセス許可を行うと、config はコンテナー内において 1 つのファイルとしてマウントされます。 コンテナー内のマウントポイントのデフォルトは、Linux コンテナーでは /<config-name> となります。 Windows コンテナーの場合、configs はすべて C:\ProgramData\Docker\configs にマウントされ、 コンテナー内に必要となる config ターゲットが、シンボリックリンクとして生成されます。 config ターゲットのデフォルトは C:\<config-name> です。

config の所有(uidgid)を設定するには、ID 値か、あるいはユーザー名やグループ名を用います。 またファイルパーミッションを設定することもできます。 この設定は Windows コンテナーにおいては無視されます。

  • 所有者が設定されていない場合、config を所有するユーザーは、コンテナーコマンドを実行したユーザー(普通は root)とそのグループ(これも普通は root)になります。
  • 所有者が設定されていない場合、config のパーミッションはすべて読み込み可(0444 モード)となります。 ただしこれはコンテナー内に umask が設定されていない場合であり、これが設定されていれば umask の値設定に従います。

configs を追加した際に、configs にアクセスできるようにサービスをアップデートしたり、configs を再読み込みしたりすることは、どのタイミングでも可能です。

configs へアクセスできるノードはスウォームマネージャーか、あるいはその configs へのアクセスが許可された稼働中のサービスタスクです。 コンテナータスクが停止すると、共有されていた configs は、そのコンテナーのメモリ内ファイルシステムからアンマウントされ、ノードのメモリからも消去されます。

config にアクセスしている稼働中のタスクコンテナーが、スウォームとの接続を失った場合、そのタスクコンテナーの config へのアクセスは維持されます。 ただし config の更新を受け取ることはできず、これができるようになるのはスウォームに再接続した後です。

個々の config を追加したり確認したり、configs すべてを一覧したりすることはいつでもできます。 ただし稼働中のサービスが config を利用している場合は、それを削除できません。 config の入れ替えでは、実行中のサービスを中断することなく config を削除する方法について説明しています。

configs のアップデートやロールバックをより簡単に行うために、config 名にバージョン番号や日付をつけることを考えてみてください。 取り扱うコンテナーの config マウントポイントを自由に管理できれば、より一層簡単になります。

スタックの更新や Compose ファイルの変更を行うには docker stack deploy -c <new-compose-file> <stack-name> を再実行します。 新たな config を用いるようにしたのであれば、それを利用してサービスが起動します。 設定は不変なものであることを忘れないでください。 つまり既存のサービスに対する設定ファイルは変更することはできません。 これを行うなら、新たな設定を別のファイルとして生成してください。

docker stack rm を実行すれば、アプリを止めてスタックを停止させることができます。 このとき、同一のスタック名により docker stack deploy から生成された config は削除されます。 これは すべての configs が削除されるということです。 サービスから参照されていなかったものや、docker service update --config-rm を実行した後に残ったものも、すべて削除されます。

docker config コマンドについての詳細

コマンドの詳細は以下のリンクを参照してください。 また サービスにおける configs の利用例 も参照してください。

利用例

本節では Docker configs の利用例を段階的に示します。

メモ: ここでの利用例では説明を簡単にするために、単一エンジンによるスウォームとスケールアップしていないサービスを用いることにします。 Linux コンテナーを例に用いますが、Windows コンテナーでも configs はサポートされています。

Compose ファイルにおける configs の定義と利用

docker stack コマンドには、Compose ファイルにて configs を定義する機能がサポートされています。 しかし configs キーは docker compose コマンドではサポートされていません。 詳しくは Compose ファイルリファレンス を参照してください。

簡単な例: configs を利用する

この簡単な例では、コマンドを少し書くだけで configs が動作することを示します。 現実的な例としては、応用例: Nginx サービスに configs を利用する に進んでください。

  1. Docker に config を追加します。 この docker config create コマンドは、最後の引数により標準入力から読み込みを行います。 最後の引数は config をどのファイルから読み込むかを示すものであって、ここではそれを - としています。

    $ echo "This is a config" | docker config create my-config -
    
  2. redis サービスを生成し、config に対してのアクセスを許可します。 デフォルトでコンテナーは /my-config にある config へのアクセスが可能です。 コンテナー内のそのファイル名は、target オプションを使って変更することができます。

    $ docker service create --name redis --config my-config redis:alpine
    
  3. docker service ps を実行して、タスクが問題なく実行しているかを確認します。 問題がなければ、出力結果は以下のようになります。

    $ docker service ps redis
    
    ID            NAME     IMAGE         NODE              DESIRED STATE  CURRENT STATE          ERROR  PORTS
    bkna6bpn8r1a  redis.1  redis:alpine  ip-172-31-46-109  Running        Running 8 seconds ago
    
  4. docker ps を実行して、redis サービスのタスクコンテナーに対する ID を取得します。 これを使って docker container exec によりコンテナーにアクセスして、config データファイルの内容を読み込むことができます。 config データファイルはデフォルトで誰でも読むことができ、ファイル名は config 名と同じです。 以下の最初のコマンドは、コンテナー ID を調べるものです。 そして 2 つめと 3 つめは、シェルのコマンド補完を用いて自動的に入力しました。

    $ docker ps --filter name=redis -q
    
    5cb1c2348a59
    
    $ docker container exec $(docker ps --filter name=redis -q) ls -l /my-config
    
    -r--r--r--    1 root     root            12 Jun  5 20:49 my-config
    
    $ docker container exec $(docker ps --filter name=redis -q) cat /my-config
    
    This is a config
    
  5. config を削除してみます。 ただし削除には失敗します。 これは redis サービスが稼働中であり、config にアクセスしているためです。

    
    $ docker config ls
    
    ID                          NAME                CREATED             UPDATED
    fzwcfuqjkvo5foqu7ts7ls578   hello               31 minutes ago      31 minutes ago
    
    
    $ docker config rm my-config
    
    Error response from daemon: rpc error: code = 3 desc = config 'my-config' is
    in use by the following service: redis
    
  6. redis サービスを更新して、稼働中のサービスからの config へのアクセスを取り除きます。

    $ docker service update --config-rm my-config redis
    
  7. 手順の 3 と 4 を繰り返してみます。 このときには、もう config へのアクセスが行われていません。 コンテナー ID は異なるものになっています。 service update コマンドを実行したので、サービスが再デプロイされたためです。

    $ docker container exec -it $(docker ps --filter name=redis -q) cat /my-config
    
    cat: can't open '/my-config': No such file or directory
    
  8. サービスを停止して削除します。 そして Docker から config も削除します。

    $ docker service rm redis
    
    $ docker config rm my-config
    

簡単な例: Windows サービスにて configs を利用する

ここでの簡単な例は Windows 上において configs を利用するものです。 利用にあたっては Microsoft Windows 10 上の Docker for Windows を用いて Windows コンテナーを稼動させて、Microsoft IIS サービスを稼動させます。 この例は config 内にウェブページを保存します。

PowerShell はインストール済であるとします。

  1. 以下のような index.html を新規生成して保存します。

    <html lang="en">
      <head><title>Hello Docker</title></head>
      <body>
        <p>Hello Docker! You have deployed a HTML page.</p>
      </body>
    </html>
    
  2. スウォームの初期化と参加を行っていない場合は、これを行います。

    docker swarm init
    
  3. index.html ファイルを、スウォームの config ファイルとして homepage という名前により保存します。

    docker config create homepage index.html
    
  4. IIS サービスを生成して homepage config へのアクセスを許可します。

    docker service create
        --name my-iis
        --publish published=8000,target=8000
        --config src=homepage,target="\inetpub\wwwroot\index.html"
        microsoft/iis:nanoserver
    
  5. IIS サービスを通じて http://localhost:8000/ にアクセスします。 手順 1 で作り出した HTML 内容が表示されるはずです。

  6. サービスと config を削除します。

    docker service rm my-iis
    
    docker config rm homepage
    

Example: Use a templated config

To create a configuration in which the content will be generated using a template engine, use the --template-driver parameter and specify the engine name as its argument. The template will be rendered when container is created.

  1. Save the following into a new file index.html.tmpl.

    <html lang="en">
      <head><title>Hello Docker</title></head>
      <body>
        <p>Hello {{ env "HELLO" }}! I'm service {{ .Service.Name }}.</p>
      </body>
    </html>
    
  2. Save the index.html.tmpl file as a swarm config named homepage. Provide parameter --template-driver and specify golang as template engine.

    $ docker config create --template-driver golang homepage index.html.tmpl
    
  3. Create a service that runs Nginx and has access to the environment variable HELLO and to the config.

    $ docker service create \
         --name hello-template \
         --env HELLO="Docker" \
         --config source=homepage,target=/usr/share/nginx/html/index.html \
         --publish published=3000,target=80 \
         nginx:alpine
    
  4. Verify that the service is operational: you can reach the Nginx server, and that the correct output is being served.

    $ curl http://0.0.0.0:3000
    
    <html lang="en">
      <head><title>Hello Docker</title></head>
      <body>
        <p>Hello Docker! I'm service hello-template.</p>
      </body>
    </html>
    

応用例: Nginx サービスに configs を利用する

この例は 2 つの部分から構成されます。 1 つめの部分は、サーバー証明書の生成に関してです。 Docker configs とは直接関係がありません。 ただし 2 つめの部分において、一連の機密情報としてそのサーバー証明書を保存して利用します。 また Nginx の設定を config として保存します。 この例では config におけるオプションの設定方法を示しており、たとえばコンテナー内のターゲットを指定したり、ファイルパーミッションを指定したりしています。

サーバー証明書の生成

自サイトに対しての root CA と TLS 証明書および鍵を生成します。 本番環境向けでは Let’s Encrypt のようなサービスを利用して、TLS 証明書や鍵を生成するかもしれませんが、この例ではコマンドラインツールを用いることにします。 ここでの手順は多少複雑です。 ただしここでは唯一、Docker secret を使って情報を保存する手順を示すものです。 この手順を行わない場合は、Let’s Encrypt の利用を通じて、サイトの鍵と証明書を生成し、それぞれを site.keysite.crt としてください。 その場合は Nginx コンテナーの設定 に進んでください。

  1. root 鍵を生成します。

    $ openssl genrsa -out "root-ca.key" 4096
    
  2. root 鍵を使って CSR を生成します。

    $ openssl req \
              -new -key "root-ca.key" \
              -out "root-ca.csr" -sha256 \
              -subj '/C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA'
    
  3. root CA を設定します。 新規に root-ca.cnf というファイルを生成して、以下の内容を書き込みます。 ここでは root CA をリーフ証明書として生成し、中間証明書とはしません。

    [root_ca]
    basicConstraints = critical,CA:TRUE,pathlen:1
    keyUsage = critical, nonRepudiation, cRLSign, keyCertSign
    subjectKeyIdentifier=hash
    
  4. 証明書にサインします。

    $ openssl x509 -req -days 3650 -in "root-ca.csr" \
                   -signkey "root-ca.key" -sha256 -out "root-ca.crt" \
                   -extfile "root-ca.cnf" -extensions \
                   root_ca
    
  5. サイト鍵を生成します。

    $ openssl genrsa -out "site.key" 4096
    
  6. サイト証明書を生成し、サイト鍵を用いてサインします。

    $ openssl req -new -key "site.key" -out "site.csr" -sha256 \
              -subj '/C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost'
    
  7. サイト証明書を設定します。 新規に site.cnf というファイルを生成して、以下の内容を書き込みます。 この証明書はサーバーを認証するためだけに用いるものとし、他の証明書のサインには用いることができないようにします。

    [server]
    authorityKeyIdentifier=keyid,issuer
    basicConstraints = critical,CA:FALSE
    extendedKeyUsage=serverAuth
    keyUsage = critical, digitalSignature, keyEncipherment
    subjectAltName = DNS:localhost, IP:127.0.0.1
    subjectKeyIdentifier=hash
    
  8. サイト証明書にサインします。

    $ openssl x509 -req -days 750 -in "site.csr" -sha256 \
        -CA "root-ca.crt" -CAkey "root-ca.key" -CAcreateserial \
        -out "site.crt" -extfile "site.cnf" -extensions server
    
  9. site.csrsite.cnf は Nginx サービスにとっては不要です。 ただし新たなサイト証明書を生成する際には必要になります。 root-ca.key は大事に保管しておきます。

Nginx コンテナーの設定

  1. Nginx の基本的な設定として、HTTPS 越しにスタティックファイルを提供するものを用意します。 TLS 証明書と鍵は Docker secrets として保存します。 こうしておけば config の入れ替えも簡単に行うことができます。

    カレントディレクトリにおいて、site.conf というファイルを新規生成し、内容を以下のようにします。

    server {
        listen                443 ssl;
        server_name           localhost;
        ssl_certificate       /run/secrets/site.crt;
        ssl_certificate_key   /run/secrets/site.key;
    
        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }
    }
    
  2. 鍵と証明書を表わす Docker secrets を 2 つ生成します。 Docker secrets はどのようなファイルであっても、サイズが 500 KB 以下であれば保存できます。 こうして鍵と証明書は、これを利用するサービスから切り離すことができます。 ここでの例では、secrets とファイル名は同一にしています。

    $ docker secret create site.key site.key
    
    $ docker secret create site.crt site.crt
    
  3. Docker config の中に site.conf ファイルを保存します。 第 1 パラメーターは config 名、第 2 パラメーターはそれを読み込むファイル名です。

    $ docker config create site.conf site.conf
    

    configs の一覧を確認します。

    $ docker config ls
    
    ID                          NAME                CREATED             UPDATED
    4ory233120ccg7biwvy11gl5z   site.conf           4 seconds ago       4 seconds ago
    
  4. Nginx を起動するサービスを生成し、2 つの secrets と config へのアクセスを許可します。 モードは 0440 とし、読み込み可とするのは所有者とそのグループのみ、つまりすべてへの読み込み許可は与えないようにします。

    $ docker service create \
         --name nginx \
         --secret site.key \
         --secret site.crt \
         --config source=site.conf,target=/etc/nginx/conf.d/site.conf,mode=0440 \
         --publish published=3000,target=443 \
         nginx:latest \
         sh -c "exec nginx -g 'daemon off;'"
    

    稼動中のコンテナー内部では、以下の 3 つのファイルが存在しています。

    • /run/secrets/site.key
    • /run/secrets/site.crt
    • /etc/nginx/conf.d/site.conf
  5. Nginx サービスが起動していることを確認します。

    $ docker service ls
    
    ID            NAME   MODE        REPLICAS  IMAGE
    zeskcec62q24  nginx  replicated  1/1       nginx:latest
    
    $ docker service ps nginx
    
    NAME                  IMAGE         NODE  DESIRED STATE  CURRENT STATE          ERROR  PORTS
    nginx.1.9ls3yo9ugcls  nginx:latest  moby  Running        Running 3 minutes ago
    
  6. そのサービスが操作可能であることを確認します。 つまり Nginx サーバーへアクセスができ、正しい TLS 証明書が用いられていることを確認します。

    $ curl --cacert root-ca.crt https://0.0.0.0:3000
    
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    <style>
        body {
            width: 35em;
            margin: 0 auto;
            font-family: Tahoma, Verdana, Arial, sans-serif;
        }
    </style>
    </head>
    <body>
    <h1>Welcome to nginx!</h1>
    <p>If you see this page, the nginx web server is successfully installed and
    working. Further configuration is required.</p>
    
    <p>For online documentation and support, refer to
    <a href="https://nginx.org">nginx.org</a>.<br/>
    Commercial support is available at
    <a href="https://www.nginx.com">www.nginx.com</a>.</p>
    
    <p><em>Thank you for using nginx.</em></p>
    </body>
    </html>
    
    $ openssl s_client -connect 0.0.0.0:3000 -CAfile root-ca.crt
    
    CONNECTED(00000003)
    depth=1 /C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA
    verify return:1
    depth=0 /C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost
    verify return:1
    ---
    Certificate chain
     0 s:/C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost
       i:/C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA
    ---
    Server certificate
    -----BEGIN CERTIFICATE-----
    …
    -----END CERTIFICATE-----
    subject=/C=US/ST=CA/L=San Francisco/O=Docker/CN=localhost
    issuer=/C=US/ST=CA/L=San Francisco/O=Docker/CN=Swarm Secret Example CA
    ---
    No client certificate CA names sent
    ---
    SSL handshake has read 1663 bytes and written 712 bytes
    ---
    New, TLSv1/SSLv3, Cipher is AES256-SHA
    Server public key is 4096 bit
    Secure Renegotiation IS supported
    Compression: NONE
    Expansion: NONE
    SSL-Session:
        Protocol  : TLSv1
        Cipher    : AES256-SHA
        Session-ID: A1A8BF35549C5715648A12FD7B7E3D861539316B03440187D9DA6C2E48822853
        Session-ID-ctx:
        Master-Key: F39D1B12274BA16D3A906F390A61438221E381952E9E1E05D3DD784F0135FB81353DA38C6D5C021CB926E844DFC49FC4
        Key-Arg   : None
        Start Time: 1481685096
        Timeout   : 300 (sec)
        Verify return code: 0 (ok)
    
  7. この例を実行した後に、次に示す例は確認しないのであれば、nginx サービスと保存した secrets、config を削除します。

    $ docker service rm nginx
    
    $ docker secret rm site.crt site.key
    
    $ docker config rm site.conf
    

ここまでの例から Nginx サービスの設定内容を、そのイメージから切り離した形で実現しました。 まったく同じイメージを使い異なる設定によって複数サイトを提供しようと思ったら、もう新たなイメージをビルドする必要はなくなったわけです。

例: config の入れ替え

config を入れ替えるには、まず新たな config を、現在利用している config とは別の名前で保存しておきます。 そしてサービスを再デプロイし、古い config を削除して、コンテナー内の同一マウントポイントに新たな config を追加します。 ここに示す例では、前述の例をもとにして、site.conf という設定ファイルを切り替える方法を示します。

  1. ローカルの site.conf ファイルを編集します。 index 行に index.php を追加し保存します。

    server {
        listen                443 ssl;
        server_name           localhost;
        ssl_certificate       /run/secrets/site.crt;
        ssl_certificate_key   /run/secrets/site.key;
    
        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm index.php;
        }
    }
    
  2. 上の site.conf ファイルを使って、新たなsite-v2.conf という Docker config を生成します。

    $ docker config create site-v2.conf site.conf
    
  3. nginx サービスを更新して、古い config から新しい config を利用するようにします。

    $ docker service update \
      --config-rm site.conf \
      --config-add source=site-v2.conf,target=/etc/nginx/conf.d/site.conf,mode=0440 \
      nginx
    
  4. docker service ps nginx を実行して、nginx サービスが問題なく再デプロイされていることを確認します。 正常であれば、古い config つまり site.conf を削除します。

    $ docker config rm site.conf
    
  5. クリーンアップします。 nginx サービスを削除し、同じく secrets と configs も削除します。

    $ docker service rm nginx
    
    $ docker secret rm site.crt site.key
    
    $ docker config rm site-v2.conf
    

こうして nginx サービスの設定は、イメージを再ビルドすることなく更新することができました。

swarm, configuration, configs