Gitlab CI Review Apps - 将部署脚本中的信息返回到Gitlab CI环境变量

3
我使用gitlab-ci进行自动化测试。现在,我通过ansible playbook将其扩展为允许在digitalocean droplets上部署审查应用程序。
这非常有效,但我需要从ansible获取一个变量到.gitlab-ci中 - 我找不到方法来完成它。
.gitlab-ci.yml
Deploy for Review:
  before_script: []
  stage: review
  script: 'cd  /home/playbooks/oewm/deployment && ansible-playbook -i inventories/review --extra-vars "do_name=$CI_PIPELINE_ID api_git_branch=$CI_BUILD_REF_NAME" digitalocean.yml'
  environment:
    name: review/$CI_BUILD_REF_NAME
    url: http://$IP_FROM_ANSIBLE
    on_stop: "Stop Review"
  only:
    - branches
  when: manual
  tags:
    - deploy

以下是来自手册的相关部分:
- name: Create DO Droplet
  delegate_to: localhost
  local_action:
    module: digital_ocean
         state=present
         command=droplet
         name=review-{{ do_name }}
         api_token={{ do_token }}
         region_id={{ do_region }}
         image_id={{ do_image }}
         size_id={{ do_size }}
         ssh_key_ids={{ do_ssh }}
         wait_timeout=500
  register: my_droplet

- name: print info about droplet
  delegate_to: localhost
  local_action:
    module:  debug
        msg="ID is {{ my_droplet.droplet.id }} IP is {{ my_droplet.droplet.ip_address }}"

那么我如何在gitlab-ci中获取droplet ID和IP呢?(ID用于后续的停止操作,IP供开发人员查看)


不确定您所说的“将其放入gitlab-ci”是什么意思。您是想设置一个gitlab-ci环境变量,并在CI作业之外设置它吗?还是您的CI运行程序作业需要在CI作业内部获取值?您希望在“Deploy for Review”部分的“script:”中添加一些内容吗?您应该更清楚一些。也许您需要编写一个脚本,解析ansible-playbook命令的输出并提取您获得的值。然后对其进行操作。 - Warren P
抱歉让自己表达不够清楚。我需要在Gitlab-CI上下文中使用Ansible Playbook内部的变量。我希望有更简单的方法,可以解析Playbook的输出来获取该值。 - Rufinus
我为您改进了标题。 - Warren P
谢谢。我一直固执地搜索整个文字串,包括 CI。 - techraf
2个回答

3

Ansible是一种使用YAML配置的脚本工具,可能几乎是完全自动化脚本环境。为什么不让它在某个地方写一个名为“./ip_address.sh”的文件,然后将该.sh文件包含到您的gitlab CI中呢?

所有这些的最顶层,在.gitlab-ci.yml中应该有这样的内容:

script:
   - ./run_ansible.sh ./out/run_file_generated_from_ansible.sh
   - . ./out/run_file_generated_from_ansible.sh
   - echo $IP_FROM_ANSIBLE
environment:
  name: review/$CI_BUILD_REF_NAME
  url: http://$IP_FROM_ANSIBLE
  on_stop: "Stop Review"

上面两个shell脚本的编写留给读者作为练习。魔法发生在Ansible“playbook”内部,它实际上只是一个脚本,在其中您可以通过文件名“./out/run_file_generate_from_ansible.sh”将变量导出到磁盘。
您没有明确说明在Gitlab-CI中需要做什么以及变量去哪里以及接下来会发生什么。因此,在上面,我只展示了一种您可以通过临时磁盘文件导出IP地址的方式。
您可以将该导出值保存为artifact,并在其他阶段捕获它,因此如果您将它们全部放在名为./out的目录中,然后在gitlab-ci.yml中声明一个artifact语句,这样这些“artifact-exports”就可以在阶段之间传递。

