GitOps的一个优点是基础设施即代码。它通过使用共享配置和策略存储库来鼓励协作。使用GitLab可以进一步增强OpenStack集群中的协作。GitLab CI可以作为CI/CD的源代码控制和编排中心,甚至可以管理Terraform的状态。
要做到这一点,你需要以下几点:
——GitLab账户或实例。
——私有OpenStack集群。
——计算机(最好是容器主机)。
GitLab和Terraform状态
目标是通过Terraform实现协作,因此需要有一个集中的状态文件。GitLab具有Terraform的托管状态。使用此功能,你可以让个人协作管理OpenStack。
创建GitLab组和项目
登录GitLab,单击菜单,然后单击Groups→View all groups。
单击New group,然后单击Create group来创建组。
为组命名以生成唯一的组URL,并邀请团队与你一起工作。
创建组后,单击“Create new project”,然后单击“Create blank project”,创建项目:
为项目命名。GitLab为你生成一个唯一的项目URL。这个项目包含Terraform脚本和Terraform状态的存储库。
创建个人访问令牌
存储库需要一个个人访问令牌来管理这个Terraform状态。在你的个人资料中,选择Edit Profile:
单击侧面板中的Access Token以访问用于创建访问令牌的菜单。保存令牌,因为你无法再次查看它。
克隆空存储库
在可以直接访问OpenStack安装的计算机上,克隆存储库,然后更改到生成的目录:
$ git clone git@gitlab.com:testgroup2170/testproject.git$ cd testproject
创建backend.tf和provider文件
创建一个后端文件,将GitLab配置为你的状态后端:
$ cat >> backend.tf << EOFterraform { backend "http" { } } EOF
此provider文件为OpenStack拉取provider:
$ cat >> provider.tf << EOFterraform { required_version = ">= 0.14.0" required_providers { openstack = { source = "terraform-provider-openstack/openstack" version = "1.49.0" } } } provider "openstack" { user_name = var.OS_USERNAME tenant_name = var.OS_TENANT password = var.OS_PASSWORD auth_url = var.OS_AUTH_URL region = var.OS_REGION } EOF
因为你在provider中声明了一个变量,所以必须在变量文件中声明它:
$ cat >> variables.tf << EOFvariable "OS_USERNAME" { type = string description = "OpenStack Username" } variable "OS_TENANT" { type = string description = "OpenStack Tenant/Project Name" } variable "OS_PASSWORD" { type = string description = "OpenStack Password" } variable "OS_AUTH_URL" { type = string description = "OpenStack Identitiy/Keystone API for authentication" } variable "OS_REGION" { type = string description = "OpenStack Region" } EOF
因为最初在本地工作,所以必须设置这些变量以使其工作:
$ cat >> terraform.tfvars << EOFOS_USERNAME = "admin" OS_TENANT = "admin" OS_PASSWORD = "YYYYYYYYYYYYYYYYYYYYYY" OS_AUTH_URL = "http://X.X.X.X:35357/v3" OS_REGION = "RegionOne" EOF
这些详细信息可在OpenStack上的rc文件中找到。
在Terraform中初始化项目
初始化项目非常不同,因为你需要告诉Terraform使用GitLab作为状态后端:
PROJECT_ID="<gitlab-project-id>" TF_USERNAME="<gitlab-username>" TF_PASSWORD="<gitlab-personal-access-token>" TF_STATE_NAME="<your-unique-state-name>" TF_ADDRESS="https://gitlab.com/api/v4/projects/${PROJECT_ID}/terraform/state/${TF_STATE_NAME}" $ terraform init \ -backend-config=address=${TF_ADDRESS} \ -backend-config=lock_address=${TF_ADDRESS}/lock \ -backend-config=unlock_address=${TF_ADDRESS}/lock \ -backend-config=username=${TF_USERNAME} \ -backend-config=password=${TF_PASSWORD} \ -backend-config=lock_method=POST \ -backend-config=unlock_method=DELETE \ -backend-config=retry_wait_min=5
要查看gitlab-project-id,请查看侧面板“Project Information”选项卡上方的项目详细信息。通常是项目名称。
对笔者来说,是42580143。
使用你的用户名作为gitlab用户名。笔者的是ajohnsc。
gitlab-personal-access-token是你在本练习之前创建的令牌。在本例中,笔者使用wwwwwwww。你可以将your-unique-state-name命名为任何名称。笔者用的是homelab。
这是笔者的初始化脚本:
PROJECT_ID="42580143" TF_USERNAME="ajohnsc" TF_PASSWORD="wwwwwwwwwwwwwwwwwwwww" TF_STATE_NAME="homelab" TF_ADDRESS="https://gitlab.com/api/v4/projects/${PROJECT_ID}/terraform/state/${TF_STATE_NAME}"
要使用文件,请执行以下操作:
$ terraform init \ -backend-config=address=${TF_ADDRESS} \ -backend-config=lock_address=${TF_ADDRESS}/lock \ -backend-config=unlock_address=${TF_ADDRESS}/lock \ -backend-config=username=${TF_USERNAME} \ -backend-config=password=${TF_PASSWORD} \ -backend-config=lock_method=POST \ -backend-config=unlock_method=DELETE \ -backend-config=retry_wait_min=5
输出类似于:
测试Terraform脚本
以下为笔者的OpenStack风格设置了VM的大小:
$ cat >> flavors.tf << EOFresource "openstack_compute_flavor_v2" "small-flavor" { name = "small" ram = "4096" vcpus = "1" disk = "0" flavor_id = "1" is_public = "true" } resource "openstack_compute_flavor_v2" "medium-flavor" { name = "medium" ram = "8192" vcpus = "2" disk = "0" flavor_id = "2" is_public = "true" } resource "openstack_compute_flavor_v2" "large-flavor" { name = "large" ram = "16384" vcpus = "4" disk = "0" flavor_id = "3" is_public = "true" } resource "openstack_compute_flavor_v2" "xlarge-flavor" { name = "xlarge" ram = "32768" vcpus = "8" disk = "0" flavor_id = "4" is_public = "true" } EOF
外部网络的设置如下:
$ cat >> external-network.tf << EOFresource "openstack_networking_network_v2" "external-network" { name = "external-network" admin_state_up = "true" external = "true" segments { network_type = "flat" physical_network = "physnet1" } } resource "openstack_networking_subnet_v2" "external-subnet" { name = "external-subnet" network_id = openstack_networking_network_v2.external-network.id cidr = "10.0.0.0/8" gateway_ip = "10.0.0.1" dns_nameservers = ["10.0.0.254", "10.0.0.253"] allocation_pool { start = "10.0.0.2" end = "10.0.254.254" } } EOF
路由设置如下:
$ cat >> routers.tf << EOFresource "openstack_networking_router_v2" "external-router" { name = "external-router" admin_state_up = true external_network_id = openstack_networking_network_v2.external-network.id } EOF
为镜像输入:
$ cat >> images.tf << EOFresource "openstack_images_image_v2" "cirros" { name = "cirros" image_source_url = "https://download.cirros-cloud.net/0.6.1/cirros-0.6.1-x86_64-disk.img" container_format = "bare" disk_format = "qcow2" } EOF
以下是Demo租户:
$ cat >> demo-project-user.tf << EOFresource "openstack_identity_project_v3" "demo-project" { name = "Demo" } resource "openstack_identity_user_v3" "demo-user" { name = "demo-user" default_project_id = openstack_identity_project_v3.demo-project.id password = "demo" } EOF
完成后,你会得到以下文件架构:
├── backend.tf ├── demo-project-user.tf ├── external-network.tf ├── flavors.tf ├── images.tf ├── provider.tf ├── routers.tf ├── terraform.tfvars └── variables.tf
发行计划
文件完成后,可以使用terraform plan命令创建平面文件:
$ terraform planAcquiring state lock. This may take a few moments... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # openstack_compute_flavor_v2.large-flavor will be created + resource "openstack_compute_flavor_v2" "large-flavor" { + disk = 0 + extra_specs = (known after apply) + flavor_id = "3" + id = (known after apply) + is_public = true + name = "large" + ram = 16384 + region = (known after apply) + rx_tx_factor = 1 + vcpus = 4 } [...] Plan: 10 to add, Releasing state lock. This may take a few moments...
创建所有平面文件后,使用terraform apply命令应用它们:
$ terraform apply -auto-approveAcquiring state lock. This may take a few moments... [...] Plan: 10 to add, 0 to change, 0 to destroy. openstack_compute_flavor_v2.large-flavor: Creating... openstack_compute_flavor_v2.small-flavor: Creating... openstack_identity_project_v3.demo-project: Creating... openstack_networking_network_v2.external-network: Creating... openstack_compute_flavor_v2.xlarge-flavor: Creating... openstack_compute_flavor_v2.medium-flavor: Creating... openstack_images_image_v2.cirros: Creating... [...] Releasing state lock. This may take a few moments... Apply complete! Resources: 10 added, 0 changed, 0 destroyed.
应用基础设施后,返回GitLab并导航到你的项目。查看Infrastructure→ Terraform确认已创建homelab状态。
销毁状态以测试CI
现在你已经创建了一个状态,请尝试销毁基础设施,以便以后可以应用CI管道。当然,这纯粹是为了从Terraform CLI迁移到Pipeline。如果你有现有的基础设施,可以跳过此步骤。
$ terraform destroy -auto-approveAcquiring state lock. This may take a few moments... openstack_identity_project_v3.demo-project: Refreshing state... [id=5f86d4229003404998dfddc5b9f4aeb0] openstack_networking_network_v2.external-network: Refreshing state... [id=012c10f3-8a51-4892-a688-aa9b7b43f03d] [...] Plan: 0 to add, 0 to change, 10 to destroy. openstack_compute_flavor_v2.small-flavor: Destroying... [id=1] openstack_compute_flavor_v2.xlarge-flavor: Destroying... [id=4] openstack_networking_router_v2.external-router: Destroying... [id=73ece9e7-87d7-431d-ad6f-09736a02844d] openstack_compute_flavor_v2.large-flavor: Destroying... [id=3] openstack_identity_user_v3.demo-user: Destroying... [id=96b48752e999424e95bc690f577402ce] [...] Destroy complete! Resources: 10 destroyed.
你现在有了每个人都可以使用的状态。你可以使用集中状态进行资源调配。通过适当的管道,你可以自动化常见任务。
设置GitLab运行程序
你的OpenStack集群不是面向公众的,OpenStack API也没有公开。你必须有一个GitLab运行程序才能运行GitLab管道。GitLab运行程序是在远程GitLab服务器上运行和执行任务的服务或代理。
在其他网络上的计算机上,为GitLab运行程序创建容器:
docker volume create gitlab-runner-config$ docker run -d --name gitlab-runner --restart always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v gitlab-runner-config:/etc/gitlab-runner \ gitlab/gitlab-runner:latest$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 880e2ed289d3 gitlab/gitlab-runner:latest "/usr/bin/dumb-init …" 3 seconds ago Up 2 seconds gitlab-runner-test
现在在GitLab项目的“Settings→ CI/CD”面板中将其注册到项目中:
向下滚动至Runners→ Collapse:
需要GitLab运行程序注册令牌和URL。禁用右侧的共享runner,以确保它仅在runner上运行。运行gitlab-runner容器以注册runner:
$ docker exec -ti gitlab-runner /usr/bin/gitlab-runner registerRuntime platform arch=amd64 os=linux pid=18 revision=6d480948 version=15.7.1 Running in system-mode. Enter the GitLab instance URL (for example, https://gitlab.com/): https://gitlab.com/ Enter the registration token: GR1348941S1bVeb1os44ycqsdupRK Enter a description for the runner: [880e2ed289d3]: dockerhost Enter tags for the runner (comma-separated): homelab Enter optional maintenance note for the runner: WARNING: Support for registration tokens and runner parameters in the 'register' command has been deprecated in GitLab Runner 15.6 and will be replaced with support for authentication tokens. For more information, see https://gitlab.com/gitlab-org/gitlab/-/issues/380872 Registering runner... succeeded runner=GR1348941S1bVeb1o Enter an executor: docker-ssh, shell, virtualbox, instance, kubernetes, custom, docker, parallels, ssh, docker+machine, docker-ssh+machine: docker Enter the default Docker image (for example, ruby:2.7): ajscanlas/homelab-runner:3.17 Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded! Configuration (with the authentication token) was saved in "/etc/gitlab-runner/config.toml"
成功后,GitLab界面将显示你的runner为有效。它看起来像这样:
现在,你可以使用该runner在GitLab中使用CI/CD管道自动调配。
设置GitLab管道
现在可以设置管道了。在存储库中添加名为.gitlab-ci.yaml的文件以定义ci/CD步骤。忽略不需要的文件,如.traform目录和敏感数据,如变量文件。
这是笔者的.gitignore文件:
$ cat .gitignore*.tfvars .terraform*
以下是.gitlab-CI.yaml中的CI管道条目:
$ cat .gitlab-ci.yamldefault: tags: - homelab variables: TF_ROOT: ${CI_PROJECT_DIR} TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/homelab cache: key: homelab paths: - ${TF_ROOT}/.terraform* stages: - prepare - validate - build - deploy before_script: - cd ${TF_ROOT} tf-init: stage: prepare script: - terraform --version - terraform init -backend-config=address=${BE_REMOTE_STATE_ADDRESS} -backend-config=lock_address=${BE_REMOTE_STATE_ADDRESS}/lock -backend-config=unlock_address=${BE_REMOTE_STATE_ADDRESS}/lock -backend-config=username=${BE_USERNAME} -backend-config=password=${BE_ACCESS_TOKEN} -backend-config=lock_method=POST -backend-config=unlock_method=DELETE -backend-config=retry_wait_min=5 tf-validate: stage: validate dependencies: - tf-init variables: TF_VAR_OS_AUTH_URL: ${OS_AUTH_URL} TF_VAR_OS_PASSWORD: ${OS_PASSWORD} TF_VAR_OS_REGION: ${OS_REGION} TF_VAR_OS_TENANT: ${OS_TENANT} TF_VAR_OS_USERNAME: ${OS_USERNAME} script: - terraform validate tf-build: stage: build dependencies: - tf-validate variables: TF_VAR_OS_AUTH_URL: ${OS_AUTH_URL} TF_VAR_OS_PASSWORD: ${OS_PASSWORD} TF_VAR_OS_REGION: ${OS_REGION} TF_VAR_OS_TENANT: ${OS_TENANT} TF_VAR_OS_USERNAME: ${OS_USERNAME} script: - terraform plan -out "planfile" artifacts: paths: - ${TF_ROOT}/planfile tf-deploy: stage: deploy dependencies: - tf-build variables: TF_VAR_OS_AUTH_URL: ${OS_AUTH_URL} TF_VAR_OS_PASSWORD: ${OS_PASSWORD} TF_VAR_OS_REGION: ${OS_REGION} TF_VAR_OS_TENANT: ${OS_TENANT} TF_VAR_OS_USERNAME: ${OS_USERNAME} script: - terraform apply -auto-approve "planfile"
该过程首先声明每个步骤和阶段都在homeab标签下,允许GitLab runner运行它。
default: tags: - homelab
接下来,在管道上设置变量。这些变量仅在管道运行时出现:
variables: TF_ROOT: ${CI_PROJECT_DIR} TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/homelab
有一个缓存,在从一个阶段运行到另一个阶段时保存特定的文件和目录:
cache: key: homelab paths: - ${TF_ROOT}/.terraform*
以下是管道所遵循的阶段:
stages: - prepare - validate - build - deploy
这声明了在运行任何阶段之前要执行的操作:
before_script: - cd ${TF_ROOT}
在prepare阶段,tf-init初始化Terraform脚本,获取provider,并将其后端设置为GitLab。尚未声明的变量稍后将作为环境变量添加。
tf-init: stage: prepare script: - terraform --version - terraform init -backend-config=address=${BE_REMOTE_STATE_ADDRESS} -backend-config=lock_address=${BE_REMOTE_STATE_ADDRESS}/lock -backend-config=unlock_address=${BE_REMOTE_STATE_ADDRESS}/lock -backend-config=username=${BE_USERNAME} -backend-config=password=${BE_ACCESS_TOKEN} -backend-config=lock_method=POST -backend-config=unlock_method=DELETE -backend-config=retry_wait_min=5
在本部分中,CI作业tf-validate和阶段 validate运行Terraform,以验证Terraform脚本没有语法错误。尚未声明的变量稍后将作为环境变量添加。
tf-validate: stage: validate dependencies: - tf-init variables: TF_VAR_OS_AUTH_URL: ${OS_AUTH_URL} TF_VAR_OS_PASSWORD: ${OS_PASSWORD} TF_VAR_OS_REGION: ${OS_REGION} TF_VAR_OS_TENANT: ${OS_TENANT} TF_VAR_OS_USERNAME: ${OS_USERNAME} script: - terraform validate
接下来,CI作业tf-build和阶段build使用terraform plan创建计划文件,并使用artifacts标记临时保存它。
tf-build: stage: build dependencies: - tf-validate variables: TF_VAR_OS_AUTH_URL: ${OS_AUTH_URL} TF_VAR_OS_PASSWORD: ${OS_PASSWORD} TF_VAR_OS_REGION: ${OS_REGION} TF_VAR_OS_TENANT: ${OS_TENANT} TF_VAR_OS_USERNAME: ${OS_USERNAME} script: - terraform plan -out "planfile" artifacts: paths: - ${TF_ROOT}/planfile
在下一节中,CI作业tf-deploy和阶段deploy将应用计划文件。
tf-deploy: stage: deploy dependencies: - tf-build variables: TF_VAR_OS_AUTH_URL: ${OS_AUTH_URL} TF_VAR_OS_PASSWORD: ${OS_PASSWORD} TF_VAR_OS_REGION: ${OS_REGION} TF_VAR_OS_TENANT: ${OS_TENANT} TF_VAR_OS_USERNAME: ${OS_USERNAME} script: - terraform apply -auto-approve "planfile"
存在变量,因此必须在“Settings→ CI/CD→ Varibles→ Expand”中声明它们:
添加所有必需的变量:
BE_ACCESS_TOKEN => GitLab Access Token BE_REMOTE_STATE_ADDRESS => This was the rendered TF_ADDRESS variable BE_USERNAME => GitLab username OS_USERNAME => OpenStack Username OS_TENANT => OpenStack tenant OS_PASSWORD => OpenStack User Password OS_AUTH_URL => Auth URL OS_REGION => OpenStack Region
在本例中,笔者使用:
BE_ACCESS_TOKEN = "wwwwwwwwwwwwwwwwwwwww" BE_REMOTE_STATE_ADDRESS = https://gitlab.com/api/v4/projects/42580143/terraform/state/homelab BE_USERNAME = "ajohnsc" OS_USERNAME = "admin" OS_TENANT = "admin" OS_PASSWORD = "YYYYYYYYYYYYYYYYYYYYYY" OS_AUTH_URL = "http://X.X.X.X:35357/v3" OS_REGION = "RegionOne"
出于保护,它被屏蔽了GitLab。
最后一步是将新文件推送到存储库:
$ git add .$ git commit -m "First commit"[main (root-commit) e78f701] First commit 10 files changed, 194 insertions(+) create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 backend.tf create mode 100644 demo-project-user.tf create mode 100644 external-network.tf create mode 100644 flavors.tf create mode 100644 images.tf create mode 100644 provider.tf create mode 100644 routers.tf create mode 100644 variables.tf$ git pushEnumerating objects: 12, done. Counting objects: 100% (12/12), done. Delta compression using up to 4 threads Compressing objects: 100% (10/10), done. Writing objects: 100% (12/12), 2.34 KiB | 479.00 KiB/s, done. Total 12 (delta 0), reused 0 (delta 0), pack-reused 0 To gitlab.com:testgroup2170/testproject.git * [new branch] main -> main
查看结果
在GitLab的CI/CD部分查看新管道。
在OpenStack方面,你可以看到Terraform创建的资源。
网络:
flavors:
镜像:
项目:
用户:
下一步
Terraform有很大的潜力。Terraform和Ansible一起用很棒。在下一篇文章中,笔者将演示Ansible如何使用OpenStack。
原文链接:
https://opensource.com/article/23/2/manage-openstack-using-terraform-and-gitlab