基于腾讯云存储COS的ClickHouse数据冷热分层方案

1   ClickHouse简介

ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS),支持PB级数据量的交互式分析,ClickHouse最初是为YandexMetrica 世界第二大Web分析平台而开发的。多年来一直作为该系统的核心组件被该系统持续使用着。目前为止,该系统在ClickHouse中有超过13万亿条记录,并且每天超过200多亿个事件被处理。它允许直接从原始数据中动态查询并生成报告。自2016 年开源以来,ClickHouse 凭借其数倍于业界顶尖分析型数据库的极致性能,成为交互式分析领域的后起之秀,发展速度非常快。

更详细的ClickHouse特性,可以参考ClickHouse的官方文档:

https://clickhouse.tech/docs/zh/introduction/distinctive-features/

2   ClickHouse的架构简述

ClickHouse是一种分布式的数据库管理系统,不同于其他主流的大数据组件,它并没有采用Hadoop生态的HDFS文件系统,而是将数据存放于服务器的本地盘,同时使用数据副本的方式来保障数据的高可用性。

ClickHouse使用分布式表实现数据的分布式存储和查询。下图演示了一个分布式表是如何存储的:

分片(Shard):包含数据的不同部分的服务器,要读取所有数据必须访问所有的分片。通过将分布式表的数据存放到多个Shard实现计算和存储的横向扩展。

副本(Replica):每个切片的数据都包含多个副本,要读取数据时访问任一副本上的数据即可。通过副本机制保证存储数据的单节点失效时数据的可用性。只有MergeTree类型的表引擎可以支持多副本。ClickHouse是在表的引擎而不是数据库引擎实现数据的副本功能的,所以副本是表级别的而不是服务器级别的。数据在插入ReplicatedMergeTree引擎的表的时候会做数据的主备同步以实现数据的多副本,在同步的过程中使用ZooKeeper做分布式协调。

分布式表(Distributed table):使用分布式引擎创建的分布式表并不存储数据,但是能够将查询任务分布到多台服务器上处理。在创建分布式表的过程中,ClickHouse会先再每个Shard上创建本地表,本地表只在对应的节点内可见,然后再将创建的本地表映射给分布式表。这样用户在访问分布式表的时候,ClickHouse会自动根据集群的架构信息,将请求转发给对应的本地表。

综上所述,一个ClickHouse集群由分片组成,而每个分片又由多个数据副本组成。一个副本对应了组成ClickHouse集群中的一个服务器节点,并使用该服务器节点上的本地盘存储数据。通过分布式表、数据分片以及数据副本,ClickHouse实现了集群的横向扩展能力并提供数据的高可用保护。

3   数据的分层存储

3.1 数据的分层存储

从19.15这个版本开始,ClickHouse开始支持multi-volume storage这个功能,它允许将ClickHouse表存储在包含多个设备的卷当中,利用这个特性,我们可以在volume中定义不同类型的磁盘,根据数据的“冷”、“热”程度将数据存放在不同类型的磁盘上(我们可以称之为Tier Storage),以实现性能与成本的平衡。

以下是Altinity网站上关于multi-volume storage的架构图:

ClickHouse的配置文件中和磁盘相关的术语:

磁盘(Disk):已经格式化成文件系统的块设备。

默认磁盘(Default Disk):在服务器设置中通过path参数指定的数据存储,默认路径为/var/lib/clickhouse/。

卷(Volume):有序的磁盘的集合。

存储策略(Storage Policy):卷的集合以及卷之间数据移动的规则。

ClickHouse存储及存储相关的策略是写在配置文件中的,你可以在/etc/clickhouse-server/config.xml文件中添加关于卷、磁盘以及存储策略的定义,也可以在/etc/clickhouse-server/config.d目录中新建xml类型的配置文件,添加相关的存储的定义。

3.2 ClickHouse支持的磁盘类型

ClickHouse主要支持DiskLocal和DiskS3两种常用的磁盘类型。

3.2.1      DiskLocal类型磁盘

DiskLocal类型磁盘使用服务器本地磁盘,并指明数据存储的路径。ClickHouse有一个名为default的DiskLocal类型的磁盘,路径为/var/lib/clickhouse/。

同样我可以自定义新增DiskLocal类型的磁盘,在ClickHouse中新增一个DiskLocal类型的磁盘,使用路径问/data,步骤如下:

  1. 服务器新挂载一块硬盘,并格式化文件系统并挂载在/data目录下。
  2. 在/etc/clickhouse-server/config.d目录下新建一个名为storage.xml的文件,并添加如下内容:
<yandex>
    <storage_configuration>
        <disks>
            <localdisk> <!-- disk name -->
                <path>/data/</path>
            </localdisk>
        </disks>
        <policies>
            <local>
                <volumes>
                    <main>
                        <disk>localdisk</disk>  
                    </main>
                </volumes>
            </local>
        </policies>
    </storage_configuration>
</yandex>
  1. 重启clickhouse-server服务之后,查看新加的磁盘:

3.2.2      DiskS3类型磁盘

ClickHouse支持DiskS3类型磁盘,使用S3接口访问存储于对象存储上的数据,原生支持AWS对象存储S3以及腾讯云对象存储COS。

下面我们在ClickHouse中再添加一个DiskS3类型的磁盘,这里我们使用腾讯云存储COS的一个存储桶作为例子,编辑/etc/clickhouse-server/config.d/storage.xml文件,内容如下:

<yandex>
    <storage_configuration>
        <disks>
            <localdisk> <!-- disk name -->
                <path>/data/</path>
            </localdisk>
            <cos>
                <type>s3</type>
                <endpoint>http://example-1250000000.cos.ap-shanghai.myqcloud.com/ck/</endpoint>
                <access_key_id>AKIDxxxxxxxx</access_key_id>
                <secret_access_key>xxxxxxxxxxx</secret_access_key>
            </cos>
        </disks>
        <policies>
            <local>
                <volumes>
                    <main>
                        <disk>localdisk</disk>  
                    </main>
                </volumes>
            </local>
            <cos>
                <volumes>
                    <main>
                        <disk>cos</disk>
                    </main>
                </volumes>
            </cos>
        </policies>
    </storage_configuration>
</yandex>

在上面的配置文件中,我们定义了两个磁盘,一个名为localdisk的Disklocal类型的磁盘,使用本地/data路径存储数据,另外一个是名为cos的DiskS3类型的磁盘,使用腾讯云对象存储的example-1250000000存储桶存储数据,并需要在配置文件中配置可以访问该存储桶账号的SecretId和SecretKey,上面的例子中access_key_id和secret_access_key分别对应访问COS存储桶账号的SecretId和SecretKey。接下来在策略中我们定义了两个策略用于将数据存储至本地磁盘或者对象存储COS。

在ClickHouse中重新加载配置后,能查询到刚才我们定义的磁盘及存储策略:

在后面的章节我们会详细演示如何将ClickHouse表中的数据存储在本地存储或者对象存储COS上。

3.3 数据移动策略

通过在配置文件中配置多个不同类型的磁盘以及存储策略,ClickHouse能够将数据存储在不同的存储介质中,同时ClickHouse还支持配置移动策略以实现数据在不同存储介质之间自动的移动。

3.3.1      基于move factor的数据移动策略

这是一种基于文件大小以及卷(volume)中各个磁盘(Disk)容量使用情况来移动数据的策略。

下面的例子中,我们建立了一个名为“moving_from_local_to_cos”的策略,在策略中我们定义了两种存储,第一个是名为“hot”的卷,这个卷中有一个名为localdisk的磁盘并设置这个磁盘上的文件最大值为1GB;第二个是名为“cold”的卷,这个卷中有一个名为cos的磁盘。

最后是move_factor参数,表示当卷的可用容量低于move_factor参数设定的值的时候,数据将被自动的移动到下一个卷,本例中当hot卷的容量低于30%的时候,hot卷中的数据将被自动的移动到cold卷。

<moving_from_local_to_cos>
    <volumes>
        <hot>
            <disk>localdisk</disk>
            <max_data_part_size_bytes>1073741824</max_data_part_size_bytes>
        </hot>
        <cold>
            <disk>cos</disk>
        </cold>
    </volumes>
    <move_factor>0.3</move_factor>
</moving_from_local_to_cos>

有一点需要强调的是,配置文件里各个卷的顺序非常重要,当ClickHouse有新数据写入的时候,数据会优先写入到第一个卷,再依次写入后面的卷。同时move factor的的移动策略也是将数据从前面的卷移动到后面的卷。所以我们在定义卷的时候,要把数据优先写入的卷放在配置文件的前面。在实际的使用场景中一般是把高性能存储放在前面,把高容量低成本的存储放在后面,这样实现新的热数据存放在高性能存储以获取极致的实时查询性能、老的历史冷数据存放在高容量存储以获取较低的存储成本以及较好的批量查询性能。

3.3.2      基于TTL的数据移动策略

ClickHouse支持表级别的TTL表达式,允许用户设置基于时间的规则,从而能够自动的在指定的磁盘或者卷之间移动数据,以实现了数据在不同的存储层之间的分层存储。下面列举了几种比较典型的TTL的写法,从例子我们可以看出,TTL表达式只是一个简单的SQL表达式,里边包含了时间以及时间的间隔,下面是TTL的一些例子:

TTL date_time + INTERVAL 1 MONTH
TTL date_time + INTERVAL 15 HOUR
TTL date_time + toIntervalMonth(ttl)
TTL date_time + toIntervalHour(ttl)
TTL date_time + INTERVAL ttl MONTH
TTL date_time + INTERVAL ttl HOUR

在新建表的时候,我们可以在建表的SQL语句后面加上TTL的表达式,用于根据TTL设置的时间策略在磁盘或者卷之间移动或者删除数据块。

