0%

** 概述 **

Ceph对象网关是一个构建在librados之上的对象存储接口,它为应用程序访问Ceph 存储集群提供了一个RESTful风格的网关。

Ceph 对象存储支持 2 种接口:

  1. 兼容S3: 提供了对象存储接口,兼容Amazon S3 RESTful接口的一个大子集。
  2. 兼容Swift: 提供了对象存储接口,兼容Openstack Swift接口的一个大子集。

Ceph对象存储使用Ceph对象网关守护进程(radosgw 以下简称RGW),它是个与Ceph存储集群交互的FastCGI模块。因为它提供了与OpenStack SwiftAmazon S3兼容的接口,RADOS要有它自己的用户管理。Ceph对象网关可与CephFS客户端或Ceph 块设备客户端共用一个存储集群。S3和Swift接口共用一个通用命名空间,所以你可以用一个接口写如数据、然后用另一个接口取出数据。

** 版本: Mimic **

radosgw-Frame.png

架构

RGW 主要由3部分组成。Frontend,用于接收回复外部客户端的http请求;REST,根据外部请求的http信息选择相应的REST、Handler、Op对请求进行分解处理;RGWRados,完成了对数据读写业务的封装。

Frontend

Frontend包括Frontend Config(RGWFrontendConfig)、Civetweb(RGWCivetWebFrontend)、beast(RGWAsioFrontend)、loadgen(RGWLoadGenFrontend)、fastcgi/fcgi(RGWFCGXFrontend)。用一个map管理所有RGWFrontendConfig配置信息对象;用一个list管理所有RGWFrontend对象。每个RGWFrontend实例使用与之对应的RGWFrontendConfig实例进行配置。

Frontend负责接收客户端的请求,然后回调process_request方法处理该请求,并将响应信息返回客户端。

  • Frontend Config
  • Civetweb
  • Beast
  • Loadgen
  • FastCGI/FCGI

REST

REST包括request process、S3 resource、Swift resource、Swift auth resource、Admin Usage resource、Admin User resource、Admin Metadata resource、Admin Realm resource、Admin Config resource、Admin Bucket resource、Admin OpState resource、Admin Log resource、 Admin Replica-log resource。每个resource包含一个RESTMgr、多个Handler、多个Op。

就在刚刚Frontend回调了process_requestprocess_request通过RESTMgr取得handler,handler再根据Http Method取得Op,Op再对数据进行读写操作。

REST Resource

S3

** 组成 **

  • RESTMgr
    有1个RGWRESTMgr_S3组成。
  • Handler
    有6个Handler组成,分别是:RGWHandler_REST_Service_S3RGWHandler_REST_Bucket_S3RGWHandler_REST_Obj_S3RGWHandler_REST_Service_S3WebsiteRGWHandler_REST_Bucket_S3WebsiteRGWHandler_REST_Obj_S3Website

** 功能 **

  • RGWHandler_REST_Service_S3, 完成如下功能:
    1. 获取Usage信息
    2. List Buckets
    3. Role信息操作
  • RGWHandler_REST_Bucket_S3,完成如下功能:
    1. Bucket ACL信息操作
    2. Bucket Core信息操作
    3. Bucket Payment信息操作
    4. Bucket LC信息操作
    5. Bucket Policy信息操作
    6. Bucket操作
  • RGWHandler_REST_Obj_S3,完成如下功能:
    1. Object ACL操作
    2. Object tagging操作
    3. Object Multipart上传操作
    4. Object 操作
    5. Object Copy操作

Swift

** 组成 **

  • RESTMgr
    RGWRESTMgr_SWIFTRGWRESTMgr_SWIFT_CrossDomainRGWRESTMgr_SWIFT_HealthCheckRGWRESTMgr_SWIFT_Info组成。
  • Handler
    RGWHandler_REST_Service_SWIFTRGWHandler_REST_Bucket_SWIFTRGWHandler_REST_Obj_SWIFTRGWHandler_REST_SWIFT_InfoRGWHandler_SWIFT_HealthCheckRGWHandler_SWIFT_CrossDomain组成。

Swift auth

** 组成 **

  • RESTMgr
    由1个RGWRESTMgr_SWIFT_Auth组成。
  • Handler
    由1个RGWHandler_SWIFT_Auth组成。

Admin

** 组成 **

  • RESTMgr
    RGWRESTMgr_UsageRGWRESTMgr_UserRGWRESTMgr_BucketRGWRESTMgr_MetadataRGWRESTMgr_LogRGWRESTMgr_OpstateRGWRESTMgr_ReplicaLogRGWRESTMgr_ConfigRGWRESTMgr_RealmRGWRESTMgr_Period组成。
  • Handler
    RGWHandler_UsageRGWHandler_UserRGWHandler_BucketRGWHandler_MetadataRGWHandler_LogRGWHandler_OpstateRGWHandler_ReplicaLogRGWHandler_ConfigRGWHandler_RealmRGWHandler_Period组成。

Op

RGWOp

  • Object Tags
    RGWGetObjTags RGWPutObjTags RGWDeleteObjTags实现对象属性user.rgw.x-amz-tagging的查询、设置、删除操作
  • Bulk
    RGWBulkDelete RGWBulkUploadOp Swift专享批量上传、删除操作
  • Usage
    RGWGetUsage 获取usage信息操作
  • Stat
    RGWStatAccount统计buckets的使用情况
  • Bucket
    RGWListBuckets列出所有buckets
    RGWGetBucketLocation获取bucket location
    RGWGetBucketVersioning获取bucket versioning、mfa-delete状态
    RGWSetBucketVersioning 设置bucket versioning
    RGWGetBucketWebsite获取bucket website信息
    RGWSetBucketWebsite设置bucket website
    RGWDeleteBucketWebsite删除bucket website
    RGWStatBucket获取bucket信息
    RGWCreateBucket创建bucket操作
    RGWDeleteBucket删除bucket操作
    RGWPutBucketPolicy设置bucket policy
    RGWGetBucketPolicy获取bucket policy
    RGWDeleteBucketPolicy删除bucket policy
  • Object
    RGWPutObj RGWPostObj RGWGetObj RGWDeleteObj RGWCopyObj实现对象的创建、修改、下载、删除、拷贝操作
    RGWListBucket列出bucket中的对象
  • Metadata
    RGWPutMetadataAccountSwift专享保存user所有属性信息(RGWUserInfo)
    RGWPutMetadataBucketSwift专享保存bucket所有属性信息
    RGWPutMetadataObjectSwift专享保存object所有属性信息
  • ACL
    RGWGetACLs RGWPutACLs 实现对bucket或object的访问控制策略属性(user.rgw.acl)的获取、设置操作
  • LC
    RGWGetLC RGWPutLC RGWDeleteLC实现对bucket的生命周期属性(user.rgw.lc)的获取、设置、删除操作
  • CORS
    RGWGetCORS RGWPutCORS RGWDeleteCORS RGWOptionsCORS实现对bucket的CORS属性(user.rgw.cors)的获取、设置、删除、Options操作
    RGWGetCrossDomainPolicySwift专享获取CrossDomain策略信息操作
  • Payment
    RGWGetRequestPayment RGWSetRequestPayment实现对bucket的payment属性的获取、设置操作
  • Multipart
    RGWInitMultipart RGWCompleteMultipart RGWAbortMultipart RGWListMultipart RGWListBucketMultiparts RGWDeleteMultiObj实现对Object的分片上传的操作
  • HealthCheck
    RGWGetHealthCheckSwift专享访问判断配置文件中的rgw_healthcheck_disabling_path是否可以被访问操作
  • Swift Info
    RGWInfoSwift专享获取Swift信息
  • Layout
    RGWGetObjLayout获取Object的layout信息
  • Bucket MateSearch Config
    RGWConfigBucketMetaSearch RGWGetBucketMetaSearch RGWDelBucketMetaSearch实现对Bucket的MetaSearch Config属性的配置、查询、删除操作
  • Cluster
    RGWGetClusterStat获取Cluster状态

