Docker Swarm mode を知る (secret)

Docker

今回は docker secret を使ってみます。

Manage sensitive data with Docker secrets に使い方が書かれています。Kubernetes の Secrets とかなり似てます。

docker secret コマンドの syntax は次のようになってます。

$ docker secret --help

Usage:  docker secret COMMAND

Manage Docker secrets

Options:
      --help   Print usage

Commands:
  create      Create a secret from a file or STDIN as content
  inspect     Display detailed information on one or more secrets
  ls          List secrets
  rm          Remove one or more secrets

Run 'docker secret COMMAND --help' for more information on a command.

Secret の登録

docker secret create で作成します。ファイルの中身を値として登録するか、ファイルの path として “-” を指定することで標準入力から値を読み取って登録します。

$ docker secret create --help

Usage:  docker secret create [OPTIONS] SECRET [file|-]

Create a secret from a file or STDIN as content

Options:
  -d, --driver string   Secret driver
      --help            Print usage
  -l, --label list      Secret labels

登録時に表示される文字列は作成した secret を指す ID です。

$ echo password | docker secret create mypass -
mhop3cx9kk5xuz4b95nokvzov
$ docker secret ls
ID                          NAME                DRIVER              CREATED             UPDATED
mhop3cx9kk5xuz4b95nokvzov   mypass                                  5 seconds ago       5 seconds ago

docker secret inspect で secret のメタ情報が確認ができます

$ docker secret inspect mypass
[
    {
        "ID": "mhop3cx9kk5xuz4b95nokvzov",
        "Version": {
            "Index": 16
        },
        "CreatedAt": "2018-03-24T14:41:50.123712806Z",
        "UpdatedAt": "2018-03-24T14:41:50.123712806Z",
        "Spec": {
            "Name": "mypass",
            "Labels": {}
        }
    }
]

ID でも名前でも確認できます。

$ docker secret inspect mhop3cx9kk5xuz4b95nokvzov
[
    {
        "ID": "mhop3cx9kk5xuz4b95nokvzov",
        "Version": {
            "Index": 16
        },
        "CreatedAt": "2018-03-24T14:41:50.123712806Z",
        "UpdatedAt": "2018-03-24T14:41:50.123712806Z",
        "Spec": {
            "Name": "mypass",
            "Labels": {}
        }
    }
]

同名の secret を複数作成することはできません

$ echo hogehoge | docker secret create mypass -
Error response from daemon: rpc error: code = AlreadyExists desc = secret mypass already exists

では値を更新したい場合はどうするかというと、secretservice で使うわけですが、どの secret をどんな名前で参照させるかを指定できるようになっているため、まず app_passwd_v1passwd として参照するようにしておき、docker service updateapp_passwd_v2passwd で参照するように service を更新します。

Service から secret を参照する

secretdocker run で起動するコンテナでは使えず、service を使う必要があります。

$ docker service create --detach --name nginx --secret mypass nginx:latest
wxmhxc0po5fxzbfh2i84ug2nf
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
622efaa68880        nginx:latest        "nginx -g 'daemon ..."   56 seconds ago      Up 56 seconds       80/tcp              nginx.1.zfcqa0ofjygrxe30jm1ftjb89

/run/secrets/ に secret 名のファイルができます

$ docker exec 622efaa68880 cat //run/secrets/mypass
password

(Windows の git-bash を使ってるので無駄な “/” があります)

Service が参照する secret を入れ替える

先程説明した方法で mypass を更新してみます

新しく mypass2 という名前の secret を作成する

$ echo hogehoge | docker secret create mypass2 -
6kuu6o995snvokzudp1m5y7f1

先程は --secretsecret 名を指定しただけでした。これは secret 名をそのまま /run/secrets/ 下のファイル名として参照させることになりますが、sourcetarget を別に指定することで secret 名と参照名を別にできます。

$ docker service update --secret-add source=mypass2,target=mypass --secret-rm mypass nginx
nginx

service が更新され、コンテナが生まれ変わりました

$ docker service ps nginx
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE             ERROR               PORTS
rmj99vdjpipe        nginx.1             nginx:latest        myvm1               Running             Running 42 seconds ago
zfcqa0ofjygr         \_ nginx.1         nginx:latest        myvm1               Shutdown            Shutdown 43 seconds ago

docker ps で container id を確認して

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
7198b32c47a0        nginx:latest        "nginx -g 'daemon ..."   About a minute ago   Up About a minute   80/tcp              nginx.1.rmj99vdjpipevrhpo2walrmm2

mypass を確認してみます。更新されてますね。

$ docker exec 7198b32c47a0 cat //run/secrets/mypass
hogehoge

docker service inspect で確認してみます

$ docker service inspect nginx | jq .[].Spec.TaskTemplate.ContainerSpec.Secrets
[
  {
    "File": {
      "Name": "mypass",
      "UID": "0",
      "GID": "0",
      "Mode": 292
    },
    "SecretID": "6kuu6o995snvokzudp1m5y7f1",
    "SecretName": "mypass2"
  }
]

service は PreviousSpec に一つ前の情報も持っているのですね