TTL date_time + INTERVAL 6 MONTH DELETE,
   date_time + INTERVAL 1 WEEK TO VOLUME 'localdisk',
   date_time + INTERVAL 4 WEEK TO DISK 'cos';

下面是一个完整的建表的语句,并配置了TTL,根据LastModifiedDate中的时间,默认将数据块放置到ttlhot卷,当LastModifiedDate的值超过三个月时将对应的数据块移动到ttlcold卷。

CREATE TABLE cos_inventory_ttl (
 appid UInt64,
 bucket String,
 key String,
 size UInt64,
 LastModifiedDate DateTime,
 etag String,
 storage_class String,
 IsMultipartUploaded String,
 Replicationstatus String
) ENGINE = MergeTree() 
ORDER BY LastModifiedDate
TTL LastModifiedDate to volume 'ttlhot',
	LastModifiedDate + toIntervalMonth(3) TO VOLUME 'ttlcold'
SETTINGS
storage_policy='ttl',
index_granularity=8192;

4   基于腾讯云存储COS的分层存储实现

在前面的章节,我们介绍了ClickHouse分布式表的数据是如何存储、ClickHouse支持的磁盘类型以及如何配置数据在各类型存储中移动的策略,接下来我们来详细介绍一下如何利用ClickHouse的这些特性以及对象存储COS的优势来解决我们在使用ClickHouse中遇到的一些问题。

4.1 当前ClickHouse数据存储的问题

在和使用ClickHouse交流的时候,客户经常会有这样一个困扰:追求极致查询性能一般是客户选择使用ClickHouse的原因,所以客户一般会选择腾讯云的增强型SSD云硬盘存放ClickHouse的数据,用于提升查询的性能,但是增强型SSD云硬盘价格昂贵,综合性能及成本考虑,客户会选择将比较老的历史数据从ClickHouse中清除。虽然绝大多数的查询都集中在最新的数据上,但是业务方偶尔还是会有访问老的历史数据的需求,如何平衡成本以及业务方偶尔访问历史数据的需求成为ClickHouse系统管理者头疼的问题。

以上面提到的客户的困扰为例,根据业务方的需求,ClickHouse集群需要尽可能存储更长时间的数据,如果这些长时间保存的数据都存放在增强型SSD云盘上,成本将会非常的高。而在实际的业务场景中,可能有95%以上的查询交互都发生在最近一天生成的数据上,剩下5%的的任务都是发生在较早的数据上的批量查询任务,如果将大量的访问频率较低的历史数据都放在高成本的增强型SSD上,会造成极大的容量及性能的浪费。

下图是引用Altinity的一个ClickHouse在实际使用中关于查询频率和对应的数据时间的统计:

4.2 腾讯云存储COS的优势

对象存储COS是腾讯云存储产品,是无目录层次结构、无数据格式限制、无容量上限,支持 HTTP/HTTPS 协议访问的分布式存储服务。

COS以存储桶的方式组织数据,存储桶空间无容量上限,按需使用、按量计费、按需扩展。使用COS作为备份存储有如下优势:

  • 按需使用按量结算:COS开服即用,开通即可用,登录腾讯云官网注册账号后,一键式开通COS服务,就可使用,无需建设成本。COS提供海量的存储空间,无需规划存储容量。COS提供按量付费的方式,避免资源浪费、降低使用成本。
  • 高可靠高可用性:COS提供高达99.99%的服务可用性,以及高达12个9的数据持久性,为数据提供可靠的保障。
  • 高性能:单个存储桶QPS可达30,000以及15Gbit/s带宽。
  • 开放兼容:COS提供全兼容行业标杆AWS S3的接口,提供terrafrom等多种生态工具支持。
  • 数据安全:COS提供多租户权限隔离,支持HTTPS加密传输,支持SSE-KMS加密等多种数据加密方式。
  • 低成本:提供具有竞争力的产品定价,提供标准存储、低频存储以及归档存储三种类型,并支持数据生命周期管理,进一步降低云存储成本。

基于以上推腾讯云对象存储COS的优势,我们推荐使用腾讯云增强型SSD云盘以及腾讯云对象存储COS构建ClickHouse的分层存储结构。增强型SSD云盘存放最近时间生成并且访问频繁的“热数据”、COS存放较早时间生成且访问不频繁的“冷数据”,并在建表的时候使用TTL实现数据根据特定时间策略的自动沉降。

通过设置的数据分层策略,我们实现了将最新生成的、交互式查询频率较高的数据存放在高性能的增强型SSD云盘上,同时根据数据的访问场景设置策略,当数据不再被高频率交互式查询访问时将数据转移到高容量、低成本的二级存储上COS,在不牺牲交互式查询性能的情况下极大地降低了总体使用成本。

通过该方案我们能够同时兼顾以下各方面:

  • 极致性能:最新的数据存放在增强型SSD云盘,为业务的即时查询提供极致性能 ;同时COS提供的高带宽、高并发为历史数据的批量查询提供了较高的性能。
  • 超大容量:腾讯云对象存储COS提供了无容量上限的存储空间,将历史数据存放在COS上后,不用再担心磁盘空间不足删除数据后导致无法满足业务部门较早数据查询的需求。
  • 低成本:相对于本地存储,COS提供了更低的成本和更高的可用性。除了标准存储以外,COS还提供成本更低的标准、低频、归档以及深度归档四种类型存储,根据数据需要访问的频率需求,将数据沉降至对应的存储类型,进一步降低成本。

4.3 基于COS的ClickHouse数据分层实现

在配置数据分层之前,我们提前准备如下环境:

  • 本地存储:挂载增强型SSD硬盘,并格式化为本地文件系统,挂载到/data路径,用于存放热数据。
  • COS存储桶:新建COS存储桶,用于存放冷数据,获取具有访问该存储桶权限账号的SecretId以及SecretKey。

4.3.1      配置ClickHouse磁盘及策略

首先我们需要配置/etc/clickhouse-server/config.d/storage.xml文件,在配置中的<disks>部分定义本地磁盘的路径以及COS存储桶的URL、访问账号的SecretId和SecretKey,同时在<policies>中定义名为<ttl>的策略,该策略中定义了<ttlhot>和<ttlcold>两个卷,分别包含本地存储以及COS存储桶。

以下是配置文件的详细内容(请根据实际场景替换本地路径以及COS的URL、access_key_id和secret_access_key):

<yandex>
    <storage_configuration>
        <disks>
            <localdisk> <!-- disk name -->
                <path>/data/</path>
            </localdisk>
            <cos>
                <type>s3</type>
                <endpoint>http://example-1250000000.cos.ap-shanghai.myqcloud.com/ck/</endpoint>
                <access_key_id>AKIDxxxxxxxx</access_key_id>
                <secret_access_key>xxxxxxxxxxx</secret_access_key>
            </cos>
        </disks>
        <policies>
            <ttl>
                <volumes>
                    <ttlhot>
                        <disk>localdisk</disk>
                        <max_data_part_size_bytes>1073741824</max_data_part_size_bytes>
                    </ttlhot>
                    <ttlcold>
                        <disk>cos</disk>
                      <max_data_part_size_bytes>5368709119</max_data_part_size_bytes>
                    </ttlcold>
                </volumes>
            </ttl>
        </policies>
    </storage_configuration>
</yandex>

修改完配置文件之后,在clickhouse客户端重新加载配置后就能看到新配置的磁盘及策略:

4.3.2      导入数据至ClickHouse

完成存储配置后,我们需要建立一个配置了TTL策略的表,并往表中导入数据以验证我们配置的分层策略。

这里我选择我们一个COS存储桶的清单作为导入的数据源,首先根据清单中各列的内容,在ClickHouse中新建一个名为cos_inventory_ttl的表,同时配置TTL策略,根据LastModifiedDate的值将热数据存放至ttlhot卷,而三个月以上的冷数据存放至ttlcold卷。

CREATE TABLE cos_inventory_ttl (
 appid UInt64,
 bucket String,
 key String,
 size UInt64,
 LastModifiedDate DateTime('Asia/Shanghai'),
 etag String,
 storage_class String,
 IsMultipartUploaded String,
 Replicationstatus String
) ENGINE = MergeTree() 
ORDER BY LastModifiedDate
TTL LastModifiedDate to volume 'ttlhot',
	LastModifiedDate + toIntervalMonth(3) TO VOLUME 'ttlcold'
SETTINGS
storage_policy='ttl',
index_granularity=8192;

然后再将生成的清单文件下载到本地并解压成csv文件,然后将csv数据批量导入到ClickHouse数据库中:

for i in *.csv
do
echo $i;
cat $i |sed 's/\+08:00//g' |clickhouse-client -u default --password='123456' --query="INSERT INTO cos_inventory_ttl FORMAT CSV";
done

4.3.3      验证数据

数据导入完成后,我们首先查看总共导入的数据的行数:

接下来,我们可以查询数据的分区存放的存储卷:

这里我们可以看到,数据已经按照预期存储在不同的磁盘上,其中约两千多万行数据存放在本地磁盘,约六千多万行数据存放在COS上。

接下来我们可以做一个查询测试,这里我们统计一下cos_user/目录下最近三个月份生成的文件的总大小:

5   总结

通过配置在ClickHouse中配置不同的存储介质以及相应的策略,我们实现了ClickHouse数据在不同存储介质的自动存储。COS无限的存储容量以及超低的存储成本,使我们的ClickHouse集群在提供极致查询性能的同时又能以低成本的方式实现数据的长期存放。

更多腾讯云对象存储COS相关的内容,请参考我们的官网:https://cloud.tencent.com/document/product/436。

6   参考文档

https://altinity.com/blog/2019/11/27/amplifying-clickhouse-capacity-with-multi-volume-storage-part-1