感谢您的回答。不幸的是,这种方法行不通。因为Ansible脚本创建了一个DigitalOcean droplet,我可以在该主机上转储变量,这样可以解决问题,但我需要在runner上使用它们。我只看到两种方法-第一种是grep playbook输出。第二种是在droplet中存储一个变量文件,然后通过ssh将其复制到runner中-但这也行不通,因为我没有runner上下文中的droplet IP :-) - Rufinus
我使用 Amazon Route53 的动态 DNS 主机(仅使用其构建ID)解决了 IP 问题,但仍需要 Droplet ID 才能销毁审核应用程序。啊,没关系 - 我必须通过 local_action 将此文件存储在运行器上。 - Rufinus
1
我已经解决了这个问题 - 我不需要在.gitlab-ci.yml文件中,而是需要在另一个playbook(名为on_stop)中。因此,在创建后,我添加了一个facts文件,该文件在销毁playbook中被收集。由于我的主机名基于管道ID,我知道要连接到哪个主机以获取该信息。 - Rufinus
如果您愿意,可以回答自己的问题并分享一些代码片段。如果您接受自己的答案,我也不会难过。 - Warren P
我已经完成了。您介意看一下并告诉我您的想法吗? - Rufinus

1
我终于成功运行了设置。我的解决方案使用AWS Route53进行动态主机名生成(一个我忽略太久的问题 - 我需要为不同的审查应用程序获取主机名)。
步骤1: 动态构建主机名。为此,我使用了$CI_PIPELINE_ID。我在Route53上创建了一个子域,我们称之为review.mydomain.com。Ansible Playbook从create_droplet中获取IP地址,并在Route53上创建一个记录,其中包含Pipeline ID。1234.review.mydomain.com。 现在我的.gitlab-ci.yml知道这个主机名(因为它可以随时构建它)-不再需要从ansible脚本中获取Digitalocean droplet IP。
步骤2: 审核后,用户应能够停止/销毁droplet。为此,我需要在创建此droplet时获取droplet id。但销毁是一个不同的playbook,稍后将由开发人员调用。
因此,我需要在某个地方存储变量。
但是等等,现在我知道了它是哪个主机,我只需在此主机上创建一个事实文件,为我存储ID。当我需要销毁主机时,ansible会为我提供这些事实,并且我知道ID。
在playbook中,它看起来像这样:
角色:digitalocean
---
- name: Create DO Droplet
  delegate_to: localhost
  local_action:
    module: digital_ocean
         state=present
         command=droplet
         name=oewm-review-{{ do_name }}
         api_token={{ do_token }}
         region_id={{ do_region }}
         image_id={{ do_image }}
         size_id={{ do_size }}
         ssh_key_ids={{ do_ssh }}
         wait_timeout=500
  register: my_droplet

- name: print info about droplet
  delegate_to: localhost
  local_action:
    module:  debug
        msg="DO-ID:{{ my_droplet.droplet.id }}"

- name: print info about droplet
  delegate_to: localhost
  local_action:
    module:  debug
        msg="DO-IP:{{ my_droplet.droplet.ip_address }}"

# DNS
- name: Get existing host information
  route53:
    command: get
    zone: "{{ r53_zone }}"
    record: "{{ do_name }}.review.mydomain.com"
    type: A
    aws_access_key: "{{ r53_access_key }}"
    aws_secret_key: "{{ r53_secret_key }}"
  register: currentip

- name: Add DNS Record for Web-Application
  route53:
    command: create
    zone: "{{ r53_zone }}"
    record: "{{ do_name }}.review.mydomain.com"
    type: A
    ttl: 600
    value: "{{ my_droplet.droplet.ip_address }}"
    aws_access_key: "{{ r53_access_key }}"
    aws_secret_key: "{{ r53_secret_key }}"
  when: currentip.set.value is not defined

- name: Add DNS Record for API
  route53:
    command: create
    zone: "{{ r53_zone }}"
    record: "api.{{ do_name }}.review.mydomain.com"
    type: A
    ttl: 600
    value: "{{ my_droplet.droplet.ip_address }}"
    aws_access_key: "{{ r53_access_key }}"
    aws_secret_key: "{{ r53_secret_key }}"
  when: currentip.set.value is not defined

- name: Add new droplet to host group
  add_host:
    hostname: "{{ my_droplet.droplet.ip_address }}"
    groupname: api,web-application
    ansible_user: root
    api_domain: "api.{{ do_name }}.review.mydomain.com"
    app_domain: "{{ do_name }}.review.mydomain.com"

- name: Wait until SSH is available on {{ my_droplet.droplet.ip_address }}
  local_action:
    module: wait_for
    host: "{{ my_droplet.droplet.ip_address }}"
    port: 22
    delay: 5
    timeout: 320
    state: started

