如何使用Docker来管理数据
※ 这是关于使用Docker进行开发的文章的续篇。
Immutability和数据
我在上述文章中提到了进行周边系统的模拟集成测试的话题。
这种方法是将开发应用程序所依赖的周边服务和中间件进行模拟化和容器化,并在开发阶段进行仿真整合测试。
将容器化意味着变得不可变,并且这也意味着每次启动时都是空白的。
将应用程序设为无状态具有许多优点,但这也意味着系统中某处存在着统一管理状态的节点,通常由数据库承担此责任。
那么,我们是否可以将数据库变为不可变状态同时保持有状态呢?
Docker 之外的解决方案。
在Docker之前,有些人认为只需要使用Docker来管理应用程序,而存储可以采用其他方法,这个想法也是可以的。
-
- 社内イントラ
-
- RDS, Cloud SQL 等クラウドサービス
-
- ホストマシン上
- VirtualBox , Hyper-V の上
■ 共同的数据库
对于社内Intranet或云服务来说,团队会共享一个数据库来进行访问。
-
- Pros
データベース管理者が管理してくれる
全員の環境が揃う
本番環境が RDS だったり、用意されたデータベースサーバなら、開発環境との差異が小さい
Cons
誰かの変更が他の誰かに影響
息が長くなり、データベースが仕様になると大変
Infrastructure as Code ができていれば問題ない
■ 个人数据库
如果在主机上或在主机上创建的虚拟机上放置数据库,就可以为自己准备一个数据库。
-
- Pros
各開発者が独立して作業ができる
テスト用に、ちょっとデータいじったり
場合によっては、スキーマをいじったり
スキーマ変更の提案前に影響範囲が分かる
Cons
自分で管理しないといけない
全員の環境が揃わない
私はできるのに、あなたはできない
息が長くなると、結局大変
Infrastructure as Code ができていれば問題ない
那么在这里,通过Docker化,你期望得到什么?
-
- データベース管理者が管理してくれる
イメージを誰かが作れば、後はそれを使うだけ
建てるのも一瞬
各開発者が独立して作業ができる
当然
環境が揃う
全員が同じイメージを使えば実現できる
本番との差異
本番も Docker で行ければ 差異はない
本当に ?
息が長くなると、結局大変
Immutable な使い方をすれば、毎度破棄 / 生成されるので、Infrastructure as Code が徹底される
因此,我们将看看Docker如何解决以下问题。
作为Docker的解决方案
数据库相关的官方容器
首先,应该如何创建数据库容器映像。
PostgreSQL和MySQL等主要的关系型数据库,以及MongoDB和Redis等所谓的NoSQL数据库,几乎都有官方的Docker镜像可用。
-
- RDB
PostgreSQL
MySQL
MariaDB
NoSQL
Redis
memcached
MongoDB
Cassandra
这些都是基本的“空数据”概念。如果没有特殊理由,我们通常会以官方形象为基础使用。
然而,如果需要调整,则可以在Dockerfile中从头构建。
数据库设计与构建
因此,我们开始进行数据库的设计和构建。
设计者负责设计数据库、用户、表等。
在这里,如果过于轻易地在Dockerfile中连续写入psql -c ~ (假设为PostgreSQL),或连接到已启动的数据库容器并执行create database ~,将会造成麻烦。
Docker 不会轻易保留状态。
在Docker中实现数据持久化
Docker要求基本上是不可更改的。
同时,它不喜欢镜像中包含数据(状态)的做法。
考虑以下,以PostgreSQL作为前提。
1. 包含数据的图像化状态
制作一个包括所有模式数据的初始图像,这是每个人首先考虑的事情。
“优点”
-
- バージョン管理が可能
-
- Immutability も完璧
- Docker Registry に登録すれば、登録 / 配布 のエコシステムに乗れる
◆ 缺点
- Docker コミュニティが推奨していない ? (後述)
实施方案
作为大致的实现方法,有以下几种想法。
A. 在 Dockerfile 中进行记录,通过 docker build 将其打包成镜像。
B. 在启动的容器中进行更改,通过 docker commit / save / export 将其打包成镜像。
– docker run ~
– docker exec psql -c “~~”
– docker commit or save or export ~
我想要选择基础设施即代码方案A,但我们需要看一下以下两种方案。
1. 通过 Dockerfile 添加的模式一种
FROM postgres:9.6
RUN psql -d postgres --username postgres -c "create table public.user (name varchar)" \
&& psql -d postgres --username postgres -c "insert into public.user (name) values ("taro")"
PS> docker build -t test .
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM postgres:9.6
---> f8d91fbcfa35
Step 2/2 : RUN psql -d postgres --username postgres -c "create table public.user (name varchar)" && psql -d postgres --username postgres -c "insert into public.user (name) values ("taro")"
---> Running in 9af317a881ca
psql: could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
嗯,其实一开始就没有联通。
● 中间件的初始化之难
事实上,Docker 的最佳实践是在运行或启动时为每个容器启动一个进程。
在进行 psql 时,PostgreSQL 还没有启动,也没有进行 initdb。
MySQL 和 MariaDB 也有类似的行为。
我认为这可能是微服务最佳实践,并且Docker是为此而设计的(docker run ~:stdout → docker log,docker run -it ~:stdin ← tty)。
如果按照这个原理和原则,似乎注入状态的方式二在启动后更好。
有些情况下,可能会强行在构建 Docker 镜像时执行 docker-entrypoint.sh (在 docker 运行时运行的shell脚本),但这仍然有些勉强。
1. 对已启动的容器进行修改的模式。
试试在启动后进行更改并保存的方法。
PS> docker run -d --name db -e POSTGRES_PASSWORD=password postgres:9.6
PS> docker exec db psql -U postgres -c "create table public.user (name varchar)"
PS> docker exec db psql -U postgres -c "insert into public.user (name) values ('taro')"
PS> docker exec db psql -U postgres -c "select * from public.user"
name
------
taro
(1 row)
将其注册为一个名为”initeddb”的新映像。
PS> docker commit db initeddb
PS> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
initeddb latest 1tiedea87262 7 seconds ago 269MB
...
所以,重新启动一下,看看效果。
PS> docker stop db ; docker rm -f db
PS> docker run -d --name new_db initeddb:latest
PS> docker exec new_db psql -U postgres -c "select * from public.user"
ERROR: relation "public.user" does not exist
LINE 1: select * from public.user
已经消失了…
● VOLUME 的陷阱
实际上,当你查看PostgreSQL官方镜像的Dockerfile时,你会看到`VOLUME /var/lib/postgresql/data`这样的描述。
这意味着 PostgreSQL 的更改保存在数据卷中,而不保存在主容器中(详细信息)。
这实际上是源自官方对于不包含数据的坚决意愿。
虽然可以通过自己编写 Dockerfile 来避免这一点,但我很好奇为什么会如此强烈地不希望包含数据。
为什么不将数据库状态包含在图像中?
正在进行中…
2. 安装垃圾车
实际上,官方提供了一种恢复数据的方法。
它可以在启动时读取放置在指定文件夹中的 *.sql, *.sql.gz 文件。
在PostgreSQL中,如果将转储文件放置在特定的文件夹(/var/lib/postgresql/data),则在使用docker run时会自动加载。
● 优点
-
- 公式に推奨された手法
-
- イメージをいくつも管理する必要が無い
その代わり、ダンプはいくつも管理する必要がある
ファイル共有することでチームで環境共有が可能
◆ 缺点
-
- 起動時に読み込まれるのが遅い
docker run が終わっても、裏ではRestore が走っていて、それが終わるまで接続できない
完全な Immutable じゃない
ダンプファイル管理を自分たちでしなくては行けない
3. 在数据量上保持不变,进行持续化和共享
利用 Volume 功能将数据文件夹作为挂载点,即使应用容器崩溃,数据仍然可以保留下来。
◆ 优点
- 起動が早い
◆ 优势
-
- 2 との違いが無い
共有・バージョン管理が必要
そのくせ
公式が推奨したやり方ではない
手順が多い
似乎没有积极选择的理由。
作为 Docker 的解决方案,但采用了不同的方法。
可以在使用 Docker 功能的同时,从略有不同的角度进行攻击。
可以利用远程文件系统或分布式文件系统。
远程文件系统
有些人也尝试直接挂载NFS或Samba/CIFS到Docker的数据卷上。
尝试使用最新的Docker功能:了解Docker的卷插件和存储驱动程序。
分布式文件系统
在Ceph、GlusterFS、flocker等技术中,也有尝试将数据卷进行分布式处理的努力。
能成为解决方案吗?
不过,由于整体而言,它与除了Docker以外的解决方案都面临同样的问题,所以这次我选择将其排除在外。
最终,哪一个是最好的?
目前采用第二种方法
开发流程会怎样发展呢?
请总结一下这个实际的使用方法。
创建数据库管理项目。
首先,创建一个专门管理数据库的项目。
我們在那裡主要負責管理三個角色。
角色1. 架构管理
目前,我們經過一些試驗,發現使用 ridgepole 來管理資料庫的架構是最好的方法。
通过Ridgepole的代码来进行模式(schema)的版本控制。
create_table "users", force: :cascade do |t|
t.string "name" ,null: false
t.string "display_name" ,null: false
t.datetime "created_at" ,null: false
t.datetime "updated_at" ,null: false
end
create_table "offices", force: :cascade do |t|
t.string "name" ,null: false
t.string "address" ,null: false
t.datetime "created_at" ,null: false
t.datetime "updated_at" ,null: false
end
...
管理虛假數據傾卸的角色。
接下来,正在进行创建开发用虚拟数据转储的数据库启动。
01_schema.sql :基础架构的转储
Ridgepole的Schema在CI时会转化为SQL。
02_dummy.sql: 数据备份
关于这个问题,我还没有完全确定我的想法。
目前,我将它分为两个阶段进行实施。
● 阶段1:使用CSV进行COPY
如果是主流系,由于数量较少,可以使用CSV进行管理,并通过COPY进行输入。这是手动操作。
● 阶段2:使用Python + Pandas创建数据
如果涉及数据相关的话,数量也相应需要一定量的,所以我们会将其拆分为一个独立的项目,使用Python手动制造大量的虚拟数据。
角色3:垃圾车的共享
我们用角色2制作的转储文件应该如何共享?
目前,由于团队成员都可以访问并且进行访问管理的原因,我们使用Google Drive进行手动共享。
无论是在S3还是Dropbox,我觉得都是一样的,但这很困难。
2. 在每个项目中获取虚拟转储文件。
每个开发者都会下载每个项目,并将其放置在特定的位置并启动。
services:
db:
container_name: postgres
image: postgres:9.5
ports:
- "5432:5432"
volumes:
- ./dump/db:/docker-entrypoint-initdb.d
environment:
- POSTGRES_USER=test
- POSTGRES_PASSWORD=test
- POSTGRES_DB=test_db
networks:
- default
我现在正在考虑的事情
我想要改变文件共享的方式。
有没有一种能够自动共享团队之间、进行版本管理的大数据方法?虽然有几个候选方案,但仍处于调查和评估阶段。
使用 Git LFS
干脆用Git来管理不是挺好的吗?
还在进行中…
使用 “noms”
我之前一直在想,要是数据库本身能提供版本控制就好了。然后我发现了一个叫norm的看起来很不错。
标准
尽管我还没开始使用过,但如果这个可以在生产环境中使用,那么它可能可以解决一些不是Docker的解决方案的问题吧。
参考: 我们来试试具有数据版本控制能力的分布式数据库Noms。
有关信息
◆ 使用 Docker 进行卷管理
严格来说,卷管理大致可以分为两种方法。
在主机上自行管理
将主机指定的目录挂载到容器中的方法。
$ docker run -d --name db -v "/path/to/data:/var/lib/postgresql/data" postgres:9.6
通过这样做,主机上的 /path/to/data 将被挂载到 /var/lib/postgresql/data,并且数据将在 /path/to/data 下保存并持久化。
这将导致主机的 /path/to/data 被挂载到 /var/lib/postgresql/data,并且数据将在 /path/to/data 下保存并持久化。
这一步骤将使主机的 /path/to/data 被挂载到 /var/lib/postgresql/data,在 /path/to/data 下保存并永久保留数据。
经过这一步,主机上的 /path/to/data 将会挂载到 /var/lib/postgresql/data,并且数据将在 /path/to/data 下保存并持久化。
$ docker exec db psql -U postgres -c "create table public.user (name varchar)"
$ docker exec db psql -U postgres -c "insert into public.user (name) values ('taro')"
$ docker stop db ; docker rm -f db
# 同じホストディレクトリをマウントして実行
$ docker run -d --name new_db -v "/path/to/data:/var/lib/postgresql/data" postgres:9.6
$ docker exec new_db psql -U postgres -c "select * from public.user"
name
------
taro
(1 row)
由于所有权的关系,在Windows上无法运行,但至少能感受到一点氛围。
-
- 保存先を明示するので、直感的で分かりやすい
データの管理は、利用者に任される
『どれが何のデータか』などのメタデータ管理
ゴミデータの削除
2. 数据量
在Docker管控的领域中创建一个文件夹,并将其用容器挂载。
PS> docker volume create --name my_data
PS> docker run -d -v my_data:/var/lib/postgresql/data postgres:9.6
这样一来,一个名为 my_data 的数据卷就创建了,它会挂载到容器的 /var/lib/postgresql/data 目录下。
如果是Windows操作系统,Data Volume 将被创建在Moby Linux虚拟机的文件系统中的/var/lib/docker/volumes/目录下,以<<哈希值>>/_data命名。
-
- Docker が管理してくれる
docker volume ~ サブコマンドで操作できる
通常のコンテナではないので、イメージの保存はできない
Data Volume をマウントして中身を tar で保存すると言う手法もあるにはある
然后,就算使用数据量,也有几种不同的用法。
给一个Docker卷命名以进行管理。
由于可以自行指定名称,并且管理可交给 Docker,所以非常方便使用。
PS> docker volume create --name my_data
PS> docker volume ls
DRIVER VOLUME NAME
local my_data
PS> docker volume rm my_data
用来做什么?
-
- データの永続化
- コンテナ間で volume の共有
使用-bv <<路径>>命令来创建临时数据卷。
PS> docker run -d -v /path/to/dir postgres:9.6
当执行此操作时,将创建一个Temporary Data Volume,并且将其挂载到/path/to/dir。
当容器关闭时,这个Data Volume就会变成一个无人引用的待删除的”野良”Data Volume。
(确切地说,如果指定了哈希值,则可以引用它。如果你有兴趣,可以尝试使用命令docker inspect | jq “.[0].Mounts”进行查询。)
◆ 用于什么?
经过一番调查,看起来
docker commit / save/ export する上で邪魔な一時ファイルを捨てる
アプリケーションコンテナを圧迫する程に一時ファイルをできる場合、それを隔離する
看起来是为了使用而准备的。
2.c 在 Dockerfile 中使用 Volume 命令。
...
VOLUME /var/lib/postgresql/data`
...
这与2.b相同,它在docker运行时创建并挂载临时数据卷。
◆ 用途是什么?
虽然目的与选项二十分相似,但还有另一个重要的角色是在docker构建时防止包含在映像中。
从-v参数来看,将其他容器作为数据卷容器进行挂载。
有一种叫做数据量容器的方法。
它是为了公开数据而设立的容器,其他利用容器可以使用 –volume-from 来使用它。
PS> docker run -d --name data_proxy -v /path/to/data -v /path/to/config busybox:latest
PS> docker run --name db1 --volume-from data_proxy postgres:9.6
PS> docker run --name db2 --volume-from data_proxy postgres:9.6
PS> docker run --name db3 --volume-from data_proxy postgres:9.6
通过这样做,db1、db2和db3现在共享了位于/path/to/data和/path/to/config的数据和配置文件。
◆ 用于什么? yú ?)
我不知道。