美文网首页DevOps
软件配置管理实践——基于Ansible和Nacos

软件配置管理实践——基于Ansible和Nacos

作者: 翟志军 | 来源:发表于2021-04-13 21:10 被阅读0次

    配置管理的预备知识

    在《持续交付》第二章的小结中,作者写道:配置管理是本书其他内容的基础。没有配置管理,根本谈不上持续集成、发布管理以及部署流水线。

    可以看出,配置的管理是多么的重要。在小结中,作者使用以下几个问题来判断你的配置管理做得是否好:

    • 是否仅依靠保存于版本控制系统的数据(除了生产数据),就可以从无到有重建生产系统?
    • 是否可以将应用程序回滚到以前某个正确的状态?
    • 是否确保在测试、试运行和正式上线时以同样的方式创建部署环境?

    如果你的配置管理流程比较好的话,所有的问题都应该是肯定的。

    经过笔者长时间的一线配置管理的经验,要想更好实践《持续交付》中的配置管理,笔者认为配置管理中心应该能做到:

    1. 配置项的定义与配置的存储的解耦。比如应用可能会从Nacos中读取,也可以从环境变量里读取。
    2. 配置项之间的相互引用:消除重复配置项。比如MySQL的连接字符串,可能同时在应用a的配置和应用b的配置中出现。
    3. 配置项版本化:配置项变更跟踪;
    4. 配置项的定义与配置的格式的解耦。比如同一个配置项在应用a使用的是XML格式,但是应用b使用的是YAML格式。
    5. 配置项的自动化校验:当用户引入一个错误的配置项时,我们应该可以知道。
    6. 配置项的作用域功能。
    7. 配置项分组功能:比如按环境对配置项进行分组。
    8. 配置项加密功能。

    目前没有一个配置中心能实现以上所有的功能。但是,基于Ansible再配合其它工具,我们可以很低成本地实现以上大部分功能,虽然有些粗糙。

    基于Ansible和Nacos的实践

    本文以Ansible结合Nacos为例介绍如何更好的进行配置管理。

    在本案例中,Ansible担任了配置中心的角色,实现配置项的定义,描述配置项与配置格式(XML、YAML、Properties)之间关系,描述配置项与配置存储(Nacos)之间的关系。Nacos纯粹只是一个存储配置的角色。

    Ansible介绍

    Ansible通常被认为是一款自动化运维工具。用户通过YAML文件描述机器或者应用的最终状态,Ansible负责实现该状态。它通过Python技术栈的Jinja2模板引擎实现了我们所需要的绝大部分配置管理功能。所以,基于它结合Jenkins这类自动化平台即可实现一个低成本的灵活的配置管理中心。

    说回来,配置中心的实现的核心其实是文本模板引擎。


    ansible.png

    Nacos介绍

    nacos.png

    Nacos是阿里巴巴开源的一个动态服务发现、配置管理和服务管理平台。不过,它的配置管理功能离上文谈到的配置中心还差太远。但是至少作为配置存储的实现,还是可以的。

    说回来,由于我们的配置管理是基于Ansible的,所以,按道理,我们是可以很容易切换配置中心的。

    整体架构

    art.png

    通过Ansible的配置功能,我能实现大部分配置中心应该实现的功能。但是,我们的应用程序不会直接读取Ansible工程中的配置,而是从Nacos中读,所以,我们通过一个Ansible任务自动化将配置发布到Nacos中。最后,再结合Jenkins实现流水线。

    Ansible工程的准备

    实际工作中,我们会为每个环境创建一个Git工程项目。这个Git工程项目是一个典型的Ansible工程结构,如下:

    .
    ├── Jenkinsfile.groovy
    ├── README.md
    ├── group_vars
    │   ├── all
    │   │   ├── 01.all.yaml
    │   │   ├── 02.nacos-config.yaml
    │   │   └── app-config
    │   │       ├── demo.json.yaml
    │   │       ├── application.yaml.yaml
    │   │       ├── demo.properties.yaml
    ├── host_vars
    ├── hosts
    └── playbook-nacos.yaml
    

    如果我们要对比不同环境的配置,只需要通过Beyond compare这类工具就可以实现。就这样可以低成本的实现了一个大的配置管理平台应该有的功能。

    以下是各个文件的介绍:

    group_vars目录是Ansible的约定目录,用于存放组级别的配置变量及全局配置变量。我们在group_vars目录下建立一个all/app-config子目录,子目录中存储各种类型配置,是为了人类能更好查找到自己的配置。all/app-config子目录下,每一个YAML文件中只定义一个全局配置。这里实现了配置格式与配置项定义的解耦。这是整个方案中,最难理解的地方。

    group_vars/all目录下的文件示例:

    demo.json.yaml

    domo_json_nacos_config: |
        {
            "message": "hello json"
        }
    

    application.yaml.yaml

    application_yaml_nacos_config: |
        ---
        server: 
            port: 8080
    

    demo.properties.yaml

    demo_properties_nacos_config: |
        spring.datasource.url={{ global_db.url }}
    

    注意,global_db.url是全局级别的配置项,它的值可以被多个配置文件引用。

    02.nacos_config.yaml

    此文件用于定义Nacos中的配置文件与我们的Ansible之间的关系。nacos_config配置项列表中的每一项都是一个Nacos更新配置的REST API的一次请求。只不过,我们通过YAML实现声明式了。

    nacos_config:
      - dataId: demo.json
        group: demoGroup
        type: json
        content: "{{ demo_json_nacos_config | from_json | to_nice_json(indent=2) }}"
        tenant: "{{ nacos_namespace }}"
    
      - dataId: application.yaml.yaml
        group: demoGroup
        type: yaml
        content: "{{ application_yaml_nacos_config}}"
        tenant: "{{ nacos_namespace }}"
    
      - dataId: demo.properties
        group: demoGroup
        type: properties
        content: "{{ demo_properties_nacos_config }}"
        tenant: "{{ nacos_namespace }}"
    

    Nacos提供了更新配置的REST API如下:

    curl -X POST 'http://127.0.0.1:8848/nacos/v1/cs/configs' \
    -d 'dataId=nacos.example&group=com.alibaba.nacos&content=contentTest'
    

    我们现在的问题就是如何将nacos_config.yaml中的配置变成请求Nacos REST API。有两个方案:

    • 方案一:在Jenkins pipeline中,使用groovy语言读取nacos_config.yaml文件,然后拼装REST请求。
    • 方案二:写一个Ansible任务,它会读取nacos_config.yaml的变量,然后通过Ansible的uri模块发起REST请求。

    我们选择了方案二。因为方案二能做到与Jenkins的解耦。将来,我们切换到其它的自动化平台,难度不会太大。而且方案二能让我们脱离自动化平台,在本地手工执行。

    以下就是我们的Ansible任务的写法:

    ---
    - name: "nacos config deploy"
      uri:
        url: "http://{{nacos_server_addr}}/nacos/v1/cs/configs"
        method: POST
        body_format: "raw"
        return_content: yes
        body:
          tenant: "{{item.tenant | urlencode}}"
          group: "{{item.group|urlencode}}"
          dataId: "{{item.dataId | urlencode}}"
          type: "{{item.type | urlencode}}"
          content: "{{item.content | urlencode}}"
        status_code: 200
        headers:
          content_type: "Content-Type:application/x-www-form-urlencoded; charset=UTF-8"
      with_items: "{{nacos_config}}"
      when: nacos_config is defined
      no_log: "{{ansible_global_no_log | default(True)}}"
     
    

    这里有一个Nacos的坑,1.x的版本下,它只支持urlencode的方式。如果配置文件太大,会请求失败。简单看了下Nacos2.x,REST API似乎没什么变化。

    结合Jenkins的流水线

    当Ansible工程准备好之后,剩下的问题就是如何自动化执行Ansible。这是Jenkins的强项,就很容易做到了。以下是核心代码:

    container('ansible') {
        withCredentials([file(credentialsId: "ansible-vault", variable: 'vaultFile'),
                         sshUserPrivateKey(credentialsId: "ssh-keyfile", keyFileVariable: 'keyfile')
        ]) {
            sh """
                ansible-playbook -i ./hosts \
                --vault-password-file ${env.vaultFile} \
                --extra-vars 'ansible_ssh_private_key_file=${keyfile}' \
                ${WORKSPACE}/playbook.yaml
            """
        }
    } //end container
    

    至此,只要提交配置仓库代码,就可以自动化发布配置到Nacos中。

    后记

    以上实践,在我们项目组已经运行1年多,目前运行良好。除了基础设施,我们已经做到了《持续交付》中的:仅依靠保存于版本控制系统的数据,就能重建系统;能快速的回滚到某个正确的版本;在多个环境中以同样的方式部署环境。

    本文虽然介绍的是基于Nacos,但是其思想也适用于行业中的其它的“配置中心”。希望能对你有帮助。如果你有什么疑问,可以留言。

    相关文章

      网友评论

        本文标题:软件配置管理实践——基于Ansible和Nacos

        本文链接:https://www.haomeiwen.com/subject/ksoclltx.html