【问题标题】:In saltstack, how do I conditionally, and iteratively ( jinja ) apply an included state在 saltstack 中,我如何有条件地迭代( jinja )应用包含状态
【发布时间】:2018-10-23 23:03:55
【问题描述】:

这乍一看似乎很简单。但我可以告诉你,我已经为此绞尽脑汁好几天了。我已经阅读了很多文档,与人们一起坐在 IRC 上,并与同事交谈过,在这一点上,我没有一个我真正认为站得住脚的答案。

我研究了几种可能的方法

  • 反应堆
  • 编排运行器

我不喜欢这两个,因为自上而下执行的必要性......它们似乎是为协调多个节点状态而不是单个节点中的工作流量身定制的。

  • 自定义状态

这是我非常想避免的事情,因为这是一个重复的工作流程,我不想构建这样的自定义。如果我和我的队友一起走这条路,就会有太多难以辨认的空间。

  • 需要/观看

这些没有重复应用状态或以逻辑顺序/工作流程应用状态的概念(我知道)。

还有一些我不会提及的。

没有进一步的讨论,这是我的困境。

目标:

  • Jenkins Master 已部署
  • 我们可以在部署过程中对部署进行单元测试
  • 我们只在必要时重启 Tomcat
  • 我们可以根据每个包更新插件
  • 非常强调良好的清洁直观清晰的盐配置

Jenkins 部署非常简单。我们放入包和配置,然后我们就设置好了。

单元测试更难。例如,我有这个状态文件。

actions/version.sls:

# Hit's the jenkins CLI interface to check for version info
# This can be used to verify that jenkins is active and the version we want

# Import some info
{%- from 'jenkins/init.sls' import jenkins_home with context %}

# Install plugins in jenkins_plugins list
jenkins_version:
  cmd.run:
    - name: java -jar jenkins-cli.jar -s "http://127.0.0.1:8080" version
    - cwd: /var/lib/tomcat/webapps/ROOT/WEB-INF/
    - user: jenkins

actions.version 基本上验证了 jenkins 是否正在运行且可查询。我们希望在构建过程中的几个点上确定这一点。

示例... tomcat 需要时间来启动。我们不得不为重新启动操作添加延迟。如果您查看下面的 start.sls,您可以看到该操作正在发生。注意在 init_delay 上打开的错误:.

actions/start.sls:

# Starts the tomcat service
tomcat_start:
  service.running:
    - name: tomcat
    - enable: True
    - full_restart: True
# Not functional atm see --> https://github.com/saltstack/salt/issues/20631
#    - init_delay: 120

# initiate a 120 second delay after any service start to let tomcat come up.
tomcat_wait:
  module.run:
    - name: test.sleep
    - length: 60

include:
  - jenkins.actions.version

现在我们通过执行actions.stop 和actions.start 来获得这种重启能力。我们有这个 actions.version 状态,我们可以使用它来验证系统是否准备好继续执行 jenkins 特定的状态工作流。

我想做一些这样的事情......

 Install Jenkins --> Grab yaml of plugins --> install plugins that need it

非常直接。

除了循环遍历我正在使用 Jinja 的插件的 yaml。
而现在我没有办法调用并确保 start.sls 和 version.sls 状态可以重复应用。

我正在寻找一个好方法。

这类似于 jenkins.sls

{% set repo_username = "foo" -%}
{% set repo_password = "bar" -%}

include:
  - jenkins.actions.version
  - jenkins.actions.stop
  - jenkins.actions.start

# Install Jenkins
jenkins:
  pkg:
    - installed

# Import Jenkins Plugins as List, and Working Path
{%- from 'jenkins/init.sls' import jenkins_home with context %}
{%- import_yaml "jenkins/plugins.sls" as jenkins_plugins %}
{%- import_yaml "jenkins/custom-plugins.sls" as custom_plugins %}
# Grab updated package list
jenkins-contact-update-server:
  cmd.run:
    - name: curl -L http://updates.jenkins-ci.org/update-center.json | sed '1d;$d' > {{ jenkins_home }}/updates/default.json
    - unless: test -d {{ jenkins_home }}/updates/default.json
    - require:
      - pkg: jenkins
      - service: tomcat
