使用 Terraform 在本地进行试验

Experimenting Locally with Terraform

我们正在研究 Terraform 作为管理我们基础架构的一种方式,它看起来非常有趣。

但是,目前我们公司 proxy/firewall 由于安全限制导致 terraform apply 失败。

在我们等待解决这些网络问题的同时,有什么方法可以让我在本地试验 Terraform 而无需连接到 Azure 或 AWS?也许用 VirtualBox?

Terraform 支持一堆 providers,但其中绝大多数是 public 基于云的。

但是,如果您想设置一个 OpenStack 集群,您可以设置一个本地 VMware vSphere cluster and use the vSphere provider to interact with that to get you going. There's also a provider for OpenStack

或者,您可以尝试使用 HPE's Eucalyptus 之类的东西,它提供与 AWS API 的兼容性,但在本地。

也就是说,除非您已经拥有数据中心 运行 VMware,否则所有这些选项都非常糟糕,并且需要花费大量精力进行设置,因此您最好等待防火墙打开相反。

不幸的是,VirtualBox 提供程序没有一个很好的无摩擦第一方实现,但你可以试试这个 third-party VirtualBox provider

如果您有权访问 AWS/Azure,您可以在那里创建一台机器。这样您就可以使用它来处理、研究 Terraform 并在不受“非部门”限制的情况下学习它。

使用不同的提供商并不是一个好主意,因为一个提供商的配置可能与另一个提供商有很大不同,甚至在身份验证方面也有很大不同(AWS 提供商可能是最好的起点)。

习惯 Terraform 擅长的一些事情(依赖管理、数据驱动配置、资源生命周期等)的一种方法是在您的工作站上使用 null_resource provisioner。这假设您对工作站有足够的控制权以在其上安装 Terraform(这在许多具有高安全性需求的地方非常困难)。

只需使用 Terraform 的 null_resource 供应器,您就可以习惯 很多 您将在云中使用的东西。如果您有能力在您的工作站上安装 Docker,您可以 真正地 达到像云一样的效果,因为 Docker 支持工作站上的集群模式。

例如,

resource "null_resource" "docker_swarm" {

    provisioner "local-exec" {
      command = "docker swarm init"
    }

    provisioner "local-exec" {
      command = <<EOF
        docker swarm leave --force

        # Careful here. This assumes you want a clean Docker slate,
        # remove all Docker volumes from your machine.
        docker volume rm -f $(docker volume ls -q) 
EOF
      when = "destroy"
    }
}

resource "null_resource" "start_stack" {

    provisioner "local-exec" {
      command = "docker stack deploy -c ./docker-stack.yml demostack"
    }

    provisioner "local-exec" {
      command = "docker stack rm demostack"
      when = "destroy"
    }

    depends_on = ["null_resource.docker_swarm"]
}

在上面那个简单的示例中,您了解了如何管理生命周期。这就是最酷的部分:这正是您在 AWS、Azure 等中启动受支持的 Docker Swarm 的方式,尽管它会更复杂一些并且需要更长的时间。

如果您没有 Docker,我相信您可以想到您的工作站上可能有的其他 create/destroy 生命周期。

祝你好运!恕我直言,Terraform 是我们键盘上最深刻的东西之一(Docker 也在那里排名)。

即使您没有 account, you can create a free 一年(您必须注意使用情况以及此免费帐户可以使用哪些资源)。

这使 www.terraform.io/intro/getting-started/ 可供您使用。