AdminOp

  • Usage
    RGWOp_Usage_Get RGWOp_Usage_Delete获取usage信息操作
  • User
    RGWOp_User_Info RGWOp_User_Create RGWOp_User_Modify RGWOp_User_Removeuser信息的获取、创建、修改、删除操作
    RGWOp_Subuser_Create RGWOp_Subuser_Modify RGWOp_Subuser_Removeswift user的创建、修改、删除操作
    RGWOp_Key_Create RGWOp_Key_RemoveAccessKey、SecretKey的创建、删除操作
    RGWOp_Caps_Add RGWOp_Caps_RemoveCaps属性的增加、删除操作
    RGWOp_Quota_Info RGWOp_Quota_SetQuota的信息的获取、设置操作
  • Bucket
    RGWOp_Bucket_Info获取bucket信息操作
    RGWOp_Get_Policy获取bucket的policy信息操作
    RGWOp_Check_Bucket_Index检查bucket index操作
    RGWOp_Bucket_Link RGWOp_Bucket_Unlink关联、去关联bucket与user操作
    RGWOp_Bucket_Remove删除bucket操作
    RGWOp_Set_Bucket_Quota设置bucket quota属性操作
    RGWOp_Object_Remove删除bucket内的object操作
  • Metadata
    RGWOp_Metadata_List RGWOp_Metadata_Get RGWOp_Metadata_Put RGWOp_Metadata_Delete RGWOp_Metadata_Lock RGWOp_Metadata_Unlock实现对Metadata数据的管理操作
  • Log
    RGWOp_BILog_List RGWOp_BILog_Info RGWOp_BILog_Delete RGWOp_BILog_Status实现对bucket index log的管理
    RGWOp_MDLog_List RGWOp_MDLog_Info RGWOp_MDLog_ShardInfo RGWOp_MDLog_Lock RGWOp_MDLog_Unlock RGWOp_MDLog_Notify RGWOp_MDLog_Delete RGWOp_MDLog_Status实现对Metadata log的管理
    RGWOp_DATALog_List RGWOp_DATALog_Info RGWOp_DATALog_ShardInfo RGWOp_DATALog_Lock RGWOp_DATALog_Unlock RGWOp_DATALog_Notify RGWOp_DATALog_Delete RGWOp_DATALog_Status实现对Data log的管理
  • Opstate
    RGWOp_Opstate_List RGWOp_Opstate_Set RGWOp_Opstate_Renew RGWOp_Opstate_Delete获取远端object的操作日志管理
  • ReplicaLog
    RGWOp_OBJLog_GetBounds RGWOp_OBJLog_SetBounds RGWOp_OBJLog_DeleteBoundsObject同步日志管理
    RGWOp_BILog_GetBounds RGWOp_BILog_SetBounds RGWOp_BILog_DeleteBoundsBucket同步日志管理
  • Zone
    RGWOp_ZoneGroupMap_Get RGWOp_ZoneConfig_Get获取zonegroup map 和 zone config操作
  • Realm
    RGWOp_Realm_Get获取realm信息操作
    RGWOp_Period_Get RGWOp_Period_Post获取、设置Period信息操作

request process

RGW的所有请求都需要回调process_request这个函数来处理,所以process_request处理过程十分重要。

处理流程

  1. 获取RGWHandler_REST
    1.1. 获取RGWRESTMgr对象
    各种REST resource 都保存在一个map中,当请求到来时根据传入的frontend_prefix和uri信息在map中查找对应的RGWRESTMgr
    1.2. 通过RGWRESTMgrget_handler获取RGWHandler_REST对象
    1.3. 调用RGWHandler_REST对象的init方法进行初始化
  2. 调用RGWHandler_REST对象的get_op方法获取RGWOp对象
  3. 调用RGWOp对象的verify_request的方法,根据不同的操作进行不同的请求授权检查
  4. 调用RGWHandler_REST对象的postauth_init方法,进行bucket和tenant解析及验证等操作
  5. 调用RGWHandler_REST对象的init_permissions方法,进行初始化权限
    5.1. 如果是创建Bucket操作(RGW_OP_CREATE_BUCKET)直接忽略此操作
    5.2. 非创建Bucket操作,通过调用RGWHandler_REST对象的do_init_permissions方法进行初始化权限
  6. 调用RGWHandler_REST对象的read_permissions方法,获取Object的AccessControlPolicy,若处理的是Bucket直接忽略
  7. 调用RGWOp对象的init_processing方法,获取bucket和user的quota信息
  8. 调用RGWOp对象的verify_op_mask验证操作的种类是否为RGW_OP_TYPE_READRGW_OP_TYPE_WRITERGW_OP_TYPE_DELETE中的一个或多个
  9. 调用RGWOp对象的verify_permission检查当前的操作在之前的init_permissions获取的策略之下是否有权限
  10. 调用RGWOp对象的verify_params检查当前操作的参数
  11. 调用RGWOp对象的pre_exec执行预执行操作
  12. 调用RGWOp对象的execute执行操作
  13. 调用RGWOp对象的complete完成操作,并整理响应结果

RGWRados

  • Bucket Op
    使用librados完成Bucket创建、删除、设置、获取等相关操作
  • Object Op
    对Object的读写、属性获取设置等操作,所有操作都分为两类,一类是System Obj,包括metadata信息、realm信息、role信息、bucket信息等rgw内一些配置信息;另一类是Normal Obj,一般为用户上传的object
  • Cache
    对Bucket info信息和User info信息及所有Object的缓存。缓存失效采用LRU算法,采用超时失效+有效窗口方式判断缓存数据是否失效。
  • Watcher
    监听.rgw.controlpool中nodify的对象,当发生对Object(包括System Obj 和 Normal Obj)的增删改操作时,都会触发nodify的更新。Watcher再调用watch_cb去更新Object的缓存信息
  • Pools
    对root pool(.rgw)、control pool(.rgw.control)、gc pool(.rgw.gc)、lc pool(.rgw.lc)、objexp pool、reshard pool的IoCtx管理及操作
  • GC
    对象的删除操作不会真的将对象删除,而是在对象的属性中增加olh.,然后将对象存入.rgw.gc中。GC中的回收线程会每隔1小时处理一次,每次处理从.rgw.gc中获取对象并删除,每次处理的超时时间为1小时。
  • Obj Expirer(OE)
    对象的Version删除功能,通过设置对象的delete_at属性来等待OE清扫线程进行删除,OE清扫线程每隔10分钟处理一次。
  • LC
    bucket通过user.rgw.lc属性配置LC,开启了LC的bucket会被随机分配到.rgw.lcpool的lc.{index}对象上。LC处理线程会随机获取lc.{index}对象,对其上记录的bucket中的object进行处理,判断其是否过期失效,若失效则删除。
  • Quota
    bucket_stats_cacheuser_stats_cache分别缓存bucket和user的quota信息,并提供check_quota操作检查是否超出quota的限制。
  • Reshard
    bucket index用于索引bucket内的Object,这些index存在shard文件上。随着bucket内object的数量增加,整个shard文件也在不断增长,当object数量超过“bucket shard数*每个shard最大容纳object数量(默认值:100000)”时,触发reshard操作分配更多的shard文件用于存储index。
  • Sync
    根据metadata log 和 data log 同步 metadata 和 data 数据。当用户对metadata或data写入操作时,保存对应log信息,然后通过notifier模块唤醒SyncProcessor模块来同步log信息并处理。
  • Realm
    包括Realm、zonggroup、zone、period四部分;period用于管理realm配置信息,一个realm包括多个zonegroup,每个zonegroup包括多个zone;一个realm中只能有一个master zonegroup,这个zonegroup中只能有一个master zone。用户修改period时,通过.rgw.rootpool中的periods.{realm id}.control对象将通知Realm Watcher进行更改realm配置。

