软件版本要求

Docker要管理运行在内部的所有containers,因此CRIU需要在Docker内运行,而不是单独运行。 组件 | 版本 —|— docker | ce=17.03+ ee=1.13+ criu | 2.0+ centos | 7.3 kernel | 3.10.0-957

criu安装

第一种方式,通过yum源安装

# yum install criu -y

第二种方式,通过源码安装

1.下载源码
# git clone https://github.com/checkpoint-restore/criu.git
2.安装编译所需的依赖包
# yum install gcc protobuf protobuf-c protobuf-c-devel protobuf-compiler protobuf-devel protobuf-python libnet-devel libnl3-devel libcap-devel  asciidoc xmlto -y
3.编译源码
# cd criu
# make install
4.测试,无报错信息表示安装成功
# criu check
Looks good.

docker安装

1.卸载旧版本
# yum remove docker docker-common docker-selinux docker-engine -y
2.配置docker-ce镜像仓库
安装依赖包
# yum install -y yum-utils device-mapper-persistent-data lvm2
添加yum源
# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
3.安装最新版Docker CE
# yum makecache fast
# yum install docker-ce -y
4.启动Docker
# systemctl enable docker
# systemctl start docker
5.开启热迁移功能
修改配置文件
# echo "{\"experimental\": true}" >> /etc/docker/daemon.json
# systemctl restart docker

进程热迁移

本机热迁移的过程不做描述,因此需要准备两台主机,下面docker进程的热迁移也一样

准备一个程序

criu提供了一个go-binding的库叫go-criu用来完成进程的热迁移,首先下载源码到GOPATH目录下

# git clone https://github.com/checkpoint-restore/go-criu.git
# cd github.com/checkpoint-restore/go-criu

2.编译

如果go环境配置没问题,直接执行make等待编译完成

# make

make过程中已经在本机执行了一次测试用例,我们主要关注跨主机的热迁移功能

3.运行测试程序

为了方便演示,可以将演示程序piggie以及criu测试程序test这两个文件cp到home目录下

# cp test/piggie test/test /home && cd /home
# ./piggie piggie.log
Child forked, pid 27636
# ps -ef | grep piggie
root     27636     1  0 10:17 ?        00:00:00 ./piggie piggie.log
root     27676  7298  0 10:17 pts/0    00:00:00 grep --color=auto piggie
# tail -f piggie.log
0
1
2
3
4
5

可以看到日志文件piggie.log中在不停的打印递增的数字

创建checkpoint

ssh到源主机node1上操作

# cd /home
# mkdir -p image
test程序的参数含义可以直接看源码checkpoint-restore\go-criu\test\main.go
# ./test dump `pidof piggie` image
CRIU version 31200
Dumping
TEST PRE DUMP
Success
创建完成后进程被杀掉了
# ps -ef | grep piggie
root     28478  7298  0 10:42 pts/0    00:00:00 grep --color=auto piggie
进程相关的数据保存在image目录下
# ll image/
total 224
-rw-r--r-- 1 root root   1762 Jan 24 10:42 core-1.img
-rw------- 1 root root  74269 Jan 24 10:42 dump.log
-rw-r--r-- 1 root root     44 Jan 24 10:42 fdinfo-2.img
-rw-r--r-- 1 root root    352 Jan 24 10:42 files.img
-rw-r--r-- 1 root root     18 Jan 24 10:42 fs-1.img
-rw-r--r-- 1 root root     32 Jan 24 10:42 ids-1.img
-rw-r--r-- 1 root root     40 Jan 24 10:42 inventory.img
-rw-r--r-- 1 root root    809 Jan 24 10:42 mm-1.img
-rw-r--r-- 1 root root    216 Jan 24 10:42 pagemap-1.img
-rw-r--r-- 1 root root 106496 Jan 24 10:42 pages-1.img
-rw-r--r-- 1 root root     22 Jan 24 10:42 pstree.img
-rw-r--r-- 1 root root     12 Jan 24 10:42 seccomp.img
-rw-r--r-- 1 root root     41 Jan 24 10:42 stats-dump

复制镜像文件到目的节点

从源节点将进程相关的数据拷贝到目的节点node2上,注意进程相关的文件都要拷过去且保持目录不变,日志文件piggie.log一定要使用创建checkpoint时间点的文件

# scp -r /home/image/ /home/piggie /home/piggie.log /home/test node2:/home

恢复

登录到node2节点上

# cd /home
# ./test restore image
CRIU version 31200
Restoring
Success

查看状态

可以看到进程会接着上次创建checkpoint的序号继续打印,看起来就像没中断过一样,至此进程热迁移就算完成了

# ps -ef | grep piggie
root     30870     1  0 10:51 ?        00:00:00 ./piggie piggie.log
root     30880 29769  0 10:52 pts/0    00:00:00 grep --color=auto piggie
# tailf piggie.log
210
211
212
213
214
215

容器热迁移

准备一个容器

以wordpress为例,首先在源节点node1上创建一个容器

# docker run --name mwp -e WORDPRESS_DB_HOST=10.30.252.241:3306 -e WORDPRESS_DB_USER=root -e WORDPRESS_DB_PASSWORD=123456 -p81:80 -d --privileged --security-opt seccomp:unconfined wordpress
ca22b1d7cc1b14ebc3e019d87718c54672b182464734d1b16a6b5f5adaf88b88

打开浏览器检查wordpress状态正常

创建checkpoint

# docker checkpoint create ca22b1d7cc1b c1

创建好的image文件默认保存在这个目录下: /var/lib/docker/containers//checkpoints//

创建checkpoint成功后通过docker ps命令看到容器进程被杀掉了

导出源容器的镜像文件

注意:导出镜像需要使用save&load,不能使用export&import,原因是存储层的数据是容器运行所需的必备文件,两者的区别参考https://jingsam.github.io/2017/08/26/docker-save-and-docker-export.html

  1. 定制镜像

将容器转换为docker镜像

# docker commit ca22b1d7cc1b wordpress:v1

成功后能看到多出来一个image

# docker images
REPOSITORY                              TAG                 IMAGE ID            CREATED             SIZE
wordpress                               v1                  872e4c495369        2 seconds ago       420MB
  1. 导出镜像

将上一步转换的docker镜像导出为一个输出文件

# docker save -o wordpressv1 wordpress:v1

