├── stardata-install.tar.gz ├── stardata-install └── install.sh ├── LICENSE ├── centos7-base.json ├── docroot └── c7-kvm-ks.cfg └── README.md /stardata-install.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stardata/packer-centos7-kvm-example/HEAD/stardata-install.tar.gz -------------------------------------------------------------------------------- /stardata-install/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "This is a basic example of a shell provisioner." 4 | yum -y install screen vim 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Stardata S.r.l. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /centos7-base.json: -------------------------------------------------------------------------------- 1 | { 2 | "builders": 3 | [ 4 | { 5 | "type": "qemu", 6 | "accelerator": "kvm", 7 | "headless": true, 8 | "qemuargs": [ 9 | [ "-m", "2048M" ], 10 | [ "-smp", "cpus=1,maxcpus=16,cores=4" ] 11 | ], 12 | "disk_interface": "virtio", 13 | "disk_size": 100000, 14 | "format": "qcow2", 15 | "net_device": "virtio-net", 16 | 17 | "iso_url": "http://centos.mirror.garr.it/centos/7.7.1908/isos/x86_64/CentOS-7-x86_64-Minimal-1908.iso", 18 | "iso_checksum": "9a2c47d97b9975452f7d582264e9fc16d108ed8252ac6816239a3b58cef5c53d", 19 | "iso_checksum_type": "sha256", 20 | 21 | "vm_name": "centos7-base", 22 | "output_directory": "centos7-base-img", 23 | 24 | "http_directory": "docroot", 25 | "http_port_min": 10082, 26 | "http_port_max": 10089, 27 | 28 | "ssh_host_port_min": 2222, 29 | "ssh_host_port_max": 2229, 30 | 31 | "ssh_username": "root", 32 | "ssh_password": "CHANGEME", 33 | "ssh_port": 22, 34 | "ssh_wait_timeout": "1200s", 35 | 36 | "boot_wait": "40s", 37 | "boot_command": [ 38 | " text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/c7-kvm-ks.cfg" 39 | ], 40 | 41 | "shutdown_command": "shutdown -P now" 42 | } 43 | ], 44 | 45 | "provisioners": 46 | [ 47 | { 48 | "type": "shell-local", 49 | "command": "tar zcf stardata-install.tar.gz stardata-install/" 50 | }, 51 | { 52 | "type": "file", 53 | "source": "stardata-install.tar.gz", 54 | "destination": "/root/stardata-install.tar.gz" 55 | }, 56 | { 57 | "type": "shell", 58 | "pause_before": "5s", 59 | "inline": [ 60 | "cd /root/", 61 | "tar zxf stardata-install.tar.gz", 62 | "cd stardata-install/", 63 | "./install.sh", 64 | "yum clean all" 65 | ] 66 | } 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /docroot/c7-kvm-ks.cfg: -------------------------------------------------------------------------------- 1 | # reference: https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Installation_Guide/sect-kickstart-syntax.html 2 | # based on one of our production installs with some modifications 3 | # and some integrations from https://raw.githubusercontent.com/geerlingguy/packer-centos-7/master/http/ks.cfg 4 | 5 | # Run the installer 6 | install 7 | 8 | # Use CDROM installation media 9 | cdrom 10 | 11 | # System language 12 | lang en_US.UTF-8 13 | 14 | # Keyboard layouts 15 | keyboard us 16 | 17 | # Enable more hardware support 18 | unsupported_hardware 19 | 20 | # Network information 21 | network --bootproto=dhcp --hostname=centos7-test.stardata.lan 22 | 23 | # System authorization information 24 | auth --enableshadow --passalgo=sha512 25 | 26 | # Root password 27 | rootpw CHANGEME 28 | 29 | # Selinux in permissive mode (will be disabled by provisioners) 30 | selinux --permissive 31 | 32 | # System timezone 33 | timezone UTC 34 | 35 | # System bootloader configuration 36 | bootloader --append=" crashkernel=auto" --location=mbr --boot-drive=vda 37 | 38 | # Run the text install 39 | text 40 | 41 | # Skip X config 42 | skipx 43 | 44 | # Only use /dev/vda 45 | ignoredisk --only-use=vda 46 | 47 | # Overwrite the MBR 48 | zerombr 49 | 50 | # Partition clearing information 51 | clearpart --none --initlabel 52 | 53 | # Disk partitioning information 54 | part pv.305 --fstype="lvmpv" --ondisk=vda --size=98000 55 | part /boot --fstype="ext4" --ondisk=vda --size=1024 --label=BOOT 56 | volgroup VGsystem --pesize=4096 pv.305 57 | logvol /opt --fstype="ext4" --size=5120 --name=LVopt --vgname=VGsystem 58 | logvol /usr --fstype="ext4" --size=10240 --name=LVusr --vgname=VGsystem 59 | logvol /var --fstype="ext4" --size=10240 --name=LVvar --vgname=VGsystem 60 | logvol swap --fstype="swap" --size=4096 --name=LVswap --vgname=VGsystem 61 | logvol / --fstype="ext4" --size=10240 --label="ROOT" --name=LVroot --vgname=VGsystem 62 | logvol /tmp --fstype="ext4" --size=5120 --name=LVtmp --vgname=VGsystem 63 | logvol /var/log --fstype="ext4" --size=10240 --name=LVvarlog --vgname=VGsystem 64 | logvol /home --fstype="ext4" --size=5120 --name=LVhome --vgname=VGsystem 65 | 66 | 67 | # Do not run the Setup Agent on first boot 68 | firstboot --disabled 69 | 70 | # Accept the EULA 71 | eula --agreed 72 | 73 | # System services 74 | services --disabled="chronyd" --enabled="sshd" 75 | 76 | # Reboot the system when the install is complete 77 | reboot 78 | 79 | 80 | # Packages 81 | 82 | %packages --ignoremissing --excludedocs 83 | @^minimal 84 | @core 85 | kexec-tools 86 | # unnecessary firmware 87 | -aic94xx-firmware 88 | -atmel-firmware 89 | -b43-openfwwf 90 | -bfa-firmware 91 | -ipw2100-firmware 92 | -ipw2200-firmware 93 | -ivtv-firmware 94 | -iwl100-firmware 95 | -iwl1000-firmware 96 | -iwl3945-firmware 97 | -iwl4965-firmware 98 | -iwl5000-firmware 99 | -iwl5150-firmware 100 | -iwl6000-firmware 101 | -iwl6000g2a-firmware 102 | -iwl6050-firmware 103 | -libertas-usb8388-firmware 104 | -ql2100-firmware 105 | -ql2200-firmware 106 | -ql23xx-firmware 107 | -ql2400-firmware 108 | -ql2500-firmware 109 | -rt61pci-firmware 110 | -rt73usb-firmware 111 | -xorg-x11-drv-ati-firmware 112 | -zd1211-firmware 113 | 114 | %end 115 | 116 | %addon com_redhat_kdump --enable --reserve-mb='auto' 117 | 118 | %end 119 | 120 | %post 121 | yum -y upgrade 122 | yum clean all 123 | %end 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Packer CentOS 7 KVM example 2 | 3 | [Packer](https://packer.io "Packer website") is a tool to automate the installation and provisioning of virtual machines to generate images for various platforms. You can have, for example, images for your test environment created with QEMU/KVM or Docker and images for your production environment created as Amazon AMI or VMware VMX images. 4 | 5 | Basically, **Packer starts a VM in a private environment, feeds an ISO to the VM to install the operating system** (using kickstart, preseed or various other automation mechanisms) **and then waits until the VM restarts and is available via SSH** or WinRM. When it is available, Packer **can run different provisioners** (from bash scripts to your favourite tool like Ansible, Chef or Puppet) to setup the system as required. Once it's done provisioning, it will shut down the VM and possibly apply post-processors that can, for example, pack a VMware image made by multiple files in a single file and so on. 6 | 7 | In this article I'll show you the steps to **create a CentOS 7 image on KVM** and explain some important settings. 8 | 9 | First thing, you'll need Packer. You can download it from https://www.packer.io/downloads.html 10 | 11 | ```bash 12 | # curl -O https://releases.hashicorp.com/packer/0.11.0/packer_0.11.0_linux_amd64.zip 13 | # curl -O https://releases.hashicorp.com/packer/0.11.0/packer_0.11.0_SHA256SUMS 14 | # curl -O https://releases.hashicorp.com/packer/0.11.0/packer_0.11.0_SHA256SUMS.sig 15 | # gpg --recv-keys 51852D87348FFC4C 16 | # gpg --verify packer_0.11.0_SHA256SUMS.sig packer_0.11.0_SHA256SUMS 17 | # sha256sum -c packer_0.11.0_SHA256SUMS 2>/dev/null | grep OK 18 | # unzip packer*.zip ; rm -f packer*.zip 19 | # chmod +x packer 20 | # mv packer /usr/bin/packer.io 21 | ``` 22 | 23 | I already did something "different" from the official documentation, sorry about that, but **CentOS and Fedora already have a completely unrelated program named packer in /usr/sbin/, so to avoid confusion I named the Packer binary packer.io**. All my examples will use this syntax, so make sure to keep that in mind when you'll check other examples on the official website or other blogs. 24 | 25 | Let's make sure we have all we need to run the example. On my CentOS 7 host, I had to install: 26 | 27 | ```bash 28 | # yum -y install epel-release 29 | # yum -y install --enablerepo=epel qemu-system-x86 30 | ``` 31 | 32 | If you're running this example on a remote host, you'll probably want to **setup X11 forwarding** to be able to see the QEMU console. You'll need to edit your server's `/etc/ssh/sshd_config` file and make sure you have these options enabled: 33 | 34 | ``` 35 | X11Forwarding yes 36 | X11UseLocalhost no 37 | ``` 38 | 39 | Then you'll need to restart sshd and make sure you have at least `xauth` installed: 40 | 41 | ```bash 42 | # service sshd restart 43 | # yum -y install xauth 44 | ``` 45 | 46 | At this point by logging to your remote host with the `-X` option to `ssh`, you should be able to forward X to your local system and see the QEMU graphical console: 47 | 48 | ```bash 49 | # ssh -X user@remotehost 'qemu-system-x86_64' 50 | ``` 51 | 52 | If you still have problems, [this article](http://www.cyberciti.biz/faq/how-to-fix-x11-forwarding-request-failed-on-channel-0/) helped me solve a few issues: http://www.cyberciti.biz/faq/how-to-fix-x11-forwarding-request-failed-on-channel-0/ 53 | 54 | **Now you'll need a work directory**. One important thing to note is that Packer will use this directory, and subdirectories, as a stage for the files, including the VM disk image, so I highly recommend to create this workdir on a *fast storage* (SSD works best). In my case, I created it on my RAID 10 array and assigned ownership to my unprivileged user: 55 | 56 | ```bash 57 | # mkdir -p /storage/packer.io/centos7-base 58 | # chown velenux:velenux -R /storage/packer.io 59 | ``` 60 | 61 | At this point you should not need the root console anymore. If you have problems starting qemu/kvm you'll probably need to add your unprivileged user to the appropriate groups and login again. 62 | 63 | We're finally ready to start exploring Packer. Our work directory will contain 3 main components: a **packer configuration file**, a **kickstart file** to setup our CentOS installation automatically and a **provisioning script** that will take care of post-installation setup of the virtual machine. 64 | 65 | To make things easier I created a **public github repo** with an example you can clone on https://github.com/stardata/packer-centos7-kvm-example 66 | 67 | The first thing we're going to examine is the packer configuration file, `centos7-base.json`: 68 | 69 | ```json 70 | { 71 | "builders": 72 | [ 73 | { 74 | "type": "qemu", 75 | "accelerator": "kvm", 76 | "headless": false, 77 | "qemuargs": [ 78 | [ "-m", "2048M" ], 79 | [ "-smp", "cpus=1,maxcpus=16,cores=4" ] 80 | ], 81 | "disk_interface": "virtio", 82 | "disk_size": 100000, 83 | "format": "qcow2", 84 | "net_device": "virtio-net", 85 | 86 | "iso_url": "http://centos.fastbull.org/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-1511.iso", 87 | "iso_checksum": "88c0437f0a14c6e2c94426df9d43cd67", 88 | "iso_checksum_type": "md5", 89 | 90 | "vm_name": "centos7-base", 91 | "output_directory": "centos7-base-img", 92 | 93 | "http_directory": "docroot", 94 | "http_port_min": 10082, 95 | "http_port_max": 10089, 96 | 97 | "ssh_host_port_min": 2222, 98 | "ssh_host_port_max": 2229, 99 | 100 | "ssh_username": "root", 101 | "ssh_password": "CHANGEME", 102 | "ssh_port": 22, 103 | "ssh_wait_timeout": "1200s", 104 | 105 | "boot_wait": "40s", 106 | "boot_command": [ 107 | " text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/c7-kvm-ks.cfg" 108 | ], 109 | 110 | "shutdown_command": "shutdown -P now" 111 | } 112 | ], 113 | 114 | "provisioners": 115 | [ 116 | { 117 | "type": "shell-local", 118 | "command": "tar zcf stardata-install.tar.gz stardata-install/" 119 | }, 120 | { 121 | "type": "file", 122 | "source": "stardata-install.tar.gz", 123 | "destination": "/root/stardata-install.tar.gz" 124 | }, 125 | { 126 | "type": "shell", 127 | "pause_before": "5s", 128 | "inline": [ 129 | "cd /root/", 130 | "tar zxf stardata-install.tar.gz", 131 | "cd stardata-install/", 132 | "./install.sh", 133 | "yum clean all" 134 | ] 135 | } 136 | ] 137 | } 138 | ``` 139 | 140 | I tried to arrange the contents to make it easier to read for newcomers. 141 | 142 | The first thing you should notice is the **general structure of the file**: we have two sections, `builders` and `provisioners`. 143 | 144 | In our example, the first is a list of only one element (the QEMU/KVM builder), but you could easily add more builders after that, to create images using different plugins. 145 | 146 | In the `provisioners` section we have 3 different provisioners that will be run in sequence: the first runs a **command on the host** system, the second **transfers a file** (created/updated by the first) **to the VM** and the third **runs a series of commands on the VM**. We'll talk a bit more about them later. 147 | 148 | Now let's examine our first `builder`: based on this configuration, Packer will run QEMU with 1 CPU/4 cores and 2G of RAM, creating a qcow2 virt-io disk with 100000M of space available. Note that qcow2 is a sparse file, or *"thin provision disk"*: the disk image will only use the space required and grow when required. Please notice how I set `"headless"` to `false`. This is a **boolean value, not a string**, and when you finish testing and debugging your Packer configuration you'll probably want to set it back to `true`. 149 | 150 | The next set of parameters inform Packer of the URI where to find the installation ISO for this image. This ISO will be downloaded and cached locally during the first build, and you will probably want to pick a better mirror from: http://isoredirect.centos.org/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-1511.iso 151 | 152 | `vm_name` is pretty self-explanatory and `output_directory` is where the final image will be, if the build completes correctly. 153 | 154 | The `http_*` parameters are required to setup the HTTP server that Packer will start during the build to serve files (for example, the kickstart file) to the virtual machine. 155 | 156 | The `ssh_host_*` parameters specify the ports that will be redirected from the Host to the VM during the build. Packer utilizes ranges because it can run multiple builds (for multiple platforms) in parallel and allocates different ports for different builds. You can read more about that on the [official documentation](https://www.packer.io/docs/builders/qemu.html). 157 | 158 | The next set of parameters specifies the values to use when accessing the VM via SSH. Note that the password must be the same you set in your kickstart and the `wait_timeout` is the maximum time that Packer will wait for the VM to become accessible via SSH. Considering it will have to install the distribution first, I set this to `1200s` (20m), altho in my tests the whole build process - including provisioning that happens after the system is available via SSH - took about 13m. 159 | 160 | The `boot_wait` parameter sets a fixed amount of time that Packer will wait before proceeding with the `boot_command`; it's important to specify a value that is **long enough to allow the system to reach the distribution boot prompt, but short enough so that the default installation won't start**. 161 | 162 | The `boot_command` parameter allows to emulate various key-presses to interact with the bootscreen. In my specific case, I'm emulating pressing the `up` key (to skip the media check), then `tab` to autocomplete the boot parameters based on the selected item and then I add the parameters required for a kickstart installation and emulate the pression of the `enter` key. 163 | Running the build you'll see this happen on your screen without any interaction on your part! 164 | 165 | Lastly, the `shutdown_command` is the command that will be run after the provisioners. 166 | 167 | Before talking about the provisioners, it's worth examining the kickstart file `docroot/c7-kvm-ks.cfg`. 168 | 169 | ```kickstart 170 | # reference: https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Installation_Guide/sect-kickstart-syntax.html 171 | # based on one of our production installs with some modifications 172 | # and some integrations from https://raw.githubusercontent.com/geerlingguy/packer-centos-7/master/http/ks.cfg 173 | 174 | # Run the installer 175 | install 176 | 177 | # Use CDROM installation media 178 | cdrom 179 | 180 | # System language 181 | lang en_US.UTF-8 182 | 183 | # Keyboard layouts 184 | keyboard us 185 | 186 | # Enable more hardware support 187 | unsupported_hardware 188 | 189 | # Network information 190 | network --bootproto=dhcp --hostname=centos7-test.stardata.lan 191 | 192 | # System authorization information 193 | auth --enableshadow --passalgo=sha512 194 | 195 | # Root password 196 | rootpw CHANGEME 197 | 198 | # Selinux in permissive mode (will be disabled by provisioners) 199 | selinux --permissive 200 | 201 | # System timezone 202 | timezone UTC 203 | 204 | # System bootloader configuration 205 | bootloader --append=" crashkernel=auto" --location=mbr --boot-drive=vda 206 | 207 | # Run the text install 208 | text 209 | 210 | # Skip X config 211 | skipx 212 | 213 | # Only use /dev/vda 214 | ignoredisk --only-use=vda 215 | 216 | # Overwrite the MBR 217 | zerombr 218 | 219 | # Partition clearing information 220 | clearpart --none --initlabel 221 | 222 | # Disk partitioning information 223 | part pv.305 --fstype="lvmpv" --ondisk=vda --size=98000 224 | part /boot --fstype="ext4" --ondisk=vda --size=1024 --label=BOOT 225 | volgroup VGsystem --pesize=4096 pv.305 226 | logvol /opt --fstype="ext4" --size=5120 --name=LVopt --vgname=VGsystem 227 | logvol /usr --fstype="ext4" --size=10240 --name=LVusr --vgname=VGsystem 228 | logvol /var --fstype="ext4" --size=10240 --name=LVvar --vgname=VGsystem 229 | logvol swap --fstype="swap" --size=4096 --name=LVswap --vgname=VGsystem 230 | logvol / --fstype="ext4" --size=10240 --label="ROOT" --name=LVroot --vgname=VGsystem 231 | logvol /tmp --fstype="ext4" --size=5120 --name=LVtmp --vgname=VGsystem 232 | logvol /var/log --fstype="ext4" --size=10240 --name=LVvarlog --vgname=VGsystem 233 | logvol /home --fstype="ext4" --size=5120 --name=LVhome --vgname=VGsystem 234 | 235 | 236 | # Do not run the Setup Agent on first boot 237 | firstboot --disabled 238 | 239 | # Accept the EULA 240 | eula --agreed 241 | 242 | # System services 243 | services --disabled="chronyd" --enabled="sshd" 244 | 245 | # Reboot the system when the install is complete 246 | reboot 247 | 248 | 249 | # Packages 250 | 251 | %packages --ignoremissing --excludedocs 252 | @^minimal 253 | @core 254 | kexec-tools 255 | # unnecessary firmware 256 | -aic94xx-firmware 257 | -atmel-firmware 258 | -b43-openfwwf 259 | -bfa-firmware 260 | -ipw2100-firmware 261 | -ipw2200-firmware 262 | -ivtv-firmware 263 | -iwl100-firmware 264 | -iwl1000-firmware 265 | -iwl3945-firmware 266 | -iwl4965-firmware 267 | -iwl5000-firmware 268 | -iwl5150-firmware 269 | -iwl6000-firmware 270 | -iwl6000g2a-firmware 271 | -iwl6050-firmware 272 | -libertas-usb8388-firmware 273 | -ql2100-firmware 274 | -ql2200-firmware 275 | -ql23xx-firmware 276 | -ql2400-firmware 277 | -ql2500-firmware 278 | -rt61pci-firmware 279 | -rt73usb-firmware 280 | -xorg-x11-drv-ati-firmware 281 | -zd1211-firmware 282 | 283 | %end 284 | 285 | %addon com_redhat_kdump --enable --reserve-mb='auto' 286 | 287 | %end 288 | 289 | %post 290 | yum -y upgrade 291 | yum clean all 292 | %end 293 | ``` 294 | 295 | As you can see the file is commented, so I will not spend too much time on it, but it's important to note how **the password is the same we set in the Packer configuration** and the **network options are set on DHCP**, because Packer will run a private network for the build and provide an IP address to the VM. 296 | The **partitioning scheme** is similar to what we use in production and provided as an example, but I highly recommend you use your own partitioning scheme that you can retrieve in the file `/root/anaconda-ks.cfg` after a _"normal"_ installation. 297 | 298 | After the operating system is installed and restarted, SSH becomes available and Packer proceeds to run the providers. 299 | 300 | In our example, **the first provider runs a shell on the Host system** to update the content of `stardata-install.tar.gz`, so if you modify `stardata-install/install.sh` you'll be uploading the updated version to the VM. 301 | 302 | The second provider, as we mentioned, copies `stardata-install.tar.gz` to the `/root/` directory in the VM. 303 | 304 | The third and last provider runs a few commands to enter `/root/`, extract the tar.gz, enter `stardata-install/` and run `./install.sh` and then runs a `yum clean all` to cleanup the `yum` cache so our image will be even smaller. 305 | 306 | We're ready for our first build. We're going to clone the repository and run `packer.io` with `PACKER_LOG=1` so we can see all the debug messages. 307 | 308 | ```bash 309 | cd /storage/centos7-base/ 310 | git clone https://github.com/stardata/packer-centos7-kvm-example.git 311 | cd packer-centos7-kvm-example 312 | PACKER_LOG=1 packer.io build centos7-base.json 313 | ``` 314 | 315 | If everything works correctly, at the end of the build you'll have your qcow2-format image in `centos7-base-img/`. 316 | 317 | For more information, you can check: 318 | 319 | * [the official documentation](https://www.packer.io/docs/ "Packer documentation") 320 | * [Jeff Geerling's repo for Vagrant and Ansible](https://github.com/geerlingguy/packer-centos-7) 321 | --------------------------------------------------------------------------------