Nautilus

编译环境

硬件

采用KVM虚拟机编译Ceph源码,具体配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 8
On-line CPU(s) list: 0-7
Thread(s) per core: 1
Core(s) per socket: 1
Socket(s): 8
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 13
Model name: QEMU Virtual CPU version 2.5+
Stepping: 3
CPU MHz: 2394.454
BogoMIPS: 4788.90
Hypervisor vendor: KVM
Virtualization type: full
L1d cache: 32K
L1i cache: 32K
L2 cache: 4096K
NUMA node0 CPU(s): 0-7
Flags: fpu de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pse36 clflush mmx fxsr sse sse2 syscall nx lm rep_good nopl cpuid tsc_known_freq pni cx16 x2apic hypervisor lahf_lm pti
1
2
3
4
# free -g
total used free shared buff/cache available
Mem: 15 7 5 0 2 7
Swap: 0 0 0

软件

OS:

1
2
# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)

kernel:

1
2
# uname -a
Linux host-10-100-13-111 4.18.20 #1 SMP Tue Sep 17 11:21:39 CST 2019 x86_64 x86_64 x86_64 GNU/Linux

gcc:

1
2
3
4
5
6
7
8
# gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/opt/rh/devtoolset-7/root/usr/libexec/gcc/x86_64-redhat-linux/7/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,lto --prefix=/opt/rh/devtoolset-7/root/usr --mandir=/opt/rh/devtoolset-7/root/usr/share/man --infodir=/opt/rh/devtoolset-7/root/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --enable-plugin --with-linker-hash-style=gnu --enable-initfini-array --with-default-libstdcxx-abi=gcc4-compatible --with-isl=/builddir/build/BUILD/gcc-7.3.1-20180303/obj-x86_64-redhat-linux/isl-install --enable-libmpx --enable-gnu-indirect-function --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
Thread model: posix
gcc version 7.3.1 20180303 (Red Hat 7.3.1-5) (GCC)

编译

pre-compile

执行install-deps.sh,安装编译ceph依赖的软件包。

cmake

安装官方文档的步骤可以正常编译,但默认的编译是含有调试信息的。所以编译后的bin文件比较大。

  1. 生成makefile文件
    使用do_cmake.sh脚本在build目录下生成Makefile文件
  2. 编译
    使用make命令编译,此过程时间较长。
  3. 瘦身
    编译后的binary文件包含有debug信息,所以文件较大。需要使用strip对其debug信息进行裁剪。裁剪有两个工具可以用一个是objcopy;另一个是strip
    eg:
    objcopy --strip-debug ./radosgw
    strip --strip-debug --strip-unneeded ./radosgw

rpmbuild

  1. 生成rpmbuild目录树
    使用rpmdev-setuptree命令创建rpmbuild目录树,该目录树会在$home目录下生成。可根据自己的需要将rpmbuildcopy到指定的目录,但后续build过程需要指定这个目录。本人将rpmbuild目录移动到/root/src目录下。
  2. 获取源码tar包到rpmbuild/SOURCES目录
    获取源码tar包可以到https://download.ceph.com/tarballs/ceph-14.2.3.tar.gz去下载。也可以将修改好的代码直接做成tar包。然后将tar包copy到rpmbuild/SOURCES目录下。
  3. 提取tar包中的ceph.spec
    从源码tar包中提取ceph.spec文件到rpmbuild/SPECS目录,用于后续rpmbuild使用。eg: tar --strip-components=1 -C /root/src/rpmbuild/SPECS/ --no-anchored -xvzf /root/src/rpmbuild/SOURCES/ceph-14.2.3.tar.gz "ceph.spec"
  4. build rpms
    rpmbuild具体使用-bb还是-ba根据个人需要而定吧。本人这里使用-bb只制作binary。eg: rpmbuild -D "_smp_mflags 4" -D "_topdir /root/src/rpmbuild" -bb /root/src/rpmbuild/SPECS/ceph.spec
    由于本人将rpmbuild移动到了/root/src目录下,所以需要使用-D "_topdir /root/src/rpmbuild"去指定rpmbuild目录。
    本人不希望rpmbuild自动推算使用几个核心去编译,需要指定4个核心编译需要指定-D "_smp_mflags 4"

此时,可以静静等待编译完成。

*** 编译的过程中可能会遇到BuildArch:noarch错误 ***

1
2
3
4
5
error: Arch dependent binaries in noarch package


RPM build errors:
Arch dependent binaries in noarch package

可以通过在spec文件中增加%define _binaries_in_noarch_packages_terminate_build 0来解决此问题。

Luminous

编译 Ceph luminous版本,luminous的改动还是蛮大的,而且改变了原有的configure为cmake。

编译环境

硬件

armv7l (Odroid XU4)

软件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ lsb_release  -a
LSB Version: 1.4
Distributor ID: Arch
Description: Arch Linux
Release: rolling
Codename: n/a

$ uname -a
Linux HomeCenter 4.14.29-1-ARCH #1 SMP PREEMPT Fri Mar 23 02:57:06 UTC 2018 armv7l GNU/Linux

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/armv7l-unknown-linux-gnueabihf/7.2.1/lto-wrapper
Target: armv7l-unknown-linux-gnueabihf
Configured with: /build/gcc/src/gcc/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://github.com/archlinuxarm/PKGBUILDs/issues --enable-languages=c,c++,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --with-system-zlib --with-isl --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-gnu-indirect-function --disable-multilib --disable-werror --enable-checking=release --enable-default-pie --enable-default-ssp --host=armv7l-unknown-linux-gnueabihf --build=armv7l-unknown-linux-gnueabihf --with-arch=armv7-a --with-float=hard --with-fpu=vfpv3-d16
Thread model: posix
gcc version 7.2.1 20180116 (GCC)

编译

按照官方的文档,编译总共分为4步。

  1. 安装编译需要的依赖包 ./install-deps.sh
  2. 使用cmake生成Makefile ./do_cmake.sh
  3. 使用Makefile编译源代码
  4. 安装包制作

由于本人使用的是archlinux,简单查看了一下install-deps.sh这个脚本,发现没有与我使用os相匹配的处理;所以本人决定跳过第一步,并且我也不打算制作安装包,所以第四部也省略了。

接下来就只剩下第2、3步了,那么ceph编译依赖的其他软件包怎么解决,就只能放倒编译过程中出现报错再去究其原因了。闲言少叙,我们开始编译。

cmake过程

从github上clone下来ceph代码,不需要取得submodule的代码,因为在do_cmake.sh中首先会做。执行do_cmake.sh后,先取submodule代码,然后创建build目录,并在build目录中执行cmake,生成Makefile。

在获取submodule这个过程由于网络问题会出现不只一次的中断,这个时候需要先删除build目录,然后在重新执行do_cmake.sh

make过程

ceph编译需要用到boost包,之前的版本都是需要用户手动安装,L版改为取源码自行编译了。可以先执行make Boost或直接执行make操作,我在编译boost的时候遇到了找不到pyconfig.h的编译错误,此时需要根据错误提示的文件及行数,找到对应的cxxflags并增加-I/usr/include/python2.7/这样再次执行make操作boost就能正常编译了。

1
2
3
4
5
6
7
8
9
Scanning dependencies of target ceph-dencoder
[ 0%] Building CXX object src/CMakeFiles/ceph-dencoder.dir/test/encoding/ceph_dencoder.cc.o
c++: internal compiler error: Killed (program cc1plus)
Please submit a full bug report,
with preprocessed source if appropriate.
See <https://github.com/archlinuxarm/PKGBUILDs/issues> for instructions.
make[2]: *** [src/CMakeFiles/ceph-dencoder.dir/build.make:64: src/CMakeFiles/ceph-dencoder.dir/test/encoding/ceph_dencoder.cc.o] Error 4
make[1]: *** [CMakeFiles/Makefile2:1609: src/CMakeFiles/ceph-dencoder.dir/all] Error 2
make: *** [Makefile:141: all] Error 2

由于我使用的是嵌入式设备,内存只有2g大小,并且没有配置swap,在编译过程中会遇到g++: internal compiler error: Killed (program cc1plus)错误,这种错误都是由于内存不足导致的。对于我的环境而言,只能通过增加swap来解决(关于增加swap的方法)。

问题解决了,就一路make下去吧。

参考&鸣谢

社区版XenServer是一款开源产品。那么拿到这款社区版首先想到的是怎么把它build出来,然后怎么把它部署起来,最后才是巴拉巴拉吧啦。。。

我们先来解决第一步,编译源码,编译之前需要先构建编译环境,构建好编译环境后以xapi为例进行编译。xapi是用OCaml这种语言写的,本人对这种语言一窍不通。这儿就不讨论了 ……

准备

本人打算使用容器进行编译xapi,所以你得先有个docker环境,然后呢编译的是xapi代码也得自己下好吧。

具体操作,巴拉巴拉吧啦。

构建编译环境

拉取xenserver-build-env

使用docker pull xenserver/xenserver-build-env命令拉取镜像,这是个漫长的过程还有可能失败,镜像还挺大。

喝杯茶,慢慢等吧。。。

启动编译容器

走到这里,说明你的镜像拉取成功了,那么我们需要启动镜像,本人推荐使用下列方法启动镜像

1
$ docker run -i -t -v /home/{xxxx}/labs/xen-api:/mnt/repos --name xapi-builder xenserver/xenserver-build-env /usr/local/bin/init-container.sh

别傻乎乎的copy直接用啊,里面的路径要根据实际情况自己调整,我懒了你不能懒。

网上坛子里有人推荐加参数-u builder,的确build的过程不推荐使用root用户;我用root是因为我要把它装到容器里,用builder会引入一些权限和环境问题。所以我用root了,您根据自己的情况而定。别盲从!!!

配置编译环境

你是否以为下载好了镜像,并且成功的启动的容器,就可以顺理成章的编译了?哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈,别做梦了。哪有那么简单,社区版不给你点儿坑你都觉得不过瘾。

安装dep包

1
# yum-buiddep xapi

我是root,不是root自己加sudo去。

初始化opam

卧槽,opam什么东西,一脸的懵B啊!?不用太深究,就是一个类似pip的包管理器;想深究的可以自己研究OPAM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# opam init

...

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

1. To configure OPAM in the current shell session, you need to run:

eval `opam config env`

2. To correctly configure OPAM for subsequent use, add the following
line to your profile file (for instance ~/.profile):

. /root/.opam/opam-init/init.sh > /dev/null 2> /dev/null || true

3. To avoid issues related to non-system installations of `ocamlfind`
add the following lines to ~/.ocamlinit (create it if necessary):

let () =
try Topdirs.dir_directory (Sys.getenv "OCAML_TOPLEVEL_PATH")
with Not_found -> ()
;;

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
...
#

看到这段英文了吧,1、2、3,乖乖的照做,要不管保你编不过。

最后切记,将eval \opam config env`加到/.bashrc/.profile`中。

使用opam安装包ocamlfind, cmdliner, jbuilder

1
2
3
4
# opam depext conf-m4.1
...
# opam install ocamlfind cmdliner jbuilder
...

安装过程中有需要点确认的授权的,改给就给,千万别搞事情!

到此为止环境的搭建算是完成了,记住这个session不要退,再开一个session去做编译一会儿有惊喜。。。

编译xapi

使用docker exec -it xapi-builder /bin/bash登录进你的容器,像上文说到的你可以使用-u builder参数。

废话不多说直接编译

1
2
3
4
5
$ cd /mnt/repo/
$ ./configure
...
$ make
...

别急,会报错的,看到了吧。stack overflow,去网上搜吧,一搜返回一堆stack overflow的网站,哈哈哈!!!

别急,还记得之前保留的那个session吗,对,就是那个session。输入ulimit -a看看里面的stack size;再对比看看报错的那个session的stack size。是不是有一种恍然大悟的感觉。没错设置一下报错session的stack size就可以了ulimit -s 16384


你刚刚经历了一个华丽的分割线。。。


如果你使用xenserver-build-env创建编译环境,可以省去很多步骤。

创建编译容器

1
2
3
$ git clone git://github.com/xenserver/xenserver-build-env
$ cd xenserver-build-env
$ ./build.sh

编译xapi

1
2
3
4
5
6
7
8
$ ./run.py -p xapi --rm

# --- you are now inside the docker container ---

$ git clone git://github.com/xapi-project/xen-api
$ cd xen-api
$ ./configure
$ make

制作RPM Package

在Centos系统中制作RPM包,需要用到rpmbuild,所以你要用yum安装好。当然上面的编译容器已经将rpmbuild安装好了。那么的接下来的重点在于SPEC文件编写,SPEC用来告诉rpmbuild,制作的每一个过程需要做哪些动作。

SPEC文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Name:   xen-api     
Version: 1.60.2
Release: 1%{?dist}
Summary: rpm xen-api modify

Group: Application/test
License: Share
Source: $RPM_SOURCE_DIR/xen-api-1.60.2_m.tar.gz


%description
print xen-api


%prep
rm -rf $RPM_BUILD_DIR/xen-api-1.60.2
zcat $RPM_SOURCE_DIR/xen-api-1.60.2_m.tar.gz | tar xvf -

%build
cd $RPM_BUILD_DIR/xen-api-1.60.2
./configure
make %{?_smp_mflags}


%install
cd $RPM_BUILD_DIR/xen-api-1.60.2
make install DESTDIR=%{buildroot}


%files
/etc
/var
/usr
/opt
%doc

创建目录SOURCESPECSBUILDRPMSSRPMS,将SPEC文件放入到SPECS文件件中,然后将源代码做成xen-api-1.60.2_m.tar.gz拷贝到SOURCE目录。最后执行rpmbuild -ba {SPEC文件}。执行完毕后,在RPM目录中生成我们需要的RPM包

参考&鸣谢

Linux下编程时,为了方便编译,往往使用Makefile文件自动完成编译,但是Makefile文件本身的书写十分复杂,规则很多。好在Linux为我们提供了自动生成功能完善的Makefile文件的工具autoconf/automake。本文讲述如何使用它们生成Makefile文件。

环境

  • OS
    ubuntu 16.04
  • aclocal
    1.15
  • autoconf
    2.69-9
  • automake
    1.15

使用示例

准备

创建一个main.c文件

1
2
3
4
5
6
#include <stdio.h>

int main(){
printf("Hello automake\n");
return 0;
}
1
2
$ ls
main.c

创建configure.in文件

运行autoscan,自动创建两个文件autoscan.logconfigure.scan

1
2
3
4
5
$ autoscan
Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/\${ <-- HERE [^\}]*}/ at /usr/bin/autoscan line 361.

$ ls
autoscan.log configure.scan main.c

