Install
Just do a basic install using ubuntu-18.04.3-live-server-amd64.iso
Prevent Floppy Errors
If your server does not have a floppy, Ubuntu will report errors. This can be prevented by preventing the loading of a floppy driver on the clients:
echo "blacklist floppy" | sudo tee /etc/modprobe.d/blacklist-floppy.conf sudo rmmod floppy sudo update-initramfs -u
Enable Netowrking
The first time I built my VM, networking didn't install correctly. If this happens, create a netplan in /etc/netplans (note that because it's YAML, indentation is important)
network: version: 2 renderer: networkd ethernets: ens160: dhcp4: true
Apply the netplan
sudo netplan apply
Verify that it worked
ifconfig
Host Name Setup
Ensure the Hostname is Set Correctly
sudo hostnamectl set-hostname puppet.bpopp.net
Modify the /etc/hosts file:
127.0.0.1 localhost 192.168.1.42 puppet.bpopp.net # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters
Install Foreman
Install Puppet
sudo apt-get -y install ca-certificates cd /tmp && wget https://apt.puppet.com/puppet6-release-bionic.deb sudo dpkg -i /tmp/puppet6-release-bionic.deb
There appear to be issues with puppet 6 and newer versions of foreman. Puppet 5 may work better:
wget https://apt.puppetlabs.com/puppet5-release-xenial.deb sudo dpkg -i puppet5-release-xenial.deb sudo apt update
Enable Foreman
echo "deb http://deb.theforeman.org/ bionic 1.22" | sudo tee /etc/apt/sources.list.d/foreman.list echo "deb http://deb.theforeman.org/ plugins 1.22" | sudo tee -a /etc/apt/sources.list.d/foreman.list sudo apt-get -y install ca-certificates wget -q https://deb.theforeman.org/pubkey.gpg -O- | sudo apt-key add -
Install
sudo apt-get update && sudo apt-get -y install foreman-installer
Run
sudo foreman-installer \ --enable-foreman-proxy \ --foreman-proxy-tftp=true \ --foreman-proxy-tftp-servername=192.168.1.42 \ --foreman-proxy-dhcp=true \ --foreman-proxy-dhcp-gateway=192.168.1.1 \ --foreman-proxy-dhcp-nameservers="192.168.1.1" \ --foreman-proxy-dhcp-range="192.168.1.150 192.168.1.175" \ --enable-foreman-compute-vmware \
To add DNS:
--foreman-proxy-dns
Add the Puppetmaster as Host
Seems to be helpful to restart the puppetmaster after installing. To add the puppetmaster as a host, run this from the master:
sudo /opt/puppetlabs/bin/puppet agent --test
Now add our first module:
sudo /opt/puppetlabs/bin/puppet module install puppetlabs/ntp
Import the module from foreman:
- Click Configure > Classes
- Select "Any Organization" and "Any Location" from the menus in the menubar at the top
- Click the "Import environments from puppet" button
- Check the box next the module and click the "Update" button
Other useful modules
sudo /opt/puppetlabs/bin/puppet module install puppetlabs/motd sudo /opt/puppetlabs/bin/puppet module install puppetlabs/accounts
/etc/puppetlabs/code/environments/production/modules ├── adrien-alternatives (v0.3.0) ├── camptocamp-kmod (v2.2.0) ├── cesnet-autoupdate (v1.0.2) ├── cesnet-hadoop (v0.9.6) ├── cesnet-spark (v0.2.0) ├── puppet-archive (v3.2.1) ├── puppet-nginx (v1.0.0) ├── puppet-php (v6.0.2) ├── puppet-zypprepo (v2.2.2) ├── puppetlabs-accounts (v4.2.0) ├── puppetlabs-apt (v6.3.0) ├── puppetlabs-concat (v6.1.0) ├── puppetlabs-git (v0.5.0) ├── puppetlabs-inifile (v2.4.0) ├── puppetlabs-kubernetes (v5.0.0) ├── puppetlabs-motd (v3.1.0) ├── puppetlabs-ntp (v8.0.0) ├── puppetlabs-registry (v2.1.0) ├── puppetlabs-stdlib (v6.0.0) invalid ├── puppetlabs-translate (v2.0.0) invalid └── saz-ssh (v1.0.3) Error: Could not uninstall module 'puppetlabs-stdlib' Other installed modules have dependencies on 'puppetlabs-stdlib' (v6.0.0) 'cesnet/autoupdate' (v1.0.2) requires 'puppetlabs-stdlib' (>= 1.0.0 < 5.0.0) 'cesnet/hadoop' (v0.9.6) requires 'puppetlabs-stdlib' (>= 1.0.0) 'cesnet/spark' (v0.2.0) requires 'puppetlabs-stdlib' (>= 1.0.0) 'puppet/archive' (v3.2.1) requires 'puppetlabs-stdlib' (>= 4.13.1 < 6.0.0) 'puppet/nginx' (v1.0.0) requires 'puppetlabs-stdlib' (>= 5.0.0 < 7.0.0) 'puppet/php' (v6.0.2) requires 'puppetlabs-stdlib' (>= 4.16.0 < 6.0.0) 'puppetlabs/accounts' (v4.2.0) requires 'puppetlabs-stdlib' (>= 5.0.0 < 7.0.0) 'puppetlabs/apt' (v6.3.0) requires 'puppetlabs-stdlib' (>= 4.16.0 < 6.0.0) 'puppetlabs/concat' (v6.1.0) requires 'puppetlabs-stdlib' (>= 4.13.1 < 7.0.0) 'puppetlabs/git' (v0.5.0) requires 'puppetlabs-stdlib' (>= 3.2.0) 'puppetlabs/kubernetes' (v5.0.0) requires 'puppetlabs-stdlib' (>= 4.20.0 <= 5.2.0) 'puppetlabs/motd' (v3.1.0) requires 'puppetlabs-stdlib' (>= 2.1.0 < 7.0.0) 'puppetlabs/ntp' (v8.0.0) requires 'puppetlabs-stdlib' (>= 4.13.1 < 7.0.0) Use `puppet module uninstall --force` to uninstall this module anyway
Configure Foreman
- Click Infrastructure > Subnets > Create Subnet
- Use the following settings:
- Name: bpopp.net
- Network address: 192.168.1.0
- Network Prefix: 24
- Network Mask: 255.255.255.0
- Gateway Address: 192.168.1.1
- Primary DNS: 192.168.1.1
- IPAM: DHCP
- Start of Ip Range: 192.168.1.150
- End of Ip Range: 192.168.1.175
- Boot Mode : DHCP
- In the tabs:
- Under Domain tab, select bpopp.net
- Under Proxies tab, select the puppet.bpopp.net proxies for DHCP, TFTP, and HTTPBoot
- From Locations and Organizations, select Default Location/Organization
Configure Operating Systems
For a CentOS box:
- Click Hosts > Operating Systems > Create Operating System
- Use the following settings:
- Name: CentOS
- Major Version: 7
- Family: RedHat
- Architecture: x86_64
- From the tabs
- In the partition table, select 'Kickstart default'
- In the installation Media, select 'CentOS Mirror'
For a Debian box:
- Click Hosts > Operating Systems > Create Operating System
- Use the following settings:
- Name: CentOS
- Major Version: 7
- Family: Debian
- Release Name: stretch (important!)
- Architecture: x86_64
- From the tabs
- In the partition table, select 'Preseed default'
- In the installation Media, select 'Debian Mirror'
Assign Partition Tables
This part of the configuration seemed really goofy, but is important. You have to assign your provisioning templates to any operating systems you want them to be available from.
For CentOS:
- Click Hosts > Provisioning Templates
- Type "kickstart" from the search box
- Assign each of the following templates to CentOS by clicking their "Association" tab and selecting CentOS. Note that you cannot change any other setting. If you would like to change anything, you have to clone them and then assign the clone to your OS.
- Kickstart Default
- Kickstart default finish
- Kikstart default iPXE
- Kickstart default PXELinux
- Kickstart default user data
- For Debian, change the following:
- Preseed default
- Preseed default finish
- Preseed default iPXE
- Preseed default PXELinux
- Preseed default user data
After assigning each template to the correct operating systems:
- Click Hosts > Operating System
- Click each operating system and click the "Templates" tab
- Assign any desired provisioning templates to the operating systems (typically all 5)
Create a Host Group
Host groups are used to quickly provision/configure servers of a specific type. For example, you can define a host group for Apache webservers that have all the correct modules installed for that specific type of server.
With all the previous configurations complete:
- Click Configure > Host groups > Create Host Group
- Use the following settings:
- Name: Basic
- Environment: production
- Puppet Master: puppet.bpopp.net
- Puppet CA: puppet.bpopp.net
- From the tabs:
- From Puppet Classes, add any desired modules for this host group
- From Network
- Domain: bpopp.net
- IPv4: bpopp.net
- From Operating System
- Architecture: x86_64
- Operating System: CentOS
- Media: CentOS Mirror
- PXELoader: PXELinux BIOS
- Root Password: whatever
- From Locations and Organizations, select Default Location/Organization
Create First Host
- Click Hosts > Create Host
- Use settings:
- Name: client1
- Organization: Default
- Location: Default
- Host Group: basic
After selecting the correct host group, each of the tabs should fill in automatically with correct information for that host group. You will, however, need to assign the interface to a specific box. There's probably a better way to do this, but what works is:
- Create a VM in ESXI
- Start it briefly to generate a Mac Address
- Stop it
- Look in the settings and copy the generated MAC address to foreman
Manifests
Took me way too long to figure this out, but after installing the accounts plugin from puppetforge, I couldn't really figure out how to configure it. Turns out it is actually really simple. There's undoubtedly multiple ways to do this, but any configuration files added /etc/puppetlabs/code/environment/production/manifests will automatically get applied to production boxes. So for example, to add a user, add the following to users.pp:
package { [ "cron", "locate", "lsof", "screen", "sudo", "unzip" ]: ensure => installed, } accounts::user { 'bpopp': group => 'bpopp', groups => [ 'sudo' ], shell => '/bin/bash', password => 'encoded', locked => false, }
To create the password, you can use:
mkpasswd -m sha-512
(may require installing apt install whois)
What I really want to be able to do is assign this script only to a specific host group, but this is a good start.
Set the Timezone
Install the timezone module
puppet module install saz-timezone
Create a timezone manifest file @ /etc/puppetlabs/code/environments/production/manifests/timezone.pp
class { 'timezone': timezone => 'America/Chicago', }
Docker Manifest File
/etc/puppetlabs/code/environments/production/manifests/docker.pp
node /^docker\d+\.bpopp\.net/ { include 'docker' docker::image { 'jupyter/pyspark-notebook': } docker::run { 'helloworld': ensure => absent, image => 'ubuntu', } docker::run { 'pyspark-notebook': image => 'jupyter/pyspark-notebook', ports => ['8888'], extra_parameters => [ '--it ' ], } }
Packages
/etc/puppetlabs/code/environments/production/manifests/packages.pp
package { 'curl': ensure => present, }
Users
To manage keys, you'll need to install the sshkeys_core module:
sudo puppet module install puppetlabs-sshkeys_core --version 2.4.0
Then:
accounts::user { 'bpopp': group => 'bpopp', # groups => [ 'sudo' ], shell => '/bin/bash', password => '$encoded', locked => false, sshkeys => [ 'ssh-rsa AAAAB3NzaCcensoredw== bpopp@bpopp.net', ], } class { 'sudo': config_file_replace => false, } sudo::conf { 'bpopp': priority => 60, content => "bpopp ALL=(ALL) NOPASSWD: ALL", }
Don't remember what this is for?
sudo::conf { 'foreman-proxy': priority => 60, content => "foreman-proxy ALL = NOPASSWD: /opt/puppetlabs/bin/puppet cert *", }
Spark Manifest
Rather elaborate script to install Spark and hadoop
node /^(spark\d+|dataiku)\.lab.bpopp\.net/ { $spark_install_path = "/usr/local/spark" $hadoop_install_path = "/usr/local/hadoop" $livy_install_path = "/usr/local/livy" $spark_filename = "spark-2.4.5.tgz" $hadoop_filename = "hadoop-3.2.1.tar.gz" $livy_filename = "apache-livy-0.6.0-incubating-bin.zip" $sparkuser = "spark" package { 'scala': ensure => 'installed', } package { 'python3': ensure => 'installed', } package { 'python': ensure => 'installed', } package { 'python3-pip': ensure => 'installed', } package { 'bsdtar': ensure => 'installed', } accounts::user { $sparkuser: group => $sparkuser, shell => '/bin/bash', password => '$6$censored/f/0HTyufJDHIc.0a/pT0vQ0S1', locked => false, } include 'archive' # NOTE: optional for posix platforms file { $spark_install_path: ensure => directory, owner => $sparkuser, group => $sparkuser, mode => '0755', } file { $livy_install_path: ensure => directory, owner => $sparkuser, group => $sparkuser, mode => '0755', } file { $hadoop_install_path: ensure => directory, owner => $sparkuser, group => $sparkuser, mode => '0755', } archive { "/tmp/${spark_filename}": ensure => present, extract => true, extract_path => $spark_install_path, extract_command => 'tar xfz %s --strip-components=1', source => "https://archive.apache.org/dist/spark/spark-2.4.5/spark-2.4.5-bin-without-hadoop.tgz", creates => "${spark_install_path}/bin", cleanup => true, require => File[$spark_install_path], } archive { "/tmp/${hadoop_filename}": ensure => present, extract => true, extract_path => $hadoop_install_path, extract_command => 'tar xfz %s --strip-components=1', source => "https://archive.apache.org/dist/hadoop/common/hadoop-3.2.1/hadoop-3.2.1.tar.gz", creates => "${hadoop_install_path}/bin", cleanup => true, require => File[$hadoop_install_path], } archive { "/tmp/${livy_filename}": ensure => present, extract => true, extract_path => $livy_install_path, extract_command => 'bsdtar xfz %s --strip-components=1', source => "http://mirrors.ibiblio.org/apache/incubator/livy/0.6.0-incubating/apache-livy-0.6.0-incubating-bin.zip", creates => "${livy_install_path}/bin", cleanup => true, require => File[$livy_install_path], } file { '/etc/profile.d/spark.sh': mode => '644', content => 'PATH=$PATH:/usr/local/spark/bin:/usr/local/hadoop/bin; JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-amd64/; PYSPARK_PYTHON=/usr/bin/python; HADOOP_HOME=/usr/local/hadoop; SPARK_DIST_CLASSPATH=$(/usr/local/hadoop/bin/hadoop classpath); SPARK_HOME=/usr/local/spark;', } file { "/usr/local/spark/conf/spark-env.sh": ensure => present, } file_line { "/usr/local/spark/conf/spark-env.sh": path => "/usr/local/spark/conf/spark-env.sh", line => "export SPARK_DIST_CLASSPATH=$(/usr/local/hadoop/bin/hadoop classpath)", } file_line { "/usr/local/hadoop/etc/hadoop/hadoop-env.sh": path => "/usr/local/hadoop/etc/hadoop/hadoop-env.sh", line => "export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-amd64/", } file { "/usr/local/livy/conf/livy-env.sh": ensure => present, } file_line { "/usr/local/livy/conf/livy-env.sh": path => "/usr/local/livy/conf/livy-env.sh", line => "export SPARK_HOME=/usr/local/spark", } }
Resetting Kernel/Boot
Foreman downloads kernel/boot files to /var/lib/tftpboot/boot. If installing newer version of the same OS, these files are cached and could return an error that says, "No kernel modules were found.". If this happens, purge this folder and they will be rebuild.
From the documentation:
The kernel and initrd are not deleted, but left in place for future installs of the same OS and architecture combination. Please note that in the unlikely case that these files are modified, the simplistic freshness check of wget will likely get confused, corrupting the downloaded versions of the files. If this happens, you should simply delete the files and let them be re-downloaded from scratch.