在当前目录下能看到导出的镜像文件wordpressv1

# ll -h wordpressv1
-rw------- 1 root root 410M Jan 24 12:54 wordpressv1

如果不需要了可以删除该镜像文件

# docker rmi wordpress:v1

复制镜像文件到目的节点

将导出的镜像文件复制到目的节点

# scp wordpressv1 node2:/home

恢复

登录到node2节点上

  1. 导入镜像
    # cd /home
    # docker load -i wordpressv1
    6b237323a705: Loading layer [==================================================>]  7.168kB/7.168kB
    Loaded image: wordpress:v1
    
  2. 查看导入的image
    # docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
    wordpress           v1                  872e4c495369        2 minutes ago       420MB
    
  3. 创建容器
    # docker create --name mwp -e WORDPRESS_DB_HOST=10.30.252.241:3306 -e WORDPRESS_DB_USER=root -e WORDPRESS_DB_PASSWORD=123456 -p81:80 --privileged--security-opt seccomp:unconfined wordpress:v1
    00d9e510226fa18f146b09613603e6f85601227eeae62f56191f043cff6effef
    
  4. 从node1节点拷贝checkpoint文件到node2节点

scp -r node1:/var/lib/docker/containers//checkpoints// node2:/var/lib/docker/containers//checkpoints/

# scp -r node1:/var/lib/docker/containers/ca22b1d7cc1b14ebc3e019d87718c54672b182464734d1b16a6b5f5adaf88b88/checkpoints/c1 /var/lib/docker/containers/00d9e510226fa18f146b09613603e6f85601227eeae62f56191f043cff6effef/checkpoints
  1. 恢复启动容器
    # docker start --checkpoint c1 00d9e510226f
    

查看状态

打开浏览器检查WordPress状态正常

官网给的例子是在容器内运行一个循环,测试也可以正常迁移

1. 启动容器
# docker run -d --name looper --security-opt seccomp:unconfined busybox  \
         /bin/sh -c 'i=0; while true; do echo $i; i=$(expr $i + 1); sleep 1; done'
2. 创建checkpoint
# docker checkpoint create looper c1
3. 检查进程日志
# docker logs looper
4. 转换为镜像
# docker commit 6f71122189b2 busybox:v1
# docker save -o busyboxv1 busybox:v1
5. 拷贝镜像到目的节点
# scp node1:/home/busyboxv1 node2:/home
6. 在目的节点导入镜像
# docker load -i /home/busyboxv1
7.在目的节点创建新容器
# docker create --name looper --security-opt seccomp:unconfined busybox:v1 /bin/sh -c 'i=0; while^Crue; do echo $i; i=$(expr $i + 1); sleep 1; done'
8. 拷贝checkpoint到目的节点
# scp -r node1:/var/lib/docker/containers/6f71122189b2d797ed821e08481e8277289a51458a6090570bad2e57cf25ec45/checkpoints/c1/ node2:/var/lib/docker/containers/22fde8c8571c5655b0ba2377925a5e2791b0858343cfdf0335c3857cc2c2f514/checkpoints/
9. 启动容器
# docker start --checkpoint c1 22fde8c8571c
10. 再次检查进程日志正常,接着上次创建checkpoint的时间点打印
# docker logs 22fde8c8571c

功能限制

目前docker将criu标记为实验性质的功能,存在以下兼容性问题需要注意

  1. TTY,不支持对交互式容器创建checkpoint;
  2. Seccomp,如果容器开启了安全计算模式,需要kernel支持(grep CONFIG_SECCOMP= /boot/config-$(uname -r) 返回CONFIG_SECCOMP=y表示支持)
  3. OverlayFS,kernel有个关于OverlayFS的bug在v4.2-rc2版本中被修复
  4. Async IO,如果容器使用了AIO需要kernel>=3.19

总结

criu能支持热迁移的功能有限,这也是docker没有将它直接封装成跨主机迁移的功能,目前只能在本地实现进程的dump和restore,而且对于在容器内运行的交互式程序不支持,这也是后面使用时要注意的一个问题。 只能说这个功能能解决的问题十分有限,另外还有很多程序不支持热迁移,例如mysql详见https://github.com/checkpoint-restore/criu/issues/484

参考资料

  1. criu_docker WIKI
  2. docker-ce安装
  3. docker容器数据迁移

一些概念

如果你执行过 ceph health 、 ceph -s 、或 ceph -w 命令,你也许注意到了集群并非总返回 HEALTH OK 。检查完 OSD 是否在运行后,你还应该检查归置组状态。你应该明白,在归置组建立连接时集群不会返回 HEALTH OK :

  1. 刚刚创建了一个存储池,归置组还没互联好;
  2. 归置组正在恢复;
  3. 刚刚增加或删除了一个 OSD ;
  4. 刚刚修改了 CRUSH 图,并且归置组正在迁移;
  5. 某一归置组的副本间的数据不一致;
  6. Ceph 正在洗刷一个归置组的副本;
  7. Ceph 没有足够空余容量来完成回填操作。

如果是前述原因之一导致了 Ceph 返回 HEALTH WARN ,无需紧张。很多情况下,集群会自行恢复;有些时候你得采取些措施。归置组监控的一件重要事情是保证集群起来并运行着,所有归置组都处于 active 状态、并且最好是 clean 状态。用下列命令查看所有归置组状态:

ceph pg stat

其结果会告诉你归置组运行图的版本号( vNNNNNN )、归置组总数 x 、有多少归置组处于某种特定状态,如 active+clean ( y )。

vNNNNNN: x pgs: y active+clean; z bytes data, aa MB used, bb GB / cc GB avail

==Note Ceph 同时报告出多种状态是正常的。==

除了归置组状态之外, Ceph 也会报告数据占据的空间( aa )、剩余空间( bb )和归置组总容量。这些数字在某些情况下是很重要的:

  • 快达到 near full ratiofull ratio 时;
  • 由于 CRUSH 配置错误致使你的数据没能在集群内分布。

归置组唯一标识符

归置组 ID 包含存储池号(不是存储池名字),后面跟一个点( . ),然后是归置组 ID 一个十六进制数字。用 ceph osd lspools 可查看存储池号及其名字,默认存储池名字 data 、 metadata 、和 rbd 对应的存储池号分别是 0 、 1 、 2 。完整的归置组 ID 格式如下:

{pool-num}.{pg-id}

典型长相:

0.1f

用下列命令获取归置组列表:

ceph pg dump

你也可以让它输出到 JSON 格式,并保存到文件:

ceph pg dump -o {filename} --format=json

要查询某个归置组,用下列命令:

ceph pg {poolnum}.{pg-id} query

Ceph 会输出成 JSON 格式。

{
  "state": "active+clean",
  "up": [
    1,
    0
  ],
  "acting": [
    1,
    0
  ],
  "info": {
    "pgid": "1.e",
    "last_update": "4'1",
    "last_complete": "4'1",
    "log_tail": "0'0",
    "last_backfill": "MAX",
    "purged_snaps": "[]",
    "history": {
      "epoch_created": 1,
      "last_epoch_started": 537,
      "last_epoch_clean": 537,
      "last_epoch_split": 534,
      "same_up_since": 536,
      "same_interval_since": 536,
      "same_primary_since": 536,
      "last_scrub": "4'1",
      "last_scrub_stamp": "2013-01-25 10:12:23.828174"
    },
    "stats": {
      "version": "4'1",
      "reported": "536'782",
      "state": "active+clean",
      "last_fresh": "2013-01-25 10:12:23.828271",
      "last_change": "2013-01-25 10:12:23.828271",
      "last_active": "2013-01-25 10:12:23.828271",
      "last_clean": "2013-01-25 10:12:23.828271",
      "last_unstale": "2013-01-25 10:12:23.828271",
      "mapping_epoch": 535,
      "log_start": "0'0",
      "ondisk_log_start": "0'0",
      "created": 1,
      "last_epoch_clean": 1,
      "parent": "0.0",
      "parent_split_bits": 0,
      "last_scrub": "4'1",
      "last_scrub_stamp": "2013-01-25 10:12:23.828174",
      "log_size": 128,
      "ondisk_log_size": 128,
      "stat_sum": {
        "num_bytes": 205,
        "num_objects": 1,
        "num_object_clones": 0,
        "num_object_copies": 0,
        "num_objects_missing_on_primary": 0,
        "num_objects_degraded": 0,
        "num_objects_unfound": 0,
        "num_read": 1,
        "num_read_kb": 0,
        "num_write": 3,
        "num_write_kb": 1
      },
      "stat_cat_sum": {

      },
      "up": [
        1,
        0
      ],
      "acting": [
        1,
        0
      ]
    },
    "empty": 0,
    "dne": 0,
    "incomplete": 0
  },
  "recovery_state": [
    {
      "name": "Started\/Primary\/Active",
      "enter_time": "2013-01-23 09:35:37.594691",
      "might_have_unfound": [

      ],
      "scrub": {
        "scrub_epoch_start": "536",
        "scrub_active": 0,
        "scrub_block_writes": 0,
        "finalizing_scrub": 0,
        "scrub_waiting_on": 0,
        "scrub_waiting_on_whom": [

        ]
      }
    },
    {
      "name": "Started",
      "enter_time": "2013-01-23 09:35:31.581160"
    }
  ]
}

后续子章节详述了常见状态。

  1. 存储池在建中(creating)

创建存储池时,它会创建指定数量的归置组。 Ceph 在创建一或多个归置组时会显示 creating ;创建完后,在其归置组的 Acting Set 里的 OSD 将建立互联;一旦互联完成,归置组状态应该变为 active+clean ,意思是 Ceph 客户端可以向归置组写入数据了。 image

  1. 互联建立中(peering)

Ceph 为归置组建立互联时,会让存储归置组副本的 OSD 之间就其中的对象和元数据状态达成一致。 Ceph 完成了互联,也就意味着存储着归置组的 OSD 就其当前状态达成了一致。然而,互联过程的完成并不能表明各副本都有了数据的最新版本。

权威历史 Ceph 不会向客户端确认写操作,直到 acting set 里的所有 OSD 都完成了写操作。这样处理保证了从上次成功互联起, acting set 中至少有一个成员确认了每个写操作。 有了各个已确认写操作的精确记录, Ceph 就可以构建和散布一个新的归置组权威历史——一个完整、完全有序的操作集,如果被采用,就能把一个 OSD 的归置组副本更新到最新。

  1. 活跃(active)

eph 完成互联后,一归置组状态会变为 active 。 active 状态意味着数据已完好地保存到了主归置组和副本归置组。

  1. 整洁(clean)

某一归置组处于 clean 状态时,主 OSD 和副本 OSD 已成功互联,并且没有偏离的归置组。 Ceph 已把归置组中的对象复制了规定次数。

  1. 已降级(degraded)

当客户端向主 OSD 写入数据时,由主 OSD 负责把数据副本写入其余副本 OSD 。主 OSD 把对象写入存储器后,在副本 OSD 创建完对象副本并报告给主 OSD 之前,主 OSD 会一直停留在 degraded 状态。

归置组状态可以处于 active+degraded 状态,原因在于一 OSD 即使尚未持有所有对象也可以处于 active 状态。如果一 OSD 挂了, Ceph 会把分配到此 OSD 的归置组都标记为 degraded ;那个 OSD 重生后,它们必须重新互联。然而,客户端仍可以向处于 degraded 状态的归置组写入新对象,只要它还在 active 状态。

如果一 OSD 挂了,且老是处于 degraded 状态, Ceph 会把 down 的 OSD 标记为在集群外( out )、并把那个 down 掉的 OSD 上的数据重映射到其它 OSD 。从标记为 down 到 out 的时间间隔由 mon osd down out interval 控制,默认是 300 秒。

归置组也会被降级( degraded ),因为 Ceph 找不到本应存在于此归置组中的一或多个对象,这时,你不能读写找不到的对象,但仍能访问位于降级归置组中的其它对象。

  1. 恢复中(recovering)

Ceph 被设计为可容错,可抵御一定规模的软、硬件问题。当某 OSD 挂了( down )时,其内的归置组会落后于别的归置组副本;此 OSD 重生( up )时,归置组内容必须更新到当前状态;在此期间, OSD 处于 recovering 状态。

恢复并非总是这些小事,因为一次硬件失败可能牵连多个 OSD 。比如一个机柜或房间的网络交换机失败了,这会导致多个主机上的 OSD 落后于集群的当前状态,故障恢复后每一个 OSD 都必须恢复。

