关于cloud:三个最好的朋友TerraformAnsible-和-Jenkins

47次阅读

共计 11048 个字符,预计需要花费 28 分钟才能阅读完成。

应用 Jenkins CI/CD Pipeline 通过 Terraform 和 Ansible 创立 AWS 基础设施。

首先,我要感激 Derek Morgan 和他的 Terraform、Ansible and Jenkins 课程。我最近实现了他的课程,再怎么举荐也不为过。它从小开始,到最初你有一个简单的工作我的项目。实现每门课程后,我都会尝试创立本人的我的项目来强化我所学的常识,而这篇文章正是记录了这一点。
该项目标最终后果是 Terraform 代码,该代码创立了一个带有公共子网和 EC2 实例的 AWS 环境,以及一个在新创建的 EC2 实例上装置 Docker 的 Ansible Playbook 剧本脚本文件。当代码被推送到 GitHub 存储库时,GitHub Webhook 将触发 Jenkins CI/CD pipeline,该 pipeline 将执行一系列操作,具体取决于咱们是将代码推送到开发分支还是主分支。

留神 :本文显示了实现的我的项目,但心愿您在构建它时测试每个局部以验证一切正常,而不是一下子就齐全构建它。对于这个,我不打算具体阐明我是如何做到的。例如,我不会具体介绍如何设置 Terraform,而后在构建基础设施时逐渐测试代码,以及如何在将 playbook 利用到 Jenkins 管道之前配置 Ansible 并在本地测试它们等。只是意识到这些步骤在开发过程中是典型的。依照我列出此文档的形式,如果您依照这种形式独自进行测试,您将遇到谬误。如果您想理解更多详细信息,我再次建议您查看 Derek 的课程。
免责申明:尽管本文将带您实现该项目标步骤,但它并不是一个教程,而是我最近的一个副我的项目的文档。如果您收到任何谬误,请不要发送怨恨邮件。

先决条件

  • GitHub 帐户
  • AWS CLI
  • 装置 Terraform
  • AWS 账户
  • 具备管理员权限的 AWS 用户
  • AWS Cloud9(您能够应用其余 IDE,但要意识到某些步骤可能会有所不同)
  • Terraform Cloud 帐户
  • 装置 Git

开始设置环境

创立您的 Amazon Cloud9 环境

应用除平台局部之外的所有默认值。抉择Ubuntu Server 18.04LTS

调配 Elastic IP

为了避免 Cloud9 实例在每次敞开和重新启动时更改其公共 IP 地址,请为其调配一个弹性 IP。这也将容许稍后将公共 IP 调配给平安组的 Terraform 变量。只需确保在删除 Cloud9 实例后删除弹性 IP,否则将收取费用。

  • 在 AWS 控制台中导航到 EC2
  • NetworkSecurity下抉择Elastic IP
  • 单击调配Allocate Elastic IP address
  • 点击Allocate
  • Action 下拉列表中抉择Associate Elastic IP Address
  • 实例:抉择您的 Cloud9 实例,公有 IP 地址:抉择您的 Cloud9 公有 IP 地址。单击Associate

    调整 Cloud9 实例的大小

    Derek 的课程提供了一个很棒的大小调整脚本来扩充 Cloud9 实例的存储空间,这将是必须的。

  • 在 Cloud9 中创立一个名为 resize.sh 的文件
  • 将 Derek 存储库中的代码复制到您的 resize.sh 文件中
  • 运行chmod +x resize.sh
  • 运行./resize.sh

创立 SSH 密钥

  • 在您的终端中运行ssh-keygen -t rsa
  • 输出保留密钥的文件(我命名为 mykey):/home/ubuntu/.ssh/<key name>
  • 没有明码
  • 通过运行 ls ~/.ssh 验证您的密钥是否已创立

装置 jq

运行sudo apt install jq

Fork Repo

如果您想 fork 并援用它,能够在这里找到我的代码:
https://github.com/troy-ingra…