修改configure.scan,AC_INIT里面的参数: AC_INIT(main,1.0, test@263.com);添加宏AM_INIT_AUTOMAKE, 它是automake所必备的宏,也同前面一样,PACKAGE是所要产生软件套件的名称,VERSION是版本编号;在AC_OUTPUT后添加输出文件Makefile。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.69])
AC_INIT(main, 1.0, main@google.com)
AC_CONFIG_SRCDIR([main.c])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE(main, 1.0)

# Checks for programs.
AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_OUTPUT([Makefile])

configure.scan重命名成configure.ac

1
2
$ ls
autoscan.log configure.ac main.c

生成aclocal相关文件

运行aclocal,生成文件aclocal.m4、目录autom4te.cache,处理本地的宏定义。

1
2
3
$ aclocal
$ ls
aclocal.m4 autom4te.cache autoscan.log configure.ac main.c

生成configure文件

运行autoconf

1
2
3
$ autoconf
$ ls
aclocal.m4 autom4te.cache autoscan.log configure configure.ac main.c

生成config.h.in文件

运行autoheader,生成config.h.in,该工具通常会从“acconfig.h”文件中复制用户附加的符号定义,因此此处没有附加符号定义,所以不需要创建“acconfig.h”文件。

1
2
3
$ autoheader
$ ls
aclocal.m4 autom4te.cache autoscan.log config.h.in configure configure.ac main.c

运行automake

运行automake之前先创建一个Makefile.am,这一步是创建Makefile很重要的一步,automake要用的脚本配置文件是Makefile.am,用户需要自己创建相应的文件。之后,automake工具转换成Makefile.in

Makefile.am

1
2
3
4
AUTOMAKE_OPTIONS=foreign

bin_PROGRAMS=main
main_SOURCES=main.c
  • AUTOMAKE_OPTIONS为设置automake的选项
    由于GNU对自己发布的软件有严格的规范,比如必须附带许可证声明文件COPYING等,否则automake执行时会报错。automake提供了三种软件等级:foreign、gnu和gnits,让用户选择采用,默认等级为gnu。在本例使用foreign等级,它只检测必须的文件。
  • bin_PROGRAMS定义要产生的执行文件名
    如果要产生多个执行文件,每个文件名用空格隔开。
  • main_SOURCES定义“main”这个执行程序所需要的原始文件
    如果”main”这个程序是由多个原始文件所产生的,则必须把它所用到的所有原始文件都列出来,并用空格隔开。例如:若目标体“main”需要“main.c”、“sunq.c”、“main.h”三个依赖文件,则定义main_SOURCES=main.c sunq.c main.h。要注意的是,如果要定义多个执行文件,则对每个执行程序都要定义相应的file_SOURCES。
1
2
3
4
$ automake --add-missing
configure.ac:8: warning: AM_INIT_AUTOMAKE: two- and three-arguments forms are deprecated. For more info, see:
configure.ac:8: http://www.gnu.org/software/automake/manual/automake.html#Modernize-AM_005fINIT_005fAUTOMAKE-invocation
Makefile.am: installing './depcomp'

使用automake对其生成“configure.in”文件,在这里使用选项“—adding-missing”可以让automake自动添加有一些必需的脚本文件。

1
2
$ ls
Makefile.am Makefile.in aclocal.m4 autom4te.cache autoscan.log compile config.h.in configure configure.ac depcomp install-sh main.c missing

运行configure生成Makefile

通过运行自动配置设置文件configure,把Makefile.in变成了最终的Makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... no
checking for mawk... mawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking whether gcc understands -c and -o together... yes
checking for style of include used by make... GNU
checking dependency style of gcc... gcc3
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating config.h
config.status: executing depfiles commands

$ ls
Makefile Makefile.am Makefile.in aclocal.m4 autom4te.cache autoscan.log compile config.h config.h.in config.log config.status configure configure.ac depcomp install-sh main.c missing stamp-h1

make编译工程

1
2
3
4
5
6
7
$ make
make all-am
make[1]: Entering directory '/root/automake'
gcc -DHAVE_CONFIG_H -I. -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.c
mv -f .deps/main.Tpo .deps/main.Po
gcc -g -O2 -o main main.o
make[1]: Leaving directory '/root/automake'
1
2
3
4
$ ls
Makefile Makefile.am Makefile.in aclocal.m4 autom4te.cache autoscan.log compile config.h config.h.in config.log config.status configure configure.ac depcomp install-sh main main.c main.o missing stamp-h1
$ ./main
Hello automake

参考鸣谢

Ceph官网有一篇“Getting started with the Docker RBD volume plugin”里面提到了一个驱动“github.com/yp-engineering/rbd-docker-plugin”,此驱动使用的是krbd(kernel RBD)。krbd与librbd相比很多功能被阉割了,如果你只想用Ceph作为backend,那你可以使用这个驱动。如果你不甘心只是简单的使用Ceph,还想体验他的很多特性,那你可以考虑下面这个方案。。。

原理

本方案的想法来自于Ceph的一个命令nbd-rbd,只要你的系统支持nbd,并且支持docker,nbd与rbd的通讯方式依然使用socket文件的形式,只是rbd client相关程序需要放置到容器中;因为有些操作系统没有包管理器,不能方便的安装软件包(如:CoreOS)。

docker_volplugin_nbd_rbd.png

图画的这么清晰,还需要再说明吗!好吧,还是再说点儿吧。

  • 每个nbd设备与rbd设备之间使用一个socket文件进行通讯
  • NBDServer用于管理所有nbd设备
  • RBDServer用于管理所有rbd设备

实现

*** Todo… ***

参考&鸣谢

docker中容器的运行离不开image,一个image中都存放有哪些东西呢?这些东西又是以何种方式组织在一起的呢?今天让我们来撕开这层面纱,看看image到底是什么样。

导出tar包

首先我们使用docker save,将一个docker image保存成tar包,我们以nginx官方镜像为例

1
2
3
4
5
6
7
8
9
10
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx 1.11.3 4efb2fcdb1ab 15 months ago 183MB

$ docker save nginx:1.11.3 -o ./nginx.tar
$ tar -xvf ./nginx.tar -C ./nginx
...
$ ls -l ./
drwxrwxr-x 5 zhoub zhoub 4096 11月 28 16:55 nginx
-rw------- 1 zhoub zhoub 191400960 11月 28 16:52 nginx.tar

走近nginx目录

现在nginx.tar已经被解开了,接下来就要进入nginx目录一探究竟了。首先tree一下nginx目录,看看都有什么文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ tree -L 2 ./nginx
./nginx
├── 01d69dac180439f10b5f2f06b71fa19c9f89fb85cb5fd543dba9bb2ca9b620ef
│   ├── json
│   ├── layer
│   ├── layer.tar
│   └── VERSION
├── 4efb2fcdb1ab05fb03c9435234343c1cc65289eeb016be86193e88d3a5d84f6b.json
├── 80cdba09b091f72276c031bd64488c3da5f82022812bc995f0859abe0855a872
│   ├── json
│   ├── layer
│   ├── layer.tar
│   └── VERSION
├── c575e5db41b92dbf9e1814b8ea9cf989ef650e37cfcef12fa402e630db6a581f
│   ├── json
│   ├── layer
│   ├── layer.tar
│   └── VERSION
├── manifest.json
└── repositories

从目录结构上来看,有三个总体的描述文件repositoriesmanifest.json4efb2fcdb1ab05fb03c9435234343c1cc65289eeb016be86193e88d3a5d84f6b.json,还有三个目录,这三个目录的结构基本相同都由jsonlayerVERSION组成。

通过这些名字,我们先简单猜测一下,image由很多layer组成,上层layer基于下层layer构建而成,下层layer基于基层layer构成。到底image是不是按我们的猜测构成的呢,让我们来看一看总体描述的三个文件。

