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
我使用 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.
我终于设置好了运行。我的解决方案使用 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