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

configs について

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

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

注釈

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

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

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> です。

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

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

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

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

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

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

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

  • docker config create
  • docker config inspect
  • docker config ls
  • docker config rm

利用例

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

注釈

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

簡単な例: 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 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 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 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 Server 2016 上の Docker 17.06 EE、または Microsoft Windows 10 上の Docker Windows 17.06 CE を用いて Microsoft IIS サービスを稼動させます。 この例は config 内にウェブページを保存します。

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

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

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

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

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

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

    PS> docker service rm my-iis
    
    PS> docker config rm homepage
    

応用例: 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;
        }
    }
    
  1. 鍵と証明書を表わす Docker secrets を 2 つ生成します。 Docker secrets はどのようなファイルであっても、サイズが 500 KB 以下であれば保存できます。 こうして鍵と証明書は、これを利用するサービスから切り離すことができます。 ここでの例では、secrets とファイル名は同一にしています。

    $ docker secret create site.key site.key
    
    $ docker secret create site.crt site.crt
    
  1. 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
    
  1. Nginx を起動するサービスを生成し、2 つの secrets と config へのアクセスを許可します。

    $ docker service create \
         --name nginx \
         --secret site.key \
         --secret site.crt \
         --config source=site.conf,target=/etc/nginx/conf.d/site.conf \
         --publish 3000: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
  1. 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
    
  1. そのサービスが操作可能であることを確認します。 つまり 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 please refer to
    <a href="http://nginx.org/">nginx.org</a>.<br/>
    Commercial support is available at
    <a href="http://nginx.com/">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)
    
  1. この例を実行した後に、次に示す例は確認しないのであれば、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;
        }
    }
    
  1. 上の site.conf ファイルを使って、新たな site-v2.conf という Docker config を生成します。

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

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

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

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

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

参考

Store configuration data using Docker Configs
https://docs.docker.com/engine/swarm/configs/