manifest.json:

1
{"Config":"4efb2fcdb1ab05fb03c9435234343c1cc65289eeb016be86193e88d3a5d84f6b.json","RepoTags":["nginx:1.11.3"],"Layers":["80cdba09b091f72276c031bd64488c3da5f82022812bc995f0859abe0855a872/layer.tar","c575e5db41b92dbf9e1814b8ea9cf989ef650e37cfcef12fa402e630db6a581f/layer.tar","01d69dac180439f10b5f2f06b71fa19c9f89fb85cb5fd543dba9bb2ca9b620ef/layer.tar"]}]

通过manifest.json文件我们可以看出,nginx:1.11.3这个image的配置文件是4efb2fcdb1ab05fb03c9435234343c1cc65289eeb016be86193e88d3a5d84f6b.json,layer一共有三个分别是80cdba09b091f72276c031bd64488c3da5f82022812bc995f0859abe0855a872/layer.tarc575e5db41b92dbf9e1814b8ea9cf989ef650e37cfcef12fa402e630db6a581f/layer.tar01d69dac180439f10b5f2f06b71fa19c9f89fb85cb5fd543dba9bb2ca9b620ef/layer.tar

image的配置文件中都对image进行了哪些配置呢?让我们来看一看
4efb2fcdb1ab05fb03c9435234343c1cc65289eeb016be86193e88d3a5d84f6b.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
{
"architecture":"amd64",
"author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"",
"config":{
"Hostname":"2da0903ff372",
"Domainname":"",
"User":"",
"AttachStdin":false,
"AttachStdout":false,
"AttachStderr":false,
"ExposedPorts":{"443/tcp":{},"80/tcp":{}},
"Tty":false,
"OpenStdin":false,
"StdinOnce":false,
"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NGINX_VERSION=1.11.3-1~jessie"],
"Cmd":["nginx","-g","daemon off;"],
"ArgsEscaped":true,
"Image":"sha256:27ca9d70a764c6955e354f5fdc706b03ed47601213ac9fc638a8943fcc7680f8",
"Volumes":null,
"WorkingDir":"",
"Entrypoint":null,
"OnBuild":[],
"Labels":{}
},
"container":"25ab8b143580751e438246801e7ba720f6f84ee9faca3522532e7fbe1ed7021f",
"container_config":{
"Hostname":"2da0903ff372",
"Domainname":"",
"User":"",
"AttachStdin":false,
"AttachStdout":false,
"AttachStderr":false,
"ExposedPorts":{"443/tcp":{},"80/tcp":{}},
"Tty":false,
"OpenStdin":false,
"StdinOnce":false,
"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NGINX_VERSION=1.11.3-1~jessie"],
"Cmd":["/bin/sh","-c","#(nop) CMD [\"nginx\" \"-g\" \"daemon off;\"]"],
"ArgsEscaped":true,
"Image":"sha256:27ca9d70a764c6955e354f5fdc706b03ed47601213ac9fc638a8943fcc7680f8",
"Volumes":null,
"WorkingDir":"",
"Entrypoint":null,
"OnBuild":[],
"Labels":{}
},
"created":"2016-08-23T18:51:23.709520142Z",
"docker_version":"1.10.3",
"history":[
{"created":"2016-07-28T17:47:54.990622865Z","created_by":"/bin/sh -c #(nop) ADD file:0e0565652aa852f62033d99f84892216020d30f64521ded5e72d4940bc4c9697 in /"},
{"created":"2016-07-28T17:47:55.809686499Z","created_by":"/bin/sh -c #(nop) CMD [\"/bin/bash\"]","empty_layer":true},
{"created":"2016-08-23T18:49:31.945397165Z","author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"","created_by":"/bin/sh -c #(nop) MAINTAINER NGINX Docker Maintainers \"docker-maint@nginx.com\"","empty_layer":true},
{"created":"2016-08-23T18:49:33.057946615Z","author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"","created_by":"/bin/sh -c #(nop) ENV NGINX_VERSION=1.11.3-1~jessie","empty_layer":true},
{"created":"2016-08-23T18:51:19.236946594Z","author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"","created_by":"/bin/sh -c apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 \t\u0026\u0026 echo \"deb http://nginx.org/packages/mainline/debian/ jessie nginx\" \u003e\u003e /etc/apt/sources.list \t\u0026\u0026 apt-get update \t\u0026\u0026 apt-get install --no-install-recommends --no-install-suggests -y \t\t\t\t\t\tca-certificates \t\t\t\t\t\tnginx=${NGINX_VERSION} \t\t\t\t\t\tnginx-module-xslt \t\t\t\t\t\tnginx-module-geoip \t\t\t\t\t\tnginx-module-image-filter \t\t\t\t\t\tnginx-module-perl \t\t\t\t\t\tnginx-module-njs \t\t\t\t\t\tgettext-base \t\u0026\u0026 rm -rf /var/lib/apt/lists/*"},
{"created":"2016-08-23T18:51:21.574094006Z","author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"","created_by":"/bin/sh -c ln -sf /dev/stdout /var/log/nginx/access.log \t\u0026\u0026 ln -sf /dev/stderr /var/log/nginx/error.log"},
{"created":"2016-08-23T18:51:22.632895061Z","author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"","created_by":"/bin/sh -c #(nop) EXPOSE 443/tcp 80/tcp","empty_layer":true},
{"created":"2016-08-23T18:51:23.709520142Z","author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"","created_by":"/bin/sh -c #(nop) CMD [\"nginx\" \"-g\" \"daemon off;\"]","empty_layer":true}
],
"os":"linux",
"rootfs":{
"type":"layers",
"diff_ids":[
"sha256:2f71b45e4e254ddceb187b1467f5471f0e14d7124ac2dd7fdd7ddbc76e13f0e5",
"sha256:d7953e5e5bba5c637ce6eb81a4528a777c4265386cb12e9bff8124ef97538746",
"sha256:69ecf026ff94793ab573754612e6fa40b28331a47c3584de0b32d39a88959c37"
]
}
}

看完这个配置文件我惊到了,如此庞大的一个配置文件告诉我们dockerfile能配的我能配,dockerfile不能配的我也能配。。。这个配置文件具体就不分析了。。。

刚刚看完那个累心的配置文件,接下来让我们来说说这三个layer,哪一个layer是入口呢?

repositories:

1
{"nginx":{"1.11.3":"01d69dac180439f10b5f2f06b71fa19c9f89fb85cb5fd543dba9bb2ca9b620ef"}}

我们通过查看repositories可以看出1.11.3这个版本的layer入口是01d69dac180439f10b5f2f06b71fa19c9f89fb85cb5fd543dba9bb2ca9b620ef。那么三个layer之间的依赖关系怎样呢?让我们进入各个layer一探究竟吧。

layer 01d69dac180439f10b5f2f06b71fa19c9f89fb85cb5fd543dba9bb2ca9b620ef

该layer中的json文件描述的本layer的配置信息

json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
{
"id":"01d69dac180439f10b5f2f06b71fa19c9f89fb85cb5fd543dba9bb2ca9b620ef",
"parent":"c575e5db41b92dbf9e1814b8ea9cf989ef650e37cfcef12fa402e630db6a581f",
"created":"2016-08-23T18:51:23.709520142Z",
"container":"25ab8b143580751e438246801e7ba720f6f84ee9faca3522532e7fbe1ed7021f",
"container_config":{
"Hostname":"2da0903ff372",
"Domainname":"",
"User":"",
"AttachStdin":false,
"AttachStdout":false,
"AttachStderr":false,
"ExposedPorts":{"443/tcp":{},"80/tcp":{}},
"Tty":false,
"OpenStdin":false,
"StdinOnce":false,
"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NGINX_VERSION=1.11.3-1~jessie"],
"Cmd":["/bin/sh","-c","#(nop) CMD [\"nginx\" \"-g\" \"daemon off;\"]"],
"ArgsEscaped":true,
"Image":"sha256:27ca9d70a764c6955e354f5fdc706b03ed47601213ac9fc638a8943fcc7680f8",
"Volumes":null,
"WorkingDir":"",
"Entrypoint":null,
"OnBuild":[],
"Labels":{}
},
"docker_version":"1.10.3",
"author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"",
"config":{
"Hostname":"2da0903ff372",
"Domainname":"",
"User":"",
"AttachStdin":false,
"AttachStdout":false,
"AttachStderr":false,
"ExposedPorts":{"443/tcp":{},"80/tcp":{}},
"Tty":false,
"OpenStdin":false,
"StdinOnce":false,
"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NGINX_VERSION=1.11.3-1~jessie"],
"Cmd":["nginx","-g","daemon off;"],
"ArgsEscaped":true,
"Image":"sha256:27ca9d70a764c6955e354f5fdc706b03ed47601213ac9fc638a8943fcc7680f8",
"Volumes":null,
"WorkingDir":"",
"Entrypoint":null,
"OnBuild":[],
"Labels":{}
},
"architecture":"amd64",
"os":"linux"
}

我们可以看到parent字段指出了当前layer的父layer是c575e5db41b92dbf9e1814b8ea9cf989ef650e37cfcef12fa402e630db6a581f
当前layer中数据内容请见layer.tar

layer.tar:

1
2
3
4
5
6
7
$ tree ./layer
./layer
└── var
└── log
└── nginx
├── access.log -> /dev/stdout
└── error.log -> /dev/stderr

layer c575e5db41b92dbf9e1814b8ea9cf989ef650e37cfcef12fa402e630db6a581f

该layer中的json文件描述的本layer的配置信息

json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"id":"c575e5db41b92dbf9e1814b8ea9cf989ef650e37cfcef12fa402e630db6a581f",
"parent":"80cdba09b091f72276c031bd64488c3da5f82022812bc995f0859abe0855a872",
"created":"1970-01-01T08:00:00+08:00",
"container_config":{
"Hostname":"",
"Domainname":"",
"User":"",
"AttachStdin":false,
"AttachStdout":false,
"AttachStderr":false,
"Tty":false,
"OpenStdin":false,
"StdinOnce":false,
"Env":null,
"Cmd":null,
"Image":"",
"Volumes":null,
"WorkingDir":"",
"Entrypoint":null,
"OnBuild":null,
"Labels":null
}
}

该layer中的父layer是80cdba09b091f72276c031bd64488c3da5f82022812bc995f0859abe0855a872
当前layer中的数据内容请见layer.tar

layer.tar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
$ tree -L 2 ./layer
./layer
├── etc
│   ├── alternatives
│   ├── apt
│   ├── ca-certificates
│   ├── ca-certificates.conf
│   ├── default
│   ├── fonts
│   ├── group
│   ├── group-
│   ├── gshadow
│   ├── gshadow-
│   ├── init.d
│   ├── ld.so.cache
│   ├── logrotate.d
│   ├── nginx
│   ├── passwd
│   ├── passwd-
│   ├── perl
│   ├── rc0.d
│   ├── rc1.d
│   ├── rc2.d
│   ├── rc3.d
│   ├── rc4.d
│   ├── rc5.d
│   ├── rc6.d
│   ├── shadow
│   ├── shadow-
│   ├── ssl
│   ├── subgid
│   ├── subgid-
│   ├── subuid
│   ├── subuid-
│   └── ucf.conf
├── lib
│   └── x86_64-linux-gnu
├── tmp
├── usr
│   ├── bin
│   ├── lib
│   ├── local
│   ├── sbin
│   └── share
└── var
├── cache
├── lib
└── log

layer 80cdba09b091f72276c031bd64488c3da5f82022812bc995f0859abe0855a872

该layer中的json文件描述的本layer的配置信息
json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"id":"80cdba09b091f72276c031bd64488c3da5f82022812bc995f0859abe0855a872",
"created":"1970-01-01T08:00:00+08:00",
"container_config":{
"Hostname":"",
"Domainname":"",
"User":"",
"AttachStdin":false,
"AttachStdout":false,
"AttachStderr":false,
"Tty":false,
"OpenStdin":false,
"StdinOnce":false,
"Env":null,
"Cmd":null,
"Image":"",
"Volumes":null,
"WorkingDir":"",
"Entrypoint":null,
"OnBuild":null,
"Labels":null
}
}