Playbook digitalocean.yml:

---
- name: Launch DO Droplet
  hosts: all
  run_once: true
  gather_facts: false
  roles:
    - digitalocean

- name: Store Facts
  hosts: api
  tasks:
    - name: Ensure facts directory exists
      file:
        path: "/etc/ansible/facts.d"
        state: directory

    - name: store variables on host for later fact gathering
      template:
        src={{ playbook_dir }}/roles/digitalocean/templates/digitalocean.fact.js2
        dest="/etc/ansible/facts.d/digitalocean.fact"
        mode=0644

- name: Deploy 
  hosts: api
  roles:
    - deployroles

Playbook digitalocean_destroy.yml:

- name: Add Host to Inventory
  hosts: all
  vars:
    r53_zone: review.mydomain.com
    r53_access_key: "xxxx"
    r53_secret_key: "xxxx"
  tasks:
    - name: Get existing DNS host information
      route53:
        command: get
        zone: "{{ r53_zone }}"
        record: "{{ do_name }}.review.mydomain.com"
        type: A
        aws_access_key: "{{ r53_access_key }}"
        aws_secret_key: "{{ r53_secret_key }}"
      register: currentip

    - name: Remove DNS Record for Web-Application
      route53:
        command: delete
        zone: "{{ r53_zone }}"
        record: "{{ do_name }}.review.mydomain.com"
        type: A
        ttl: 600
        value: "{{ my_droplet.droplet.ip_address }}"
        aws_access_key: "{{ r53_access_key }}"
        aws_secret_key: "{{ r53_secret_key }}"
      when: currentip.set.value is defined

    - name: Remove DNS Record for API
      route53:
        command: delete
        zone: "{{ r53_zone }}"
        record: "api.{{ do_name }}.review.mydomain.com"
        type: A
        ttl: 600
        value: "{{ my_droplet.droplet.ip_address }}"
        aws_access_key: "{{ r53_access_key }}"
        aws_secret_key: "{{ r53_secret_key }}"
      when: currentip.set.value is defined

    - name: Add droplet to host group
      add_host:
        hostname: "{{ do_name }}.review.mydomain.com"
        groupname: api,web-application
        ansible_user: root

- name: Digitalocean
  hosts: api
  vars:
    do_token: xxxxx
  tasks:
    - name: Delete Droplet
      delegate_to: localhost
      local_action:
        module: digital_ocean
             state=deleted
             command=droplet
             api_token={{ do_token }}
             id="{{ ansible_local.digitalocean.DO_ID }}"

.gitlab-ci.yml中相关部分:

Deploy for Review:
  before_script: []
  stage: review
  script:
    - 'cd  /home/playbooks/myname/deployment && ansible-playbook -i inventories/review --extra-vars "do_name=$CI_PIPELINE_ID api_git_branch=$CI_BUILD_REF_NAME" digitalocean.yml'
  environment:
    name: review/$CI_BUILD_REF_NAME
    url: http://$CI_PIPELINE_ID.review.mydomain.com
    on_stop: "Stop Review"
  only:
    - branches
  when: manual
  tags:
    - deploy

Stop Review:
  before_script: []
  stage: review
  variables:
    GIT_STRATEGY: none
  script:
    - 'cd  /home/playbooks/myname/deployment && ansible-playbook -i inventories/review --extra-vars "do_name=$CI_PIPELINE_ID" digitalocean_destroy.yml'
  when: manual
  environment:
    name: review/$CI_BUILD_REF_NAME
    action: stop
  only:
    - branches
  tags:
    - deploy

# STAGING
Deploy to Staging:
  before_script: []
  stage: staging
  script:
    - 'cd  /home/playbooks/myname/deployment && ansible-playbook -i inventories/staging --extra-vars "api_git_branch=$CI_BUILD_REF_NAME" deploy.yml'
  environment:
    name: staging
    url: https://staging.mydomain.com
  when: manual
  tags:
    - deploy

1
最好不要依赖IP地址,而是使用DNS。最好保留您原来的问题,这样那些正在搜索“传递变量值”的人就可以找到一种技术。但更好的方法是不要这样做,所以这里的两个答案都很好。 - Warren P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接