# Install plugins in jenkins_plugins list
{% for plugin in jenkins_plugins %}
jenkins-plugin-{{ plugin }}:
  cmd.run:
    - name: java -jar jenkins-cli.jar -s "http://127.0.0.1:8080" install-plugin "{{ plugin }}"
    - unless: java -jar jenkins-cli.jar -s "http://127.0.0.1:8080" list-plugins | grep "{{ plugin }}"
    - cwd: /var/lib/tomcat/webapps/ROOT/WEB-INF/
    - user: jenkins
    - require:
      - pkg: jenkins
      - service: tomcat

这是我卡住的地方。要求不会这样做。并列出 的行动似乎并没有在盐中线性安排。我需要 能够验证 jenkins 是否已启动并准备就绪。我需要 能够在单个插件后重新启动tomcat 添加了迭代。我需要能够做到这一点才能满足 插件顺序中的依赖关系。

      - sls: jenkins.actions.version
      - sls: jenkins.actions.stop
      - sls: jenkins.actions.start
#    This can't work for several reasons
#    - watch_in:
#      - sls: jenkins-safe-restart
{% endfor %}

# Install custom plugins in the custom_plugins list
{% for cust_plugin,cust_plugin_url in custom_plugins.iteritems() %}
# manually downloading the plugin, because jenkins-cli.jar doesn't seem to work direct to artifactory URLs.
download-plugin-{{ cust_plugin }}:
  cmd.run:
    - name: curl -o {{ cust_plugin }}.jpi -O "https://{{ repo_username }}:{{ repo_password }}@{{ cust_plugin_url }}"
    - unless: java -jar jenkins-cli.jar -s "http://127.0.0.1:8080" list-plugins | grep "{{ cust_plugin }}"
    - cwd: /tmp
    - user: jenkins
    - require:
      - pkg: jenkins
      - service: tomcat
# installing the plugin ( REQUIRES TOMCAT RESTART AFTER )
custom-plugin-{{ cust_plugin }}:
  cmd.run:
    - name: java -jar jenkins-cli.jar -s "http://127.0.0.1:8080" install-plugin /tmp/{{ cust_plugin }}.jpi
    - unless: java -jar jenkins-cli.jar -s "http://127.0.0.1:8080" list-plugins | grep "{{ cust_plugin }}"
    - cwd: /var/lib/tomcat/webapps/ROOT/WEB-INF/
    - user: jenkins
    - require:
      - pkg: jenkins
      - service: tomcat
{% endfor %}