从配置中可以看出,没有parent字段了。说明这个layer应该就是base layer了。(我十分好奇nginx镜像是用哪个基础镜像做出来的?)
当前layer中的数据内容请见layer.tar
layer.tar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ tree -L 1 ./layer
./layer
├── bin
├── boot
├── dev
├── etc
├── home
├── lib
├── lib64
├── media
├── mnt
├── opt
├── proc
├── root
├── run
├── sbin
├── srv
├── sys
├── tmp
├── usr
└── var
1
2
$ cat ./layer/etc/debian_version
8.5

噢,原来nginx镜像是基于debian 8.5做出来的。

Ceph号称统一存储,何为统一,就是将块、文件、对象统一到一起。RadosGW(简称RGW)就是Ceph中提供对象存储服务的模块。它能提供S3和Swift两种对象存储接口,当然也是最主流的两种接口。

本文不打算介绍概念性的东西,只为扒一扒RGW的架构。废话不多讲,直接上图

rgw_arch.png

我觉得画的挺清楚了,就不细讲了。


一张图不过瘾,再来一发。

rgw_structural

DNSmasq是一个小巧且方便地用于配置DNS和DHCP的工具,适用于小型网络,它提供了DNS功能和可选择的DHCP功能。它服务那些只在本地适用的域名,这些域名是不会在全球的DNS服务器中出现的。DHCP服务器和DNS服务器结合,并且允许DHCP分配的地址能在DNS中正常解析,而这些DHCP分配的地址和相关命令可以配置到每台主机中,也可以配置到一台核心设备中(比如路由器),DNSmasq支持静态和动态两种DHCP配置方式。

安装

rhel7.4上的安装yum install -y dnsmasq, 完成dnsmasq的安装同时也推荐安装bind-utils,这个包提供很多dns测试相关工具yum install -y bind-utils, 如dignslookup

配置启动

创建两个dnsmasq节点,一个做base dns,一个做upper dns

base dnsdnsmasq.conf配置:

1
2
3
4
5
6
7
listen-address=172.17.0.2,127.0.0.1

# 配置上游DNS服务器
server=/dockxen.mydns/172.17.0.3

# 配置别名
cname=web.dockxen.dns,rhel82

*** 在使用CNAME时,<target>必须本机可访问对象,所以此处我将rhel82放到/etc/hosts中 ***

base dns/etc/hosts配置:

1
2
3
4
127.0.0.1       localhost
::1 localhost ip6-localhost ip6-loopback
172.17.0.2 dnsbase
192.168.1.82 rhel82

upper dns配置:

1
2
3
4
5
6
address=/myweb.dockxen.mydns/192.168.1.82

listen-address=172.17.0.3,127.0.0.1

# The fields are <name>,<target>,<port>,<priority>,<weight>
srv-host=_ldap._tcp.dockxen.mydns,myweb.dockxen.mydns,8000,0,100

完成base dnsupper dns的配置后使用dnsmasq -d debug模式启动,可以看到部分调试信息

测试验证

测试验证需要安装bind-utils,测试中会用到其中的dignslookup,开始测试前修改/etc/resolv.conf中的nameserver 172.17.0.2

