Building a VM image with infrastructure tools using Packer
In Yandex Compute Cloud, you can create a VM disk image with additional infrastructure tools using Packer
Use Packer to build a VM image based on Ubuntu Linux 20.04 LTS, with its parameters specified in a configuration file. Add these tools commonly used with Yandex Cloud, to the image:
- Yandex Cloud CLI 0.91.0 or higher
- Terraform
1.1.9 - kubectl
1.23 - Docker
20.10.16 or higher - Git
2.25.1 or higher - Helm
3.9.0 - jq
1.6 or higher - tree
1.8.0 or higher - gRPCurl
1.8.6 - Pulumi
3.33.2 - tmux
3.0a or higher
Using Packer, you will create an auxiliary VM that will have the required software installed on it. Next, you will build an image based on its boot disk. After that, you will delete the auxiliary VM and boot disk.
Follow the same steps to create your own image with all required software.
To build an image and create a VM from it:
- Get your cloud ready.
- Set up your working environment.
- Prepare the image configuration.
- Build the image.
- Create a VM from the image.
If you no longer need the resources you created, delete them.
Get your cloud ready
Sign up for Yandex Cloud and create a billing account:
- Go to the management console
and log in to Yandex Cloud or create an account if you do not have one yet. - On the Yandex Cloud Billing
page, make sure you have a billing account linked and it has theACTIVE
orTRIAL_ACTIVE
status. If you do not have a billing account, create one.
If you have an active billing account, you can go to the cloud page
Learn more about clouds and folders.
Required paid resources
The cost of building a VM image and creating a VM from it includes:
- Fee for storing built images (see Yandex Compute Cloud pricing).
- Fee for VM computing resources (see Yandex Compute Cloud pricing).
Set up your working environment
-
Install Packer:
-
Download Packer and install it by following this guide on the Packer website
.You can also download Packerfor your platform from this Yandex Cloud
mirror. -
After the download is complete, add the path to the directory with the executable to the
PATH
variable. To do this, run this command:export PATH=$PATH:<Packer_executable_directory_path>
Note
Yandex Cloud requires Packer 1.5 or higher.
-
-
Configure the Yandex Compute Builder plugin
:-
Create a file named
config.pkr.hcl
with these contents:packer { required_plugins { yandex = { version = ">= 1.1.2" source = "github.com/hashicorp/yandex" } } }
-
Install the plugin:
packer init <path_to_config.pkr.hcl>
Result:
Installed plugin github.com/hashicorp/yandex v1.1.2 in ...
-
-
Get the info on the available subnets and availability zones. If you do not have any subnets, create one.
CLIAPI-
Run this command:
yc vpc subnet list
Result:
+----------------------+----------------------+----------------------+----------------+---------------+-----------------+ | ID | NAME | NETWORK ID | ROUTE TABLE ID | ZONE | RANGE | +----------------------+----------------------+----------------------+----------------+---------------+-----------------+ | b0c29k6anelk******** | intro2-ru-central1-d | enp45glgitd6******** | | ru-central1-d | [10.130.0.0/24] | | e2ltcj4urgpb******** | intro2-ru-central1-b | enp45glgitd6******** | | ru-central1-b | [10.129.0.0/24] | | e9bn57jvjnbu******** | intro2-ru-central1-a | enp45glgitd6******** | | ru-central1-a | [10.128.0.0/24] | +----------------------+----------------------+----------------------+----------------+---------------+-----------------+
-
From the
ID
column, save the ID of the subnet that will host the auxiliary VM for creating the image; from theZONE
column, save the appropriate availability zone. You will need these parameters later on.
Use the list REST API method for the Subnet resource or the SubnetService/List gRPC API call.
-
-
Specify the values of the variables required for building the image in the command line.
export YC_FOLDER_ID=$(yc config get folder-id) export YC_ZONE="<availability_zone>" export YC_SUBNET_ID="<subnet_ID>" export YC_TOKEN=$(yc iam create-token)
Where:
YC_FOLDER_ID
: ID of the folder with the auxiliary VM used for creating the image. Its value gets populated automatically.YC_ZONE
: Availability zone that will host the auxiliary VM used for creating the image and that you got earlier.YC_SUBNET_ID
: ID of the subnet that will host the auxiliary VM used for creating the image and that you got earlier.YC_TOKEN
: IAM token required for creating VM images; it also gets populated automatically.
-
Generate an SSH key pair. You will need these keys to create a VM and connect to it.
Prepare the image configuration
-
Create an HCL
configuration file, such astoolbox.pkr.hcl
. -
In the configuration file, describe the image parameters:
# Yandex Cloud Toolbox VM Image based on Ubuntu 20.04 LTS # # Provisioner docs: # https://www.packer.io/docs/builders/yandex # variable "YC_FOLDER_ID" { type = string default = env("YC_FOLDER_ID") } variable "YC_ZONE" { type = string default = env("YC_ZONE") } variable "YC_SUBNET_ID" { type = string default = env("YC_SUBNET_ID") } variable "TF_VER" { type = string default = "1.1.9" } variable "KCTL_VER" { type = string default = "1.23.0" } variable "HELM_VER" { type = string default = "3.9.0" } variable "GRPCURL_VER" { type = string default = "1.8.6" } variable "GOLANG_VER" { type = string default = "1.17.2" } variable "PULUMI_VER" { type = string default = "3.33.2" } source "yandex" "yc-toolbox" { folder_id = "${var.YC_FOLDER_ID}" source_image_family = "ubuntu-2004-lts" ssh_username = "ubuntu" use_ipv4_nat = "true" image_description = "Yandex Cloud Ubuntu Toolbox image" image_family = "my-images" image_name = "yc-toolbox" subnet_id = "${var.YC_SUBNET_ID}" disk_type = "network-hdd" zone = "${var.YC_ZONE}" } build { sources = ["source.yandex.yc-toolbox"] provisioner "shell" { inline = [ # Global Ubuntu things "sudo apt-get update", "echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selections", "sudo apt-get install -y unzip python3-pip python3.8-venv", # Yandex Cloud CLI tool "curl --silent --remote-name https://storage.yandexcloud.net/yandexcloud-yc/install.sh", "chmod u+x install.sh", "sudo ./install.sh -a -i /usr/local/ 2>/dev/null", "rm -rf install.sh", "sudo sed -i '$ a source /usr/local/completion.bash.inc' /etc/profile", # Docker "curl --fail --silent --show-error --location https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-keyring.gpg", "echo \"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null", "sudo apt-get update", "sudo apt-get install -y docker-ce containerd.io", "sudo usermod -aG docker $USER", "sudo chmod 666 /var/run/docker.sock", "sudo useradd -m -s /bin/bash -G docker yc-user", # Docker Artifacts "docker pull hello-world", "docker pull -q amazon/aws-cli", "docker pull -q golang:${var.GOLANG_VER}", # Terraform (classic way) #"curl --fail --silent --show-error --location https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-keyring.gpg", #"echo \"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main\" | sudo tee /etc/apt/sources.list.d/hashicorp.list > /dev/null", #"sudo apt-get update", #"sudo apt-get install -y terraform", # # Alternative Option "curl --silent --location https://hashicorp-releases.yandexcloud.net/terraform/${var.TF_VER}/terraform_${var.TF_VER}_linux_amd64.zip --output terraform.zip", "unzip terraform.zip", "sudo install -o root -g root -m 0755 terraform /usr/local/bin/terraform", "rm -rf terraform terraform.zip", # Terraform configuration file ? #"cat <<EOF > ~/.terraformrc \n provider_installation { network_mirror { url = \"https://terraform-mirror.yandexcloud.net/\" include = [\"registry.terraform.io/*/*\"] } direct { exclude = [\"registry.terraform.io/*/*\"] } } \n EOF", # kubectl "curl --silent --location --remote-name https://dl.k8s.io/release/v${var.KCTL_VER}/bin/linux/amd64/kubectl", "sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl", "rm -rf kubectl", # Helm "curl --silent --show-error --location --remote-name https://get.helm.sh/helm-v${var.HELM_VER}-linux-amd64.tar.gz", "tar zxf helm-v${var.HELM_VER}-linux-amd64.tar.gz", "sudo install -o root -g root -m 0755 linux-amd64/helm /usr/local/bin/helm", "rm -rf helm-v${var.HELM_VER}-linux-amd64.tar.gz", "rm -rf linux-amd64", # User can add own repo after login like this: # helm repo add stable https://charts.helm.sh/stable ## grpccurl "curl --silent --show-error --location --remote-name https://github.com/fullstorydev/grpcurl/releases/download/v${var.GRPCURL_VER}/grpcurl_${var.GRPCURL_VER}_linux_x86_64.tar.gz", "tar zxf grpcurl_${var.GRPCURL_VER}_linux_x86_64.tar.gz", "sudo install -o root -g root -m 0755 grpcurl /usr/local/bin/grpcurl", "rm -rf grpcurl_${var.GRPCURL_VER}_linux_x86_64.tar.gz", "rm -rf grpcurl", # Pulumi "curl --silent --show-error --location --remote-name https://get.pulumi.com/releases/sdk/pulumi-v${var.PULUMI_VER}-linux-x64.tar.gz", "tar zxf pulumi-v${var.PULUMI_VER}-linux-x64.tar.gz", "sudo cp pulumi/* /usr/local/bin/", "rm -rf pulumi-v${var.PULUMI_VER}-linux-x64.tar.gz", "rm -rf pulumi", # Other packages "sudo apt-get install -y git jq tree tmux", # Clean "rm -rf .sudo_as_admin_successful", # Test - Check versions for installed components "echo '=== Tests Start ==='", "yc version", "terraform version", "docker version", "kubectl version --client=true", "helm version", "grpcurl --version", "git --version", "jq --version", "tree --version", "pulumi version", "echo '=== Tests End ==='" ] } }
Warning
You cannot use both the provisioner "shell"
and metadata
parameters in the configuration file at the same time.
Build the image
-
In the command line, navigate to the configuration file directory.
cd <path_to_configuration_file_directory>
-
Make sure the image configuration file is correct using this command:
packer validate yc-toolbox.pkr.hcl
Where
yc-toolbox.pkr.hcl
is the configuration file name.If the configuration is correct, you will get this message:
The configuration is valid.
-
Build the image with the command:
packer build yc-toolbox.pkr.hcl
Where
yc-toolbox.pkr.hcl
is the configuration file name. -
After the image is built, you will get a notification message:
... ==> Builds finished. The artifacts of successful builds are: --> yandex.yc-toolbox: A disk image was created: yc-toolbox (id: fd83j475posv********) with family name infra-images
Save the ID of the image you built (
id
). Use this ID to create a VM later. -
Make sure you can see the image you built Yandex Cloud.
CLIAPIRun this command:
yc compute image list
Result:
+----------------------+------------+-----------+----------------------+--------+ | ID | NAME | FAMILY | PRODUCT IDS | STATUS | +----------------------+------------+-----------+----------------------+--------+ | fd83j475posv******** | yc-toolbox | my-images | f2ek1vhoppg2******** | READY | +----------------------+------------+-----------+----------------------+--------+
Use the list REST API method for the Image resource or the ImageService/List gRPC API call.
Create a VM from the image
-
Specify the values of the variables required for creating your VM. To do this, run this command:
export VM_NAME="<VM_name>" export YC_IMAGE_ID="<image_ID>" export YC_SUBNET_ID="<subnet_ID>" export YC_ZONE="<availability_zone>"
Where:
VM_NAME
: Name of the new VM.YC_IMAGE_ID
: ID of the image to create the VM from. You got this ID earlier.YC_SUBNET_ID
: ID of the subnet to host the VM. You got this ID earlier.YC_ZONE
: Availability zone for the VM that you got earlier.
-
Create your VM from the image you built.
CLIAPIRun this command:
yc compute instance create \ --name $VM_NAME \ --hostname $VM_NAME \ --zone=$YC_ZONE \ --create-boot-disk size=20GB,image-id=$YC_IMAGE_ID \ --cores=2 \ --memory=8G \ --core-fraction=100 \ --network-interface subnet-id=$YC_SUBNET_ID,ipv4-address=auto,nat-ip-version=ipv4 \ --ssh-key <path_to_public_part_of_SSH_key>
Where:
--name
: Name of the new VM.--hostname
: VM host name.--zone
: Availability zone.--create-boot-disk
: Boot disk properties:size
stands for size, andimage-id
, for the ID of the image being used.--cores
: Number of vCPUs.--memory
: Amount of RAM.--core-fraction
: Basic vCPU performance in percentage.--network-interface
: Network interface proterties, wheresubnet-id
is the subnet ID,ipv4-address
, the internal IPv4 address, andnat-ip-version
, the IP specification for the egress NAT.--ssh-key
: Public part of the SSH key.
The command outputs info on the VM you created. Save the VM's public IP address:
... one_to_one_nat: address: 62.84.122.151 ...
Learn more about creating a VM from a custom image.
Use the create REST API method for the Instance resource or the InstanceService/Create gRPC API call.
-
Connect to the VM over SSH:
ssh -i <path_to_private_part_of_SSH_key> yc-user@<VM_public_IP_address>
How to delete the resources you created
To stop paying for the resources you created: