はじめに
dynamonodb-localのDockerイメージを使用する際に、データの永続化のためにvolumesを指定したが、以下のようにwarningが出てホスト側のマウント先に書き込みができずに困った。今回はその解決策について備忘録を残す。
dynamodb-local | WARNING: [sqlite] cannot open DB[1]: com.almworks.sqlite4java.SQLiteException: [14] unable to open database file
dynamodb-local | Apr 30, 2022 12:02:35 PM com.almworks.sqlite4java.Internal log
dynamodb-local | SEVERE: [sqlite] SQLiteQueue[shared-local-instance.db]: error running job queue
dynamodb-local | com.almworks.sqlite4java.SQLiteException: [14] unable to open database file
dynamodb-local | at com.almworks.sqlite4java.SQLiteConnection.open0(SQLiteConnection.java:1480)
dynamodb-local | at com.almworks.sqlite4java.SQLiteConnection.open(SQLiteConnection.java:282)
dynamodb-local | at com.almworks.sqlite4java.SQLiteConnection.open(SQLiteConnection.java:293)
dynamodb-local | at com.almworks.sqlite4java.SQLiteQueue.openConnection(SQLiteQueue.java:464)
dynamodb-local | at com.almworks.sqlite4java.SQLiteQueue.queueFunction(SQLiteQueue.java:641)
dynamodb-local | at com.almworks.sqlite4java.SQLiteQueue.runQueue(SQLiteQueue.java:623)
dynamodb-local | at com.almworks.sqlite4java.SQLiteQueue.access$000(SQLiteQueue.java:77)
dynamodb-local | at com.almworks.sqlite4java.SQLiteQueue$1.run(SQLiteQueue.java:205)
dynamodb-local | at java.lang.Thread.run(Thread.java:748)
※調べた所、少なくとも以下の3通りのやり方があるようなので、それぞれのアプローチについて1つずつ見ていく。
-
user: root
を指定し、rootユーザーで起動する - あらかじめ手動でvolumesで指定しているホスト側のマウント先になるディレクトリを作成し
chmod 777
に変更する - 自分で新しくイメージを作成し、Dockerのvolumeにマウントする
実際の解決策を見ていく前に
DynamoDB Localについて
公式にDockerイメージを使う場合のやり方が書かれているのでそちらを参照。
※Docker Hubでいうとamazon/dynamodb-localにイメージがある。
そもそもなぜホスト側のマウント先に書き込みができないのか?
amazon/dynamodb-local
のDockerイメージの定義を見てみると、USER dynamodblocal
となっており、dynamodblocalというユーザーでDynamoDBを起動・実行している事が分かる。
そして、AWSに書かれてるようなdocker-compose.yamlのvolumesで指定しているホスト側のディレクトリの所有者は以下の通り、rootユーザーになっている(docker-compose up でコンテナを起動すると、自動でホスト側のdataディレクトリ以下が作成されるが、そのディレクトリの所有者がrootになってしまうという事)。
[study@localhost node-express]$ tree . -I node_modules
.
...
├── data
│ └── dynamodb
[study@localhost data]$ ls -l
合計 0
drwxr-xr-x. 2 root root 38 4月 30 21:30 dynamodb
つまり、ユーザーの違いによるパーミッションの問題が発生し、volumesで指定しているホスト側のディレクトリ(今回だと./data/dynamodb
)に書き込みができず、データのファイル(shared-local-instance.db
)も作成できないので読み込みもできず(unable to open database file
)…という事が起きている。
解決策
その1 user: root
を指定し、rootユーザーで起動・実行する
これはrootユーザーが所有者なのでDynamoDBのプロセスもrootユーザーにしてしまい、パーミッションの問題を発生させないようにするアプローチ。この設定をすると、以下の動画の通り、warningが表示されず、データの永続化ができている事が分かる(shared-local-instance.db
というファイルがホスト側に作成されているので)。
※ソースコード全体は以下。
- 参考:Dockerfile reference USER(日本語はここ)
その2 あらかじめ手動でvolumesで指定しているホスト側のマウント先になるディレクトリを作成しchmod 777
に変更する
次に見ていく方法としては、dockerのvolumesで指定しているホスト側のディレクトリを手動で作成し、そのパーミッションを777(すべてのユーザーで全ての操作(読み取り・書き込み・実行)を許可する)に変更する(Linuxのパーミッションについては参考に示した記事等を参照)。
これによりdynamodb-localの実行ユーザーdynamodblocalでもホスト側のディレクトリに書き込みができるようになる。
[study@localhost node-express]$ mkdir -p ./data/dynamodb
[study@localhost node-express]$ chmod 777 ./data/dynamodb
[study@localhost node-express]$ cd data
[study@localhost data]$ ls -l
合計 0
drwxrwxrwx. 2 study study 6 5月 2 16:35 dynamodb
[study@localhost node-express]$ cd data/dynamodb/
[study@localhost dynamodb]$ ls -l
合計 16
-rw-r--r--. 1 1000 1000 16384 5月 2 16:35 shared-local-instance.db
その3 自分で新しくイメージを作成し、Dockerのvolumeにマウントする
最後の方法は、dynamodb-localのイメージの実行ユーザーであるdynamodblocalでdataディレクトリを作成するように、Dockerfileを自分で定義し、Dockerのvolumes(/var/lib/docker/volumes
以下)にマウントする、という方法。
まずは以下のようにDockerfileを作成し、amazon/dynamodb-localのイメージを上書きする。
FROM amazon/dynamodb-local:latest
WORKDIR /home/dynamodblocal
RUN whoami; \ # <- 実行ユーザーを確認するためにわざと書いている(本来は不要)
mkdir data;
あとは、docker-compose.yamlの方を以下のように書き換えればOK。
version: '3.9'
services:
dynamodb-local:
command: -jar DynamoDBLocal.jar -sharedDb -dbPath ./data
image: my-dynamodb-local
build:
context: ./build
dockerfile: Dockerfile
container_name: dynamodb-local
ports:
- '8000:8000'
volumes:
- dynamodb-data:/home/dynamodblocal/data
working_dir: /home/dynamodblocal
volumes:
dynamodb-data:
1点注意として、公式にも書かれている通り、トップレベル要素のvolumesではそのキー以下は空でもよいが、キーになっていない(:
がない)とIn file './docker-compose.yaml', volume must be a mapping, not a string.
というエラーがでる。
An entry under the top-level volumes key can be empty, in which case it uses the platform’s default configuration for creating a volume.(トップレベルのボリュームキーの下のエントリーは空でもよく、その場合、ボリュームを作成するためのプラットフォームのデフォルトの設定が使用されます)
上記のように設定すると、以下の動画の通りwarningは出なくなる。
ちなみに、永続データがどこに保存されるか?だが、/var/lib/docker/volumes
というディレクトリにデフォルトの設定でディレクトリが作成され、そこにマウントされる。
[root@localhost volumes]# pwd
/var/lib/docker/volumes
[root@localhost volumes]# ls -l
合計 32
brw-------. 1 root root 253, 0 4月 30 16:54 backingFsBlockDev
-rw-------. 1 root root 65536 5月 2 17:04 metadata.db
drwx-----x. 3 root root 19 5月 2 17:04 node-express_dynamodb-data
[root@localhost volumes]# cd node-express_dynamodb-data
[root@localhost node-express_dynamodb-data]# ls -l
合計 0
drwxr-xr-x. 2 1000 1000 38 5月 2 17:17 _data
[root@localhost node-express_dynamodb-data]# cd _data/
[root@localhost _data]# ls -l
合計 16
-rw-r--r--. 1 1000 1000 16384 5月 2 17:17 shared-local-instance.db
また、ちなみに上記のuser/groupの1000
についてだが、dynamodblocalのuser/groupのidが1000であり、dynamodblocalによりホスト側のvolume(今回だとnode-express_dynamodb-data)が作成されている事が分かる。
[study@localhost node-express]$ docker-compose up
Creating volume "node-express_dynamodb-data" with default driver
Building dynamodb-local
...
Step 3/3 : RUN whoami; id; mkdir data;
---> Running in e82a742db0d7
dynamodblocal
uid=1000(dynamodblocal) gid=1000(dynamodblocal) groups=1000(dynamodblocal) # <- idコマンドの実行結果
Removing intermediate container e82a742db0d7
---> ca5db73e1d11
...
※ソースコード全体は以下。
まとめとして
あまり手動で何をやるというのは避けたいという気持ちもあるので、その場合には解決策のその1を使うのがいいような気がするが、rootユーザーに引っかかる場合には、その2やその3に挙げたような方法で解決する事になるのだろうと思う。