【问题讨论】:

    标签: salt-stack jinja2


    【解决方案1】:

    如果不使用反应器、信标,尤其是不编写自己的 python 执行模块,您将无法实现这一目标。

    Jenkins Master 被部署

    用函数install(...):在python中编写一个jenkins执行模块。在该函数中,您可以通过调用现有的执行模块或自己编写它们来管理任何依赖项。

    我们可以对部署进行单元测试

    在 jenkins 模块的安装函数中,您可以根据安装结果触发特定的events

    if not _run_deployment_phase(...):
        __salt__['event.send']('jenkins/install/error', {
                'finished': False,
                'message': "Something failed during the deployment!",
        })
    

    你会 map that event to reactor sls files 并处理它。

    我们只在必要时重启tomcat

    编写一个tomcat模块。添加一个_is_up(...) 函数,您可以通过解析tomcat 日志的结果来检查tomcat 是否已启动。在状态模块中调用该函数并添加一个 mod_watch 函数。

    def mod_watch():
        # required dict to return
        return_dict = {
               "name": "Tomcat install",
               "changes": {}, 
               "result": False,
               "comment": "",
                }
        if __salt__["tomcat._is_up"]():
          return_dict["result"] = True
          return_dict["comment"] = "Tomcat is up."
    
        if __opts__["test"]:
            return_dict["result"] = None
            return_dict["comment"] = "comment here about what will change"
            return return_dict
    
        # execute changes now
    
        return return_dict
    

    在状态文件中使用您的状态模块。

    install tomcat:
      tomcat.install:
        - name: ...
        - user: ...
        ...
    
    wait until tomcat is up:
      cmd.run:
        - name: ...
        - watch:
          - tomcat: install tomcat
    

    我们可以根据每个包更新插件

    向您的 jenkins 执行模块添加一个名为 install_plugin 的函数。查看 pkg.install 代码复制界面。

    非常强调干净直观清晰的盐配置

    编写 python 执行模块以获得简单且可维护的配置逻辑。在您自己的状态模块中使用该执行模块。内部状态文件调用您自己的状态模块,并为您喜欢的任何状态渲染器提供单独的配置。

    【讨论】:

    • 所以基本上不要使用盐。
    • 编写自己的模块并使用反应器就是使用盐。如果您查看 salts 现有模块的源代码,您会发现一切都建立在状态模块中使用的执行模块之上。您可以轻松地在一个模块中实现所有步骤,并使用 python 向 jenkins 发出所需的 API 请求,并通过现有模块检查服务是否启动,或者检查您是否从 tomcat 获得已知的 http 响应,这将允许您轻松处理单独使用 jinja 会很烦人的错误。
    【解决方案2】:

    根据设计,状态只执行一次。如果您需要多次发生相同的操作,则需要多个状态。此外,包含仅包含一次。

    您应该将所有代码放入一个 sls 文件中,并通过 jinja 迭代生成状态,而不是所有这些包含/需要的东西。

    如果你要做的是添加一堆插件,添加配置文件,然后在最后重新启动,那么你真的应该按顺序执行所有内容,不要使用require,使用listen或listen_in ,而不是 watch 或 watch_in。

    listen/listen_in 导致触发动作在状态运行结束时发生。它们类似于 Ansible 中的处理程序的概念。

    【讨论】:

    • 我需要在插件添加期间进行条件重启以满足插件中的交叉依赖网络。不是最后。条件是一个重点,除非某些单元测试验证需要,否则我不想重新启动。我也想在州申请期间等待。 listen_in 不适用于 SLS,listen 无法实现我的目标。无法共享 SLS 文件违背了可重用干净代码的目标。
    • 可重用的干净代码在配置管理中被高估了,通常是浪费时间:ryandlane.com/blog/2014/10/08/…
    • 你想要完成的事情是可能的。我们现在正在使用 jenkins 进行此操作,但是无论您使用哪种配置管理系统,您尝试处理它的方式都非常困难。您可以使用 jinja 函数来使其更加模块化,您可以在其中传入插件的名称并生成相关状态以及服务重启命令。请注意,您需要使用 cmd.wait 来重新启动,而不是直接触发服务定义。
    • 我非常不同意你的做法。它只是无法扩展。
    • 您是否安装了多个配置不同的 jenkins 服务器?如果不是,那么使代码通用的意义何在?它不会使其更具可扩展性,因为无论如何您都不会重用它。干净的可重用代码的重点是重用它。
    【解决方案3】:

    这是一个很老的问题,但是如果您将 Jenkins/tomcat 启动/停止程序更改为标准的 init/systemd/windows 服务(所有行为良好的服务都应该如此),您可以有一个 service.running for Jenkins 服务并将其添加到您的每个 custom-plugin-{{ cust_plugin }} 状态。

    require_in: 
      - svc: jenkins
    watch_in: 
      - svc: jenkins
    

    您可以继续使用带有 onchanges 的 cmd.run 模块。您必须将 onchanges_in: 添加到每个 custom-plugin-{{ cust_plugin }} 状态,但您需要在 on changes 列表中至少有一项,否则每次状态运行时都会触发该命令。

    【讨论】:

      【解决方案4】:

      如果你使用 require 你会导致 salt 重新排序你的状态。如果您希望您的状态按顺序运行,只需按照您希望它们运行的​​顺序编写它们即可。

      Watch/watch_in 也会重新排序您的状态。如果您改用listen/listen_in,它会将触发的操作按在状态运行结束时触发的顺序排列。

      见:

      http://ryandlane.com/blog/2014/07/14/truly-ordered-execution-using-saltstack/ http://ryandlane.com/blog/2015/01/06/truly-ordered-execution-using-saltstack-part-2/

      【讨论】:

      • 您不能(直接)调用 sls 文件,并且我确实尝试执行类似于 no-op 的操作来调用 sls 状态。这也不起作用。如果我的理解是正确的,那可能是因为状态应用程序的安排方式......我认为一旦应用状态,在给定状态内,它只是假设它已被永久应用。
      猜你喜欢
      • 1970-01-01
      • 2021-10-01
      • 2020-11-29
      • 2019-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-15
      相关资源
      最近更新 更多