kolla-ceph 2: 修复一些部署中遇到的问题

系列链接

  1. https://www.jianshu.com/p/f18a1b3a4920 如何用kolla来部署容器化ceph集群
  2. https://www.jianshu.com/p/a39f226d5dfb 修复一些部署中遇到的问题
  3. https://www.jianshu.com/p/d520fed237c0 在kolla ceph中引入device classes特性
  4. https://www.jianshu.com/p/d6e047e1ad06 支持bcache设备和iscsi及多路径设备
  5. 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是集群里最怕重启的组件.

你可能感兴趣的:(kolla-ceph 2: 修复一些部署中遇到的问题)