1

系列专栏声明:比较流水,主要是写一些踩坑的点,和实践中与文档差距较大的地方的思考。这个专栏的典型特征可能是 次佳实践,争取能在大量的最佳实践中生存。

HashiCorp Packer,通过调用云厂商的接口,把构建基础镜像的过程代码化,即 Infra as Code -> IaC。个人之前的做法是用 Markdown 写安装的步骤,然后起一台新实例从上往下复制粘贴执行,然后对这个实例做镜像和备份。从 Markdown 到 Code 是一个 0 到 1 的质变。

Vultr 的代码如下,官方文档

variable "vultr_api_key" {
  type      = string
  default   = "${env("VULTR_API_KEY")}"
  sensitive = true
}

packer {
  required_plugins {
    vultr = {
      version = ">=v2.3.2"
      source = "github.com/vultr/vultr"
    }
  }
}

source "vultr" "debian11" {
  api_key              = "${var.vultr_api_key}"
  os_id                = "477"
  plan_id              = "vc2-1c-1gb"
  region_id            = "sgp"
  snapshot_description = "Debian 11 Bullseye ${formatdate("YYYY-MM-DD hh:mm", timestamp())}"
  ssh_key_ids          = ["uuid-aaaa", "uuid-bbbb"]
  ssh_username         = "root"
  state_timeout        = "25m"
}

build {
  sources = ["source.vultr.debian11"]

  provisioner "shell" {
    script = "packer.sh"
  }
}

安装脚本的关键部分如下:

#!/bin/bash

sleep 5m
# apt-get update
apt-get install -y --no-install-recommends zsh

useradd core
mkdir -p /home/core/.ssh
touch /home/core/.zshrc
chown -R core:core /home/core
adduser core sudo
chsh -s /usr/bin/zsh core
su core -c 'sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"'

su core -c 'git config --global core.editor "vim"'

踩坑如下:

  1. 一些值比如 os_id, plan_id, region_id 需要用 Open Api 去查,官方文档;上面用到的这些值都是公开报价,不涉及到帐号信息,所以可以直接调接口,不需要去实现 Authorization: Bearer
  2. Cloud-Init, User-Data, Start-Script 看上去是需要用 Token 去查的,并且部分接口在 /api/v1 还没有实现到 /api/v2,文档不太完善,由于暂时没有用到,等用到了再来补充
  3. Vultr 允许指定多个 ssh pub keyauthorized_keys。我们是用 root 帐号初始化的,所以它们是被导入了 /root/.ssh/authorized_keys,上面我新建的那个 core 帐号就没有;感觉需要某种初始化策略,通过云厂商提供的 RAM 机制,去 KMS 里面拉取,待研究
  4. 重点!!!似乎 Debian 系有某种机制,在第一次被拉起来的时候会去执行 apt-get update,因此我们的脚本上手第一行也执行 apt-get update 的话就会报错。官方的建议是先 always-sleep-on-packer-provisioning

阿里云代码如下,官方文档

variable "access_key" {
  type      = string
  default   = "${env("ALICLOUD_ACCESS_KEY")}"
  sensitive = false
}

variable "secret_key" {
  type      = string
  default   = "${env("ALICLOUD_SECRET_KEY")}"
  sensitive = true
}

packer {
  required_plugins {
    alicloud = {
      version = ">= 0.0.1"
      source  = "github.com/hashicorp/alicloud"
    }
  }
}

source "alicloud-ecs" "debian10" {
      access_key = "${var.access_key}"
      secret_key = "${var.secret_key}"
      region = "cn-hangzhou"
      image_name = "Debian-10-Buster-${formatdate("YYYYMMDDhhmm", timestamp())}"
      source_image = "debian_10_11_x64_20G_alibase_20211027.vhd"
      system_disk_mapping {
          disk_size = 20
      }
      ssh_username = "root"
      instance_type = "ecs.t6-c1m2.large"
      zone_id = "cn-hangzhou-i"
      vpc_id = "vpc-aaaa"
      vswitch_id = "vsw-bbbb"
      security_group_id = "sg-cccc"
      internet_charge_type = "PayByTraffic"
}

build {
  sources = ["source.alicloud-ecs.debian10"]

  provisioner "shell" {
    script = "packer.sh"
  }
}

踩坑点如下:

  1. 阿里云的所有 Open Api 都必须用 AK 访问,即使列举 Regions 也没有公开接口。但是如果使用子帐号的 AK 就会发现,DescribeRegions 的权限是全局的,DescribeImages 的权限是要主动去向子帐号配置的,具体自己尝试和开工单吧。
  2. Packer 支持在起实例的时候,不申请公网 EIP,而是通过 VPC IP 连接;但实际上在阿里云如果不开公网 EIP,实例也没办法去公网源更新和装软件。简单起见,我还是走 EIP 模式了。
  3. 阿里云的 Debian 似乎不会执行 apt-get update,很神奇,但是我还是决定 sleep 了。
  4. authorized_keys 只支持指定一个值,不支持数组,不知道图啥。
  5. 重点!!!security_group_id 也不支持数组,由于我把安全组分了几类,组合起来用的,所以没有一个恰好适合所有场景,尤其是安装的时候要允许一些公网连接的情况下,所以简单起见,专门为 packer 模式新建了一个安全组。
  6. 重点!!!有些软件是装不上的,你懂的,比如 ohmyzsh,所以我在国内的 coding.net 上 fork 了一份,类似场景类似处理;顺便提一句,阿里云有 docker 源,自己搜一下
$ curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/debian/gpg | sudo apt-key add -
$ sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/debian $(lsb_release -cs) stable"

参考文献

  1. Install Packer
  2. Packer Tutorial For Beginners – Automate AMI Creation
  3. Vultr: Creating Snapshots With Packer
  4. 阿里云公共镜像发布记录

理斯特
18 声望9 粉丝

web/mobile/iot, front/back, js/java