github.com/hashicorp/packer@v1.14.3/website/content/guides/packer-on-cicd/pipelineing-builds.mdx (about) 1 --- 2 page_title: Packer Build Pipelines 3 description: >- 4 Learn how to shorten Packer build times and improve reliability. Start from an ISO, customize using the virtualbox-ovf builder, and improve efficiency. 5 --- 6 7 # Why Create a Template Pipeline? 8 9 A common issue users face when beginning to create their Packer templates is 10 that while they may need several specialized images, the early provisioning 11 steps are all the same. It can feel tedious to copy all of those images' basic 12 configuraton into each build template. It can feel even more tedious to wait a 13 long time for similar builds to run duplicate steps. 14 15 This is one reason why Packer recommends breaking your builds into small, 16 discrete steps. Not only does it allow you to create "base" images that you can 17 build from to create further customizations, but it also allows you to save 18 time in your build process because the "base" images are likely to change less 19 than your customizations. 20 21 It also makes it so that a failing build takes less time to debug and re-run. 22 23 In this example, we will use the Virtualbox builders, but the concepts from 24 this example can be applied to other builders as well. 25 26 ## Starting from an ISO 27 28 Here is an extremely basic virtualbox-iso template: 29 30 <Tabs> 31 <Tab heading="HCL2"> 32 33 ```hcl 34 source "virtualbox-iso" "step_1" { 35 boot_command = ["<esc><wait>", "<esc><wait>", "<enter><wait>", 36 "/install/vmlinuz<wait>", " initrd=/install/initrd.gz", 37 " auto-install/enable=true", " debconf/priority=critical", 38 " preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ubuntu_preseed.cfg<wait>", 39 " -- <wait>", "<enter><wait>"] 40 disk_size = "40960" 41 guest_os_type = "Ubuntu_64" 42 http_directory = "./http" 43 iso_checksum = "sha256:946a6077af6f5f95a51f82fdc44051c7aa19f9cfc5f737954845a6050543d7c2" 44 iso_url = "http://old-releases.ubuntu.com/releases/14.04.1/ubuntu-14.04-server-amd64.iso" 45 shutdown_command = "echo 'vagrant' | sudo -S shutdown -P now" 46 ssh_password = "vagrant" 47 ssh_port = 22 48 ssh_username = "vagrant" 49 vm_name = "vbox-example" 50 } 51 build { 52 sources = ["source.virtualbox-iso.step_1"] 53 54 55 provisioner "shell" { 56 inline = ["echo initial provisioning"] 57 } 58 post-processor "manifest" { 59 output = "stage-1-manifest.json" 60 } 61 } 62 ``` 63 64 </Tab> 65 <Tab heading="JSON"> 66 67 ```json 68 { 69 "builders": [ 70 { 71 "type": "virtualbox-iso", 72 "vm_name": "vbox-example", 73 "boot_command": [ 74 "<esc><wait>", 75 "<esc><wait>", 76 "<enter><wait>", 77 "/install/vmlinuz<wait>", 78 " initrd=/install/initrd.gz", 79 " auto-install/enable=true", 80 " debconf/priority=critical", 81 " preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ubuntu_preseed.cfg<wait>", 82 " -- <wait>", 83 "<enter><wait>" 84 ], 85 "http_directory": "./http", 86 87 "disk_size": "40960", 88 "guest_os_type": "Ubuntu_64", 89 "iso_checksum": "sha256:946a6077af6f5f95a51f82fdc44051c7aa19f9cfc5f737954845a6050543d7c2", 90 "iso_url": "http://old-releases.ubuntu.com/releases/14.04.1/ubuntu-14.04-server-amd64.iso", 91 92 "shutdown_command": "echo 'vagrant' | sudo -S shutdown -P now", 93 94 "ssh_port": 22, 95 "ssh_username": "vagrant", 96 "ssh_password": "vagrant" 97 } 98 ], 99 "provisioners": [ 100 { 101 "type": "shell", 102 "inline": ["echo initial provisioning"] 103 } 104 ], 105 "post-processors": [ 106 { 107 "type": "manifest", 108 "output": "stage-1-manifest.json" 109 } 110 ] 111 } 112 ``` 113 114 </Tab> 115 </Tabs> 116 117 In order to build using this template, create a directory named "http" in your 118 current working directory. Copy the minimal example from our 119 [preseed guide](/packer/guides/automatic-operating-system-installs/preseed_ubuntu#examples) 120 into a file in your http directory and name it "ubuntu_preseed.cfg". Copy the 121 above json template into your current working directory and save it as 122 "example_virtualbox_iso.json" 123 124 To run the build, call `packer build example_virtualbox_iso.json`. 125 126 This example does not set the output_directory or output_filename, so the file 127 will be placed in a default name of "output-virtualbox-iso/vbox-example.ovf" -- 128 the builder will print this file name to the UI output, but in this example the 129 [manifest](/packer/docs/post-processors/manifest) post-processor 130 to will store build information, including the names of the output files, in a 131 json file named "stage-1-manifest.json". From there, you can programmatically 132 look up the output file information. 133 134 ## Customizing the iso using the virtualbox-ovf builder 135 136 That output filename generated in the first stage can be used as the 137 [source_path](/packer/plugins/builders/virtualbox/ovf#source_path) 138 for the virtualbox-ovf builder. 139 140 <Tabs> 141 <Tab heading="HCL2"> 142 143 ```hcl 144 source "virtualbox-ovf" "step_2" { 145 shutdown_command = "echo 'vagrant' | sudo -S shutdown -P now" 146 source_path = "output-virtualbox-iso/vbox-example.ovf" 147 ssh_password = "vagrant" 148 ssh_port = 22 149 ssh_username = "vagrant" 150 vm_name = "virtualbox-example-ovf" 151 } 152 153 build { 154 sources = ["source.virtualbox-ovf.step_2"] 155 156 provisioner "shell" { 157 inline = ["echo secondary provisioning"] 158 } 159 } 160 161 ``` 162 163 </Tab> 164 <Tab heading="JSON"> 165 166 ```json 167 { 168 "builders": [ 169 { 170 "type": "virtualbox-ovf", 171 "vm_name": "virtualbox-example-ovf", 172 173 "shutdown_command": "echo 'vagrant' | sudo -S shutdown -P now", 174 "source_path": "output-virtualbox-iso/vbox-example.ovf", 175 176 "ssh_password": "vagrant", 177 "ssh_port": 22, 178 "ssh_username": "vagrant" 179 } 180 ], 181 "provisioners": [ 182 { 183 "inline": ["echo secondary provisioning"], 184 "type": "shell" 185 } 186 ] 187 } 188 ``` 189 190 </Tab> 191 </Tabs> 192 193 ## More efficiencies 194 195 You may find that you want to run time-consuming import post-processors like 196 the "amazon-import" post-processor independently of the build that produces 197 the artifacts you want to process. 198 199 In this case, you can use a null builder 200 and manually modify the input to the post-processing chain so that you can get 201 the behavior you want. The below example shows a "vagrant" post-processor 202 being used with a null builder, and manually sets the artifact from our 203 stage-2 ovf build: 204 205 <Tabs> 206 <Tab heading="HCL2"> 207 208 ```hcl 209 source "null" "step_3" { 210 communicator = "none" 211 } 212 213 build { 214 sources = ["source.null.step_3"] 215 216 post-processors { 217 post-processor "artifice" { 218 files = ["output-virtualbox-ovf/virtualbox-example-ovf.ovf", "output-virtualbox-ovf/virtualbox-example-ovf-disk001.vmdk"] 219 } 220 post-processor "vagrant" { 221 provider_override = "virtualbox" 222 } 223 } 224 } 225 ``` 226 227 </Tab> 228 <Tab heading="JSON"> 229 230 ```json 231 { 232 "builders": [ 233 { 234 "type": "null", 235 "communicator": "none" 236 } 237 ], 238 "post-processors": [ 239 [ 240 { 241 "type": "artifice", 242 "files": [ 243 "output-virtualbox-ovf/virtualbox-example-ovf.ovf", 244 "output-virtualbox-ovf/virtualbox-example-ovf-disk001.vmdk" 245 ] 246 }, 247 { 248 "type": "vagrant", 249 "provider_override": "virtualbox" 250 } 251 ] 252 ] 253 } 254 ``` 255 256 </Tab> 257 </Tabs> 258 259 By using the null builder instead of just running an ovf builder, we can spare 260 ourselves all of the time Packer would normally spend launching and destroying 261 VMs. 262 263 ## Putting it all together 264 265 Packer templates don't come with a custom "glue" to bind them together. We 266 recommend using your CI system or wrapping scripts to connect the templates 267 into a chain. 268 269 ## Chaining together several of the same builders to make "save points" 270 271 If you want to use the same builder for several builds in a row, this can feel 272 tedious to implement in json. We recommend you try using HCL configs so that 273 you can reuse the same source in several builds: 274 275 HCL templates work by allowing you to draw sources and variables from multiple 276 different files in a single directory, so the following files are assumed to 277 exist in their own folder: 278 279 sources.pkr.hcl 280 281 ```hcl 282 // In your sources file, you can create a configuration for a builder that you 283 // want to reuse between multiple steps in the build. Just leave the source 284 // and destination images out of this source, and set them specifically in each 285 // step without having to set all of the other options over and over again. 286 287 source "docker" "example" { 288 commit = true 289 // any other configuration you want for your Docker containers 290 } 291 ``` 292 293 build.pkr.hcl 294 295 ```hcl 296 build { 297 // Make sure to name your builds so that you can selectively run them one at 298 // a time. 299 name = "step1" 300 301 source "source.docker.example" { 302 image = "ubuntu" 303 } 304 305 provisioner "shell" { 306 inline = ["echo example provisioner"] 307 } 308 provisioner "shell" { 309 inline = ["echo another example provisioner"] 310 } 311 provisioner "shell" { 312 inline = ["echo a third example provisioner"] 313 } 314 315 // Make sure that the output from your build can be used in the next build. 316 // In this example, we're tagging the Docker image so that the step-2 317 // builder can find it without us having to track it down in a manifest. 318 post-processor "docker-tag" { 319 repository = "ubuntu" 320 tag = ["step-1-output"] 321 } 322 } 323 324 build { 325 name = "step2" 326 327 source "source.docker.example" { 328 // This is the tagged artifact from the stage 1 build. You can retrieve 329 // this from a manifest file and setting it as a variable on the command 330 // line, or by making sure you define and know the output of the build, 331 // if it's something you can define like an output name or directory. 332 image = "ubuntu:step-1-output" 333 // disable the pull if your image tag only exists locally 334 pull = false 335 } 336 337 provisioner "shell" { 338 inline = ["echo another provision!"] 339 } 340 } 341 ``` 342 343 pipeline.sh 344 345 ```sh 346 #!/bin/bash 347 set -e # abort if there is an issue with any build 348 packer build -only='step1.docker.example' . 349 packer build -only='step2.docker.example' . 350 ``` 351 352 To run the pipeline, call pipeline.sh. You can create as many build steps as 353 you want. Each can either inhabit one file, or you can put multiple steps in 354 a single file like shown in the example above.