Ceph 提供了几个选项来均衡资源竞争,如新服务请求、恢复数据对象和恢复归置组到当前状态。 osd recovery delay start 选项允许一 OSD 在开始恢复进程前,先重启、重建互联、甚至处理一些重放请求;osd recovery threads 选项限制恢复进程的线程数,默认为 1 线程; osd recovery thread timeout 设置线程超时,因为多个 OSD 可能交替失败、重启和重建互联; osd recovery max active 选项限制一 OSD 最多同时接受多少请求,以防它压力过大而不能正常服务; osd recovery max chunk 选项限制恢复数据块尺寸,以防网络拥塞。

  1. 回填中(backfilling)

有新 OSD 加入集群时, CRUSH 会把现有集群内的部分归置组重分配给它。强制新 OSD 立即接受重分配的归置组会使之过载,用归置组回填可使这个过程在后台开始。只要回填顺利完成,新 OSD 就可以对外服务了。

在回填运转期间,你可能见到以下几种状态之一: backfill_wait 表明一回填操作在等待时机,尚未开始; backfill 表明一回填操作正在进行; backfill_too_full 表明需要进行回填,但是因存储空间不足而不能完成。某归置组不能回填时,其状态应该是 incomplete 。

Ceph 提供了多个选项来解决重分配归置组给一 OSD (特别是新 OSD )时相关的负载问题。默认, osd_max_backfills 把双向的回填并发量都设置为 10 ; osd backfill full \ ratio 可让一 OSD 在接近占满率(默认 85% )时拒绝回填请求,如果一 OSD 拒绝了回填请求,在 osd backfill retry interval 间隔之后将重试(默认 10 秒); OSD 也能用 osd backfill scan min 和 osd backfill scan max 来管理扫描间隔(默认 64 和 512 )。

  1. 被重映射(remapped)

负责维护某一归置组的 Acting Set 变更时,数据要从旧集合迁移到新的。新的主 OSD 要花费一些时间才能提供服务,所以老的主 OSD 还要持续提供服务、直到归置组迁移完。数据迁移完后,运行图会包含新 acting set 里的主 OSD 。

  1. 发蔫(stale)

虽然 Ceph 用心跳来保证主机和守护进程在运行,但是 ceph-osd 仍有可能进入 stuck 状态,它们没有按时报告其状态(如网络瞬断)。默认, OSD 守护进程每半秒( 0.5 )会一次报告其归置组、出流量、引导和失败统计状态,此频率高于心跳阀值。如果一归置组的主 OSD 所在的 acting set 没能向监视器报告、或者其它监视器已经报告了那个主 OSD 已 down ,监视器们就会把此归置组标记为 stale 。

启动集群时,会经常看到 stale 状态,直到互联完成。集群运行一阵后,如果还能看到有归置组位于 stale 状态,就说明那些归置组的主 OSD 挂了( down )、或没在向监视器报告统计信息。

找出故障归置组

如前所述,一个归置组状态不是 active+clean 时未必有问题。一般来说,归置组卡住时 Ceph 的自修复功能往往无能为力,卡住的状态细分为:

  • Unclean: 归置组里有些对象的副本数未达到期望次数,它们应该在恢复中;
  • Inactive: 归置组不能处理读写请求,因为它们在等着一个持有最新数据的 OSD 回到 up 状态;
  • Stale: 归置组们处于一种未知状态,因为存储它们的 OSD 有一阵子没向监视器报告了(由 mon osd report timeout 配置)。

为找出卡住的归置组,执行:

ceph pg dump_stuck [unclean|inactive|stale|undersized|degraded]

详情见归置组子系统,关于排除卡住的归置组见排除归置组错误

定位对象

要把对象数据存入 Ceph 对象存储,一 Ceph 客户端必须:

  1. 设置对象名
  2. 指定一存储池

Ceph 客户端索取最新集群运行图、并用 CRUSH 算法计算对象到归置组的映射,然后计算如何动态地把归置组映射到 OSD 。要定位对象,只需要知道对象名和存储池名字,例如:

ceph osd map {poolname} {object-name}

练习:定位一个对象

反正是练习,我们先创建一个对象。给 rados put 命令指定一对象名、一个包含数据的测试文件路径、和一个存储池名字,例如:

rados put {object-name} {file-path} --pool=data
rados put test-object-1 testfile.txt --pool=data

用下列命令确认 Ceph 对象存储已经包含此对象:

rados -p data ls

现在可以定位对象了:

ceph osd map {pool-name} {object-name}
ceph osd map data test-object-1

Ceph 应该输出对象的位置,例如:

osdmap e537 pool 'data' (0) object 'test-object-1' -> pg 0.d1743484 (0.4) -> up [1,0] acting [1,0]

要删除测试对象,用 rados rm 即可,如:

rados rm test-object-1 --pool=data

随着集群的运转,对象位置会动态改变。 Ceph 动态重均衡的优点之一,就是把你从人工迁移中解救了,详情见体系结构

PLACEMENT GROUP STATES

When checking a cluster’s status (e.g., running ceph -w or ceph -s), Ceph will report on the status of the placement groups. A placement group has one or more states. The optimum state for placement groups in the placement group map is active + clean.

  1. Creating

Ceph is still creating the placement group.

  1. Active

Ceph will process requests to the placement group.

  1. Clean

Ceph replicated all objects in the placement group the correct number of times.

  1. Down

A replica with necessary data is down, so the placement group is offline.

  1. Replay

The placement group is waiting for clients to replay operations after an OSD crashed.

  1. Splitting

Ceph is splitting the placement group into multiple placement groups. (functional?)

  1. Scrubbing

Ceph is checking the placement group for inconsistencies.

  1. Degraded

Ceph has not replicated some objects in the placement group the correct number of times yet.

  1. Inconsistent

Ceph detects inconsistencies in the one or more replicas of an object in the placement group (e.g. objects are the wrong size, objects are missing from one replica after recovery finished, etc.).

  1. Peering

The placement group is undergoing the peering process

  1. Repair

Ceph is checking the placement group and repairing any inconsistencies it finds (if possible).

  1. Recovering

Ceph is migrating/synchronizing objects and their replicas.

  1. Backfill

Ceph is scanning and synchronizing the entire contents of a placement group instead of inferring what contents need to be synchronized from the logs of recent operations. Backfill is a special case of recovery.

  1. Wait-backfill

