プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

Roundcube を動かしている docker コンテナのストレージ設定を変更

docker のコンテナで動かしている Roundcube ですが、先日メールを送る際に、添付ファイルをつけ忘れるというあるあるをやってしまいました。メールのありがちなミスですね。

で、調べてみると Roundcube のプラグインで添付ファイル忘れを警告してくれるもの( attachment_reminder )があるので、それを導入することにしました。

Roundcubeのプラグイン〜添付ファイル〜 | OSSのデージーネット

せっかくなので、ついでに、 Roundcube のバージョンも上げたいと思います。ということで、その作業に先立ち、まずは Roundcube のコンテナの永続データの持ち方を見直してみようと思いましたので、その際の作業をメモっておきます。

なんで永続データの持ち方を変えるのか

いま動いている Roundcube のコンテナの設定内容を確認してみます。こちらの記事で紹介したように docker-compose.yml は

version: '3'

services:
  https-portal:
    image: steveltn/https-portal:1
    volumes:
      - ./data/ssl_certs:/var/lib/https-portal
    (略)
  
  roundcubemail:
    image: myroundcube:latest
    container_name: myrc
    volumes:
      - ./www:/var/www/html
      - ./db/sqlite:/var/roundcube/db
    (略)

となっており、2つのコンテナで3つのボリューム(厳密にはバインドマウント)を指定しています。

これらは、 QNAP 上のファイルシステムの特定のフォルダを、docker 側に割り当てる方法になっています。実際のデータは、 QNAP 側の Container Station 用のデータ領域下にある application/(アプリケーション名)/ 以下に、 www や data といったフォルダが作られ,、その中に保存されています。

普段だと、コンテナが使っているデータを直接見ることができて、それなりに便利だし、なによりわかりやすいと思います。が、気をつけないといけないのが、

  • QNAP でアプリケーションを削除すると、このフォルダ(アプリケーション名のフォルダ)は消されてしまう

という点です(QNAP でというのがポイントです)。

今回調べて初めて知ったのですが、 docker の場合、起動したコンテナに対して、元にしたイメージを最新版に更新するには、一度コンテナを削除して再度立ち上げる必要がある、という挙動になるそうです。ちなみに、 QNAP の Container Station には再作成というのがありますが、やってることは一度削除して同じ名前で作っているだけですので、本質的には同じですね。

となると、せっかくデータを残したいと思って、わざわざフォルダを指定しているのに、QNAP でコンテナを作り直すと、そこのデータが消えてしまうことになります(もっとも、アプリケーション名の外部のフォルダを最初から指定しておけば、消えることはないですけどね)。

ということで、QNAP 上でコンテナ(アプリケーション)を消しても、消えてしまわないように、永続データを持ち方を変更することにします(一応書いておくと、 QNAP 上から操作しないのであれば不要な作業になります)。

データの保存方法

docker でのデータの保存方法を調べてみると、下記のドキュメントにあるように3つ存在するようです。

Docker でのデータ管理 — Docker-docs-ja 24.0 ドキュメント

しかし、 tmpfs マウントはメモリ上にしか作られないので、ホスト側にファイルを作るものとしては、バインドマウントとボリュームになります。ボリュームにも、名前付きボリュームと匿名ボリュームがあるそうです。今回は、 docker が管理する名前付きボリュームを使ってみようと思います。

ちなみに、ネットを調べていると、この『ボリューム』という用語が、上記のドキュメントにある docker が管理する領域を利用する方法を指す場合と、バインドマウントを含めてホストとファイルを共有する方法という2つの意味で使われているため、なかなかややこしいですね。

永続化したいデータを名前付きボリュームへ移動

ということで、バインドマウントではなく、名前付きボリュームを作成して、そちらにデータを持たせるようにします。今回は名前付きボリュームが、既に動かしているアプリケーションのデータを移行する移行先になるため、手作業で名前付きボリュームを作りたいと思います。

名前付きボリュームの作成

作業前の名前付きボリュームはこんな感じになってました。

[ユーザ名@NAS名 ~]$ docker volume ls
DRIVER    VOLUME NAME
local     2f40159df0420ac580e90daf4ab80d6d7e6254702ccdc2dee6e1c919fe4e38ca
local     89150fae22bbd638b52d990488969696fedf86e9c9c82b51c3e5980634f21bdf
local     186999773b4d354046bcefbfcfc9fad5fb034ecf97d21a0eddde94e92c4cf710
local     c30c7cc6ef15fe2f9ace1a799d4afa1a0e1935eb0ebcb745f87e48aa261e9cfe
local     wp_db_data
local     wp_www
[ユーザ名@NAS名 ~]$ 

まずは、名前付きボリュームを作ります。今回の Roundcube アプリケーションでは3つのボリュームを使っているので、3つ作ります。