https://altinity.com/blog/2020/3/23/putting-things-where-they-belong-using-new-ttl-moves

https://altinity.com/presentations/clickhouse-tiered-storage-intro
https://altinity.com/presentations/clickhouse-tiered-storage-intro

https://cloud.tencent.com/developer/article/1688478

https://mp.weixin.qq.com/s/9PZTws3KSzlybHXM6XC2hg


发表在 ClickHouse | 标签为 , | 留下评论

使用腾讯云数据湖计算DLC分析COS清单

对象存储支持海量的数据存储,腾讯云对象存储COS单个存储桶理论上支持无限数目的文件,实际生产中,单个桶的文件数目可达数十亿、数百亿甚至数千亿。这么大量级的存储量,给我们去分析存储桶中数据文件的分布、单个目录下总文件数及文件大小、特定后缀文件的数目和大小等场景,带来了极大的困难。例如在海量数据的场景下统计某个目录(对象前缀)下所有文件的大小,只能列出该目录下所有文件,然后将所有文件大小相加的方式获取总大小,根据客户的实际反馈,在文件数目非常大的情况下,这种方式不是特别友好,耗时非常久,还需要长期占有主机端资源做list object以及统计容量操作。

如果对于容量统计的时效性要求不高,可以采用清单的方式。COS支持每天、每周生成一次清单以及即时清单,清单中包含了存储桶中所有对象的列表以及每个对象对应的一些信息,包括每个对象的大小。清单生成之后,将会以多个压缩之后的csv文件的方式存放在COS上,我们可以使用腾讯云数据湖计算(DLC)引擎直接分析存放在COS上的清单文件。

1、生成清单

参考如下文档查看清单的描述以及如何配置清单:

对象存储 清单功能概述 – 开发者指南 – 文档中心 – 腾讯云 (tencent.com)

对象存储 开通清单功能 – 控制台指南 – 文档中心 – 腾讯云 (tencent.com)

用户配置一项清单任务后,COS 将根据配置定时扫描用户存储桶内指定的对象,并输出一份清单报告,清单报告支持 CSV 格式文件。目前 COS 清单报告中支持记录以下信息:

清单信息描述
AppID账号的 ID
Bucket执行清单任务的存储桶的名称
fileFormat文件格式
listObjectCount列出的对象数量
listStorageSize列出的对象大小
filterObjectCount筛选的对象数量
filterStorageSize筛选的对象大小
Key存储桶中的对象文件名称。使用 CSV 文件格式时,对象文件名称采用 URL 编码形式,必须解码然后才能使用
VersionId对象版本 ID。在存储桶上启用版本控制后,COS 会为添加到存储桶的对象指定版本号。如果列表仅针对对象的当前版本,则不包含此字段
IsLatest如果对象的版本为最新,则设置为 True。如果列表仅针对对象的当前版本,则不包含此字段
IsDeleteMarker如果对象是删除标记,则设置为 True。如果列表仅针对对象的当前版本,则不包含此字段
Size对象大小(以字节为单位)
LastModifiedDate对象的最近修改日期(以日期较晚者为准)
ETag实体标签是对象的哈希。ETag 仅反映对对象的内容的更改,而不反映对对象的元数据的更改。ETag 可能是也可能不是对象数据的 MD5 摘要。是与不是取决于对象的创建方式和加密方式
StorageClass用于存储对象的存储类,有关更多信息,请参见 存储类型
IsMultipartUploaded如果对象以分块上传形式上传,则设置为 True,有关更多信息,请参见 分块上传
Replicationstatus设置为 PENDING、COMPLETED、FAILED 或 REPLICA。有关更多信息,请参见 跨地域复制行为说明

清单报告及相关的 Manifest 相关文件会发布在目标存储桶中,其中清单报告会发布在以下路径:

destination-prefix/appid/source-bucket/config-ID/YYYYMMDD/manifest.json
destination-prefix/appid/source-bucket/config-ID/YYYYMMDD/manifest.checksum

有关 Mainfest 文件的介绍如下:

  • manifest.json 和 manifest.chenksum 都属于 Manifest 文件,manifest.json 描述清单报告的位置,manifest.checksum 是作为 manifest.json 文件内容的 MD5。每次交付新的清单报告时,均会带有一组新的 Manifest 文件。
  • manifest.json 包含的每个 Manifest 均提供了有关清单的元数据和其他基本信息,这些信息包括:
  • 源存储桶名称。
  • 目标存储桶名称。
  • 清单版本。
  • 时间戳,包含生成清单报告时开始扫描存储桶的日期与时间。
  • 清单文件的格式与架构。
  • 目标存储桶中清单报告的对象键,大小及 md5Checksum。

以下是一个参考的manifest.json文件。

{
 "sourceAppid": "125xxxxx",
 "sourceBucket": "examplebucket",
 "destinationAppid": "125xxxxx",
 "destinationBucket": "examplebucket",
 "fileFormat": "CSV",
 "listObjectCount": "4915",
 "listStorageSize": "34659527588",
 "filterObjectCount": "4915",
 "filterStorageSize": "34659527588",
 "fileSchema": "Appid, Bucket, Key, Size, LastModifiedDate, ETag, StorageClass, IsMultipartUploaded, ReplicationStatus, Tag",
 "files": [
  {
   "key": "cos_bucket_inventory/1253960454/examplebucket/instant_instant_20220517120608/data/55cb8f79a304fed2daad4564358780e1.csv.gz",
   "size": "164745",
   "md5Checksum": "e9fabcef4afff3b71661169ab4a092a9"
  }
 ]
}

  1. 创建DLC外表

腾讯云数据湖计算 DLC(Data Lake Compute,DLC)提供了敏捷高效的数据湖分析与计算服务。该服务采用无服务器架构(Serverless)设计,用户无需关注底层架构或维护计算资源,使用标准 SQL 即可完成对象存储服务(COS)及其他云端数据设施的联合分析计算。借助该服务,用户无需进行传统的数据分层建模,大幅缩减了海量数据分析的准备时间,有效提升了企业数据敏捷度。

DLC简单易用,参考https://cloud.tencent.com/document/product/1342/72493文档实现一分钟完成COS对象存储上的数据分析。

下面是在DLC上创建外部表的过程:

上图中,通过“选择COS位置”按钮,浏览到COS上存放所有清单文件的路径,DLC会自动扫描目录下的所有的文件做查询。

创建表的字段信息,参考清单manifest.json文件中的”fileSchema”字段:

"fileSchema": "Appid, Bucket, Key, Size, LastModifiedDate, ETag, StorageClass, IsMultipartUploaded, ReplicationStatus, Tag"
  1. 清单分析查询

在DLC控制台,创建新的查询请求,并在右上角选择查询引擎,即可开始对COS上的数据进行分析:

上述查询,分析了存储桶中,以cktier/为前缀的对象的总数目以及总的大小。

(本示例中使用了Presto作为分析引擎,如果分析的清单数据量较大,也可采用Spark引擎)

以下是一些常用场景的查询分析SQL语句。

统计prefix/前缀的所有对象总数目及总容量大小:

select  count(*) as Total_Count,  sum(size) / 1024 / 1024 / 1024 as Total_Size_GBfrom  hui.hui_inventoryWHERE  KEY LIKE 'prefix/%';

统计所有后缀名为mp4(不区分大小写)的文件总数目及总容量大小:

select  count(*) as Total_Count,  sum(size) / 1024 / 1024 / 1024 as Total_Size_GBfrom  hui.hui_inventoryWHERE  lower(KEY) LIKE '%.mp4';

统计所有后缀名为mp3(不区分大小写)且修改时间是最近180天的文件总数目及总容量大小:

select  count(*) as Total_Count,  sum(size) / 1024 / 1024 / 1024 as Total_Size_GBfrom  hui.hui_inventoryWHERE  lower(KEY) LIKE '%.mp3'  AND (    to_unix_timestamp(localtimestamp()) - to_unix_timestamp(      replace(lastmodifieddate, 'T', ' '),      'yyyy-MM-dd HH:mm:ss+08:00'    )  ) / 3600 / 24 < 180;
发表在 COS | 留下评论

基于COS的Hive数据冷热分层方案

1. 存算分离及冷热分层方案简介

1.1 存算一体的问题

在Hadoop生态中,一直是以HDFS作为基础存储数据,计算资源和存储资源耦合在一起,将数据尽量的靠近计算资源,也就是所谓的移动计算比移动数据更划算。

但随着数字时代数据量爆发式的增长、计算存储规模的不断扩大,这种计算、存储耦合的架构带来的多种问题不断浮现出来:

  • 存储计算耦合,无法单独扩容计算或者存储资源
  • 数据三副本存储,较低的空间利用率增加成本
  • 集群扩容需做数据均衡,占用网络资源影响业务
  • HDFS文件数目增长带来元数据膨胀问题
  • 冷数据低成本存储比较难实现

同时随着网络带宽的发展,移动计算带来的收益也不那么明显,而存算一体带来的上面那些问题也开始影响业务的发展,越来越多的客户开始考虑将大数据部署到云上,并采用对象存储作为数据存储的存算分离方案。

1.2 存算分离的优势

基于腾讯云EMR+Goosefs+COS的存算分离方案,提供了托管的Hadoop计算平台,计算端缓存服务以及高可用、高性能的海量对象存储服务,相对于传统的存算一体方案,具备如下优势:

  • 根据业务场景,按需扩缩容计算资源,提供弹性能力
  • 海量存储空间,无需考虑存储容量扩容、文件数目等问题
  • 计算端缓存方案,提升性能,轻松应对数据湖场景
  • 存储生命周期管理,热数据、温数据及冷数据分层存储,降低存储成本

同时腾讯云COS提供了支持标准Hadoop文件系统的hadoop-cos插件,使各种大数据计算框架能够像访问HDFS一样,读写存储在COS上的数据,减少大数据业务在迁移到存算分离架构的迁移成本。