$ docker service inspect nginx | jq .[].PreviousSpec.TaskTemplate.ContainerSpec.Secrets
[
  {
    "File": {
      "Name": "mypass",
      "UID": "0",
      "GID": "0",
      "Mode": 292
    },
    "SecretID": "mhop3cx9kk5xuz4b95nokvzov",
    "SecretName": "mypass"
  }
]

Stack で secret を使う

次の内容で docker-compose.yml を用意します

version: '3.1'

services:
   db:
     image: mysql:latest
     volumes:
       - db_data:/var/lib/mysql
     environment:
       MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD_FILE: /run/secrets/db_password
     secrets:
       - db_root_password
       - db_password

   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
     secrets:
       - db_password


secrets:
   db_password:
     file: db_password.txt
   db_root_password:
     file: db_root_password.txt

volumes:
    db_data:

db_password.txt, db_root_password.txt というパスワードの書かれたファイルを用意して docker stack deploy で deploy します

$ docker stack deploy --compose-file docker-compose.yml secrets-test
Creating network secrets-test_default
Creating service secrets-test_wordpress
Creating service secrets-test_db

secret が作成されています stack 名が secret 名の prefix として付加されていますね

$ docker secret ls
ID                          NAME                            DRIVER              CREATED             UPDATED
mhop3cx9kk5xuz4b95nokvzov   mypass                                              About an hour ago   About an hour ago
6kuu6o995snvokzudp1m5y7f1   mypass2                                             16 minutes ago      16 minutes ago
jl70yk1b4ydo5rhc5mol7pybp   secrets-test_db_password                            20 seconds ago      20 seconds ago
in18f83ord099qiu2q8r4ctkc   secrets-test_db_root_password                       20 seconds ago      20 seconds ago

docker ps で container id を確認

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
c9ae594886b9        mysql:latest        "docker-entrypoint..."   3 minutes ago       Up 3 minutes        3306/tcp            secrets-test_db.1.35sznd2qiyucfstwepz85o22h
7198b32c47a0        nginx:latest        "nginx -g 'daemon ..."   18 minutes ago      Up 18 minutes       80/tcp              nginx.1.rmj99vdjpipevrhpo2walrmm2

ファイルがありますね

$ docker exec c9ae594886b9 ls //run/secrets/
db_password
db_root_password

中身も確認できました

$ docker exec c9ae594886b9 cat //run/secrets/db_password
DbAppPass
$ docker exec c9ae594886b9 cat //run/secrets/db_root_password
DbRootPass

パスワードファイルを書き換えて deploy すると secret のパスワードは更新されるでしょうか?

$ cat db_password.txt
DbAppPass2
$ docker stack deploy --compose-file docker-compose.yml  secrets-test
failed to update secret secrets-test_db_password: Error response from daemon: rpc error: code = InvalidArgument desc = only updates to Labels are allowed

ダメでした…

servicessecrets には LONG SYNTAX があり、source, target, uid, gid, mode が指定可能でした。そこで、docker-compose.yml を次のように書き換えて deploy すると更新できました。

version: '3.1'

services:
   db:
     image: mysql:latest
     volumes:
       - db_data:/var/lib/mysql
     environment:
       MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD_FILE: /run/secrets/db_password
     secrets:
       - db_root_password
       - source: db_password2
         target: db_password

   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
     secrets:
       - source: db_password2
         target: db_password


secrets:
   db_password2:
     file: db_password2.txt
   db_root_password:
     file: db_root_password.txt

volumes:
    db_data:

更新のため、再 deploy

$ docker stack deploy --compose-file docker-compose.yml  secrets-test
Updating service secrets-test_db (id: thimv598po993rnbetm85z7kt)
Updating service secrets-test_wordpress (id: jkje0u6n7z690zm0y34p9j2go)

secret が増えました

$ docker secret ls
ID                          NAME                            DRIVER              CREATED             UPDATED
mhop3cx9kk5xuz4b95nokvzov   mypass                                              About an hour ago   About an hour ago
6kuu6o995snvokzudp1m5y7f1   mypass2                                             31 minutes ago      31 minutes ago
jl70yk1b4ydo5rhc5mol7pybp   secrets-test_db_password                            15 minutes ago      15 minutes ago
m8drh3ldgnt0vstft2xewte78   secrets-test_db_password2                           39 seconds ago      39 seconds ago
in18f83ord099qiu2q8r4ctkc   secrets-test_db_root_password                       15 minutes ago      39 seconds ago

コンテナが更新されて

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
722657b03d5f        mysql:latest        "docker-entrypoint..."   42 seconds ago      Up 36 seconds       3306/tcp            secrets-test_db.1.mw3wxtfgagoei3bxd8c3yfsh5
7198b32c47a0        nginx:latest        "nginx -g 'daemon ..."   29 minutes ago      Up 29 minutes       80/tcp              nginx.1.rmj99vdjpipevrhpo2walrmm2

パスワードも更新されました。まあ、コンテナ起動時にこの変更が DB に反映されるのかというのは別問題だけどね

$ docker exec 722657b03d5f cat //run/secrets/db_password
DbAppPass2

以上