[ユーザ名@NAS名 ~]$ docker volume create rcmail_ssl_certs
rcmail_ssl_certs
[ユーザ名@NAS名 ~]$ docker volume create rcmail_www
rcmail_www
[ユーザ名@NAS名 ~]$ docker volume create rcmail_sqlite
rcmail_sqlite
[ユーザ名@NAS名 ~]$ docker volume ls
DRIVER    VOLUME NAME
local     2f40159df0420ac580e90daf4ab80d6d7e6254702ccdc2dee6e1c919fe4e38ca
local     89150fae22bbd638b52d990488969696fedf86e9c9c82b51c3e5980634f21bdf
local     186999773b4d354046bcefbfcfc9fad5fb034ecf97d21a0eddde94e92c4cf710
local     c30c7cc6ef15fe2f9ace1a799d4afa1a0e1935eb0ebcb745f87e48aa261e9cfe
local     rcmail_sqlite
local     rcmail_ssl_certs
local     rcmail_www
local     wp_db_data
local     wp_www
[ユーザ名@NAS名 ~]$ 

データの移行作業

次に、今のバインドマウントにあるデータを作成した名前付きボリュームに移行させます。念のため、一度アプリケーションを停止させ、作業前にアプリケーションフォルダ以下にあるデータのバックアップを取っておきます。

バックアップが取れたら、次にデータを名前付きボリュームにコピーします。名前付きボリュームへデータをコピーするには、一時的な作業用のコンテナを立ち上げて、それに対してファイルをコピーするのが一番簡単で確実なようです。

一時的な作業用コンテナは busybox を利用しています。立ち上げ時に -v で名前付きボリュームをコンテナ内のフォルダにマウントしておきます。

[ユーザ名@NAS名 アプリケーション名]$ ls
data/  db/  docker-compose.resource.yml  docker-compose.yml  qnap.json  www/
[ユーザ名@NAS名 アプリケーション名]$ 
[ユーザ名@NAS名 アプリケーション名]$ docker run -d --name tmp_container -v rcmail_ssl_certs:/data busybox tail -F /dev/null
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
ec562eabd705: Pull complete 
Digest: sha256:9ae97d36d26566ff84e8893c64a6dc4fe8ca6d1144bf5b87b2b85a32def253c7
Status: Downloaded newer image for busybox:latest
c764efa16e97b60f0e23d8a807db5405855123b3b88efdcf8a7bffaec5e8315c
[ユーザ名@NAS名 アプリケーション名]$ 

ここで tail -F /dev/null を指定しておくことで、なにもせず停止もしないコンテナを立ち上げることができます。なお、今回は始めて busybox を使ったのでイメージのダウンロードとかも行われました。

コンテナ(tmp_container)が起動したら docker cp でファイルをコピーします。

[ユーザ名@NAS名 アプリケーション名]$ docker cp data/ssl_certs/. tmp_container:/data/
ERRO[0000] Can't add file /(フルパス)/(アプリケーション名)/data/ssl_certs/default_server/default_server.key to tar: open /(フルパス)/(アプリケーション名)/data/ssl_certs/default_server/default_server.key: permission denied 
ERRO[0000] Can't add file /(フルパス)/(アプリケーション名)/data/ssl_certs/dhparam.pem to tar: archive/tar: missed writing 1704 bytes 
(略)
Error response from daemon: Error processing tar file(exit status 1): unexpected EOF
[ユーザ名@NAS名 アプリケーション名]$ 

これでうまくいくはんずだったんですが、証明書関係のファイルについてはコピー時にパーミッションで怒られてしまいました。なので、sudoをつけて再実行してみます。

[ユーザ名@NAS名 アプリケーション名]$ sudo docker cp data/ssl_certs/. tmp_container:/data/
Password: 
[ユーザ名@NAS名 アプリケーション名]$ 

問題なくコピーできました。作業が終われば、このコンテナは削除しておきます。

[ユーザ名@NAS名 アプリケーション名]$ docker stop tmp_container
tmp_container
[ユーザ名@NAS名 アプリケーション名]$ docker rm tmp_container
tmp_container
[ユーザ名@NAS名 アプリケーション名]$ 

同様に、他の2つの名前付きボリュームにもコピーしておきます