Terraform Cloud

[Terraform Cloud]容许您将 Terraform 状态存储在近程平安地位,而不是将其存储在本地。这容许更好的安全性和更好的团队合作。

  • 创立一个新的 workspace
  • 抉择CLI-driven workflow
  • 命名您的 workspace,而后创立 workspace
  • 复制 workspace 提供的示例代码并将其增加到 backends.tf 以替换以后后端
  • 通过单击 Remote 并抉择 Local 来设置Execution Mode

创立 Terraform Cloud 令牌

  • 单击浏览器右上角的Profile,而后抉择User settings
  • User settings 下抉择Token
  • 单击创立Create API token
  • 输出形容并单击创立Create API token
  • 复制提供的令牌并将其保留在平安的中央(如果您失落了它,您能够随时使令牌过期并创立另一个)
  • 导航到 Cloud9 终端并运行terraform login
  • 键入yes,而后粘贴之前复制的 Terraform Cloud 令牌
  • 运行terraform init

这将咱们的 Terraform 代码连贯到咱们的 Terraform Cloud 的 workspace 的替换了 Terraform Cloud 的本地后端。Jenkins 将来在运行管道时也会拜访 Terraform Cloud。

Terraform

作为与云无关的产品,HashiCorp 的 Terraform 反对多云和本地。它是一个开源工具,具备企业版和社区版,应用 HashiCorp 本人的 HashiCorp 配置语言 (HCL)。Terraform 的 HCL 容许开发人员学习一种语言以应用多种云产品和本地提供商,而不用为每种语言学习新的服务和语言。HCL 是一种申明性语言,专一于最终状态,而不是过程语言,其中所有命令都按编写的程序执行。Hashicorp 为开发人员提供无关 Terraform 的文档,并且能够拜访官网和社区模板的 Terraform Registry。

更新 variables.tf

  • 将 access_ip 变量批改为您的集体公共 IP CIDR
  • 将 cloud9_ip 变量批改为您的 Cloud9 IP CIDR

更新 tfvars 文件

  • 将 main.tfvars 和 dev.tfvars 中的 key_name 变量更新为您的密钥名称
  • 将 main.tfvars 和 dev.tfvars 中的 public_key_path 变量更新为您的密钥名称

Terraform 源码

Terraform 代码能够在这 GitHub 的 repo 中找到:所以我不打算在这里探讨所有内容。我将重点介绍一些有助于使文件更加通用和整洁的注意事项。

Data Source

应用一个 AWS Available Zones 的 Data Source 来确定将要在其中启动资源的 Region 中以后可用的 AZ。应用 Local Value 来设置 AZ 的名称。而后应用 length 函数依据 AZ 的数量来确定计数。而后该计数用于索引 cidr_block 和 availability_zones,而不是为每一个独自创立一个 AZ 定义块。

locals {azs = data.aws_availability_zones.available.names}

data "aws_availability_zones" "available" {}

resource "aws_subnet" "public_subnet" {count                   = length(local.azs)
  vpc_id                  = aws_vpc.vpc.id
  cidr_block              = var.public_cidr[count.index]
  map_public_ip_on_launch = true
  availability_zone       = local.azs[count.index]


  tags = {Name = "docker-public"}
}

Data Source 还用于指定在该 Region 行将要启动的映像的 AMI ID。这打消了对 AMI ID 进行硬编码或创立 AMI 和 Region 的映射的须要。请留神 ami 如何调用 Data Source 来确定 Ubuntu AMI ID。

data "aws_ami" "ubuntu" {
  most_recent = true

  owners = ["099720109477"]

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
  }
}

resource "aws_instance" "web" {
  count                  = var.instance_count
  ami                    = data.aws_ami.ubuntu.id
  instance_type          = "t3.micro"
  vpc_security_group_ids = [aws_security_group.sg.id]
  subnet_id              = aws_subnet.public_subnet[count.index].id
  key_name               = aws_key_pair.docker_auth.id

  tags = {Name = "docker-instance"}
}