1.3 冷热分层的需求

我们在和各类客户交流的时候发现,大家普遍认可存算分离是大数据发展的趋势,特别是在数据湖的场景,但是不可避免的存在一定的升级、改造成本。当前还有不少客户的大数据系统还是在IDC基于物理服务器以及本地盘自建的,或者是云上购买物理机或虚拟机基于本地盘、云盘自建,由于各方面原因,短期内还不方便对大数据系统做升级改造。

但是自建大数据系统,特别是存算一体带来的HDFS存储容量过高管理困难问题、计算存储资源不匹配带来的资源浪费问题以及HDFS文件系统文件数目过多带来的元数据膨胀问题,都困扰着大数据管理员,这个问题在离线数仓Hive的场景下尤为明显。

腾讯云提供了Hive场景下HDFS+COS的数据冷热分层方案。对于同一个Hive表,不同的分区数据访问热度不一样,对于一些时间较老的分区,访问频次会比较低,访问性能也没有太高的要求,可以利用IDC到云上的专线或者云上的内网资源,将IDC或者云上自建的HDFS上的Hive冷分区移动到对象存储COS上,实现数据的冷热分层。由于不对Hive表做修改,不会对业务端访问造成任何影响,可以实现业务端透明的数据冷热分层存储。

Hive的冷热分层方案具备如下优势:

  • 降低成本:冷数据存放至COS,并结合COS多存储类型(标准、低频、归档、深度归档)以及生命周期策略自动沉降,较少存储成本。
  • 减少HDFS文件数据:冷数据移动到COS之后,HDFS文件系统文件数目减少,解决元数据膨胀问题。
  • 业务端透明:同一个Hive表不同的分区存放不同的存储类型,但是业务访问Hive表的方式不变,存储改造对业务端透明。
  • 长期演进:冷热分层虽然作为一个过渡方案,但是不影响大数据架构由存算一体到存算分离的技术架构长期演进。

下面我们介绍如何在业务端无感知的情况下,将Hive表中冷数据分区从HDFS迁移到腾讯云对象存储COS上。

2. Hive数据的冷热分层存储

2.1 生成测试数据

为了验证Hive数据冷热分层方案,我们利用脚本生成了一批数据,并写入到csv文件中,包含order_id、seller_id、price、tag、order_date等字段信息:

[hadoop@10 /data]$ head 3.csv
order_1629443046,seller_261,434.37,1,20210721
order_1629443047,seller_252,901.47,0,20210729
order_1629443048,seller_65,858.44,1,20210818
order_1629443049,seller_99,923.94,0,20210729
order_1629443050,seller_81,868.92,1,20210812
order_1629443051,seller_142,989.22,0,20210722
order_1629443052,seller_0,430.96,0,20210810
order_1629443053,seller_27,665.8,0,20210802
order_1629443054,seller_85,74.3,1,20210819
order_1629443055,seller_34,913.02,0,20210723

接下来我们根据CSV中个字段的内容,创建一个测试表,这个Hive表默认的存储路径是在HDFS上:

create table order_sample (order_id string, seller_id string, price double, tag int)  partitioned by (dt string) ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe' WITH SERDEPROPERTIES  ('serialization.format' = '1') STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat' LOCATION 'hdfs:///data/order_sample';

接下来创建一个中间转换表,并导入CSV数据:

create table test (order_id string, seller_id string, price double, tag int, order_date string) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' stored as textfile;

load data local inpath '/data/3.csv' into table test;

将中间转换表中各个分区的数据导入数据至测试表:

for i in `hive -e "select order_date from test group by order_date;"`
do
hive -e "insert overwrite table order_sample PARTITION(dt) select * from test where order_date='${i}';"
done

查看测试表数据:

hive> select count(*) from order_sample;
OK
30000000
Time taken: 2.767 seconds, Fetched: 1 row(s)
hive> select * from order_sample limit 10;
OK
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
order_1629443046 seller_261 434.37 1 20210721
order_1629443066 seller_292 3.02 1 20210721
order_1629443121 seller_51 209.34 1 20210721
order_1629443122 seller_47 619.43 1 20210721
order_1629443123 seller_230 92.58 1 20210721
order_1629443136 seller_32 553.62 0 20210721
order_1629443148 seller_272 767.66 0 20210721
order_1629443168 seller_265 803.19 0 20210721
order_1629443172 seller_255 749.48 1 20210721
order_1629443179 seller_64 839.39 1 20210721
Time taken: 0.221 seconds, Fetched: 10 row(s)

2.2 Hive冷数据迁移至COS

接下来,我们使用distcp,将部分冷数据所在的分区上的数据从HDFS复制到COS上:

hadoop distcp hdfs:///data/order_sample/dt=20210721/ cosn://hui-shanghai-1250000000/data/order_sample/dt=20210721/
hadoop distcp hdfs:///data/order_sample/dt=20210722/ cosn://hui-shanghai-1250000000/data/order_sample/dt=20210722/
hadoop distcp hdfs:///data/order_sample/dt=20210723/ cosn://hui-shanghai-1250000000/data/order_sample/dt=20210723/

在Hive中,修改已经迁到COS上的冷数据分区的路径:

alter table order_sample partition (dt='20210721') set location "cosn://hui-shanghai-1250000000/data/order_sample/dt=20210721/";
alter table order_sample partition (dt='20210722') set location "cosn://hui-shanghai-1250000000/data/order_sample/dt=20210722/";
alter table order_sample partition (dt='20210723') set location "cosn://hui-shanghai-1250000000/data/order_sample/dt=20210723/";

为了验证迁移到COS上的分区的访问,我们删除这部分分区在HDFS上对应的文件夹:

hadoop dfs -rm -r -f hdfs:///data/order_sample/dt=20210721/
hadoop dfs -rm -r -f hdfs:///data/order_sample/dt=20210722/
hadoop dfs -rm -r -f hdfs:///data/order_sample/dt=20210723/

3. 混合分区验证

在上面的步骤中,我们在Hive中修改了部分分区所在的路径并指向了COS,在Hive metastor会同步更新相应的字段。我们可以通过查询Hive metastore相关的表,以确认分区路径已经修改。

在Hive metastor的表中,以下几个表存储了Hive表及分区的相关信息:

  • TBLS表:Hive表的具体信息
  • PARTITIONS表:Hive表分区的信息
  • SDS表:提供table/partition对应的文件系统路径location,以及对这个数据读取的InputFormat、是否压缩、是否是子文件夹存储、SerDe类(对应于SERDES表)

EMR的Hive metastor存储在腾讯云CDB MySQL数据库中,连接相关的MySQL示例,并通过如下SQL语句查看分区的路径信息:

select TBLS.TBL_NAME,PARTITIONS.PART_NAME,SDS.LOCATION
from SDS,TBLS,PARTITIONS
where PARTITIONS.SD_ID = SDS.SD_ID
and TBLS.TBL_ID=PARTITIONS.TBL_ID
order by 1,2;

TBLS_TBL_NAME	PARTITIONS_PART_NAME	SDS_LOCATION
order_sample	dt=20210721	cosn://hui-shanghai-1250000000/data/order_sample/dt=20210721/
order_sample	dt=20210722	cosn://hui-shanghai-1250000000/data/order_sample/dt=20210722/
order_sample	dt=20210723	cosn://hui-shanghai-1250000000/data/order_sample/dt=20210723/
order_sample	dt=20210724	hdfs://10.0.0.10:4007/data/order_sample/dt=20210724
order_sample	dt=20210725	hdfs://10.0.0.10:4007/data/order_sample/dt=20210725
order_sample	dt=20210726	hdfs://10.0.0.10:4007/data/order_sample/dt=20210726
order_sample	dt=20210727	hdfs://10.0.0.10:4007/data/order_sample/dt=20210727
order_sample	dt=20210728	hdfs://10.0.0.10:4007/data/order_sample/dt=20210728
order_sample	dt=20210729	hdfs://10.0.0.10:4007/data/order_sample/dt=20210729
order_sample	dt=20210730	hdfs://10.0.0.10:4007/data/order_sample/dt=20210730

接下来,我们可以在Hive中查询COS以及HDFS分区上的数据:

select * from order_sample where dt='20210721' limit 10;
select * from order_sample where dt='20210722' limit 10;
select * from order_sample where dt='20210723' limit 10;
select * from order_sample where dt='20210724' limit 10;
select * from order_sample where dt='20210725' limit 10;
select * from order_sample where dt='20210726' limit 10;

下面的查询验证了,当需要访问的数据在HDFS和COS上混合存储时,业务端的访问是完全透明无感知的:

hive> select count(*),dt from order_sample where dt<'20210801' group by dt;
Query ID = hadoop_20210823165807_f3c7db99-94df-4bd5-aa48-22e058f42a0a
Total jobs = 1
Launching Job 1 out of 1
Status: Running (Executing on YARN cluster with App id application_1629447707661_0175)

OK
967674 20210721
967639 20210722
969561 20210723
967100 20210724
966771 20210725
967459 20210726
968008 20210727
965999 20210728
967780 20210729
967701 20210730
968122 20210731
Time taken: 38.665 seconds, Fetched: 11 row(s)
发表在 COS | 留下评论

使用COS作为Docker Registry存储

除了使用本地的文件系统作为Docker repository的存储以外,Docker Registry还可以通过Storage  Driver支持其他各种类型的存储,其中包括AWS S3以及S3兼容的对象存储。由于腾讯云对象存储COS完全兼容S3,因此在Docker Registry中配置S3 storage driver访问COS,从而将Docker image文件存放在COS,节约本地的存储空间、降低使用成本。