The placement group is waiting in line to start backfill.

  1. Backfill-toofull

A backfill operation is waiting because the destination OSD is over its full ratio.

  1. Incomplete

Ceph detects that a placement group is missing information about writes that may have occurred, or does not have any healthy copies. If you see this state, try to start any failed OSDs that may contain the needed information or temporarily adjust min_size to allow recovery.

  1. Stale

The placement group is in an unknown state - the monitors have not received an update for it since the placement group mapping changed.

  1. Remapped

The placement group is temporarily mapped to a different set of OSDs from what CRUSH specified.

  1. Undersized

The placement group fewer copies than the configured pool replication level.

  1. Peered

The placement group has peered, but cannot serve client IO due to not having enough copies to reach the pool’s configured min_size parameter. Recovery may occur in this state, so the pg may heal up to min_size eventually.


Openstack taskflow介绍

前言

TaskFlow是OpenStack中的一个Python库,主要目的是让task(任务)执行更加容易可靠,能将轻量的任务对象组织成一个有序的流。 若未安装taskflow到环境中:

pip install taskflow

目前TaskFlow支持三种模式:

  • 线性:运行一个任务或流的列表,是一个接一个串行方式运行。
  • 无序:运行一个任务或流的列表,以并行的方式运行,顺序与列表顺序无关,任务之间不存在依赖关系。
  • 图:运行一个图标(组节点和边缘节点)之间组成的任务/流依赖驱动的顺序。

任务的状态

就像任何其他的任务流系统一样,每个任务都有一些状态:

  • PENDING
  • RUNNING
  • SUCCESS
  • FAILURE

你也可以创建自定义的状态。

举个例子

线性:

#!/usr/bin/env python
# coding=utf-8

from __future__ import print_function
import taskflow.engines
from taskflow.patterns import linear_flow as lf
from taskflow import task


class A(task.Task):
    def execute(self, a_msg, *args, **kwargs):
        print('A : {}' . format(a_msg))


class B(task.Task):
    def execute(self, b_msg, *args, **kwargs):
        print('B : {}' . format(b_msg))

flow = lf.Flow('simple-linear-listen').add(
    A(),
    B()
    )

engine = taskflow.engines.load(flow, store=dict(a_msg='a', b_msg='b'))
engine.run()

Output:

A : a
B : b

说明:A任务永远都会在B任务之前。 检查任务状态 修改代码:

#!/usr/bin/env python
# coding=utf-8

from __future__ import print_function
import taskflow.engines
from taskflow.patterns import linear_flow as lf
from taskflow import task


def flow_watch(state, details):
    print("Flow State:{}".format(state))
    print("Flow Details:{}".format(details))


class A(task.Task):
    def execute(self, a_msg, *args, **kwargs):
        print('A:{}' . format(a_msg))


class B(task.Task):
    def execute(self, b_msg, *args, **kwargs):
        print('B:{}' . format(b_msg))

flow = lf.Flow('simple-linear-listen').add(
    A(),
    B()
    )

engine = taskflow.engines.load(flow, store = dict(a_msg = 'a', b_msg = 'b'))
engine.notifier.register('*', flow_watch)
engine.run()

注册了一个监听器将报告给flow_wtach函数。 Output:

Flow State:RUNNING
Flow Details:{'engine': <taskflow.engines.action_engine.engine.SerialActionEngine object at 0x0333A2D0>, 'old_state': 'PENDING', 'flow_name': u'simple-linear-listen', 'flow_uuid': '59385218-0fc8-4566-a308-17b0d69cf8b2'}
A:a
B:b
Flow State:SUCCESS
Flow Details:{'engine': <taskflow.engines.action_engine.engine.SerialActionEngine object at 0x0333A2D0>, 'old_state': 'RUNNING', 'flow_name': u'simple-linear-listen', 'flow_uuid': '59385218-0fc8-4566-a308-17b0d69cf8b2'}

当流状态发生改变,就会被捕捉到,若只监听流状态,也可以改为:

engine.notifier.register('SUCCESS', flow_watch)

也可以做到监听任务:

任务异常

在一组任务中,若其中一个发生异常,流的任务失败,就需要处理异常工作:

#!/usr/bin/env python
# coding=utf-8

from __future__ import print_function
import taskflow.engines
from taskflow.patterns import linear_flow as lf
from taskflow import task


class A(task.Task):
    def execute(self, a_msg, *args, **kwargs):
        print('A : {}' . format(a_msg))

    def revert(self, a_msg, *args, **kwargs):
        print('A {} revert' . format(a_msg))


class B(task.Task):
    def execute(self, b_msg, *args, **kwargs):
        print('B : {}' . format(b_msg))

    def revert(self, b_msg, *args, **kwargs):
        print('B {} revert' .format(b_msg))


class C(task.Task):
    def execute(self, c_msg, *args, **kwargs):
        print('C : {}' . format(c_msg))
        raise IOError('C IOError')


flow = lf.Flow('simple-linear-listen').add(
    A(),
    B(),
    C()
    )

engine = taskflow.engines.load(flow, store = dict(a_msg = 'a', b_msg = 'b',c_msg = 'c'))
try:
    engine.run()
except Exception as e:
    print("flow failed:{}" .format(e))

Output:

A : a
B : b
C : c
B b revert
A a revert
flow failed:C IOError

说明,如果出现异常,会执行revert函数进行清理工作。 相关链接:TaskFlow维基


通过命令行修改vcenter server ip的方法

When deploying the VMware vCenter Server Appliance (VCSA) it will default look for a DHCP address. When there is no DHCP server available the following error is displayed:

NO NETWORKING DETECTED.

no_networking_detected

it is possible to manually configure a static IP address by using the command line. Here are the steps:

  • Open a console session of the VCSA
  • Login as: root
  • Default password is: vmware
  • Execute the following command:
$ /opt/vmware/share/vami/vami_config_net

After executing the command, a menu is displayed. Within the menu It is possible to change the IP address, hostname, DNS, Default gateway and proxy server.

vami_config_net

After allocate a static IP Address to the VCSA the post configuration can be done by using the following URL: https://static-ip-address:5480

原文链接


拷贝Source Insight中的彩色代码的方法

将Source Insight 的代码着色拷贝出来(到Word里面) /copy the colorful code in Source Insight

