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

Gitlab CI Review Apps - get information from a deployment script back into a gitlab ci environment variable

我使用 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? (后面的Stop动作需要ID,给开发者查看的IP)

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 脚本留给 reader 作为练习。魔法发生在 Ansible "playbook" 内部,它实际上只是一个脚本,您 "export a variable to disk" 的文件名是“./out/run_file_generate_from_ansible.sh”。

你没有说清楚的是你需要在 Gitlab-CI 中用那个变量做什么,它在哪里结束,接下来会发生什么。所以在上面,我只是展示了一种方法,你可以 "export" 通过一个临时的磁盘文件,一个 IP 地址。

您可以将导出的值保存为工件并在其他阶段捕获它,这样 "artifact-exports" 可以在阶段之间传递,如果您将它们全部放在名为 ./out 的目录中然后在gitlab-ci.yml.

中声明一个artifacts语句

我终于设置好了运行。我的解决方案使用 AWS Route53 来生成动态主机名。 (我忽略了很长时间的问题 - 我需要不同评论应用程序的主机名)

第一步: 动态构建主机名。为此,我使用 $CI_PIPELINE_ID 我在 Route53 上为此示例创建了一个子域,我们称之为 review.mydomain.com。 Ansible Playbook 从 create_droplet 中获取 IP,并使用管道 ID 在 Route53 上创建一条记录。 1234.review.mydomain.com.

现在我的 .gitlab-ci.yml 知道这个主机名(因为它可以随时构建它)——不再需要从 ansible skript 中获取 Digitalocean droplet IP。

第 2 步: 审查后 - 用户应该能够 stop/destroy 液滴。为此,我需要创建此 Droplet 时获得的 Droplet ID。 但 destroy 是一个不同的剧本,稍后将 运行 - 由开发人员调用。
所以我需要一种在某处存储变量的方法。
但是等等,既然我知道它是哪个主机,我就可以在这个主机上创建一个事实文件,为我存储 ID。当我需要销毁主机时,ansible 给我提供了事实,我知道了 ID。

在剧本中它看起来像这样:

角色: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

剧本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

剧本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