[ユーザ名@NAS名 アプリケーション名]$ docker run -d --name tmp_container -v rcmail_www:/data busybox tail -F /dev/null
af2454e5010061c4d4fae5ff27d8ac7245a78716f3e8a72abf94b078b2ab3e28
[ユーザ名@NAS名 アプリケーション名]$ docker cp www/. tmp_container:/data/
[ユーザ名@NAS名 アプリケーション名]$ docker stop tmp_container
tmp_container
[ユーザ名@NAS名 アプリケーション名]$ docker rm tmp_container
tmp_container
[ユーザ名@NAS名 アプリケーション名]$ 
[ユーザ名@NAS名 アプリケーション名]$ docker run -d --name tmp_container -v rcmail_sqlite:/data busybox tail -F /dev/null
e5326f49e14e4a2847611ed41c4d796181a975d8cd2414defe69adf6fe0e544e
[ユーザ名@NAS名 アプリケーション名]$ docker cp db/sqlite/. tmp_container:/data/
[ユーザ名@NAS名 アプリケーション名]$ docker stop tmp_container
tmp_container
[ユーザ名@NAS名 アプリケーション名]$ docker rm tmp_container
tmp_container
[ユーザ名@NAS名 アプリケーション名]$ 

なお、バインドマウントの data と db については、そのサブフォルダをコピーしている点、ご注意ください。

アプリケーションの再起動

ここまでできたら、docker-compose.yml を変更して、名前付きボリュームを使って起動するようにしてみます。

コンテナイメージにタグ付け

その前に、いまのコンテナイメージにタグ付けを行っておきます。この後 Roundcube のアップデートなどをやり始めると、コンテナイメージをいろいろと変更することになるので、現状動作しているイメージを見失わないようにするためです。

【Docker】イメージ名とタグ名を変更する方法 | エンジニアの眠れない夜

まずは、対象となるイメージのイメージ ID を調べます。

[ユーザ名@NAS名 アプリケーション名]$ docker images
REPOSITORY                  TAG                         IMAGE ID       CREATED         SIZE
myroundcube                 latest                      664cbf63c985   10 months ago   596MB
[ユーザ名@NAS名 アプリケーション名]$ 

イメージIDがわかりましたのでタグをつけます。

[ユーザ名@NAS名 アプリケーション名]$ docker tag 664cbf63c985 myroundcube:0.1

タグが変わったことを確認します。

[ユーザ名@NAS名 アプリケーション名]$ docker images
REPOSITORY                  TAG                         IMAGE ID       CREATED         SIZE
myroundcube                 0.1                         664cbf63c985   10 months ago   596MB
myroundcube                 latest                      664cbf63c985   10 months ago   596MB
[ユーザ名@NAS名 アプリケーション名]$ 

無事変わったというか、タグが増えてますね。

アプリケーションの起動

QNAP の Container Station から『アプリケーション』を選択し、いま動いているアプリケーションを選びます。歯車アイコンをクリックし、『再作成』を選びます。

docker-compose.yml を入力する画面が表示されるので、下記のようにします。

version: '3'

services:
  https-portal:
    image: steveltn/https-portal:1.23.1
    ports:
      - 'xxxx:80'
      - 'yyyy:443'
    restart: always
    volumes:
      - rcmail_ssl_certs:/var/lib/https-portal
    environment:
      DOMAINS: 'xxxx.yyyy.zzzz -> http://roundcubemail:80'
      STAGE: 'production'
      # FORCE_RENEW: 'true'
      CLIENT_MAX_BODY_SIZE: 0
  
  roundcubemail:
    image: myroundcube:0.1
    container_name: myrc
#    restart: unless-stopped
    volumes:
      - rcmail_www:/var/www/html
      - rcmail_sqlite:/var/roundcube/db
#    ports:
#      - 9001:80
    environment:
      - ROUNDCUBEMAIL_DB_TYPE=sqlite
      - ROUNDCUBEMAIL_SKIN=elastic
      - ROUNDCUBEMAIL_DEFAULT_HOST=ssl://メールサーバー名
      - ROUNDCUBEMAIL_DEFAULT_PORT=ポート番号
      - ROUNDCUBEMAIL_SMTP_SERVER=ssl://メールサーバー名
      - ROUNDCUBEMAIL_SMTP_PORT=ポート番号
      - ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE=30M

volumes:
    rcmail_ssl_certs:
        external: true
    rcmail_www:
        external: true
    rcmail_sqlite:
        external: true

以前のものと比較して

  • バインドマウントではなく、ボリュームを指定した
  • アプリケーションのイメージのタグをバージョン番号まで含めて指定した

という点が主に異なる点になります。

入力が終わったら、『更新』ボタンを押します。

しばらく待つと、既存のアプリケーション(およびコンテナ)の削除が行われ、続いて作成が行われます。なお、画面上の進捗表示がうまく更新されないことがあるようで、しばらく待っても変化がないような場合は、 ContainerStation 自体を一度閉じてから再度開くと進展がある場合があります。

エラー等がなく無事に起動したら完了です。 あとは、 Roundcube にログインして、問題なく動作するか確認しておきます。特に問題もなく無事に動いていました。

まとめ

これで、永続データを(狭義の)ボリュームに移行できたので、次は Roundcube 自体のアップデートを行ってみたいと思います。