the only method is

  1. open the file with Source Insight

  2. make sure you have installed a PDF maker tool ,such as Adobe Acrobat 7.0 Professional or JAWS PDF Creator or PDF Factory ,then you can get a virtual PDF Printer . ^_^

so ,Select File -> Print ,then select the “Adobe PDF” as the printer ,click OK .then you will get a PDF format file with the colorful code ! so you can copy them as you want ! ^_^

but in the end ,I find some problem of this method.

the outcome is that we can see colorful code in PDF file,but when we copy them to other place ,such as Word or the current clipboard ,the color is disapear !!! feel sad ….

【将Source Insight里面着色代码拷贝到Word里面的方法】

其实方法很简单,我的是英文版的Source Insight,

用Source Insight打开文件后,去File->Print,然后在 常规->选择打印机中,选择“Foxit Reader PDF Printer”,然后就可以确定,就可以输出一个pdf文件了,比如此处的part_dos.c变成了part_dos.pdf,

然后,去pdf文件里面复制代码,粘贴到word里面,就是可以保留颜色的代码了。

原文链接: http://www.crifan.com/source_insight_copy_the_color_code_in_the_method_%7C_source_insight_will_copy_the_color_code_out_to_the_word_inside_approach/


Ceph源代码目录结构详解

从GitHub上Clone的Ceph项目,其目录下主要文件夹和文件的内容为:

根目录

  • [src]:各功能某块的源代码
  • [qa]:各个模块的功能测试(测试脚本和测试代码)
  • [wireshark]:#wireshark的ceph插件。
  • [admin]:管理工具,用于架设文档服务器等
  • [debian]:用于制作debian(Ubuntu)安装包的相关脚本和文件
  • [doc]:用于生成项目文档,生成结果参考http://ceph.com/docs/master/
  • [man]:ceph各命令行工具的man文件
  • configure.ac:用于生成configure的脚本
  • Makefile.am:用于生成Makefile的脚本
  • autogen.sh:负责生成configure。
  • do_autogen.sh:生成configure的脚本,实际上通过调用autogen.sh实现
  • ceph.spec.in:RPM包制作文件

src目录

  • [include]:头文件,包含各种基本类型的定义,简单通用功能等。
  • [common]:共有模块,包含各类共有机制的实现,例如线程池、管理端口、节流阀等。
  • [log]:日志模块,主要负责记录本地log信息(默认/var/log/ceph/目录)
  • [global]:全局模块,主要是声明和初始化各类全局变量(全局上下文)、构建驻留进程、信号处理等。
  • [auth]:授权模块,实现了三方认知机制。
  • [crush]:Crush模块,Ceph的数据分布算法
  • [msg]:消息通讯模块,包括用于定义通讯功能的抽象类Messenger以及目前的实现SimpleMessager
  • [messages]:消息模块,定义了Ceph各节点之间消息通讯中用到的消息类型。
  • [os]:对象(Object Store)模块,用于实现本地的对象存储功能,
  • [osdc]:OSD客户端(OSD Client),封装了各类访问OSD的方法。
  • [mon]:mon模块
  • [osd]:osd部分
  • [mds]:mds模块
  • [rgw]:rgw模块的
  • [librados]:rados库模块的代码
  • [librdb]:libbd库模块的代码
  • [client]:client模块,实现了用户态的CephFS客户端
  • [mount]:mount模块
  • [tools]:各类工具
  • [test]:单元测试
  • [perfglue]:与性能优化相关的源代码
  • [json_spirit]:外部项目json_spirit
  • [leveldb]:外部项目leveldb from google
  • [gtest]:gtest单元测试框架
  • [doc]:关于代码的一些说明文档
  • [bash_completion]:部分bash脚本的实现
  • [pybind]:python的包装器
  • [script]:各种python脚本
  • [upstart]:各种配置文件

qemu-img resize yourname.img +10G 

首先要用命令增加分区大小,针对qemu-kvm使用以上命令

LVM

情境描述:虚拟机用的磁盘 image 已经扩容,或对应于物理机的话,就是磁盘的容量已经增加了。 然后我们希望把扩大的容量用起来,而且不影响现有的文件系统(不格盘)。

实际使用过程中,我们有时候需要对虚拟机镜像的硬盘扩容,比如,一开始我们创建虚拟机的时候,以为 20G 的磁盘空间就够了,可某一次我们可能一次性就要拷贝一个 10G+ 的文件进虚拟机,这时候我们就傻了。

我们通过 VMware 或者 VirtualBox 的图形界面或者一些命令,我们可以很轻松地扩大虚拟机的磁盘大小,但是,磁盘变大后,系统并不会把它们利用起来。所以这时候,我们就要考虑怎么才能让这些多出来的空间能够被虚拟机里的 Linux 系统用起来。

在此之前,先补充一个“磁盘 MBR”的知识[1]

1个硬盘分为两个区域,一个是 MBR(主引导分区),一个是数据区域。

MBR 里记录了两个重要信息:引导程序与磁盘分区表。

分区表定义了“第 n 个磁盘块是从第 x 个柱面到第 y 个柱面”,所以,系统每次都取 n 号磁盘块时,就只会读取第 x 到第 y 个扇区之间数据。

由于 MBR 容量有限,设计的时候,只设计成4个分区记录。用起来,可以作4个主分区,或者3个主分区和一个扩展分区。

如果超过四个分区,系统允许在额外的硬盘空间放另一份磁盘分区信息,那就是扩展分区,当硬盘被分出一个扩展分区的时候,实际上扩展分区在 MBR 磁盘分区表中的信息为另外那份分区表的位置。所以,在 扩展分区 里面还要划分 逻辑分区 才能使用。

每个硬盘最多只允许4个主分区,其他的分区只能放在扩展分区中。

这样就明白了,因为主分区的个数有限,而且我们希望增加的容量也只是作为存储使用,所以加在拓展分区 (extended) 就可以了。(而如果你是土豪,总共4个主分区,你还打算这次再用一个主分区的名额,那你可以跳过 Part1,直接看下面的 Part2 了。)

PART1

我们要把增加的容量加在拓展分区(extended)里。要对 extended 分区进行扩容,这个 fdisk 就做不了,需要用 parted 命令(如果系统不自带 parted,那就从源上装一个):

parted /dev/xxx