(参考文档:https://docs.docker.com/registry/storage-drivers/s3/

下面的步骤描述了如何配置Docker Registry的S3 storage driver以访问COS。

一、环境描述

操作系统:Ubuntu 18.04.4 LTS

Docker:19.03.6

docker-compose:1.26.2

二、配置步骤

1、在腾讯云控制台,建立存储桶example-125000000,并获取有访问该存储桶权限账号的secretid和secretkey。

2、安装Docker.io以及docker-compose软件。

3、获取Docker Registry镜像。

docker pull registry

4、新建docker-compose.yml文件,内容如下:

version: "3"
services:
  registry:
    image: registry
    container_name: docker-registry
    restart: always
    ports:
      - "5000:5000"
    environment:
       REGISTRY_STORAGE: s3
       REGISTRY_STORAGE_S3_ACCESSKEY: AKIDxxxxxxxxxxxx
       REGISTRY_STORAGE_S3_SECRETKEY: xxxxxxxxxxxxxxx
       REGISTRY_STORAGE_S3_REGION: ap-shanghai
       REGISTRY_STORAGE_S3_BUCKET: example-125000000
       REGISTRY_STORAGE_S3_REGIONENDPOINT: cos.ap-shanghai.myqcloud.com

REGISTRY_STORAGE:存储的类型,使用S3及S3兼容类型。

REGISTRY_STORAGE_S3_ACCESSKEY:访问存储桶账号的secretid。

REGISTRY_STORAGE_S3_SECRETKEY:访问存储桶账号的secretkey。

REGISTRY_STORAGE_S3_REGION:存储桶所在的地域,例如上海则填写ap-shanghai。

REGISTRY_STORAGE_S3_BUCKET:存储桶的名字。

REGISTRY_STORAGE_S3_REGIONENDPOINT:COS的endpoint,以上海的存储桶为例,填入cos.ap-shanghai.myqcloud.com。

5、编辑/etc/docker/daemon.json文件,加入以下内容,允许http方式访问registry:

{
  "insecure-registries": [
    "172.17.66.66:5000"
  ]
}

请将上例中的IP地址替换为Docker Registry服务器的IP。

6、进入到docker-compose.yml文件所在目录,使用docker-compose运行Registry容器。

docker-compose up -d
docker ps

三、验证配置

1、Push镜像到Registry,查看配置是否生效。

docker tag busybox:latest 172.17.66.66:5000/busybox:latest
docker push 172.17.66.66:5000/busybox:latest

2、使用控制台或者cosbrower浏览example-125000000存储桶的文件,看看docker/registry/v2/repositories目录下是否有文件生成,确认镜像已经存储在COS上。

发表在 COS, docker | 留下评论

使用Nextcloud挂载腾讯云对象存储COS

我们之前搭建的基于Nextcloud的网络云盘使用的是虚拟机的本地硬盘作为存储空间存放用户上传至网盘的数据。但使用本地盘有诸多不便,例如价格昂贵,扩容困难等。而Nextcloud支持挂载对象存储作为external storage。对象存储提供了海量的存储空间,具备高并发、高扩展性、低成本以及可靠安全等特点。我们使用对象存储作为Nextcloud的存储空间是再合适不过了。

Nextcloud支持以下类型的外接存储:

  • FTP
  • WebDav
  • Nextcloud
  • SFTP
  • Amazon S3
  • OpenStack Object Storage

在这里,我们使用完全兼容Amazon S3的对象存储,腾讯云Cloud Object Storage(COS)。

首先,我们必须在Nextcloud的控制台,启用External Storage。使用管理员账号登陆,点击右上角的用户头像图标,点击“+Apps”:

找到“External storage support”,点击“Enable”以启用。

然后选择头像里的“Settings”开始配置外接存储:

进入后,新增Amazon S3类型的存储,如下图:

各项配置内容级说明如下:

  • Bucket:新建的Bucket的名字,不能COS上现有Bucket名字冲突。
  • Hostname:COS的endporint,与bucket所在的region有关,例如我这里新建的bucket放在ap-shanghai,则填入地址cos.ap-shanghai.myqcloud.com。
  • Region:填入Bucket所在的区域,我这里为ap-shanghai。
  • Access Key:访问COS的secret_id.
  • Secret Key:访问COS的secret_key。

输入完点击保存后,Nextcloud会自动在COS上建立制定的Bucket。我们回到Nextcloud应用的主界面,发现多出来一个名为AmazonS3的文件夹。我们将文件上传到AmazonS3文件夹的时候,数据就自动存放在腾讯云对象存储COS上了。

发表在 docker | 留下评论

自建网络云盘

如果自己有多余的云主机,可以尝试自己搭建一套网络云盘,方便文件的存储和共享。这里推荐一个开源的产品——Nextcloud。

Nextcloud是一款开源免费的私有云存储网盘项目,可以让你快速便捷地搭建一套属于自己或团队的云同步网盘,从而实现跨平台跨设备文件同步、共享、版本控制、团队协作等功能。它的客户端覆盖了Windows、Mac、Android、iOS、Linux 等各种平台,也提供了网页端以及 WebDAV接口,所以你几乎可以在各种设备上方便地访问你的云盘。

当然,我最喜欢的一点还是官方提供了docker镜像,可以一键式部署。

在开始部署之前,确保以下环境已具备:

  • 一台Linux云主机,配置不需要太高,但是硬盘空间最好大一点,毕竟是用来做网络云盘的
  • 云主机安装好docker
  • 公网IP、公网域名以及SSL证书

这套环境主要由一下三个组件构成:

  • MariaDB(MySQL)
  • Nextcloud
  • Nginx

其中MariaDB为后台数据库,Nextcloud为应用程序,Nginx用于实现https中证书的卸载。三个组件均以容器的方式运行,使用docker-compose编排容器的运行。

以下是docker-compose.yaml的配置:

version: '2'

services:
  db:
    image: mariadb
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    restart: always
    volumes:
      - /nextcloud/db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=XXXXXXXXX
      - MYSQL_PASSWORD=XXXXXXXXX
      - MYSQL_DATABASE=XXXXXXXXX
      - MYSQL_USER=XXXXXXXXX
  app:
    image: nextcloud
    links:
      - db
    volumes:
      - /nextcloud/app:/var/www/html
    restart: always
  nginx:
    image: nginx:alpine
    ports:
      - 80:80
      - 443:443
    links:
      - app
    volumes:
      - /nextcloud/nginx:/etc/nginx
    restart: always

Nginx中实现HTTPS证书卸载的配置如下:

stream {
	upstream stream_backend {
		server app:80;	
	}
	server {
		listen 443 ssl;
		proxy_pass stream_backend;
		ssl_certificate /etc/nginx/ssl/cert.pem;
	        ssl_certificate_key /etc/nginx/ssl/cert.key;
	        ssl_protocols       TLSv1.2 TLSv1.3;
		ssl_prefer_server_ciphers on;
	        ssl_ciphers         HIGH:!aNULL:!MD5;
		ssl_session_cache	shared:SSL:20m;
		ssl_session_timeout	4h;
		ssl_handshake_timeout	30s;
	}
	
	server {
		listen 80;
		proxy_pass	stream_backend;
	}

}

HTTP到HTTPS的跳转我没有使用Nginx,而是在应用程序端也就是Nextcloud实现的,修改/nextcloud/app/config/config.php配置文件的,修改项如下:

  array (
    0 => 'xx.xx.xx.xx',
    'www.gaohui.xyz',
  ),
  'overwrite.cli.url' => 'https://www.gaohui.xyz',
  'overwritehost' => 'www.gaohui.xyz',
  'overwriteprotocol' => 'https',

config.php配置文件中各配置的说明可参考通目录下config.sample.php文件。

配置完成后,浏览器输入域名就可以进到Nextcloud开始基础配置啦。同时你也可以下载iOS或者Android的客户端,手机上查看网盘内容或者使用手机上传下载文件了。

Nextcloud还提供了一个命令行管理工具occ,有时候需要用它来管理用户,例如重置密码什么的,详细可以去研究下Nextcloud的官方文档:

root@nextcloud:~# docker exec --user www-data 381 php occ
Nextcloud 18.0.3

Usage:
  command [options] [arguments]

Options:
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
      --no-warnings     Skip global warnings, show command output only
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  check                               check dependencies of the server environment
  help                                Displays help for a command
  list                                Lists commands
  status                              show some status information
  upgrade                             run upgrade routines after installation of a new release. The release has to be installed before.
 activity
  activity:send-mails                 Sends the activity notification mails
 app
  app:check-code                      check code to be compliant
  app:disable                         disable an app
  app:enable                          enable an app
  app:getpath                         Get an absolute path to the app directory
  app:install                         install an app
  app:list                            List all available apps
  app:remove                          remove an app
  app:update                          update an app or all apps
 background
  background:ajax                     Use ajax to run background jobs
  background:cron                     Use cron to run background jobs
  background:webcron                  Use webcron to run background jobs
 broadcast
  broadcast:test                      test the SSE broadcaster
 config
  config:app:delete                   Delete an app config value
  config:app:get                      Get an app config value
  config:app:set                      Set an app config value
  config:import                       Import a list of configs
  config:list                         List all configs
  config:system:delete                Delete a system config value
  config:system:get                   Get a system config value
  config:system:set                   Set a system config value
 dav
  dav:create-addressbook              Create a dav addressbook
  dav:create-calendar                 Create a dav calendar
  dav:list-calendars                  List all calendars of a user
  dav:move-calendar                   Move a calendar from an user to another
  dav:remove-invalid-shares           Remove invalid dav shares
  dav:send-event-reminders            Sends event reminders
  dav:sync-birthday-calendar          Synchronizes the birthday calendar
  dav:sync-system-addressbook         Synchronizes users to the system addressbook
 db
  db:add-missing-indices              Add missing indices to the database tables
  db:convert-filecache-bigint         Convert the ID columns of the filecache to BigInt
  db:convert-mysql-charset            Convert charset of MySQL/MariaDB to use utf8mb4
  db:convert-type                     Convert the Nextcloud database to the newly configured one
 encryption
  encryption:change-key-storage-root  Change key storage root
  encryption:decrypt-all              Disable server-side encryption and decrypt all files
  encryption:disable                  Disable encryption
  encryption:enable                   Enable encryption
  encryption:encrypt-all              Encrypt all files for all users
  encryption:list-modules             List all available encryption modules
  encryption:set-default-module       Set the encryption default module
  encryption:show-key-storage-root    Show current key storage root
  encryption:status                   Lists the current status of encryption
 federation
  federation:sync-addressbooks        Synchronizes addressbooks of all federated clouds
 files
  files:cleanup                       cleanup filecache
  files:recommendations:recommend     
  files:scan                          rescan filesystem
  files:scan-app-data                 rescan the AppData folder
  files:transfer-ownership            All files and folders are moved to another user - shares are moved as well.
 group
  group:add                           Add a group
  group:adduser                       add a user to a group
  group:delete                        Remove a group
  group:list                          list configured groups
  group:removeuser                    remove a user from a group
 integrity
  integrity:check-app                 Check integrity of an app using a signature.
  integrity:check-core                Check integrity of core code using a signature.
  integrity:sign-app                  Signs an app using a private key.
  integrity:sign-core                 Sign core using a private key.
 l10n
  l10n:createjs                       Create javascript translation files for a given app
 log
  log:file                            manipulate logging backend
  log:manage                          manage logging configuration
  log:tail                            Tail the nextcloud logfile
  log:watch                           Watch the nextcloud logfile
 maintenance
  maintenance:data-fingerprint        update the systems data-fingerprint after a backup is restored
  maintenance:mimetype:update-db      Update database mimetypes and update filecache
  maintenance:mimetype:update-js      Update mimetypelist.js
  maintenance:mode                    set maintenance mode
  maintenance:repair                  repair this installation
  maintenance:theme:update            Apply custom theme changes
  maintenance:update:htaccess         Updates the .htaccess file
 migrations
  migrations:execute                  Execute a single migration version manually.
  migrations:generate                 
  migrations:generate-from-schema     
  migrations:migrate                  Execute a migration to a specified version or the latest available version.
  migrations:status                   View the status of a set of migrations.
 notification
  notification:generate               Generate a notification for the given user
 security
  security:certificates               list trusted certificates
  security:certificates:import        import trusted certificate
  security:certificates:remove        remove trusted certificate
 sharing
  sharing:cleanup-remote-storages     Cleanup shared storage entries that have no matching entry in the shares_external table
  sharing:expiration-notification     Notify share initiators when a share will expire the next day.
 text
  text:reset                          Reset a text document
 trashbin
  trashbin:cleanup                    Remove deleted files
  trashbin:expire                     Expires the users trashbin
 twofactorauth
  twofactorauth:cleanup               Clean up the two-factor user-provider association of an uninstalled/removed provider
  twofactorauth:disable               Disable two-factor authentication for a user
  twofactorauth:enable                Enable two-factor authentication for a user
  twofactorauth:enforce               Enabled/disable enforced two-factor authentication
  twofactorauth:state                 Get the two-factor authentication (2FA) state of a user
 update
  update:check                        Check for server and app updates
 user
  user:add                            adds a user
  user:delete                         deletes the specified user
  user:disable                        disables the specified user
  user:enable                         enables the specified user
  user:info                           show user info
  user:lastseen                       shows when the user was logged in last time
  user:list                           list configured users
  user:report                         shows how many users have access
  user:resetpassword                  Resets the password of the named user
  user:setting                        Read and modify user settings
 versions
  versions:cleanup                    Delete versions
  versions:expire                     Expires the users file versions
 workflows
  workflows:list                      Lists configured workflows

最终效果图如下:

发表在 docker | 留下评论

SSL证书的卸载

当时腾讯申请域名的时候可以免费申请SSL证书,所以我就把这个站点变成了https的访问方式。如何开启SSL并且处理SSL证书,我想了好多个办法,这里写一下我采取的各种方案:

  1. 像最开始的文章写道的,利用wordpress自带的apache web server启用SSL,并做了http到https的redirect。但是这个比较麻烦,需要重新打包wordpress容器。以后wordpress官方容器升级了,你如果想更新,还得重新打包。
  2. 后来学习Azure的七层负载均衡,就使用Azure的application gateway做了SSL卸载。将IIS类型的证书上传到application gateway,并将我的服务器配置到application gateway的后端,启用http和https,并配置http到https的redirect。这个方案最大的问题就是贵。看账单,网站没什么流量的情况下,application gateway的费用比服务器的费用还高。
  3. 正好这几天在学习Nginx,想到用Nginx做反向代理并做SSL证书卸载。同时Nginx、wordpress和后端的MySQL都以容器的方式运行

这里写一下第三个方案具体是怎么实现的。

都已经有三个容器了,我就使用docker-compose做了个简单的编排。步骤如下:

1、docker中建一个bridge的网络,这样三个网络都在这个网络下运行,实现三个容器之间使用主机名就能通信。(其实docker-compose会根据project的名称自动建一个bridge的网络并将所有的service都运行在这个网络下)

docker create network wordpress_network

2、建一个临时的Nginx容器,并将临时容器里的/etc/nginx文件夹的内容都拷贝到本机。

docker run --name nginx -d -p 8085:80 nginx
docker cp nginx:/etc/nginx /home/hui/wordpress/
docker stop nginx
docker rm nginx

3、编辑docker-compose.yml文件,编排三个容器的运行,内容如下:

version: '3'
 services:
     db:
       image: mysql:5.7
       volumes:
         - /home/hui/wordpress/mysql:/var/lib/mysql
       restart: always
       environment:
         MYSQL_ROOT_PASSWORD: 123456
         MYSQL_DATABASE: wordpress
     wordpress:
       depends_on:
         - db
       image: wordpress:latest
       volumes:
         - /home/hui/wordpress/app:/var/www/html
       restart: always
       environment:
         WORDPRESS_DB_HOST: db:3306
         WORDPRESS_DB_USER: root
         WORDPRESS_DB_PASSWORD: 123456
     nginx:
       depends_on:
         - wordpress
       image: nginx:latest
       volumes:
         - /home/hui/wordpress/nginx:/etc/nginx
       ports:
         - "80:80"
         - "443:443"
       restart: always
 networks:
     default:
       external:
         name: wordpress_network

4、将Nginx类型的证书两个文件拷贝至相应的文件夹,编辑/home/hui/wordpress/nginx/nginx.conf文件,配置Nginx的反向代理、SSL证书卸载以及http到https的重定向:

    server {
        listen 80;
        location / {
                return 301 https://$host$request_uri;
        }
    }
    server {
        listen 443 ssl;
        server_name www.gaohui.xyz;
        keepalive_timeout 70;

        ssl_certificate     /etc/nginx/ssl/1_www.gaohui.xyz_bundle.crt;
        ssl_certificate_key /etc/nginx/ssl/2_www.gaohui.xyz.key;
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers         HIGH:!aNULL:!MD5;

        location / {
                proxy_set_header Host $host;
                proxy_set_header X-Real_ip $remote_addr;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_pass http://wordpress;
        }
    }

5、配置文件写好之后,在docker-compose.yml所在的路径运行命令:

docker-compose up -d

6、使用浏览器访问网站,配置wordpress。

最后说一下,wordpress使用SSL访问,并且前端是Nginx这种反向代理的话,是需要特殊配置的,nginx配置文件里proxy_set_header X-Forwarded-Proto $scheme;这个参数就是这个目的。详细可以参考wordpress的文档:

https://wordpress.org/support/topic/compatibility-with-wordpress-behind-a-reverse-proxy/

发表在 docker | 留下评论

Nginx负载均衡策略

Nginx支持六种负载均衡策略,其中开源版的Nginx支持四种,Nginx Plus支持两种:

  1. Round Robin

所有请求将被分布发放给后端的所有服务器,在发布请求时还会考虑每台服务器设置的权重。该策略为默认策略,不需要额外的设置:

下例中,请求被分配给后端backend1和backend2两台服务器,两台服务器的权重未设置的话则表示权重都为1。

upstream backend {
   # no load balancing method is specified for Round Robin
   server backend1.example.com;
   server backend2.example.com;
} 

同时可以设置weight这个参数来设置服务器的权重,用于当后端服务器的性能不均时为性能更好的服务器设置高权重,以便高性能服务器能处理更多的请求。下例中,backend1的权重设置为3,backend2权重未做设置,则未默认值1:

upstream backend {
   server backend1.example.com weight=3;
   server backend2.example.com;
} 
  1. Least Connection

当有请求过来时,Nginx会将请求发送给后端连接数最少的服务器,同时也会考虑后端服务器的权重值:

upstream backend {
    least_conn;
    server backend1.example.com;
    server backend2.example.com;
} 
  1. IP Hash

每个请求按照访问的IP地址的hash结果分配服务器,这样每个访客会访问固定的后端服务器,可以解决会话保持的问题:

upstream backend {
    ip_hash;
    server backend1.example.com;
    server backend2.example.com;
} 

如果后端的某台服务器需要临时从负载均衡组里移除出来,可以在配置文件中将该服务器标注为down,用以保存客户端IP的hash。原本应该发送给该服务器的请求会自动的转发给下一台服务器:

upstream backend {
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com down;
} 
  1. Generic Hash

按照用户指定的键值,例如文本字符串、变量或者这些的组合,做hash再决定请求分发给后端的哪台服务器。例如这个用户指定的键值可以是一对IP地址和端口,或者是URI。

upstream backend {
    hash $request_uri consistent;
    server backend1.example.com;
    server backend2.example.com;
} 

如果指定了consistent参数,则使用一致性哈希算法。该方法可以确保在向组中添加或者删除服务器时,只有少数键值重新映射到不同的服务器,这有助于为高速缓存服务器实现更好的高速缓存命中率。

  1. Least Time(Nginx Plus Only)

Nginx Plus选择平均时延最低且连接数最少的后端服务器处理每个请求。least_time参数提供了如下变量用于计算最低时延:

  • header – 从服务器获取第一个字节所用的时间
  • last_byte – 从服务器获取全部相应的时间
  • last_byte inflight – 从服务器获取全部相应的时间,包括未完成的请求
upstream backend {
    least_time header;
    server backend1.example.com;
    server backend2.example.com;
} 
  1. Random

对于每个请求,Nginx将会随机的发送给后端的服务器。如果使用了two这个参数,则Nginx会随机的选择两台服务器,并使用如下规则从这两台中选出一台用于处理请求:

  • least_conn – 活动连接数最少的服务器
  • least_time=header (Nginx Plus) – 接收响应header平均用时最少的服务器($upstream_header_time)
  • least_time=last_byte (Nginx Plus) – 接收全部响应平均用时最少的服务器($upstream_response_time)
upstream backend {
    random two least_time=last_byte;
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
    server backend4.example.com;
} 

一般在分布式环境里面,如果使用多个负载均衡器将请求分发给后端,我们可以使用Random策略。如果使用一个负载均衡处理所有的请求,请使用其他的策略,例如round robin,least connection或者least time。

接下来我们做个测试,验证一下Round Robin中的权重。

首先,我们需要一台Linux服务器,我这里使用一台Ubuntu 18.04。前端一台Nginx做负载均衡,后端使用三台Nginx做为web server。四台Nginx服务器均运行在容器中,其中前端负载均衡Nginx映射主机的8080端口供外部访问,三台后端Nginx服务器不映射主机端口,直供前端负载均衡Nginx容器内部访问。

  1. 服务器安装docker:
apt-get install docker.io
  1. docker建议一个桥接的网络,用于所有容器之间的通信:
docker network create nginx_network
  1. 使用桥接网络创建四个nginx容器,其中一个作为前端的负载均衡,三个作为后端的web server:
docker run --name nginx_frontend -d --network nginx_network -p 8080:80 nginx
docker run --name nginx_backend1 -d --network nginx_network nginx
docker run --name nginx_backend2 -d --network nginx_network nginx
docker run --name nginx_backend3 -d --network nginx_network nginx
  1. 将nginx_frontend容器的/etc/nginx/nginx.conf文件从容器中拷贝出来:
docker cp nginx_frontend:/etc/nginx/nginx.conf .
  1. 编辑拷贝出来的nginx配置文件,配置如下:
 http {    
     upstream backend {
         server nginx_backend1 weight=1;
         server nginx_backend2 weight=3;
         server nginx_backend3 weight=6;
     }
     server {
         listen 8080;
         location / {
             proxy_pass http://backend;
         }
     }   
 }

由于我们所有的四个容器都创建在nginx_network上,所以四个容器之间可以通过容器名名进行通信,配置文件里面server后面直接写容器名是没问题的。

  1. 修改每台Web Server的index.html文件用于区分:
docker exec -it nginx_backend1 /bin/bash
echo "This is nginx_backend1" > /usr/share/nginx/html/index.html
exit
docker exec -it nginx_backend2 /bin/bash
echo "This is nginx_backend2" > /usr/share/nginx/html/index.html
exit
docker exec -it nginx_backend3 /bin/bash
echo "This is nginx_backend3" > /usr/share/nginx/html/index.html
exit
  1. 重启前端做负载均衡的nginx_frontend容器,使修改的配置文件生效:
nginx -s reload
  1. 使用脚本验证权重设置是否生效:
for i in $(seq 1 100)
do 
curl -L http://localhost:8080 >> curl_output
sleep 1
done
cat curl_output |grep nginx_backend1|wc –l
cat curl_output |grep nginx_backend2|wc –l
cat curl_output |grep nginx_backend3|wc -l 

发表在 docker | 留下评论

给运维团队使用的虚拟机自动化部署工具

在日常VMware虚拟化平台运维中,有大量的虚拟机部署的需求,基于此需求,我使用Excel+PowerCLI做了一个可视化的VMware虚拟机自动化部署工具。

在开发或者使用该工具前,确保已完成如下工作:

1、创建各类型操作系统的虚拟机模板,并且并且模板中已安装最新版的VMware Tools。
2、建立针对Windows和Linux的虚拟机自定义规范:使用web client登陆VC,菜单->主页->策略和配置文件->虚拟机自定义规范,点击新建。我建立了两个,auto_win和auto_linux分别供Windows系统和Linux系统使用。两个规范中网络均使用DHCP,针对Windows的规范中工作组或域里输入域名、用户名以及密码。

(经过我测试,Windows 2008R2及以后版本均支持,包括Windows 2019;Linux支持RHEL6、RHEL7、Ubuntu 16.04,不支持Ubuntu 1804)

使用该工具能实现如下功能:

1、使用Excel实现可视化的参数输入,界面如下图:

2、各参数输入尽量使用下拉框实现,避免误操作,例如:

实现方法:在excel中,数据->数据验证->数据验证:

来源中填写公式:

=IF(A2="Test",OFFSET(Sheet2!$A$1,,,SUMPRODUCT(N(LEN(Sheet2!$A:$A)>0)),),OFFSET(Sheet2!$G$1,,,SUMPRODUCT(N(LEN(Sheet2!$G:$G)>0)),))

同时在Sheet2中的A列中将所有可供测试环境选择的参数填入各行,在G列中 将所有可供开发环境选择的参数填入各行 。如果还要支持更多的环境,就需要写嵌套的if语句了。

3、支持单次部署多台虚拟机,并支持多VC环境部署(我的代码里写入了我们这里开发和测试环境的VC信息),支持同时部署多种类型的操作系统。

4、点击“点击开始部署虚拟机按钮”之后开始批量化部署,Excel会调用D:\automation\VMDeployment.ps1脚本,按钮的源代码如下:

Private Sub CommandButton1_Click()
Dim retval
retval = Shell("powershell ""D:\automation\VMDeployment.ps1""", 1)

End Sub

5、部署完毕后,VC会根据我们输入的信息,自动完成虚拟机的主机名更改、IP、DNS、网关等网络配置、Windows系统的加域以及CPU、内存参数的设置。

以下为VMDeployment.ps1脚本的内容,连接VC的密码为隐藏,详细方法可参考之前的文章:

$encryptedText = "01000000d08c9ddf0115d1118c7a00c04fc297eb010000007e60d35c88a6754d97d685911ef7ce3a000000000200000000001066000000010000200000004f64c1c377a3d41cd7f5126ee11de07ade363c1fe75a7046e19f1c3564c2616a000000000e8000000002000020000000d89d6a13999fdae8f3aa94a3391e4577e7007a23058df3dd775baef097a5721d200000009f319764aaa0333a6ebbb535794d9193c02195bb710922bccb223cb4482fe48b40000000bf4e33bad709c942feeafdc5a1f3b254c17b3274212caa5219857f2621252d75f5e983bbf9afc92430cc120a2d585ab73b45943ea8c87e696039a70601d90fbd"

$encryptedPassword = $encryptedText | ConvertTo-SecureString
$cred = New-Object -TypeName System.Management.Automation.PSCredential("JustGiveAName", $encryptedPassword)

Echo "Importing Excel file..."
$FilePath = "D:\automation\VMInput.xlsm"
$SheetName = "Sheet1"
$objExcel = New-Object -ComObject Excel.Application
$objExcel.Visible = $false
$WorkBook = $objExcel.Workbooks.Open($FilePath)
$WorkSheet = $WorkBook.sheets.item($SheetName)

Echo "Deploying virtual machines..."
$k = 1
$endRows = $k + $WorkSheet.Range("M21").Text 

if($WorkSheet.Range("M21").Text -gt 0)
{
 for($i=2; $i -le $endRows; $i++)
 {
 $Environment = $WorkSheet.Range("A$i").Text
 $Hostname = $WorkSheet.Range("B$i").Text
 $IPAddr = $WorkSheet.Range("C$i").Text
 $Netmask = $WorkSheet.Range("D$i").Text
 $Gateway = $WorkSheet.Range("E$i").Text
 $DNSA = $WorkSheet.Range("F$i").Text
 $DNSB = $WorkSheet.Range("G$i").Text
 $PortGroupName = $WorkSheet.Range("H$i").Text
 $Storagelocation = $WorkSheet.Range("I$i").Text
 $CPUs = $WorkSheet.Range("J$i").Text
 $Memory = $WorkSheet.Range("K$i").Text
 $VMTemplate = $WorkSheet.Range("L$i").Text
 echo $Hostname, $IPAddr, $Netmask, $Gateway, $DNSA, $DNSB, $PortGroupName, $Storagelocation, $CPUs, $Memory, $VMTemplate

 switch($Environment)
 {
 "Test" {$vCenterServer = "192.168.102.51"; $vCenterUser = "test\vmadmin"; $ClusterName = "Test-Cluster"}
 "Dev" {$vCenterServer = "172.16.15.51"; $vCenterUser = "dev\vmadmin"; $ClusterName = "Dev-Cluster"}
 }
 
 switch($VMTemplate)
 {
 "auto-win2008r2-cn" {$OSType = "Windows"}
 "auto-win2016-cn" {$OSType = "Windows"}
 "auto-win2019-en" {$OSType = "Windows"}
 "auto-win2019-cn" {$OSType = "Windows"}
 "auto-rhel69-en" {$OSType = "Linux"}
 "auto-rhel73-en" {$OSType = "Linux"}
 "auto-ubuntu1804-en" {$OSType = "Linux"}
 "auto-ubuntu1604-en" {$OSType = "Linux"}
 }
 
 switch($OSType)
 {
 "Linux" {$basespec = "auto_linux"}
 "Windows" {$basespec = "auto_win"}
 }

 
 
 Echo "Input the credential for vCenter Server"
 Echo "Connecting to vCenter Server..."
 Connect-VIServer $vCenterServer -user $vCenterUser -password $cred.GetNetworkCredential().Password
 
 
 if($OSType -eq "Windows")
 {
 $SpecName = "TempSpec" + (Get-Random)
 Get-OSCustomizationSpec -Name $basespec |New-OSCustomizationSpec -Name $SpecName -Type NonPersistent
 Get-OSCustomizationSpec $SpecName |Get-OSCustomizationNicMapping | Remove-OSCustomizationNicMapping -Confirm:$false
 New-OSCustomizationNicMapping -Spec $specName -IpMode UseStaticIP -IpAddress $IPAddr -SubnetMask $Netmask -DefaultGateway $Gateway -DNS $DNSA,$DNSA2
 New-VM -Name $Hostname -ResourcePool $ClusterName -Datastore $Storagelocation -Template $VMTemplate 
 Get-VM -Name $Hostname | Set-VM -MemoryGB $Memory -NumCpu $CPUs -confirm:$false
 Get-VM -Name $Hostname | Get-NetworkAdapter |Set-NetworkAdapter -NetworkName $PortGroupName -Confirm:$false
 Get-VM -Name $Hostname| Get-NetworkAdapter|Set-NetworkAdapter -StartConnected $True -confirm:$false
 Get-VM -Name $Hostname | Set-VM -OSCustomizationSpec $specName -Confirm:$false |Start-VM
 Remove-OSCustomizationSpec $specName -Confirm:$false
 Disconnect-VIServer -Confirm:$false
 }
 else
 {
 $SpecName = "TempSpec" + (Get-Random)
 Get-OSCustomizationSpec -Name $basespec |New-OSCustomizationSpec -Name $SpecName -Type NonPersistent
 Get-OSCustomizationSpec $SpecName |Get-OSCustomizationNicMapping | Remove-OSCustomizationNicMapping -Confirm:$false
 New-OSCustomizationNicMapping -Spec $specName -IpMode UseStaticIP -IpAddress $IPAddr -SubnetMask $Netmask -DefaultGateway $Gateway 
 New-VM -Name $Hostname -ResourcePool $ClusterName -Datastore $Storagelocation -Template $VMTemplate 
 Get-VM -Name $Hostname | Set-VM -MemoryGB $Memory -NumCpu $CPUs -confirm:$false
 Get-VM -Name $Hostname | Get-NetworkAdapter |Set-NetworkAdapter -NetworkName $PortGroupName -Confirm:$false
 Get-VM -Name $Hostname| Get-NetworkAdapter|Set-NetworkAdapter -StartConnected $True -confirm:$false
 Get-VM -Name $Hostname | Set-VM -OSCustomizationSpec $specName -Confirm:$false 
 Get-VM -Name $Hostname| Get-NetworkAdapter|Set-NetworkAdapter -StartConnected $True -confirm:$false
 Get-VM -Name $Hostname | Start-VM 
 Remove-OSCustomizationSpec $specName -Confirm:$false
 Get-VM -Name $Hostname| Get-NetworkAdapter|Set-NetworkAdapter -Connected $True -confirm:$false
 Get-VM -Name $Hostname| Get-NetworkAdapter|Set-NetworkAdapter -StartConnected $True -confirm:$false
 Disconnect-VIServer -Confirm:$false
 }
 }

 $WorkBook.close()
 $objExcel.quit()
 get-process excel |stop-process

 Write-Host 'Deployment Finished, Press Any Key to Exit!' -NoNewline
 $null = [Console]::Readkey('?')
}

else
{
 Echo "No VM to deploy"
 $WorkBook.close()
 $objExcel.quit()
 get-process excel |stop-process

 Write-Host 'Press Any Key to Exit!' -NoNewline
 $null = [Console]::Readkey('?')
}

发表在 Powershell | 留下评论

PowerShell脚本中密码加密保存

在做自动化的时候会使用到PowerShell脚本,有时候会有远程连接其他服务器的场景,例如连接VMware vCenter Server,出于安全考虑,不建议将密码明文的方式保存在脚本中,可以使用SecureString的方式加密保存。

SecureString 是 .net 中的一个类型,它是为了解决安全性而设计出来的一种特殊的字符串类型。比如你使用一个密码字符串创建 SecureString 对象,你无法通过这个对象还原出原来的密码字符串,但是却可以把 SecureString 对象当做密码使用。
Encrypted Standard String 是指经过加密后的一个字符串。

ConvertTo-SecureString命令可以通过明文的字符串创建SecureString对象:

$SecurePwd = ConvertTo-SecureString "password" -AsPlainText -Force

接下来通过ConvertFrom-SecureString,我们可以把一个SecureString对象转换成Encrypted Standard String。在创建Credential的时候使用生成的Encrypted Standard String,从而避免密码以明文的方式保存在系统中。

那我们在脚本中如何使用这一长串的经过转换的字符串呢?还是使用ConvertTo-SecureString。参照如下脚本:

$SecurePwd = ConvertTo-SecureString "password" -AsPlainText -Force
ConvertFrom-SecureString $SecurePwd
01000000d08c9ddf0115d1118c7a00c04fc297eb010000007e60d35c88a6754d97d685911ef7ce3a000000000200000000001066000000010000200000004f64c1c377a3d41cd7f5126ee11de07ade363c1fe75a7046e19f1c3564c2616a000000000e8000000002000020000000d89d6a13999fdae8f3aa94a3391e4577e7007a23058df3dd775baef097a5721d200000009f319764aaa0333a6ebbb535794d9193c02195bb710922bccb223cb4482fe48b40000000bf4e33bad709c942feeafdc5a1f3b254c17b3274212caa5219857f2621252d75f5e983bbf9afc92430cc120a2d585ab73b45943ea8c87e696039a70601d90fbd
$encryptedText = "01000000d08c9ddf0115d1118c7a00c04fc297eb010000007e60d35c88a6754d97d685911ef7ce3a000000000200000000001066000000010000200000004f64c1c377a3d41cd7f5126ee11de07ade363c1fe75a7046e19f1c3564c2616a000000000e8000000002000020000000d89d6a13999fdae8f3aa94a3391e4577e7007a23058df3dd775baef097a5721d200000009f319764aaa0333a6ebbb535794d9193c02195bb710922bccb223cb4482fe48b40000000bf4e33bad709c942feeafdc5a1f3b254c17b3274212caa5219857f2621252d75f5e983bbf9afc92430cc120a2d585ab73b45943ea8c87e696039a70601d90fbd"
$encryptedPassword = $encryptedText | ConvertTo-SecureString
$cred = New-Object -TypeName System.Management.Automation.PSCredential("JustGiveAName", $encryptedPassword)
$username = "admin"
$password = $cred.GetNetworkCredential().Password

这样在脚本中使用用户名和密码的时候就直接调用$username和$password变量啦。

但是使用这种方法还是有一个限制,就是生成的那一长串字符自能在当前机器上使用,如果复制到其他机器上使用,就会在执行ConvertTo-SecureString时报错。这是一种安全限制,如果需要在其他机器使用,需要采用新的办法,使用一个Key文件来生成这个加密后的长字符串。

ConvertTo-SecureSting和ConvertFrom-SecureString命令都支持选项-key。在处理密码时通过Key选项可以提供额外的安全性以及在多台机器上使用加密后密码的能力:

首先生成32位的Key文件,并保存在AES.key文件中:

$keyFile = "D:\aes.key"
$key = New-Object Byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($key)
$key | out-file $keyFile

使用key生成加密后的密码字符串:

$SecurePwd = ConvertTo-SecureString "password" -AsPlainText -Force
ConvertFrom-SecureString $SecurePwd -key $key
76492d1116743f0423413b16050a5345MgB8AGcAcQBKADkANgBvAGIAdQAxAGIAMQA1ADgAZQAzADUAVQBTADIAcwBIAEEAPQA9AHwAZQAxAGYAOAAyAGEAZQA4ADUAMABjADcANwA2ADAAYwAwAGMAOQBjAGIAYQBiAGQAYgBiAGIAMQAwADIAMgA3ADgAMQA3ADcANABhADkAZABjADQAOAA5AGMAZQBhADcAZQA2AGYAMQBmADkAMwBlADIANgA2ADEANwA2ADIAOAA=
$encryptedText = "76492d1116743f0423413b16050a5345MgB8AGcAcQBKADkANgBvAGIAdQAxAGIAMQA1ADgAZQAzADUAVQBTADIAcwBIAEEAPQA9AHwAZQAxAGYAOAAyAGEAZQA4ADUAMABjADcANwA2ADAAYwAwAGMAOQBjAGIAYQBiAGQAYgBiAGIAMQAwADIAMgA3ADgAMQA3ADcANABhADkAZABjADQAOAA5AGMAZQBhADcAZQA2AGYAMQBmADkAMwBlADIANgA2ADEANwA2ADIAOAA="
$keyFile = "D:\aes.key"
$key = Get-Content $keyFile
$username = "admin"
$Cred = New-Object -TypeName System.Management.Automation.PSCredential `
          -ArgumentList $username, ($encryptedText | ConvertTo-SecureString -Key $key)
$password = $cred.GetNetworkCredential().Password

发表在 Powershell | 留下评论