Ansible

Ansible 是一个简略的配置自动化工具,它应用 SSH 而不是在指标主机上安装代理。通常须要简单脚本来自动化的事件当初能够应用 Ansible 应用 Ansible Playbook 在几行代码中实现。Ansible Playbooks 应用 YAML,与大多数脚本语言相比,它是一种易于浏览的纯英语语言。应用 Ansible,您能够治理要跟踪或批改的主机的纯文本清单文件。这些主机能够组合在一起,也能够独自分组在不同的组题目下。在构建 Ansible Playbook 时,您能够辨别哪些组应该运行哪些模块。

装置 Ansible

在 Cloud9 实例上运行以下命令以装置 Ansible。

sudo apt update
sudo apt install software-properties-common
sudo add-apt-repository --yes --update ppa:ansible/ansible
sudo apt install ansible

配置 Ansible 的 Hosts

  1. 运行sudo vim /etc/ansible/hosts
  2. 在文件顶部插入以下内容:

    [hosts]
    localhost
    [hosts:vars]
    ansible_connection=local
    ansible_python_interpreter=/usr/bin/python3
  3. 退出并保留。

    配置

    为防止提醒输出 ECDSA 密钥指纹,请按如下配置 ansible.cfg 文件。

  4. 运行sudo vim /etc/ansible/ansible.config
  5. 在“# host_key_checking = False”一行中去掉正文合乎“#”
  6. 退出并保留。

    Ansible Playbook: docker.yml

    与其手动运行命令来装置 Docker,不如应用上面的 docker.yml Ansible Playbook。Playbook 分为几个工作,每个工作都有一个形容所采取行动的名称。须要留神的一些主题是应用“{{变量名称}}”表示法而不是创立多个工作来应用 Ansible Playbook 变量。另请留神 ansible.builtin.apt 或 apt 用于包治理。Ansible 通过应用 State 来查看包是否存在于实例上。如果指标是删除一个包,那么 State 将从 preset 更改为 absent。
    另请留神,hosts 援用的是 main 而不是 local 的。

  7. name: Install Docker
    hosts: main
    become: yes

    tasks:

    • name: Update apt cache
      apt: update_cache=yes cache_valid_time=3600
    • name: Upgrade all apt packages
      apt: upgrade=dist
    • name: Install dependencies
      apt:
      name: “{{packages}}”
      state: present
      update_cache: yes
      vars:
      packages:

      • apt-transport-https
      • ca-certificates
      • curl
      • software-properties-common
      • gnupg-agent
    • name: Add an apt signing key for Docker
      apt_key:
      url: https://download.docker.com/l…
      state: present
    • name: Add apt repository for stable version
      apt_repository:
      repo: deb [arch=amd64] https://download.docker.com/l… focal stable
      state: present
    • name: Install Docker
      apt:
      name: “{{packages}}”
      state: present
      update_cache: yes
      vars:
      packages:

      • docker-ce
      • docker-ce-cli
      • containerd.io

Jenkins

Jenkins 是一个独立的开源自动化服务器,用于自动化与构建、测试和交付 / 部署软件相干的工作。Jenkins Pipeline 通过应用插件和 Jenkinsfile 将继续交付管道实现到 Jenkins。Jenkinsfile 能够是申明式的或脚本式的,并蕴含管道要遵循的步骤列表。

应用 Ansible 装置 Jenkins

您能够在 Cloud9 实例上手动装置 Jenkins,但为了取得更多练习,您也能够应用 Ansible。
运行ansible-playbook playbook/jenkins.yml