进入交互模式,用 help 查看帮助命令。

一些值得特别说明的命令:

  • print 查看分区表。留意要操作的分区 ‘Number’ 这一项,后面操作要用到。
  • unit 改变 parted 所用的描述大小的默认单位(比如设为 ‘compact’ 就是以 ‘MB’ 为单位)。
    值得注意的是,如果用 MB/GB 这样的单位,磁盘 sector 的选取会有误差的。parted 会为你选最近的 sector,但未必精确。比如 unit 为 MB,那么可能产生 +-500KB 的误差;如果是 GB,那就可能 +-500MB 的误差,这就无法容忍了。所以如果是’创建分区’这样的操作,建议用 ‘MiB’ 这样的单位,而不是 ‘MB’。’MiB’ 会是一个精确值,parted 不会像对待 ‘MB’ 那样去找它最近的单元。
  • resize <minor> <start> <end> 对指定 minor 号(或 Number 号)的分区从 start 位置到 end 位置 这里 start/end 可以是 xxxMB,也可以是负值,表示从磁盘末尾往前多少的位置,比如 -0 就是指到磁盘的末尾。

更多命令详情请参考: http://www.gnu.org/software/parted/manual/html_chapter/parted_toc.html

实战:

操作前,print 结果如下。现有磁盘62.3G,只分给 extended 8G,还有50多G根本没分配。

Number  Start   End     Size    Type      File system  Flags
1      1049kB  256MB   255MB   primary   ext2         boot
2      257MB   8589MB  8332MB  extended
5      257MB   8589MB  8332MB  logical                lvm

我希望把这50多G全部用于扩大extended。

用命令:

resize 2 257MB -0

其实,只需输入 resize 2 ,回车,剩下的两个参数,parted 会通过交互的方式让你填写的。-0 表示到那个分区的磁盘末尾。

现在再 print 看一下,

Number  Start   End     Size    Type      File system  Flags
1      1049kB  256MB   255MB   primary   ext2         boot
2      257MB   62.3GB  62.0GB  extended
5      257MB   8589MB  8332MB  logical                lvm

extended 区已经扩大成功了。

extended 区只是相当于“一块物理硬盘”,想把增加出来的空间用上,还要把 Number 为 5 的 lv 扩大。

而 logic volumn 的扩大依赖于它所在的 volumn group 的大小。因为 logic volumn 是从 volumn group 里分出来的,如果 volumn group 不变大,那么 logic volumn 是无法超过 volumn group 的。所以 真正是应该把空间加到 volumn group 上去

PART2

要增加 volumn group 的大小,先用 fdisk 在 extended 上,利用刚才增加但还未分配出去的磁盘空间创建出一个新分区。通过 fdisk <disk_dev_name> 进入交互模式,可以通过命令 m 查看帮助。首先,输入 n 创建新分区,然后选择 l 设置新分区为逻辑分区,接下来依次设置分区的起始、终止位置(默认即完全利用这块磁盘上剩余的所有空间,所以默认即可)。创建出的分区,编号为 6。可以用命令 p 看一下。

   Device Boot      Start         End      Blocks   Id  System
/dev/vda1   *        2048      499711      248832   83  Linux
/dev/vda2          501758   121634815    60566529    5  Extended
/dev/vda5          501760    16775167     8136704   8e  Linux LVM
/dev/vda6        16777216   121634815    52428800   83  Linux

接下来,由于我们要用 LVM 来管理这个新分区,我们需要把新分区的管理系统从 Linux 改为 Linux LVM。在交互模式下,输入命令 t,然后选择刚才创建的 6,输入 8e (Linux LVM 的代号)。最后,我们要把刚才的这些操作真正写入硬盘,输入命令 w

至此,我们通过 fdisk -l 已经可以看到 /dev/vda6 被创建出来了。

再执行

vgextend <your_vg_name> /dev/vda6 

把新分区加进 volumn group (VG Name 可通过 vgdisplay 查到)。

现在用 vgs 查看 volumn group 的状态,发现 volumn group 已经变大。

  VG         #PV #LV #SN Attr   VSize  VFree
  jiang51-vg   2   2   0 wz--n- 57.75g 50.03g

然后把这个 volumn group 里面的 logic volumn 变大。

命令(最后那个’Logic Volumn name’可通过 lvdisplay 查到):

lvresize -l +100%FREE <Logic Volumn name>

警告: 如果操作时出现下面这样的 warning,就说明现在 logic volumn 的总大小还不对,resize 不但不增加空间,反而在缩小空间,如果继续操作下去,必将丢失数据。应立即停止!按 n 取消。

WARNING: Reducing active and open logical volume to 32.00 MiB
  THIS MAY DESTROY YOUR DATA (filesystem etc.)
Do you really want to reduce root? [y/n]

最后,要更新 logic volumn 上的文件系统,不然从 df 看出文件系统是不知道 logic volumn 变大的。

用命令(其中的 file_system_name 通过 df 找到):

resize2fs -p <file_system_name>

这样,磁盘 extended 分区的扩容终于完成了。


非LVM

fdisk /dev/sda
d
n
p
1

w
partprobe

# df -Th看磁盘分区类型如果是ext4使用命令
resize2fs /dev/sda1
# 如果是xfs使用命令
xfs_growfs /
df -Th
  • 最重要的一步:“删除现在的分区,重新分区” 按d删除现在的分区1,注意:删除后千万不要按w保存!直接按n创建新的分区,然后从原有的柱面开始,一直分到最后的尺寸(默认值两次回车即可,如果之前的分区不是从第一柱面开始,则需要记录之前分区的起始柱面),新的分区操作完毕后,按w保存。

URL

http://www.yunwei123.com/%E9%98%BF%E9%87%8C%E4%BA%91%E7%A3%81%E7%9B%98%E6%89%A9%E5%AE%B9%EF%BC%8D%E9%92%88%E5%AF%B9windowslinux%E6%97%A0%E6%8D%9F%E6%89%A9%E5%AE%B9%E5%88%86%E5%8C%BA%E5%A4%A7%E5%B0%8F%E6%96%B9%E6%B3%95/
http://askubuntu.com/questions/335401/how-to-change-partitions-on-ubuntu-virtual-machine


Epitome

I want to have os image that can login with password.

