系列链接
- https://www.jianshu.com/p/f18a1b3a4920 如何用kolla来部署容器化ceph集群
- https://www.jianshu.com/p/a39f226d5dfb 修复一些部署中遇到的问题
- https://www.jianshu.com/p/d520fed237c0 在kolla ceph中引入device classes特性
- https://www.jianshu.com/p/d6e047e1ad06 支持bcache设备和iscsi及多路径设备
- https://www.jianshu.com/p/ab8251fc991a 一个ceph容器化部署编排项目
本篇介绍一些对kolla ceph中遇到的问题的修复.
--limit bug修复
commit url : https://review.openstack.org/#/c/648576/
在上篇的部署中我们提到可以使用--limit来限制部署的节点,如果你的mon和osd节点是分开的,你可以用limit特性来对特定节点的服务进行部署. --limit后面既可以跟具体节点名,也可以使用group名切片的方式,例如storage-osd[0:1], 意思是包括storage-osd组中的前两个节点.
比如我们只想修复node3节点的osd,就可以用--limit属性
kolla-ansible/tools/kolla-ansible deploy --configdir ceph-test -i ceph-test/multinode-inventory --passwords ceph-test/passwords.yml --tags ceph -e openstack_release=cephRocky-7.0.2.0002 --limit ceph-node3
但是kolla-ansible会报错
TASK [ceph : Fetching Ceph keyrings] ***************************************************************************************************************************************
fatal: [ceph-node3]: FAILED! => {"failed": true, "msg": "'delegate_host' is undefined"}
出错的任务如下:
- name: Fetching Ceph keyrings
command: docker exec ceph_mon fetch_ceph_keys.py
register: ceph_files_json
changed_when: (ceph_files_json.stdout | from_json).changed
failed_when: (ceph_files_json.stdout | from_json).failed
delegate_to: "{{ delegate_host }}"
run_once: True
因为delegate_host需要在mon节点读取,而这次部署只有osd节点,所以该值为空,简单的调整如下:
- name: Fetching Ceph keyrings
command: docker exec ceph_mon fetch_ceph_keys.py
register: ceph_files_json
changed_when: (ceph_files_json.stdout | from_json).changed
failed_when: (ceph_files_json.stdout | from_json).failed
delegate_to: "{{ delegate_host if delegate_host is defined else groups['ceph-mon'][0] }}"
run_once: True
如果delegate_host未定义就去第一个mon节点获取.
但是这种做法有个问题,当第一个mon节点需要重装的时候, 如果使用--limit ceph-node1, 那么 delegate_host 是已定义但是None的状态,然后groups['ceph-mon'][0]的mon也是无法工作的.
所以最好是循环检测一下所有mon的状态, 如下:
---
- name: Running ceph mon check
become: true
shell: docker exec ceph_mon ceph health --connect-timeout 1
delegate_to: "{{ item }}"
register: ceph_mon_check
changed_when: False
failed_when: False
with_items: "{{ groups['ceph-mon'] }}"
run_once: true
- name: Registering active mon
set_fact:
active_mon: "{{ item.1 }}"
when: item.0.rc is defined and item.0.rc == 0
run_once: true
with_together:
- "{{ ceph_mon_check.results }}"
- "{{ groups['ceph-mon'] }}"
通过这个active_mon我们可以知道集群有没有运行的mon, 第一个mon节点除了集群初始化的时候生成keyring以外,完全可以把它当成普通的mon一样来部署修复.
- 修复获取keyrings的任务
- name: Fetching Ceph keyrings
command: docker exec ceph_mon fetch_ceph_keys.py
register: ceph_files_json
changed_when: (ceph_files_json.stdout | from_json).changed
failed_when: (ceph_files_json.stdout | from_json).failed
delegate_to: "{{ active_mon if active_mon is defined else delegate_host }}"
run_once: True
这样就解决了kolla部署ceph必须包含mon节点的问题, 我们可以用--limit来限制任何要部署的节点.
- kolla中第一个mon的修复问题
kolla中, groups['ceph-mon'][0]
是个特殊的mon,当检测到该节点并没有对应的卷(ceph_mon_config)之后, 并且其他mon节点也没有对应卷,会启动generate_cluster.yml任务, 重新生成新的keyring和monmap. 所以对于一个部署好的集群, 如果第一个mon有问题, 需要重新安装mon的时候, 是不能直接用kolla加 --limit来修复的, 使用--limit 后kolla部署的第一个mon节点无法加入旧的集群中.
有了以上的mon检测任务, 这个问题就很好解决了,当有运行的mon的时候, 就不用启动这个任务. 修改如下:
- include_tasks: generate_cluster.yml
when:
- delegate_host == 'None' and inventory_hostname == groups['ceph-mon'][0]
- active_mon is not defined
这样我们可以直接用kolla来修复mon.
mon部署问题
commit url : https://review.openstack.org/#/c/652606/
kolla的mon服务启动的时候很奇怪, 当我使用3个mon的时候, 有时候会卡住, 有时候不卡, 这个卡就是所有mon一直处在选举的状态,ceph集群无法访问.当尝试4个5个mon的时候会百分百卡住.
我对比了kolla的mon启动脚本与ceph-deploy的部署日志.
先看kolla的关于mon初始化的脚本:
if [[ ! -e "${MON_DIR}/keyring" ]]; then
KEYRING_TMP="/tmp/ceph.mon.keyring"
# Generate keyring for current monitor
ceph-authtool --create-keyring "${KEYRING_TMP}" --import-keyring "${KEYRING_ADMIN}"
ceph-authtool "${KEYRING_TMP}" --import-keyring "${KEYRING_MON}"
mkdir -p "${MON_DIR}"
ceph-mon --mkfs -i "${HOSTNAME}" --monmap "${MONMAP}" --keyring "${KEYRING_TMP}"
rm "${KEYRING_TMP}"
fi
重点是mkfs这句, 不管任何情况下都是用monmap, 关键是这个monmap也不全,只有第一个节点.
再看ceph-deploy的部署日志:
[root@ceph-node1 ceph-deploy]# ceph-deploy mon create-initial
...
[ceph-node1][INFO ] Running command: ceph-mon --cluster ceph --mkfs -i ceph-node1 --keyring /var/lib/ceph/tmp/ceph-ceph-node1.mon.keyring --setuser 167 --setgroup 167
...
[ceph-node2][DEBUG ] create the monitor keyring file
[ceph-node2][INFO ] Running command: ceph-mon --cluster ceph --mkfs -i ceph-node2 --keyring /var/lib/ceph/tmp/ceph-ceph-node2.mon.keyring --setuser 167 --setgroup 167
###############分割线,添加mon#############
[root@ceph-node1 ceph-deploy]# ceph-deploy mon add ceph-node3
...
[ceph-node3][INFO ] Running command: ceph-mon --cluster ceph --mkfs -i ceph-node3 --monmap /var/lib/ceph/tmp/ceph.ceph-node3.monmap --keyring /var/lib/ceph/tmp/ceph-ceph-node3.mon.keyring --setuser 167 --setgroup 167
ceph-deploy中,当集群初始化的时候, 所有mon节点都不使用monmap, 只有当新加节点的时候才添加monmap.
所以修改kolla的初始化mon脚本如下:
if [[ ! -e "${MON_DIR}/keyring" ]]; then
KEYRING_TMP="/tmp/ceph.mon.keyring"
# Generate keyring for current monitor
ceph-authtool --create-keyring "${KEYRING_TMP}" --import-keyring "${KEYRING_ADMIN}"
ceph-authtool "${KEYRING_TMP}" --import-keyring "${KEYRING_MON}"
mkdir -p "${MON_DIR}"
mon_stat=$(ceph mon stat --connect-timeout 1 || true)
mon_check=$(echo $mon_stat | awk '/mons/{print $0}' | wc -l)
if [[ ${mon_check} -eq 0 ]]; then
ceph-mon --mkfs -i "${HOSTNAME}" --keyring "${KEYRING_TMP}"
else
MONMAP_TMP="/tmp/ceph.${HOSTNAME}.monmap"
ceph mon getmap -o "${MONMAP_TMP}"
ceph-mon --mkfs -i "${HOSTNAME}" --monmap "${MONMAP_TMP}" --keyring "${KEYRING_TMP}"
rm "${MONMAP_TMP}"
fi
rm "${KEYRING_TMP}"
fi
首先检查集群的状态,如果命令有内容输出,那么肯定有一半多mon在运行,这个时候就需要获取monmap并使用.反之,就认为是集群初始化,不使用monmap.
这个修改和上面的修改搭配,基本可以应对mon的问题.
服务单独指定问题
commit url : https://review.opendev.org/#/c/648626/
我们的ceph集群有很多服务,如果只有某一项服务发生了变化,我们只想改动这个服务对应的容器,那么怎么避免其他的服务发生变化就是需要解决的问题.
这个问题最常见的就是某个osd出现问题了,我们要修复这个osd, 虽然可以用--limit来指定节点,但是如果该节点有其他服务,比如mon/mds/mgr这些,那们如何只让程序对osd进行处理:
首先我们可以在all.yml中定义一个字典:
ceph_install_daemons:
mon-daemon: "yes"
mgr-daemon: "yes"
osd-daemon: "yes"
rgw-daemon: "yes"
mds-daemon: "yes"
nfs-daemon: "yes"
注意和enable来区分,enable某个服务是指这个ceph集群中启用了这个服务,而ceph_install_daemons中包含的项是你这次部署具体想改动的项.默认的包含所有服务,如果你只想改动osd,那么在globals.yml修改一下:
ceph_install_daemons:
mon-daemon: "no"
mgr-daemon: "no"
osd-daemon: "yes"
rgw-daemon: "no"
mds-daemon: "no"
nfs-daemon: "no"
这样我们就达到了只改动osd的目的.
当然,在kolla-ansible中你要修改一下ceph的deploy.yml:
---
- include_tasks: config.yml
- include_tasks: bootstrap_mons.yml
when:
- inventory_hostname in groups['ceph-mon']
- ceph_install_daemons['mon-daemon'] | bool
- include_tasks: distribute_keyrings.yml
- include_tasks: start_mons.yml
when:
- inventory_hostname in groups['ceph-mon']
- ceph_install_daemons['mon-daemon'] | bool
- include_tasks: start_mgrs.yml
when:
- inventory_hostname in groups['ceph-mgr']
- ceph_install_daemons['mgr-daemon'] | bool
- include_tasks: start_ceph_dashboard.yml
when:
- enable_ceph_dashboard | bool
- inventory_hostname in groups['ceph-mon']
- include_tasks: start_nfss.yml
when:
- enable_ceph_nfs | bool
- inventory_hostname in groups['ceph-nfs']
- ceph_install_daemons['nfs-daemon'] | bool
- name: configuring client.admin caps
kolla_ceph_keyring:
name: client.admin
caps: "{{ ceph_client_admin_keyring_caps }}"
run_once: True
delegate_to: "{{ groups['ceph-mon'][0] }}"
- include_tasks: bootstrap_osds.yml
when:
- inventory_hostname in groups['ceph-osd']
- ceph_install_daemons['osd-daemon'] | bool
- include_tasks: start_osds.yml
when:
- inventory_hostname in groups['ceph-osd']
- ceph_install_daemons['osd-daemon'] | bool
- include_tasks: start_rgws.yml
when:
- enable_ceph_rgw | bool
- inventory_hostname in groups['ceph-rgw']
- ceph_install_daemons['rgw-daemon'] | bool
- include_tasks: start_rgw_keystone.yml
when:
- enable_ceph_rgw_keystone | bool
- inventory_hostname in groups['ceph-rgw']
- ceph_install_daemons['rgw-daemon'] | bool
- include_tasks: start_mdss.yml
when:
- enable_ceph_mds | bool
- inventory_hostname in groups['ceph-mds']
- ceph_install_daemons['mds-daemon'] | bool
upgrade.yml和reconfigure.yml也要做类似的修改.
有了这个选项, 再加上--limit, 你就可以把改动限制到某个节点上具体的某个服务上,不用担心影响全局的ceph集群.
crush map change
commit url : https://review.opendev.org/#/c/647664/
在kolla中, 当进行osd的bootstrap的时候, 会先将host对应的bucket移动到default下面. 所以, 如果你安装了一个ceph集群, 然后修改了对应的crush map, 比如把host移动到rack下面, 这时问题来了:当集群中的一个osd损坏之后, 如果用kolla重新部署, 那么对应的host又被移动到default下面.
ceph osd crush add-bucket "${host_bucket_name}" host
ceph osd crush move "${host_bucket_name}" root=${CEPH_ROOT_NAME:-default}
# Adding osd to crush map
ceph osd crush add "${OSD_ID}" "${OSD_INITIAL_WEIGHT}" host="${HOSTNAME}${CEPH_ROOT_NAME:+-${CEPH_ROOT_NAME}}"
解决问题的办法就是, 添加一个判断, 如果osd对应的host已经在crush map中,就不移动host对应的bucket.
host_bucket_name="${HOSTNAME}${CEPH_ROOT_NAME:+-${CEPH_ROOT_NAME}}"
host_bucket_check=$(ceph osd tree | awk '/'"${host_bucket_name}"'/{print $0}' | wc -l)
if [[ "${host_bucket_check}" -eq 0 ]]; then
ceph osd crush add-bucket "${host_bucket_name}" host
ceph osd crush move "${host_bucket_name}" root=${CEPH_ROOT_NAME:-default}
fi
# Adding osd to crush map
ceph osd crush add "${OSD_ID}" "${OSD_INITIAL_WEIGHT}" host="${HOSTNAME}${CEPH_ROOT_NAME:+-${CEPH_ROOT_NAME}}"
mon和osd使用hostname
commit url : https://review.opendev.org/654417
用kolla部署的mon和osd, 名称默认都是IP, 有时候我们想部署成hostname为主的集群,这样在维护的时候可以更直观的知道是哪台节点.
我的修改中增加了对hostname的支持, 用法很简单, 在globals.yml中定义如下变量即可.
ceph_mon_host_type: "HOSTNAME"
ceph_osd_host_type: "HOSTNAME"
NOTE: 不要对已经部署好的集群进行如下操作, 这样会导致旧的集群mon丢失, 因为mon在初始化的时候是以mon对应的节点名称命名的,之前使用IP创建的, 现在改成hostname会造成db丢失. 对osd的影响则是如果修复之前损坏的osd,则会修改之前节点的crush map.
总结
通过以上的修复,我们可以做到针对某一个ceph组件具体到某一个节点上的修改.这个粒度其实还不够小, 后面我会讲一下如何避免同一节点上多个osd节点的重启. 毕竟osd是集群里最怕重启的组件.