分布式对象存储Ambry(1)简介与集群部署

2021-04-12 15:19:21 浏览数 (1)

Ambry简介

LinkedIn开源了多媒体对象存储数据库Ambry,开源不久,算是一个很新的事物。Ambry用来做对象存储,非常适合用于存储像LinkedIn这种社交媒体网站的对象数据(如图片,视频等等)。 LinkedIn过去使用的是一种闭源技术,非常复杂,而且也难以随着用户数和数据量的增长而扩展。LinkedIn工程总监Sriram Subramanian在博客中指出:

我们开始尝试在市场中寻找更好的替代方案,包括各种分布式文件系统、存储一体机、云服务和内部部署方案都考察过,通过权衡我们的设计目标和得失后发现,我们需要自己开发一个能更好满足我们需求的方案——Ambry,如今Ambry已经在LinkedIn的生产环境中使用多年,表现良好。

之前我的博客翻译了Ambry作者的官方博客,大家感兴趣的可以去看看:

  • 分布式对象存储Ambry - 官方博客翻译与摘录(1)背景介绍
  • 分布式对象存储Ambry - 官方博客翻译与摘录(2)Ambry设计目标
  • 分布式对象存储Ambry - 官方博客翻译与摘录(3)整体设计
  • 分布式对象存储Ambry - 官方博客翻译与摘录(4)路由设计
  • 分布式对象存储Ambry - 官方博客翻译与摘录(5)运维与迁移

Ambry可以简单地理解成为一个静态资源服务器,实现CDN网络的功能。Ambry的优点在于:

  1. 简单的高可用以及水平可扩展
  2. 低操作与运维开销
  3. 低MTTR(Mean Time To Recover)
  4. 跨机房多活
  5. 对于大小对象操作灵活并高效
  6. 节约成本(支持P2P,硬盘阵列,磁盘交换等等)

我们利用Ambry,可以很方便的部署一个静态资源服务器集群,从而快捷稳定的提供内容服务。

Ambry集群部署

官网没有集群部署的教程,这里补充下。

代码编译打包

我们先把代码从github拉下来,本地编译,我的IDE用的IDEA,变异的过程中,发现有个依赖失效了,所以一直报:Error:Cause: peer not authenticated; 在项目根目录build.gradle中的第15行:

gradle的buildscript还依赖于buildscript.gradle,这个文件中保存着一个关于gradle-license-plugin的依赖(如果不能翻墙,这个依赖可能下载不到,所以我们需要修改库地址和库依赖版本),我们将它改变成maven库中的gradle-license-plugin对应的版本依赖:

代码语言:javascript复制
repositories {
    repositories {
        // For license plugin.
        maven {
            url "http://repo1.maven.org/maven2"
        }
    }
}

dependencies {
    classpath "nl.javadude.gradle.plugins:license-gradle-plugin:0.10.0"
}

之后,就可以导入这个项目,执行./gradlew allJar 会生成一个ambry.jar的文件。 Ambry分为前端与后端。Ambry包含负责保存和检索数据的数据节点(data node),前端节点(Frontend node)将请求经过预处理发送到后端数据节点,并且集群管理者(Cluster manager)管理并协调数据节点上的数据。数据节点之间互相复制数据,并且可以跨机房复制,并需要保证写之后读的一致性。前端提供HTTP API,包括POST,GET和DELETE对象。同样的,这个路由库可以直接被客户端调用以提升性能。

部署设计

一个简单的部署架构(不包含集群管理者)如下图所示:

Ambry代码中本身不包含集群管理者,Ambry的集群配置有配置文件,这个分布式配置管理中心一般基于zookeeper实现(不得不说,LinkedIn的所有开源分布式框架,都离不开Zookeeper)。国内的配置中心还有disconf(baidu的)和diamond(阿里的),这里Ambry的集群相关的配置文件都是Json格式的,利用原生的zookeeper就可以实现。 我们下面的示例配置将按照上面的图配置,不会加入集群配置中心,每个后台Ambry数据节点的集群配置Json文件都是保存在本地,配置中心的部署方法,在后面的关于数据迁移与横向扩展相关章节文章中会涉及。 我们现在拿到了Ambry.jar,但是还要一些配置文件,而且,启动脚本也还没提供。我们先设计下这个集群的基本配置:

我们建立一个三个节点的在同一个机房的集群,将后端Ambry节点部署在xx.xx.8.133,xx.xx.8.134,xx.xx.8.135这三台机器上,同时,数据目录都是本机的/home/ambry/data这个目录,每个节点都是读写节点。前端节点部署在xx.xx.8.133这台机器上。集群名称叫做my_ambry_cluster。 接下来编写配置文件:

首先是集群硬件配置hardware_layout.json:

代码语言:javascript复制
{
//填写集群名称
    "clusterName": "my_ambry_cluster",
    "version": 1,
    "datacenters": [
        {
            "dataNodes": [
                {
                    "disks": [
                        {
                        //填写容量,这里是20G
                            "capacityInBytes": 21474836480,
                            "hardwareState": "AVAILABLE",
                        //保存路径
                            "mountPath": "/home/ambry/data"
                        }
                    ],
                    "hardwareState": "AVAILABLE",
                    "hostname": "xx.xx.8.133",
                    "port": 6670,
                    "sslport": 7670
                },
                {
                    "disks": [
                        {
                            "capacityInBytes": 21474836480,
                            "hardwareState": "AVAILABLE",
                            "mountPath": "/home/ambry/data"
                        }
                    ],
                    "hardwareState": "AVAILABLE",
                    "hostname": "xx.xx.8.134",
                    "port": 6670,
                    "sslport": 7670
                }
            ,
                {
                    "disks": [
                        {
                            "capacityInBytes": 21474836480,
                            "hardwareState": "AVAILABLE",
                            "mountPath": "/home/ambry/data"
                        }
                    ],
                    "hardwareState": "AVAILABLE",
                    "hostname": "xx.xx.8.135",
                    "port": 6670,
                    "sslport": 7670
                }
            ],
            "name": "Datacenter"
        }
    ]
}

接着是集群逻辑分片设置partition_layout.json:

代码语言:javascript复制
{
    "clusterName": "my_ambry_cluster",
    "version": 1,
    "partitions": [
        {
            "id": 0,
            "partitionState": "READ_WRITE",
            //这里是10G,因为我们下面的配置是put成功一个,备份一个,所以,每个硬盘20G其实是存储10G数据还有10G其他的备份
            "replicaCapacityInBytes": 10737418240,
            "replicas": [
                {
                    "hostname": "xx.xx.8.133",
                    "mountPath": "/home/ambry/data",
                    "port": 6670
                },
                {
                    "hostname": "xx.xx.8.134",
                    "mountPath": "/home/ambry/data",
                    "port": 6670
                },
                {
                    "hostname": "xx.xx.8.135",
                    "mountPath": "/home/ambry/data",
                    "port": 6670
                }
            ]
        }
    ]
}

Server数字证书集群配置

由于集群配置必须要SSL连接,所以,需要在每一台机器上生成数字证书。这里用java自带的keystore生成数字证书。 Keytool是一个Java数据证书的管理工具。Keytool将密钥(key)和证书(certificates)存在一个称为keystore的文件中在keystore里。 在每台机器上执行:

代码语言:javascript复制
$ keytool -genkeypair -alias certificatekey -validity 7000 -keystore keystore.jks
$ keytool -export -alias certificatekey -keystore keystore.jks -rfc -file selfsignedcert.cer
$ keytool -import -alias certificatekey -file selfsignedcert.cer  -keystore truststore.jks

记住你的配置的keytool文件位置还有密码,在每台机器上配置server.ssl.properties:

代码语言:javascript复制
#填写本机的ip地址,这里举例
host.name=xx.xx.8.133

port=6670

#the path to the store. this location must exist
ssl.keystore.path=/home/ambry/keystore.jks
# set key store password. this cannot be blank
ssl.keystore.password=xxxxxx

ssl.key.password=xxxxxxxxxxx

# the path to the trust store. this location must exist
ssl.truststore.path=/home/ambry/truststore.jks
# set trust store password. this cannot be blank
ssl.truststore.password=xxxxxxxxx

之后,我们在每台机器上面启动:

代码语言:javascript复制
java -Xms2g -Xmx4g -Dlog4j.configuration=file:./log4j.properties -jar ambry.jar --serverPropsFilePath ./server.ssl.properties --hardwareLayoutFilePath ./hardware_layout.json --partitionLayoutFilePath ./partition_layout.json & > logs/server.log

看到日志中启动成功,即可。

启动前端

前端也会依赖–hardwareLayoutFilePath ./hardware_layout.json –partitionLayoutFilePath ./partition_layout.json这两个配置,另外还有前端配置文件frontend.properties:

代码语言:javascript复制
# rest server
rest.server.blob.storage.service.factory=com.github.ambry.frontend.AmbryBlobStorageServiceFactory

# router
# 如果是本机,就填写localhost,因为不参与端口监听
router.hostname=localhost
# datacenter一定要填写之前hardware_layout.json中的
router.datacenter.name=Datacenter
# 在put请求时,只要成功一个partition就算是成功
router.put.success.target=1

# coordinator
# 如果是本机,就填写localhost,因为不参与端口监听
coordinator.hostname=localhost
# datacenter一定要填写之前hardware_layout.json中的
coordinator.datacenter.name=Datacenter
# 超时限制,在某个请求处理过长时间时会停掉
coordinator.operation.timeout.ms=100000
# 连接池相关,这个需要设置的长一些,否则大文件来不及上传或者读取
connectionpool.read.timeout.ms=100000
connectionpool.connect.timeout.ms=100000
# netty连接空闲时间,这个需要设置的长一些,否则大文件来不及上传
netty.server.idle.time.seconds = 600

填写好后,启动:

代码语言:javascript复制
java  -Dlog4j.configuration=file:log4j.properties -cp "*" com.github.ambry.frontend.AmbryFrontendMain  --serverPropsFilePath frontend.properties --hardwareLayoutFilePath sfpp_prod_ambry_hardware_layout.json --partitionLayoutFilePath sfpp_prod_ambry_partition_layout.json &

常见问题

  1. 找不到某个配置文件: 对于后端:–serverPropsFilePath ./server.ssl.properties –hardwareLayoutFilePath ./hardware_layout.json –partitionLayoutFilePath ./partition_layout.json 这三个参数必须正确而且文件存在。
  2. 某个属性为null: 检查是否哪个配置出错,三个配置文件中的属性有交叉,很容易写的不一致,需要细心点。

0 人点赞