验证A

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[root@dnclient /]# dig myweb.dockxen.mydns

; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> myweb.dockxen.mydns
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22751
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;myweb.dockxen.mydns. IN A

;; ANSWER SECTION:
myweb.dockxen.mydns. 0 IN A 192.168.1.82

;; Query time: 0 msec
;; SERVER: 172.17.0.2#53(172.17.0.2)
;; WHEN: Mon Nov 20 09:29:48 UTC 2017
;; MSG SIZE rcvd: 53

[root@dnclient /]# nslookup myweb.dockxen.mydns
Server: 172.17.0.2
Address: 172.17.0.2#53

Name: myweb.dockxen.mydns
Address: 192.168.1.82

验证CNAME

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[root@dnclient /]# dig web.dockxen.dns

; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> web.dockxen.dns
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55429
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;web.dockxen.dns. IN A

;; ANSWER SECTION:
web.dockxen.dns. 0 IN CNAME rhel82.
rhel82. 0 IN A 192.168.1.82

;; Query time: 0 msec
;; SERVER: 172.17.0.2#53(172.17.0.2)
;; WHEN: Mon Nov 20 09:30:59 UTC 2017
;; MSG SIZE rcvd: 80

[root@dnclient /]# nslookup web.dockxen.dns
Server: 172.17.0.2
Address: 172.17.0.2#53

web.dockxen.dns canonical name = rhel82.
Name: rhel82
Address: 192.168.1.82

验证SRV

1
2
3
4
5
6
7
[root@dnclient /]# dig +noall +answer SRV _ldap._tcp.dockxen.mydns
_ldap._tcp.dockxen.mydns. 0 IN SRV 0 100 8000 myweb.dockxen.mydns.
[root@dnclient /]# nslookup -type=srv _ldap._tcp.dockxen.mydns
Server: 172.17.0.2
Address: 172.17.0.2#53

_ldap._tcp.dockxen.mydns service = 0 100 8000 myweb.dockxen.mydns.

应用

etcd的服务发现

etcd各个节点之间的通讯使用域名方式访问,由于etcd节点ip地址可以以dhcp方式获取,每次etcd节点重启都有可能导致访问地址发生变化。若使用/etc/hosts文件进行“域名-ip”映射涉及到/etc/hosts文件同步分发问题,所以使用dnsmasq来完成“域名-ip”的映射。

关于etcd的服务发现配置请见参考&鸣谢

kube-dns的服务发现

请见《K8s服务发现分析》

参考&鸣谢

Matplotlib数据可视化第三方库(官网)。由各种可视化类构成。

matplotlib.pyplot

matplotlib.pyplot是绘制各类可视化图形的命令子库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/python
#encoding:utf-8

import matplotlib.pyplot as plt

plt.plot([1,2,3,4,5,6])
plt.ylabel("value")

# 将图像保存成PNG格式
plt.savefig("./test", dpi=600)


# x值[0,2,4,6,8]; y值[3,1,4,5,2]
plt.plot([0,2,4,6,8], [3,1,4,5,2])
plt.ylabel("value")

# X轴[-1,10]; Y轴[0,10]
plt.axis([-1,10,0,10])

plt.show()

plot

plt.plot(x,y,format_string, **kwargs)

  • x X轴数据,列表或数组,只有一条曲线的时候可选。
  • y Y轴数据,列表或数组。
  • format_string 控制曲线的各式字符串,可选。
  • kwargs 第二组或更多(x,y,format_string)

format_string分别由“颜色字符”、“风格字符”、“标记字符”构成

** 颜色字符 **

颜色字符 说明 颜色字符 说明
‘b’ 蓝色 ‘m’ 洋红色
‘g’ 绿色 ‘y’ 黄色
‘r’ 红色 ‘k’ 黑色
‘c’ 青色 ‘w’ 白色
‘#008000’ RGB某颜色 ‘0.8’ 灰度值字符串

如果用户不指定颜色,系统会自动指定不重复的颜色。

** 风格字符 **

风格字符 说明
‘-‘ 实现
‘–’ 破折线
‘-.’ 点划线
‘:’ 虚线
‘’ 无线条

*** 更多关于标记内容请见HERE ***

1
2
3
4
5
6
import numpy as np
import matplotlib.pyplot as plt

a = np.arange(10)
plt.plot(a,a*1.5,'go-', a,a*2.5,'rx', a,a*3.5,'*', a,a*4.5,'b-.')
plt.show()

分区绘制

subplot

plt.subplot(nrows, ncols, plot_number) 将绘制区域分割成 nrows x ncols个区域,plot_number指定绘制的是哪个区域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np
import matplotlib.pyplot as plt

def f(t):
return np.exp(-t) * np.cos(2*np.pi*t)

a = np.arange(0.0,5.0,2.0)

plt.subplot(2,1,1)
plt.plot(a, f(a))

plt.subplot(2,1,2)
plt.plot(a, np.cos(2*np.pi*a), 'r--')

plt.show()

参考&鸣谢

  • NumPy
  • PIL
    PIL(Python Image Library)库是一个具有强大图像处理能力的第三方库

图像=>数组

图像采用的色彩模式为RGB模式,即每个像素点由R(红色)、G(绿色)、B(蓝色)组成。我们人眼所能看到的颜色都是由这三种颜色变化叠加得到的。

  • R红色,取值范围,0~255
  • G绿色,取值范围,0~255
  • B蓝色,取值范围,0~255

图像是一个由像素组成的二维矩阵,每个元素是一个RGB值。

PIL安装

OSX: pip install pillow

图像处理

图像加载

turkey_orig.jpg

1
2
3
4
5
6
7
8
9
10
>>> from PIL import Image
>>> import numpy as np
>>> img = np.array(Image.open("turkey_orig.jpg"))
>>> img_l = np.array(Image.open("turkey_orig.jpg").convert('L'))
>>> print img.shape, img.dtype
(1184, 2104, 3) uint8
# img是一个三维数组每一个元素代表RGB的一个值
>>> print img_l.shape, img_l.shape
(1184, 2104) uint8
# img_l是一个二维数组每一个元素对应一个灰度值

图像变换

返选效果

1
md_img = [255,255,255] - img

turkey_modify_1.jpg

底片效果

1
md_img = 255 - img_l

turkey_modify_2.jpg

区间变换

1
md_img = (100.0/255.0)*img_l + 150

turkey_modify_3.jpg

平方变换

1
md_img = 255 * ((img_l/255.0)**2)

turkey_modify_4.jpg

手绘效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
img_l_float = img_l.astype("float")

# 深度值,范围为0~100
depth = 10

# 提取x和y方向的梯度值
grad = np.gradient(img_l_float)
grad_x, grad_y = grad

# 根据深度调整梯度值
grad_x = grad_x*depth/100
grad_y = grad_y*depth/100

# 此处计算的是什么?
A = np.sqrt(grad_x**2 + grad_y**2 + 1.0)
uni_x = grad_x/A
uni_y = grad_y/A
uni_z = 1.0/A

# 为什么要引入光源?
vec_el = np.pi/2.2
vec_az = np.pi/4.0
dx = np.cos(vec_el)*np.cos(vec_az)
dy = np.cos(vec_el)*np.sin(vec_az)
dz = np.sin(vec_el)

#dx, dy, dz = 1,1,1
md_img = 255*(dx*uni_x + dy*uni_y + dz*uni_z)
md_img = md_img.clip(0,255)

无光源效果:
turkey_modify_6.jpg

有光源效果:
turkey_modify_5.jpg

图像保存

1
2
save_img = Image.fromarray(md_img.astype('uint8'))
save_img.save("turkey_modify.jpg")

参考&鸣谢