一、背景
最近工作中接觸數據安全相關的內容,順便接觸一下現在最新的私有云相關的一些技術,下面是工作中的一個場景。爲了保證數據的安全性,需要確保每個客戶的數據我們保存在各自的一個存儲中,用戶之間數據是不可見的。這樣有幾種解決方法:
使用分佈式文件存儲,通過文件系統的權限管控來實現在應用層做一層權限管控使每個用戶的數據使用各自的存儲引擎。
我們的設計方案就是基於第三種方案實現的。
二、設計
用戶在註冊的時候,幫用戶生成創建自己的docker容器,並安裝好MinIO(MinIO是一個對象存儲服務器,具體可以官網https://min.io/)。根據業務在MinIO中創建對象的bucket,並設置對bucket操作的監聽,從而進行下一步操作。
三、實現
所用功能都是用Java實現。
用戶註冊成功時,會往消息隊列發送消息;後臺服務監聽消息隊列,接收到創建的指令,就創建MinIO的docker,並創建bucket,設置監聽
//配置監聽的哪一個隊列,同時在沒有queue和exchange的情況下會去創建並建立綁定關係
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "test-container", durable = "true"),
exchange = @Exchange(value = ExchangeConstants.CONTAINER_EXCHANGE, durable = "true", type = "topic"),
key = "container.*"
)
)
// 創建docker,安裝minio
@RabbitHandler
public void minIoContainer(@Payload String s, @Headers Map<String, Object> headers, Channel channel) throws IOException {
log.info("headers:{}", headers);
String routingKey = (String) headers.get(AmqpHeaders.RECEIVED_ROUTING_KEY);
log.info("routingKey:{}", routingKey);
/*
* Delivery Tag 用來標識信道中投遞的消息。RabbitMQ 推送消息給 Consumer 時,會附帶一個 Delivery Tag,
* 以便 Consumer 可以在消息確認時告訴 RabbitMQ 到底是哪條消息被確認了。
* RabbitMQ 保證在每個信道中,每條消息的 Delivery Tag 從 1 開始遞增。
*/
Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
// 創建docker client
DockerClient client = DockerClientBuilder.getInstance("tcp://127.0.0.1:2375").build();
// 創建container,並綁定端口
ExposedPort port9000 = ExposedPort.tcp(9000);
Ports portBindings = new Ports();
portBindings.bind(port9000, Ports.Binding.bindPort(9009));
/**
* 創建container,並設置minio的桶通知策略,將消息發送到rabbitmq,支持多種消息中間件
*/
CreateContainerResponse containerResponse = client.createContainerCmd(IMAGE_NAME).withName(containerName)
.withEnv("MINIO_ACCESS_KEY=adminliu", "MINIO_SECRET_KEY=admin1234",
"MINIO_NOTIFY_AMQP_ENABLE=on",
"MINIO_NOTIFY_AMQP_URL=amqp://guest:[email protected]:5672",
"MINIO_NOTIFY_AMQP_EXCHANGE=lfexchange",
"MINIO_NOTIFY_AMQP_EXCHANGE_TYPE=fanout",
"MINIO_NOTIFY_AMQP_ROUTING_KEY=bucketlogs",
"MINIO_NOTIFY_AMQP_DURABLE=on")
.withHostConfig(newHostConfig().withPortBindings(portBindings))
.withExposedPorts(port9000)
.withCmd("server", "/data")
.exec();
// 啓動 container
client.startContainerCmd(containerResponse.getId()).exec();
// 創建 minio bucket
MinioClient minioClient = new MinioClient("http://172.0.0.1:46202", "admin1234", "adminpassword");
boolean found = false;
try {
found = minioClient.bucketExists(bucketName);
if (found) {
System.out.println("bucket is already exist");
} else {
minioClient.makeBucket(bucketName);
System.out.println("create bucket successful");
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
// 設置對bucket操作的監聽,會將操作的結果
try {
NotificationConfiguration notification = client.getBucketNotification(bucketName);
List<QueueConfiguration> queueConfigurations = notification.queueConfigurationList();
QueueConfiguration queueConfiguration = new QueueConfiguration();
queueConfiguration.setQueue("arn:minio:sqs::_:amqp");
List<EventType> eventList = new LinkedList<EventType>();
// 對put操作增加桶通知
eventList.add(EventType.OBJECT_CREATED_PUT);
queueConfiguration.setEvents(eventList);
queueConfigurations.add(queueConfiguration);
notification.setQueueConfigurationList(queueConfigurations);
client.setBucketNotification(bucketName, notification);
System.out.println("bucket notification is set successfully");
} catch (Exception e) {
e.printStackTrace();
}
channel.basicAck(deliveryTag, false);
}
//配置監聽的哪一個隊列,同時在沒有queue和exchange的情況下會去創建並建立綁定關係
完成這些設置之後,當有文件往這個bucket裏上傳完成的時候,會往rabbitmq中發送消息,格式如下
{"EventName":"s3:ObjectCreated:Put","Key":"test-data/xxx.docx","Records":[{"eventVersion":"2.0","eventSource":"minio:s3","awsRegion":"","eventTime":"2020-04-15T10:17:51Z","eventName":"s3:ObjectCreated:Put","userIdentity":{"principalId":"admin1234"},"requestParameters":{"accessKey":"admin1234","region":"","sourceIPAddress":"10.14.0.209"},"responseElements":{"x-amz-request-id":"1605F6DCC93FA1E4","x-minio-deployment-id":"a9d4f076-0f77-4df2-b504-5625fe3b9387","x-minio-origin-endpoint":"http://172.17.0.4:9000"},"s3":{"s3SchemaVersion":"1.0","configurationId":"Config","bucket":{"name":"dmz-data","ownerIdentity":{"principalId":"admin1234"},"arn":"arn:aws:s3:::dmz-data"},"object":{"key":"%E6%95%B0%E6%8D%AE%E4%BA%A4%E6%8D%A2%E8%AE%A1%E7%AE%97%E5%9C%BA%E6%99%AF%E6%B5%81%E7%A8%8B%E6%A8%A1%E6%8B%9F.docx","size":180518,"eTag":"3358aeacd28a99ce9e27619d974cb6a8-1","contentType":"application/vnd.openxmlformats-officedocument.wordprocessingml.document","userMetadata":{"content-type":"application/vnd.openxmlformats-officedocument.wordprocessingml.document"},"versionId":"1","sequencer":"1605F6DCD05EB1D5"}},"source":{"host":"10.14.0.209","port":"","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36"}}]}
這樣,後面可以根據結果進行下一步操作。