download UEC image. for example, this precise. http://cloud-images.ubuntu.com/precise/current/precise-server-cloudimg-amd64-disk1.img

this image was forbidden to password login feature in terminal console. it’s reasonable for use of cloud image.

but now, I test sample image for use of OpenStack. so, I create iamge that can login with password in terminal console.

Step

  • download image
    http://cloud-images.ubuntu.com/precise/current/precise-server-cloudimg-amd64-disk1.img
  • mount os image
    this image is qcow2 image. so, use qemu-nbd.
# modprobe nbd
# dmesg | grep nbd
nbd: registered device at major 43
# ls /dev/nbd*
/dev/nbd0  /dev/nbd10  /dev/nbd12  /dev/nbd14  /dev/nbd2  /dev/nbd4  /dev/nbd6  /dev/nbd8
/dev/nbd1  /dev/nbd11  /dev/nbd13  /dev/nbd15  /dev/nbd3  /dev/nbd5  /dev/nbd7  /dev/nbd9
# qemu-nbd --connect=/dev/nbd0 /home/ubuntu/os_images/precise-server-cloudimg-amd64-disk1.img
# mkdir /mnt/target_vm
# mount /dev/nbd0p1 /mnt/target_vm

chroot to qcow2 image

# chroot /mnt/target_vm/

change password for ubuntu user

root@ubuntu12:/# passwd ubuntu
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
root@ubuntu12:/#

terminate chroot

# exit

unmount qcow2 image

# cd /
# umount /mnt/target_vm
# sudo qemu-nbd --disconnect /dev/nbd0
/dev/nbd0 disconnected

test vm

kvm -drive file=./os_images/precise-server-cloudimg-amd64-disk1-custom.img -m 512 -boot d -vnc :0

access with vnc, and check that you can login with password!

URL

http://www.geocities.co.jp/SiliconValley/2994/tool/nvp.html http://www.postcard.st/nosuz/blog/2011/09/10-14


Ceph源码编译问题记录

代码下载

  1. 从官网下载源码 链接:http://download.ceph.com/tarballs/ceph-9.2.0.tar.gz
  2. 从github克隆代码 注意:从 https://github.com/ceph/ceph.git 上克隆下来的9.2.0代码有问题。 我遇到的问题是个语法错误:
    make 时报错: ./include/rados/memory.h:1:1: error: expected unqualified-id before ‘.’ token
    如果从官网上直接下载压缩包是没问题的,但是压缩包内缺少一个文件夹debian,这个文件夹在安装依赖包的时候有用,还有打包发布也有用,所以我们要把上面两个源码包都下载下来然后手动拷贝ceph/src/debian文件夹到ceph-9.2.0相同目录下面。

如何编译

tar xvf ceph-9.2.0.tar.gz
cd ceph-9.2.0
./install-deps.sh
./configure
make -j4
make install

问题1

A compiler with support for C++11 language features is required. 重新安装高版本的gcc,且版本必须要 > 4.6 以上 gcc版本必须要 >4.6以上,由于我的系统安装了两个版本的gcc(4.3和4.8),所以我需要删除旧版本的快捷方式,重新建立gcc g++的路径

rm /usr/bin/cc
rm /usr/bin/gcc
rm /usr/bin/c++
rm /usr/bin/g++
ln -s /usr/bin/gcc-4.8 /usr/bin/gcc
ln -s /usr/bin/gcc-4.8 /usr/bin/cc
ln -s /usr/bin/g++-4.8 /usr/bin/g++
ln -s /usr/bin/g++-4.8 /usr/bin/c++

apt-get install gcc g++

问题2

checking for snappy_compress in -lsnappy… no

zypper ar http://download.opensuse.org/repositories/devel:/languages:/erlang/SLE_11_SP4/devel:languages:erlang.repo
zypper in snappy snappy-devel

问题3

./configure --without-cryptopp --without-tcmalloc --without-libxfs
configure: error: libleveldb not found
#install leveldb
wget https://leveldb.googlecode.com/files/leveldb-1.14.0.tar.gz
tar zxvf leveldb-1.14.0.tar.gz
cd leveldb-1.14.0
make
cp libleveldb.* /usr/lib
cp -r include/leveldb /usr/local/include/

问题4

no suitable crypto library found

解决方法 apt-get install libnss3-1d-dev

zypper in mozilla-nss-devel

问题5

make 
报错:
./include/rados/memory.h:1:1: error: expected unqualified-id before ‘.’ token
 ../memory.h

vi src/include/rados/memory.h vi src/include/rados/buffer.h 将 ../memory.h 改成 #include “../memory.h” 将 ../buffer.h 改成 #include “../buffer.h”

问题6

failed to run libtoolize: No such file or directory

apt-get install libtool

问题7

autogen.sh > aclocal not found)

apt-get install automake

问题8

configure: error: libsnappy not found

apt-get install libsnappy-dev

问题9

configure: error: libleveldb not found

apt-get install libleveldb-dev

问题10

configure: error: blkid/blkid.h not found (libblkid-dev, libblkid-devel)

apt-get install libblkid-dev

问题11

configure: error: libudev.h not found (libudev-dev, libudev-devel)

apt-get install libudev-dev

问题12

configure: error: libkeyutils not found (libkeyutils-dev, keyutils-libs-devel)

apt-get install libkeyutils-dev

问题13

configure: error: no FUSE found (use –without-fuse to disable)

apt-get install fuse libfuse-dev

问题14

configure: error: no tcmalloc found (use –without-tcmalloc to disable)

apt-get install google-perftools libgoogle-perftools-dev 

问题15

configure: error: no libatomic-ops found (use –without-libatomic-ops to disable)

apt-get install libatomic-ops-dev 

问题16

configure: error: libaio not found

apt-get install libaio-dev

问题17

configure: error: xfs/xfs.h not found (–without-libxfs to disable)

apt-get install xfslibs-dev

问题18

/usr/bin/ld: cannot find -ledit

apt-get install libedit-dev

问题17

No matching distribution found for setuptools

apt-get install python-pip

问题19

root@ubuntu:/work/ceph-9.2.0# ceph -s OSError: librados.so.2: cannot open shared object file: No such file or directory

ldconfig

运行ceph服务端

可以执行以下命令部署一个开发模式的ceph集群

cd src
install -d -m0755 out dev/osd0
./vstart.sh -n -x -l
# check that it's there
./ceph health