凋谢 8080 端口

  1. 在 AWS 控制台中导航到您的 Cloud9 的 Security Group。
  2. 凋谢 8080 端口,范畴 0.0.0.0/0,面向全世界。这是容许咱们拜访 Jenkins 服务器所必须的,而且它解决了咱们稍后会遇到的 GitHub Webhook 问题。这就是为什么它向世界凋谢,而不仅仅是一个 IP。
  3. 在浏览器中导航到 <Cloud9 Public IP>:8080 进行测试。

配置 Jenkins

  1. 依照屏幕上的阐明通过 sudo cat /var/lib/jenkins/secrets/initialAdminPassword 运行检索您的治理明码
  2. 将后果复制并粘贴到 Unlock Jenkins 字段中,而后单击Continue
  3. 单击Install suggested plugins
  4. 输出所需信息。保留并持续到 Jenkins。
  5. 点击 Manage Jenkins > Manage Plugins 抉择 Available 标签,查找 Ansible 并点击install without restart
  6. 点击 Manage Plugins 抉择 Available 标签,查找 Pipeline: AWS Steps 并点击install without restart

治理 Jenkins 凭证

GitHub App

  1. 在 GitHub 中单击浏览器右上角的个人资料图标,而后抉择Settings
  2. 点击左侧导航底部的Development settings
  3. 单击GitHub Apps,而后单击New GitHub App
  4. 依照 [GitHub App]() 文档中 Creating GitHub App 局部的阐明进行操作
  5. 将私钥下载到本地后,在下载文件夹中关上,而后拖放到 Cloud9 实例的顶层(这样能够确保您当前不会意外提交此文件)
  6. 在终端的顶层运行:

    openssl pkcs8 -topk8 -inform PEM -outform PEM -in [key-in-your-downloads-folder-name].pem -out converted-github-app.pem -nocrypt
  7. 这将创立一个名为 converted-github-app.pem 的文件,该文件对 Jenkins 敌对
  8. 导航到 Jenkins 点击Manage Jenkins > Manage Credentials > Jenkins
  9. 点击Global > Global credentials
  10. 点击Add Credentials
  11. Kind = GitHub App
  12. ID = [名称]
  13. Description = GitHub App Credentials
  14. App ID =(这能够在 GitHub App 中找到。`Settings > Developer settings > GitHub Apps > [您的 App 名称])
  15. Key = 复制并粘贴之前转换的密钥的 converted-github-app.pem 文件内容,而后单击OK
  16. 导航回您的 GitHub App 并通过单击 Install 来装置该应用程序
  17. 回到 Jenkins,点击你的 GitHub App Credentials。单击Update,而后单击Test connection 以验证一切正常。

Terraform Cloud 凭证

  1. 要获取 Terraform Cloud 凭据,请在终端中运行cat /home/ubuntu/.terraform.d/credentials.tfrc.json
  2. 复制输入。创立一个本地 txt 文件并粘贴输入并保留
  3. 导航回 Jenkins 并点击Add a new credentials
  4. Kind = Secret file
  5. File = (抉择刚刚保留的 txt 文件)
  6. ID = tf-creds(在上面援用到)
  7. Description = Terraform Cloud Credentials

SSH Key

  1. 在 Cloud9 终端上运行` cat /home/ubuntu/.ssh/<key name>
  2. 复制输入
  3. 返回 Jenkins 并点击Add a new credentials
  4. Kind = SSH Username with private key
  5. ID = ec2-ssh-key (在上面援用到)
  6. Description = SSH key for EC2 instances
  7. Username = ubuntu
  8. Private Key = 抉择Enter directly,粘贴后面的复制
  9. 点击OK

GitHub Webhook

  1. 导航到我的项目 GitHub 存储库并单击Settings
  2. 点击Webhooks
  3. 点击Add webhook
  4. Paylod URL = <Jenkins URL>/github-webhook/
    留神:确保开端有 ”/”。
  5. Content type = application/json
  6. Which events would you like to trigger this webhook? = Just the push event

创立 Jenkins Pipeline

  1. 导航到 Jenkins 的 Dashboard 并单击New Item
  2. 命名 pipeline 并抉择Multi-Branch Pipeline,而后单击OK
  3. 接下来配置 pipeline:
  4. Display Name = <Name of pipeline>
  5. Branches Sources = GitHub
  6. GitHub Credentials = 抉择GitHub App Credentials
  7. Repository HTTPS URL = <Your repo name>.git

留神 :保留后,您将被带到扫描存储库日志屏幕,即便它已实现,它看起来也像是在进行中。在输入底部看到SUCCESS 后,您就能够返 Dashbord 了。

Jenkinsfile

Jenkins 须要在咱们代码的根目录下应用 Jenkinsfile 来创立 Jenkins pipeline。上面是这个我的项目应用的 Jenkinsfile。

pipeline {
  agent any
  environment {
    TF_IN_AUTOMATION = 'true'
    TF_CLI_CONFIG_FILE = credentials('tf-creds')
    AWS_SHARED_CREDENTIALS_FILE='/home/ubuntu/.aws/credentials'
  }
  stages {stage('Init') {
      steps {
        sh 'ls'
        sh 'cat $BRANCH_NAME.tfvars'
        sh 'terraform init -no-color'
      }
    }
    stage('Plan') {
      steps {sh 'terraform plan -no-color -var-file="$BRANCH_NAME.tfvars"'}
    }
    stage('Validate Apply') {
      when {
        beforeInput true
        branch "dev"
      }
      input {
        message "Do you want to apply this plan?"
        ok "Apply plan"
      }
    steps {echo 'Apply Accepted'}
    }
    stage('Apply') {
      steps {sh 'terraform apply -auto-approve -no-color -var-file="$BRANCH_NAME.tfvars"'}
    }
    stage('Inventory') {
      steps {sh '''printf \\"\\n$(terraform output -json instance_ips | jq -r \'.[]\')" \\
          >> aws_hosts'''
      }
    }
    stage('EC2 Wait') {
      steps {
        sh '''aws ec2 wait instance-status-ok \\
          --instance-ids $(terraform output -json instance_ids | jq -r \'.[]\') \\
          --region us-east-1'''
      }
    }
    stage('Validate Ansible') {
      when {
        beforeInput true
        branch "dev"
      }
      input {
        message "Do you want to run Ansible?"
        ok "Run Ansible"
      }
      steps {echo 'Ansible Approved'}
        }
    stage('Ansible') {
      steps {ansiblePlaybook(credentialsId: 'ec2-ssh-key', inventory: 'aws_hosts', playbook: 'playbooks/docker.yml')
      }
    }
    stage('Validate Destroy') {
      input {
        message "Do you want to destroy?"
        ok "Destroy"
        }
      steps {echo 'Destroy Approved'}
    }
    stage('Destroy') {
      steps {sh 'terraform destroy -auto-approve -no-color -var-file="$BRANCH_NAME.tfvars"'}
    }
  }
  post {
    success {echo 'Success!'}
    failure {sh 'terraform destroy -auto-approve -no-color -var-file="$BRANCH_NAME.tfvars"'}
    aborted {sh 'terraform destroy -auto-approve -no-color -var-file="$BRANCH_NAME.tfvars"'}
  }
}

我不会介绍这个文件的所有内容,但会强调一些乏味的点。

$BRANCH_NAME 环境变量

当咱们执行 Jenkinsfile 时,应用这个环境变量能够辨别咱们的分支。例如,上面的示例执行 shell 脚本并输入启动 GitHub Webhook 的分支。对于打算阶段,这容许咱们调用 main.tfvars 或 dev.tfvars 文件,这将笼罩咱们的默认 variables.tf 文件。

条件和输出

如果分支等于 dev,则应用 when 的条件来确定是否运行输出。如果分支不等于 dev,则跳过输出。
输出用于暂停 pipeline 并期待手动抉择 Apply planAbort

EC2 期待

这可能是我从课程中学到的最不便的物品之一。在 Inventory 和 EC2 期待阶段下方都应用 shell 脚本来获取 Terraform 输入,并应用 jq 来获取实例 ID 和实例的 IP。Inventory 阶段传递从 Terraform 代码创立的实例 IP,并将它们附加到 Ansible 将应用的 aws_hosts 文件中,从而创立一个动静清单文件。EC2 期待阶段执行雷同的操作,但传递实例 ID 并执行期待命令。这将验证实例是否已实现初始化,而后再转到执行 Ansible 的下一步。

整合所有

dev 分支

  1. 通过运行 git checkout -b dev 创立一个 dev 分支。
  2. 运行git add
  3. 通过运行 git commit -m "initial commit" 提交文件。
  4. 通过运行 git push -u origin dev 将代码推送到 dev 分支并通过 GitHub 进行身份验证。
  5. 导航到您的 Jenkins 仪表板。
  6. 单击位于浏览器左下角的 Build Executor Status 下的running pipeline
  7. 将鼠标悬停在 Validate Apply 步骤上,单击Apply plan
  8. 而后 pipeline 将利用 Terraform 代码,运行清单步骤,并期待 EC2 实例初始化,这样 Ansible 就不会在尝试拜访实例时出错。
  9. 一旦达到 Validate Ansible 步骤,再次将鼠标悬停在该步骤上并抉择Run Ansible
  10. 期待时,单击 Build History 旁边的绿色复选标记以查看输入。您将看到 Ansible 正在运行,但您也能够向上滚动并查看整个 pipeline 的所有输入。
  11. 当 pipeline 在 Validate Destroy 步骤中期待时,让咱们查看咱们的实例以确保 Docker 装置正确。在输入中向上滚动并找到 instance_ips 输入并向下复制实例的公共 IP。
  12. 返回 Cloud9 终端并应用您的 SSH 密钥 SSH 进入实例。ssh -i /home/ubuntu/.ssh/[key name] [email protected][instance ip]
  13. 验证运行docker --version 13. 返回您的 Jenkinspipeline 并抉择Destroy

main 分支

因为 dev 分支曾经过验证,代码能够推送到主分支。

  1. 通过运行 git checkout main 切换到主分支。
  2. 通过运行 git merge dev 合并 dev 分支。
  3. 通过运行 git push -u origin main 将代码推送到 main 并通过 GitHub 进行身份验证。
  4. 切换回 Jenkins 并查看咱们的 pipeline。这次你应该看到 main 的另一个分支。
  5. 单击 main 以查看运行中的 pipeline。因为代码曾经在开发 pipeline 中进行了测试,Jenkinsfile 将依据设置的条件跳过验证步骤。pipeline 应该始终挪动到 Validate Destroy 阶段。
  6. 获取 Docker 实例 IP,相似于测试开发 pipeline 的形式并验证 Docker 是否已装置。一旦确认返回 pipeline 并销毁。

附加测试

转到 dev.tfvars 文件和 main.tfvars 文件并将实例计数更新为 2 或 3,并验证 pipeline 是否仍在运行并且是否创立了多个实例。

故障排除

留神 :请再次查看本文结尾的免责申明。如果您偏离上述步骤,那么您可能会失去不同的后果。例如,如果您在 Cloud9 上应用您最喜爱的 IDE,或者如果您应用 Amazon Linux 而不是 Ubuntu。所有这些都可能扭转你的后果和应该采取的步骤。
我已尽力捕获过程中的所有步骤。所有这些因素都会扭转所需的步骤。如果您始终在跟进并遇到问题,请返回您的步骤并验证您是否批改了指定的变量。还要查看创立的所有凭据。当我最后经验这所有时,这仿佛就是我所有的问题所在。如果有谬误,我倡议查看日志并查看谬误以找到解决方案。我是人,我有可能错过了一步。

正文完
 0