这需要您的真实 phone 号码(AWS 通过 'robot' 呼叫您以填写 phone 中显示的 PIN。这需要一张有效的信用卡(您可以使用 虚拟 mastercard/visa 卡 支付 1 美元)。

从那里开始,很高兴免费与真正的提供商一起玩!

来自https://www.terraform.io/intro/getting-started/build.html

For the getting started guide, we'll only be using resources which qualify under the AWS free-tier, meaning it will be free. If you already have an AWS account, you may be charged some amount of money, but it shouldn't be more than a few dollars at most.

如果你能在别处下载provider文件发给自己,就可以在项目目录下本地使用。

这是 Mac 的 Terraform datadog 提供程序的示例:

wget https://releases.hashicorp.com/terraform-provider-datadog/1.0.4/terraform-provider-datadog_1.0.4_darwin_amd64.zip
unzip terraform-provider-datadog_1.0.4_darwin_amd64.zip
mkdir -p project_directory/terraform.d/plugins/darwin_amd64
mv terraform-provider-datadog_1.0.4_darwin_amd64  project_directory/terraform.d/plugins/darwin_amd64
cd project_directory
terraform init

While we wait for these network issues to be resolved, is there any way that I can experiment with Terraform locally without needing to connect to Azure or AWS? Perhaps with Virtual Box?

我使用 KVM 来拥有尽可能多的虚拟机,因为我需要玩任何东西,我现在正在学习 Terraform,但具体目的是自动创建这些 VM。

因此,如果您正在寻找一个本地主机游乐场来学习 Terraform,那么 KVM 是一种不错的方法,而且比使用其他重度虚拟化技术(如您提到的 Virtualbox)更轻量级。

如果您想在学习 Terraform 的同时使用 AWS 和 Azure 提供商,那么这对您来说不是一个好的解决方案,否则就试一试吧。

安装 KVM

首先你需要在你的 Linux 机器上安装 KVM。

sudo apt update &&  sudo apt upgrade

检查系统是否支持硬件虚拟化:

$ egrep -c ‘(svm|vmx)’ /proc/cpuinfo
4

输出需要大于0,否则我们需要进入Bios并启用VT技术。

查看是否支持KVM加速:

正在安装工具:

sudo apt install cpu-checker

正在检查:

$ sudo kvm-ok
INFO: /dev/kvm exists
KVM acceleration can be used

正在安装 KVM

sudo apt install qemu qemu-kvm libvirt-bin  bridge-utils  virt-manager

检查是运行:

service libvirtd status

如果不是运行:

sudo service libvirtd start && sudo update-rc.d libvirtd enable

另一个检查:

sudo virsh -c qemu:///system list

如何使用 Terraform 在 KVM 中创建 VM

我们将使用此 KVM Libvirt Provider.

创建 VM
mkdir playground && cd playground

Terraform 主文件

创建文件main.tf:

################################################################################
# ENV VARS
################################################################################

# https://www.terraform.io/docs/commands/environment-variables.html

variable "VM_COUNT" {
  default = 3
  type = number
}

variable "VM_USER" {
  default = "developer"
  type = string
}

variable "VM_HOSTNAME" {
  default = "vm"
  type = string
}

variable "VM_IMG_URL" {
  default = "https://cloud-images.ubuntu.com/releases/bionic/release/ubuntu-18.04-server-cloudimg-amd64.img"
  type = string
}

variable "VM_IMG_FORMAT" {
  default = "qcow2"
  type = string
}

variable "VM_CIDR_RANGE" {
  default = "10.10.10.10/24"
  type = string
}


################################################################################
# PROVIDERS
################################################################################

# instance the provider
provider "libvirt" {
  uri = "qemu:///system"
}


################################################################################
# DATA TEMPLATES
################################################################################

# https://www.terraform.io/docs/providers/template/d/file.html

# https://www.terraform.io/docs/providers/template/d/cloudinit_config.html
data "template_file" "user_data" {
  template = file("${path.module}/cloud_init.cfg")
  vars = {
    VM_USER = var.VM_USER
  }
}

data "template_file" "network_config" {
  template = file("${path.module}/network_config.cfg")
}


################################################################################
# RESOURCES
################################################################################

resource "libvirt_pool" "vm" {
  name = "${var.VM_HOSTNAME}_pool"
  type = "dir"
  path = "/tmp/terraform-provider-libvirt-pool-ubuntu"
}

# We fetch the latest ubuntu release image from their mirrors
resource "libvirt_volume" "vm" {
  count  = var.VM_COUNT
  name   = "${var.VM_HOSTNAME}-${count.index}_volume.${var.VM_IMG_FORMAT}"
  pool   = libvirt_pool.vm.name
  source = var.VM_IMG_URL
  format = var.VM_IMG_FORMAT
}

# Create a public network for the VMs
resource "libvirt_network" "vm_public_network" {
   name = "${var.VM_HOSTNAME}_network"
   mode = "nat"
   domain = "${var.VM_HOSTNAME}.local"
   addresses = ["${var.VM_CIDR_RANGE}"]
   dhcp {
    enabled = true
   }
   dns {
    enabled = true
   }
}

# for more info about paramater check this out
# https://github.com/dmacvicar/terraform-provider-libvirt/blob/master/website/docs/r/cloudinit.html.markdown
# Use CloudInit to add our ssh-key to the instance
# you can add also meta_data field
resource "libvirt_cloudinit_disk" "cloudinit" {
  name           = "${var.VM_HOSTNAME}_cloudinit.iso"
  user_data      = data.template_file.user_data.rendered
  network_config = data.template_file.network_config.rendered
  pool           = libvirt_pool.vm.name
}

# Create the machine
resource "libvirt_domain" "vm" {
  count  = var.VM_COUNT
  name   = "${var.VM_HOSTNAME}-${count.index}"
  memory = "1024"
  vcpu   = 1

  cloudinit = "${libvirt_cloudinit_disk.cloudinit.id}"

  # TODO: Automate the creation of public network
  network_interface {
    network_id = "${libvirt_network.vm_public_network.id}"
    #network_id = "6d8e2494-835d-4baf-a14f-3a5c705febcc"
    #network_name = "vm_docker_network"
    network_name = "${libvirt_network.vm_public_network.name}"
  }

  # IMPORTANT
  # Ubuntu can hang is a isa-serial is not present at boot time.
  # If you find your CPU 100% and never is available this is why.
  #
  # This is a known bug on cloud images, since they expect a console
  # we need to pass it:
  # https://bugs.launchpad.net/cloud-images/+bug/1573095
  console {
    type        = "pty"
    target_port = "0"
    target_type = "serial"
  }

  console {
    type        = "pty"
    target_type = "virtio"
    target_port = "1"
  }

  disk {
    volume_id = "${libvirt_volume.vm[count.index].id}"
  }

  graphics {
    type        = "spice"
    listen_type = "address"
    autoport    = true
  }
}


################################################################################
# TERRAFORM CONFIG
################################################################################

terraform {
  required_version = ">= 0.12"
}

环境变量

您可以设置环境变量以覆盖 ENV VARS 部分中定义的变量的任何默认值,但每个环境变量名称必须以 TF_VAR:

为前缀
export TF_VAR_VM_COUNT=5

云初始化

创建文件cloud_init.cfg:

#cloud-config
users:
  - name: ${VM_USER}
    sudo: ALL=(ALL) NOPASSWD:ALL
    groups: users, admin
    home: /home/${VM_USER}
    shell: /bin/bash
    ssh-authorized-keys:
      - ssh-rsa your-public-key-here
ssh_pwauth: True
disable_root: false
chpasswd:
  list: |
     ${VM_USER}:linux
  expire: False
package_update: true
package_upgrade: true
packages:
    - qemu-guest-agent
    - apt-transport-https
    - ca-certificates
    - curl
    - gnupg-agent
    - software-properties-common
    - zsh
growpart:
  mode: auto
  devices: ['/']
runcmd:
  - [ sh, -c, 'curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -' ]
  - [ sh, -c, 'sudo apt-key fingerprint 0EBFCD88']
  - [ sh, -c, 'sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"' ]
  - [ sh, -c, 'sudo apt update' ]
  - [ sh, -c, 'sudo apt install -y docker-ce docker-ce-cli containerd.io' ]
  - [ sh, -c, 'printf "\nalias dk=\"sudo docker\"\n" >> /home/${VM_USER}/.bashrc' ]
  - [ sh, -c, 'printf "\nalias dkc=\"sudo docker container\"\n" >> /home/${VM_USER}/.bashrc' ]
  - [ sh, -c, 'printf "\nalias dki=\"sudo docker image\"\n" >> /home/${VM_USER}/.bashrc' ]
  - [ sh, -c, 'printf "\nalias dks=\"sudo docker service\"\n" >> /home/${VM_USER}/.bashrc' ]
  - [ sh, -c, 'printf "\nalias dkn=\"sudo docker node\"\n" >> /home/${VM_USER}/.bashrc' ]

NOTE: Add your public key into the file

网络配置

创建文件network_config.cfg:

version: 2
ethernets:
  ens3:
     dhcp4: true

运行 地形

要初始化 Terraform:

terraform init

创建虚拟机:

terraform apply

VMS IP 地址

获取每个虚拟机的 ips:

virsh net-dhcp-leases vm_network

NOTE: The VMs we created will take some seconds until they get their ips, thus you may need to repeat this command several times until you get the ips.

现在只需使用您从 virsh:

获得的 IP 地址通过 SSH 连接到其中一台虚拟机
ssh developer@vm-ip-address

玩得开心,享受你的新游乐场。