hitcounter
  • April 27, 2018
  • Iurii Medvedev

Packer и что нам нужно для начала

Packer — это инструмент для создания одинаковых образов ОС для различных платформ из одного описания. Packer отличная утилита для использовании в ваших ci/cd pipelines.

Что потребуется? Packer VirtualBox Vagrant

Установка packer

Osx

Установка в osx через brew


Теперь проверяем версию


packer -v 1.1.3


### Установка в любой Unix-like os

Для простоты установки Packer вы можете воспользоваться скриптом

```bash
#!/usr/bin/env bash
cd ~

# Prerequisites
if [ "$(uname)" == "Darwin" ]; then
    brew install jq > /dev/null 2>&1
# For Linux
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
    sudo apt-get update
    sudo apt-get install -q --assume-yes jq software-properties-common prometheus-node-exporter
fi
#check terraform and packer local archives

if [ -f ~/packer.zip ]; then
   rm -f ~/packer.zip
fi

# Get URLs for most recent versions
# For OS-X
if [ "$(uname)" == "Darwin" ]; then
    terraform_url=$(curl -s https://releases.hashicorp.com/index.json | jq '{terraform}' | egrep "darwin.*64" | sort  -r | head -1 | awk -F[\"] '{print $4}')
    packer_url=$(curl -s https://releases.hashicorp.com/index.json | jq '{packer}' | egrep "darwin.*64" | sort  -r | head -1 | awk -F[\"] '{print $4}' )

# For Linux
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
    terraform_url=$(curl -s https://releases.hashicorp.com/index.json | jq '{terraform}' | egrep "linux.*amd64" | sort -r | head -1 | awk -F[\"] '{print $4}')
    packer_url=$(curl -s https://releases.hashicorp.com/index.json | jq '{packer}' | egrep "linux.*amd64" | sort  -r | head -1 | awk -F[\"] '{print $4}')

fi

# Create a move into directory.
cd
mkdir packer > /dev/null 2>&1
mkdir -p terraform  && cd $_

# Download Terraform. URI: https://www.terraform.io/downloads.html
echo "Downloading   $terraform_url."
curl -o terraform.zip $terraform_url > /dev/null 2>&1
# Unzip and install
echo "Install terraform"
unzip -o terraform.zip  > /dev/null 2>&1
echo "done"


# Change directory to Packer
cd ~/packer

# Download Packer. URI: https://www.packer.io/downloads.html
echo "Downloading   $packer_url."
curl -o packer.zip $packer_url > /dev/null 2>&1
# Unzip and install
echo "Install packer"
unzip -o  packer.zip  > /dev/null 2>&1
echo "Installed packer"

#check terraform and packer local archives
if [ -f ~/terraform.zip ]; then
   chattr -i -a ~/terraform* > /dev/null 2>&1
   chmod ugo+w ~/terraform* > /dev/null 2>&1
   rm -f ~/terraform* > /dev/null 2>&1
fi
if [ -f ~/terraform/terraform.zip ]; then
   chattr -i -a ~/terraform/terraform* > /dev/null 2>&1
   chmod ugo+w ~/terraform/terraform* > /dev/null 2>&1
   rm -f ~/terraform/terraform* > /dev/null 2>&1
fi
if [ -f ~/packer.zip ]; then
   rm -f ~/packer.zip
fi
if [ -f ~/packer/packer.zip ]; then
   rm -f ~/packer/packer.zip
fi

echo "Add packer and terraform to user path"
if [ "$(uname)" == "Darwin" ]; then
  echo '
  # Terraform & Packer Paths.
  export PATH=~/terraform/:~/packer/:$PATH
  ' >>~/.bash_profile

  source ~/.bash_profile
# For Linux
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
  echo '
  # Terraform & Packer Paths.
  export PATH=~/terraform/:~/packer/:$PATH
  ' >>~/.bashrc

  source ~/.bashrc
fi
echo "All done. Local path"
which packer
which terraform

Как бонус этот скрипт скачивает последню версию Packer и Terrraform с официального сайта, прописывают нужные пути в переменные среды.

Установка из исходных кодов

Чтобы установить Packer из исходных кодов, необходимо установить Golang и склонировать исходные коды с официально github репозитория

$ mkdir -p $GOPATH/src/github.com/hashicorp && cd $_
$ git clone https://github.com/hashicorp/packer.git
$ cd packer

После кланирования компилируем

make dev

Ваше первый packer шаблон

После установки мы можем приступить к сборки образа, давайте смоделируем ситуацию. Есть какой-то веб сервер, на него мы хотим установить ubuntu,nginx,php Шаг первый создаем json файл для соборки в нашем примере назовем его template.json

mkdir ubuntu-packer && cd $_
vi template.json

Далее опишем все стадии сборки будущего образа.


 {
   "provisioners":[
      {
         "type":"shell",
         "execute_command":"echo 'vagrant'|sudo -S sh '{{.Path}}'",
         "override":{
            "virtualbox-iso":{
               "scripts":[
                  "scripts/base.sh",
                  "scripts/vagrant.sh",
                  "scripts/virtualbox.sh",
                  "scripts/cleanup.sh",
                  "scripts/zerodisk.sh",
                  "scripts/nginx.sh"
               ]
            }
         }
      }
   ],
   "post-processors":[
      {
         "type":"vagrant",
         "override":{
            "virtualbox":{
               "output":"ubuntu-14-04-x64-virtualbox.box"
            }
         }
      }
   ],
   "builders":[
      {
         "type":"virtualbox-iso",
         "boot_command":[
            "",
            "",
            "",
            "/install/vmlinuz",
            " auto",
            " console-setup/ask_detect=false",
            " console-setup/layoutcode=us",
            " console-setup/modelcode=pc105",
            " debian-installer=en_US",
            " fb=false",
            " initrd=/install/initrd.gz",
            " kbd-chooser/method=us",
            " keyboard-configuration/layout=USA",
            " keyboard-configuration/variant=USA",
            " locale=en_US",
            " netcfg/get_hostname=ubuntu-1404",
            " netcfg/get_domain=vagrantup.com",
            " noapic",
            " preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg",
            " -- ",
            ""
         ],
         "boot_wait":"10s",
         "disk_size":40960,
         "guest_os_type":"Ubuntu_64",
         "http_directory":"http",
         "iso_checksum":"dd54dc8cfc2a655053d19813c2f9aa9f",
         "iso_checksum_type":"md5",
         "iso_url":"http://releases.ubuntu.com/14.04/ubuntu-14.04.5-server-amd64.iso",
         "ssh_username":"vagrant",
         "ssh_password":"vagrant",
         "ssh_port":22,
         "ssh_wait_timeout":"10000s",
         "shutdown_command":"echo 'shutdown -P now' > /tmp/shutdown.sh; echo 'vagrant'|sudo -S sh '/tmp/shutdown.sh'",
         "vboxmanage":[
            [
               "modifyvm",
               "{{.Name}}",
               "--memory",
               "512"
            ],
            [
               "modifyvm",
               "{{.Name}}",
               "--cpus",
               "1"
            ]
         ]
      }
   ]
}

Минимальный шаблон мы создали, давайте рассмотрим, что он в себе содержит:

Builder

В качестве билдера я использую virtualbox

"builders":[
   {
      "type":"virtualbox-iso",
      "boot_command":[
         "",
         "",
         "",
         "/install/vmlinuz",
         " auto",
         " console-setup/ask_detect=false",
         " console-setup/layoutcode=us",
         " console-setup/modelcode=pc105",
         " debian-installer=en_US",
         " fb=false",
         " initrd=/install/initrd.gz",
         " kbd-chooser/method=us",
         " keyboard-configuration/layout=USA",
         " keyboard-configuration/variant=USA",
         " locale=en_US",
         " netcfg/get_hostname=ubuntu-1404",
         " netcfg/get_domain=vagrantup.com",
         " noapic",
         " preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg",
         " -- ",
         ""
      ],
      "boot_wait":"10s",
      "disk_size":40960,
      "guest_os_type":"Ubuntu_64",
      "http_directory":"http",
      "iso_checksum":"dd54dc8cfc2a655053d19813c2f9aa9f",
      "iso_checksum_type":"md5",
      "iso_url":"http://releases.ubuntu.com/14.04/ubuntu-14.04.5-server-amd64.iso",
      "ssh_username":"vagrant",
      "ssh_password":"vagrant",
      "ssh_port":22,
      "ssh_wait_timeout":"10000s",
      "shutdown_command":"echo 'shutdown -P now' > /tmp/shutdown.sh; echo 'vagrant'|sudo -S sh '/tmp/shutdown.sh'",
      "vboxmanage":[
         [
            "modifyvm",
            "{{.Name}}",
            "--memory",
            "512"
         ],
         [
            "modifyvm",
            "{{.Name}}",
            "--cpus",
            "1"
         ]
      ]
   }
]

В билдере у меня указаны следующие параметры:

  • type – тип билдера могут быть следующими

    • Alicloud ECS

    • Amazon EC2

    • Azure

    • CloudStack

    • DigitalOcean

    • Docker

    • File

    • Google Cloud

    • Hyper-V

    • LXC

    • LXD

    • NAVER Cloud

    • Null

    • 1&1

    • OpenStack

    • Oracle

    • Parallels

    • ProfitBricks

    • QEMU

    • Scaleway

    • Triton

    • VirtualBox

    • VMware Cписок постоянно изменятеся

  • boot_command - команды которые выполняются, при зашрузки с cd

  • boot_wait - время которое, packer будет ожидать загрузку

  • disk_size - разрмер диска, в создаваемой виртальной машины

  • guest_os_type - тип устанавливаемой операционной системы

  • iso_checksum - чек сумма для проверки iso образа

  • iso_checksum_type - тип чек-суммы, могут быть md5, sha1, sha256, или sha512

  • iso_url - источник откуда мы будем выкачивать iso образ, может быть либо локальным, либо ссылка на образ или все вместе

  • ssh_username, ssh_password, ssh_port, ssh_wait_timeout - параметры для работы по sshб пользователь,пароль, порт и таймаут после, которого пакер будет считать, что сервер недоступен

  • shutdown_command - команда/команды для выключение

  • vboxmanage - данный параметр служит для изменения параметров виртуальной машины через Virtualbox manager, вы можете изменить количество ядер, оперативной памати и тд

Provisioners

"provisioners":[
   {
      "type":"shell",
      "execute_command":"echo 'vagrant'|sudo -S sh '{{.Path}}'",
      "override":{
         "virtualbox-iso":{
            "scripts":[
               "scripts/base.sh",
               "scripts/vagrant.sh",
               "scripts/virtualbox.sh",
               "scripts/cleanup.sh",
               "scripts/zerodisk.sh",
               "scripts/nginx.sh"
            ]
         }

В моем примере я использую только bash скрипты, но настройка и установка ПО в процессе сборки не ограничивается скриптами, вы можете использовать любые другие ci/cd инструменты и утилиты.

Все мои скрипты раположены в отдельной папке для простоты

Post-processors

  ],
   "post-processors":[
      {
         "type":"vagrant",
         "override":{
            "virtualbox":{
               "output":"ubuntu-14-04-x64-virtualbox.box"
            }
         }
      }
   ],

В моем случае, после сборки я хочу получить vagrant box.

Подготовка preseed

Технология preseed позволяет заранее указать ответы на вопросы, задаваемые при установке, убрав таким образом необходимость отвечать на них вручную. Это позволяет создать полностью автоматические сценарии со всеми необходимыми настройками.

Если копнуть чуть глубже, то можно выяснить, что Debian Installer использует систему debconf для управления процессом установки, а технология preseed просто заранее добавляет нужные ответы в базу данных debconf. Таким образом с помощью preseed можно настроить не только установщик, но и другие приложения, использующие debconf, хотя эта особенность вам вряд ли пригодится.

Каждая инструкция preseed состоит обычно из четырёх частей: владельца, названия параметра, типа параметра и значения. Между частями обязательно должен быть ровно один пробел. Установщик носит имя d-i, и именно это значение будет стоять в первом поле в большинстве инструкций

Приступим, вначале создаем папку где будем хранить файл с preseed

mkdir http

Теперь создаем файл который будет использоваться для автомотизации установки ubuntu с iso образа, vi http/preseed.cfg

Содержание достаточно простое:

debconf debconf/frontend select Noninteractive
choose-mirror-bin mirror/http/proxy string
d-i base-installer/kernel/override-image string linux-server
d-i clock-setup/utc boolean true
d-i clock-setup/utc-auto boolean true
d-i finish-install/reboot_in_progress note
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true
d-i partman-auto-lvm/guided_size string max
d-i partman-auto/choose_recipe select atomic
d-i partman-auto/method string lvm
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-lvm/device_remove_lvm boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
d-i partman/confirm_write_new_label boolean true
 
# Default user
d-i passwd/user-fullname string vagrant
d-i passwd/username string vagrant
d-i passwd/user-password password vagrant
d-i passwd/user-password-again password vagrant
d-i passwd/username string vagrant
 
# Minimum packages (see postinstall.sh)
d-i pkgsel/include string openssh-server
d-i pkgsel/install-language-support boolean false
d-i pkgsel/update-policy select none
d-i pkgsel/upgrade select none
 
d-i time/zone string UTC
d-i user-setup/allow-password-weak boolean true
d-i user-setup/encrypt-home boolean false
tasksel tasksel/first multiselect standard, ubuntu-server


После этого, создаем директорию со скриптами и сами скрипты

Скрипты для Packer(ci/cd на коленки)


mkdir scripts 

scripts kenny$ tree
.
├── base.sh
├── cleanup.sh
├── nginx.sh
├── vagrant.sh
├── virtualbox.sh
└── zerodisk.sh

Теперь можно и начинать писать скрипты, мои примеры ниже

base.sh


apt-get update
apt-get -y upgrade
apt-get -y install linux-headers-$(uname -r)

sed -i -e '/Defaultss+env_reset/a Defaultstexempt_group=sudo' /etc/sudoers
sed -i -e 's/%sudo ALL=(ALL:ALL) ALL/%sudo ALL=NOPASSWD:ALL/g' /etc/sudoers

echo "UseDNS no" >> /etc/ssh/sshd_config

cleanup.sh

apt-get -y autoremove
apt-get -y clean

echo "cleaning up guest additions"
rm -rf VBoxGuestAdditions_*.iso VBoxGuestAdditions_*.iso.?

echo "cleaning up dhcp leases"
rm /var/lib/dhcp/*

echo "cleaning up udev rules"
rm /etc/udev/rules.d/70-persistent-net.rules
mkdir /etc/udev/rules.d/70-persistent-net.rules
rm -rf /dev/.udev/
rm /lib/udev/rules.d/75-persistent-net-generator.rules

vagrant.sh


date > /etc/vagrant_box_build_time

mkdir /home/vagrant/.ssh
wget --no-check-certificate 'https://github.com/mitchellh/vagrant/raw/master/keys/vagrant.pub' -O /home/vagrant/.ssh/authorized_keys
chown -R vagrant /home/vagrant/.ssh
chmod -R go-rwsx /home/vagrant/.ssh

virtualbox.sh


apt-get -y install virtualbox-guest-utils

zerodisk.sh

dd if=/dev/zero of=/EMPTY bs=1M rm -f /EMPTY

nginx.sh


export DEBIAN_FRONTEND=noninteractive

mv /tmp/sources.list /etc/apt/sources.list
wget http://www.dotdeb.org/dotdeb.gpg -O - | apt-key add -

apt-get update

apt-get -qy install nginx
cp /tmp/nginx.conf /etc/nginx/sites-available/default

apt-get -qy install
php5-fpm
php5-cli
php5-intl
php5-xhprof

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
apt-get -qy install mysql-client mysql-server php5-mysqlnd

Валидация темплейта и сборка с помощью Packer

Для валидации нашего темплейта достаточно выполнить команду:


 packer validate template.json

После выполнения команды мы должны увидеть следующее сообщение


Template validated successfully.

После проверки мы можем приступать к сборке:

 packer build ubuntu.json 

После сборки мы получим Vagrant box c именем ubuntu-14-04-x64-virtualbox.box, который уже можно использовать,

Использование Amazon Web services(aws) для сборки Packer’ом

При использование aws в качестве билдера, шаблон упроститься и сократиться, примерный вид шаблона ниже

template_aws.json

{
  "provisioners": [
    {
      "type": "shell",
      "execute_command": "echo 'vagrant'|sudo -S sh '{{.Path}}'",
      "override": {
        "amazon-ebs": {
          "scripts": [
            "scripts/base.sh",
            "scripts/cleanup.sh",
	        "scripts/nginx.sh"
          ]
        }
      }
    }
  ],
  "variables": {
    "aws_access_key": "",
    "aws_secret_key": ""
  },
  "builders": [
    {
      "type": "amazon-ebs",
      "access_key": "{{ user `aws_access_key` }}",
      "secret_key": "{{ user `aws_secret_key` }}",
      "region": "us-east-1",
      "source_ami": "ami-2d39803a",
      "instance_type": "t2.micro",
      "ssh_username": "ubuntu",
      "ami_name": "my-template-{{timestamp}}",
      "associate_public_ip_address": "true",
      "force_deregister": "true",
      "vpc_id": "vpc-80090ae7",
      "launch_block_device_mappings": [
        {
          "device_name": "/dev/sda1",
          "volume_type": "gp2",
          "volume_size": "50",
          "delete_on_termination": "true"
        }]
    }
  ]
}

Как вы можете видеть, я изменил тип билдера и добавил переменные для подключения к aws

  • access_key - access ключ для aws

  • secret_key - sercret ключ для aws

  • region - AWS регион где будет запущен инстанс

  • source_ami - исходная ami(Amazon Machine Images), которую мы будем использовать как базу

  • instance_type - тип aws инстанса

  • ssh_username - пользователь для подключения по ssh

  • ami_name - имя будушей ami, которая будет доступна после сборки

  • associate_public_ip_address - Будет ли у временного инстанса внешний ip адрес, если вы собираете со своей машины, то бесусловно нужен, если из сети aws, то можно и не использовать

  • force_deregister - иногда нужно использоать чтобы не остовались не нужные инстансы

  • vpc_id - в какой vpc сети мы будем запускать наш инстанс

  • launch_block_device_mappings - Какого размера будет EBS(Elastic Block Storage) и каике у него будут параметры

Так же не забываем задать переменные для подключения к aws, вы можете либо добавить их в шаблон либо вводить с консоли.

Валидация и сборка

Валидируем наш новый шаблон

packer validate template_aws.json

Если валидация прошла успешно, то начинаем сборку будущей ami

packer build template_aws.json

После сборки вы можете использовать новую ami для ваших будущих инстансов