Puppet
Contents
Architektura
zasada działania
infrastructure as a code
ad hoc changes
scaling
wprowadzanie zmian w zależności od faktów
developer env changes
instalacja ręczna pakietów
Manifests
Files with
.pp
extension
file { "/var/www":
ensure => "directory",
owner => "www-data",
group => "www-data",
mode => "0755",
}
exec { "package definition update":
command => "/usr/bin/apt update",
}
package { ["nmap", "htop", "git"]:
ensure => "latest",
require => Exec["package definition update"],
}
Model
klient server
standalone - puppet apply
fakty i kolejność wykonywania manifestów
Components
zmienne
classes
resources
facts
Puppet language
DSL
ruby
ERB templates
Templates
ERB templates
Comment
<%# This is a comment. %>
Variables
There are two ways to access variables in an ERB template:
@variable
scope["variable"]
Example:
scope["ntp::tinker"]
Printing variables
ServerName <%= @fqdn %>
ServerAlias <%= @hostname %>
If
if <CONDITION>
... code ...
elsif <CONDITION>
... other code ...
end
<% if @broadcast != "NONE" %>
broadcast <%= @broadcast %>
<% end %>
For
<% @values.each do |val| -%>
Some stuff with <%= val %>
<% end -%>
If $values was set to ["one", "two"], this example would produce:
Some stuff with one
Some stuff with two
Instalacja i konfiguracja
$ sudo apt update
$ sudo apt install puppet
Zaglądnij do katalogu
/etc/puppet
.Co się tam znajduje?
Jeżeli nie ma katalogu
/etc/puppet/code
to go stwórz$ mkdir -p /etc/puppet/manifests
Przejdź do katalogu
/etc/puppet/code
Resources
File
file { "resource title":
path => # (namevar) The path to the file to manage. Must be fully...
ensure => # Whether the file should exist, and if so what...
backup => # Whether (and how) file content should be backed...
checksum => # The checksum type to use when determining...
checksum_value => # The checksum of the source contents. Only md5...
content => # The desired contents of a file, as a string...
ctime => # A read-only state to check the file ctime. On...
force => # Perform the file operation even if it will...
group => # Which group should own the file. Argument can...
ignore => # A parameter which omits action on files matching
links => # How to handle links during file actions. During
mode => # The desired permissions mode for the file, in...
mtime => # A read-only state to check the file mtime. On...
owner => # The user to whom the file should belong....
provider => # The specific backend to use for this `file...
purge => # Whether unmanaged files should be purged. This...
recurse => # Whether to recursively manage the _contents_ of...
recurselimit => # How far Puppet should descend into...
replace => # Whether to replace a file or symlink that...
selinux_ignore_defaults => # If this is set then Puppet will not ask SELinux...
selrange => # What the SELinux range component of the context...
selrole => # What the SELinux role component of the context...
seltype => # What the SELinux type component of the context...
seluser => # What the SELinux user component of the context...
show_diff => # Whether to display differences when the file...
source => # A source file, which will be copied into place...
source_permissions => # Whether (and how) Puppet should copy owner...
sourceselect => # Whether to copy all valid sources, or just the...
target => # The target for creating a link. Currently...
type => # A read-only state to check the file...
validate_cmd => # A command for validating the file's syntax...
validate_replacement => # The replacement string in a `validate_cmd` that...
# ...plus any applicable metaparameters.
}
ensure
(Property: This attribute represents concrete state on the target system.)
Whether the file should exist, and if so what kind of file it should be. Possible values are present, absent, file, directory, and link.
- present
accepts any form of file existence, and creates a normal file if the file is missing. (The file will have no content unless the content or source attribute is used.)
- absent
ensures the file doesn’t exist, and deletes it if necessary.
- file
ensures it’s a normal file, and enables use of the content or source attribute.
- directory
ensures it’s a directory, and enables use of the source, recurse, recurselimit, ignore, and purge attributes.
- link
ensures the file is a symlink, and requires that you also set the target attribute. Symlinks are supported on all Posix systems and on Windows Vista / 2008 and higher. On Windows, managing symlinks requires Puppet agent’s user account to have the “Create Symbolic Links” privilege; this can be configured in the “User Rights Assignment” section in the Windows policy editor. By default, Puppet agent runs as the Administrator account, which has this privilege.
# Equivalent resources:
file { "/etc/inetd.conf":
ensure => "/etc/inet/inetd.conf",
}
file { "/etc/inetd.conf":
ensure => link,
target => "/etc/inet/inetd.conf",
}
Unless
exec { "set hostname":
command => "/bin/hostname -F /etc/hostname",
unless => "/usr/bin/test $(hostname) = $(/bin/cat /etc/hostname)",
}
Podmiana zawartości w pliku
file { "/tmp/my-file.pp":
ensure => present,
owner => root,
group => root,
mode => "0644",
content => "Lorem ipsum...\n",
}
HTTPS problem
Gdyby wystąpił problem z certyfikatem ssl
przy instalacji modułów należy:
postaw maszynę w Amazonie (Ubuntu LTS)
zainstaluj squid:
$ sudo apt update $ sudo apt install squid
na maszynie gościa (tam gdzie chcesz instalować moduł Puppet ustaw:
$ export http_proxy=http://<IP>:3128 $ export https_proxy=http://<IP>:3128
Lub:
[user] http_proxy = http://<IP>:3128 https_proxy = http://<IP>:3128$ sudo service puppet restart $ sudo su - $ puppet module install
Narzędzia pomocnicze
Facter
Przyjrzyj się wynikom poleceń:
$ facter $ facter ipaddress $ facter lsbdistdescription
Co zauważyłeś? Jak można wykorzystać te informacje?
Kod przedstawia wynik polecenia
facter
na świeżej maszynie Ubuntu postawionej w Amazon AWS:disks => { xvda => { size => "8.00 GiB", size_bytes => 8589934592 } } ec2_metadata => { ami-id => "ami-hahahafc254eaf55", ami-launch-index => "0", ami-manifest-path => "(unknown)", block-device-mapping => { ami => "/dev/sda1", root => "/dev/sda1" }, events => { maintenance => { history => "[]", scheduled => "[]" } }, hostname => "ip-172-13-37-1.eu-central-1.compute.internal", identity-credentials => { ec2 => { info => "{ "Code" : "Success", "LastUpdated" : "2019-04-09T11:53:57Z", "AccountId" : "624006931337" }" } }, instance-action => "none", instance-id => "i-02f361764hahaha10dbe", instance-type => "t2.micro", local-hostname => "ip-172-13-37-1.eu-central-1.compute.internal", local-ipv4 => "172.13.37.1", mac => "06:6b:ha:ha:ha:82", network => { interfaces => { macs => { 06:6b:ha:ha:ha:82 => { device-number => "0", interface-id => "eni-07ed83hahahacc247", ipv4-associations => { 127.0.0.1 => "172.13.37.1" }, local-hostname => "ip-172-13-37-1.eu-central-1.compute.internal", local-ipv4s => "172.13.37.1", mac => "06:6b:ha:ha:ha:82", owner-id => "624006931337", public-hostname => "ec2-127-0-0-1.eu-central-1.compute.amazonaws.com", public-ipv4s => "127.0.0.1", security-group-ids => "sg-02d60687273e1337", security-groups => "launch-wizard-6", subnet-id => "subnet-26e91337", subnet-ipv4-cidr-block => "172.13.0.0/20", vpc-id => "vpc-d44885bd", vpc-ipv4-cidr-block => "172.13.0.0/16", vpc-ipv4-cidr-blocks => "172.13.0.0/16" } } } }, placement => { availability-zone => "eu-central-1b" }, profile => "default-hvm", public-hostname => "ec2-127-0-0-1.eu-central-1.compute.amazonaws.com", public-ipv4 => "127.0.0.1", reservation-id => "r-0a097a6cd34ehaha", security-groups => "launch-wizard-6", services => { domain => "amazonaws.com", partition => "aws" } } facterversion => 3.10.0 filesystems => btrfs,ext2,ext3,ext4,squashfs,vfat fips_enabled => false identity => { gid => 0, group => "root", privileged => true, uid => 0, user => "root" } is_virtual => true kernel => Linux kernelmajversion => 4.15 kernelrelease => 4.15.0-1032-aws kernelversion => 4.15.0 load_averages => { 15m => 0.01, 1m => 0.18, 5m => 0.04 } memory => { system => { available => "830.03 MiB", available_bytes => 870350848, capacity => "15.64%", total => "983.91 MiB", total_bytes => 1031708672, used => "153.88 MiB", used_bytes => 161357824 } } networking => { domain => "eu-central-1.compute.internal", fqdn => "ip-172-13-37-1.eu-central-1.compute.internal", hostname => "ip-172-13-37-1", interfaces => { eth0 => { bindings => [ { address => "172.13.37.1", netmask => "255.255.240.0", network => "172.13.37.0" } ], bindings6 => [ { address => "fe80::46b:haha:haha:da82", netmask => "ffff:ffff:ffff:ffff::", network => "fe80::" } ], ip => "172.13.37.1", ip6 => "fe80::46b:haha:haha:da82", mac => "06:6b:ha:ha:da:82", mtu => 9001, netmask => "255.255.240.0", netmask6 => "ffff:ffff:ffff:ffff::", network => "172.13.37.0", network6 => "fe80::" }, lo => { bindings => [ { address => "127.0.0.1", netmask => "255.0.0.0", network => "127.0.0.0" } ], bindings6 => [ { address => "::1", netmask => "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", network => "::1" } ], ip => "127.0.0.1", ip6 => "::1", mtu => 65536, netmask => "255.0.0.0", netmask6 => "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", network => "127.0.0.0", network6 => "::1" } }, ip => "172.13.37.1", ip6 => "fe80::46b:haha:haha:da82", mac => "06:6b:ha:ha:da:82", mtu => 9001, netmask => "255.255.240.0", netmask6 => "ffff:ffff:ffff:ffff::", network => "172.13.37.0", network6 => "fe80::", primary => "eth0" } os => { architecture => "amd64", distro => { codename => "bionic", description => "Ubuntu 18.04.2 LTS", id => "Ubuntu", release => { full => "18.04", major => "18.04" } }, family => "Debian", hardware => "x86_64", name => "Ubuntu", release => { full => "18.04", major => "18.04" }, selinux => { enabled => false } } path => /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin processors => { count => 1, isa => "x86_64", models => [ "Intel(R) Xeon(R) CPU E5-2676 v3 @ 2.40GHz" ], physicalcount => 1 } ruby => { platform => "x86_64-linux-gnu", sitedir => "/usr/local/lib/site_ruby/2.5.0", version => "2.5.1" } system_uptime => { days => 0, hours => 0, seconds => 1732, uptime => "0:28 hours" } timezone => UTC virtual => xenu
Korzystanie z faktów w manifestach
Sposób klasyczny, jako zmienne na głównym poziomie
# Definicja operatingsystem = "Ubuntu" # Wykorzystanie case $::operatingsystem { "CentOS": { include centos } "MacOS": { include mac } }
Jako zmienne w tablicy faktów
# Definicja $facts["fact_name"] = "Ubuntu" # Wykorzystanie case $facts["fact_name"] { "CentOS": { include centos } "MacOS": { include mac } }
Tworzenie nowych faktów:
require "facter" Facter.add(:system_role) do setcode "cat /etc/system_role" end
require "facter" Facter.add(:system_role) do setcode do Facter::Util::Resolution.exec("cat /etc/system_role") end end
Druga metoda tworzenia faktów:
$ export FACTER_system_role=$(cat /etc/system_role); facter
Hiera
---
:backends:
- yaml
:hierarchy:
- "nodes/%{::fqdn}"
- "roles/%{::role}"
- common
:yaml:
:datadir: /etc/puppet/hiera/
:logging:
- console
nginx::nginx_servers:
'devops-alldomains':
server_name:
- '~^(?<fqdn>.+?)$'
www_root: '/var/www/$fqdn'
index_files:
- 'index.php'
try_files:
- '$uri'
- '$uri/'
- '/index.php?$args'
access_log: '/var/log/nginx/devops-alldomains-access.log'
error_log: '/var/log/nginx/devops-alldomains-error.log'
'devops-alldomains-ssl':
server_name:
- '~^(?<fqdn>.+?)$'
listen_port: '443'
ssl_port: '443'
www_root: '/var/www/$fqdn'
ssl: true
ssl_key: '/etc/ssl/www/$fqdn.key'
ssl_cert: '/etc/ssl/www/$fqdn.crt'
index_files:
- 'index.php'
try_files:
- '$uri'
- '$uri/'
- '/index.php?$args'
access_log: '/var/log/nginx/devops-alldomains-access-ssl.log'
error_log: '/var/log/nginx/devops-alldomains-error-ssl.log'
nginx::nginx_locations:
'devops-alldomains-loc':
location: '~ \.php$'
www_root: '/var/www/$fqdn'
server: 'devops-alldomains'
fastcgi: 'unix:/var/run/php7-fpm.sock'
fastcgi_split_path: '^(.+\.php)(/.*)$'
fastcgi_index: 'index.php'
fastcgi_param:
'SCRIPT_FILENAME': '$document_root$fastcgi_script_name'
'devops-alldomains-ssl-loc':
location: '~ \.php$'
www_root: '/var/www/$fqdn'
server: 'devops-alldomains-ssl'
ssl: true
ssl_only: true
fastcgi: 'unix:/var/run/php7-fpm.sock'
fastcgi_split_path: '^(.+\.php)(/.*)$'
fastcgi_index: 'index.php'
fastcgi_param:
'SCRIPT_FILENAME': '$document_root$fastcgi_script_name'
Augias
Examples
Exec {
path => "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
}
group { "www-data":
ensure => present
}
user { "www-data":
ensure => present,
gid => "www-data"
}
Exec { "update package definition":
command => "/usr/bin/apt update"
}
package { [
"git",
"vim",
"nmap",
"htop",
"wget",
"curl",
"nginx",
"python3",
"python3-dev",
"python3-pip",
"p7zip-full",
"uwsgi",
"uwsgi-plugin-python3",
"postgresql-client",
"postgresql",
"postgresql-server-dev-all",
"libmemcached-dev"
] :
ensure => latest,
}
file {[
"/var/www",
"/var/www/log",
"/var/www/public",
"/var/www/public/media",
"/var/www/public/static",
"/var/www/tmp",
"/var/www/src"]:
ensure => directory,
owner => "www-data",
group => "www-data",
mode => "0755",
}
Exec { path => "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" }
group { "vagrant": ensure => present }
user { "vagrant": ensure => present, gid => "vagrant" }
exec { "apt-get update": command => "/usr/bin/apt-get update" }
package { [
"git",
"vim",
"nmap",
"htop",
"wget",
"curl",
"nginx",
"python3",
"python3-dev",
"python3-pip",
"p7zip-full",
"uwsgi",
"uwsgi-plugin-python3",
"postgresql-9.3",
"postgresql-server-dev-9.3",
"libmemcached-dev"
] :
ensure => latest,
require => Exec["apt-get update"],
}
file { [
"/var/www",
"/var/www/log",
"/var/www/public",
"/var/www/public/media",
"/var/www/public/static",
"/var/www/tmp",
"/var/www/src"
]:
ensure => directory,
owner => "vagrant",
group => "vagrant",
mode => "0755",
}
file { "/var/www/database":
ensure => directory,
owner => "vagrant",
group => "vagrant",
mode => "0700",
}
service { "nginx":
ensure => running,
enable => true,
require => Package["nginx"],
}
service { "uwsgi":
ensure => running,
enable => true,
require => [Package["uwsgi"], Exec["python requirements"], File["/var/www/tmp"]],
}
file { "/etc/nginx/sites-enabled/default":
content => template("/var/www/src/conf/nginx.conf"),
require => Package["nginx"],
notify => Service["nginx"],
ensure => file,
}
file { "/etc/uwsgi/apps-enabled/default.ini":
content => template("/var/www/src/conf/uwsgi.ini"),
require => Package["uwsgi"],
notify => Service["uwsgi"],
ensure => file,
}
exec { "python requirements":
command => "pip3 install -r /var/www/src/requirements.txt",
require => Package["python3-pip", "python3-dev", "libmemcached-dev", "postgresql-9.3", "postgresql-server-dev-9.3"],
}
exec { "collectstatic":
command => "python3 /var/www/src/manage.py collectstatic --noinput",
require => Exec["python requirements"],
}
Modules
$ puppet module search apache
$ puppet module install puppetlabs-apache
Java
class { "java" :
package => "java-1.8.0-openjdk-devel",
}
java::oracle { "jdk8" :
ensure => "present",
version => "8",
java_se => "jdk",
}
java::oracle { "jdk8" :
ensure => "present",
version_major => "8u101",
version_minor => "b13",
java_se => "jdk",
}
JBoss
To install JBoss Application Server you can use just, it will install Wildfly 8.2.0.Final by default:
include jboss
To install JBoss EAP or older JBoss AS use:
class { "jboss":
product => "jboss-eap",
version => "6.4.0.GA",
}
or use hiera:
jboss::params::product: "jboss-as"
jboss::params::version: "7.1.1.Final"
$user = "jb-user"
$passwd = "SeC3eT!1"
node "controller" {
include jboss::domain::controller
include jboss
jboss::user { $user:
ensure => "present",
password => $passwd,
}
}
References
Assignments
Puppet package installation
- English
Create manifest in
/etc/puppet/code/packages.pp
Puppet should install those packages:
nmap
htop
git
Make sure that
apt update
command is run before
- Polish
Manifest do tego zadania zapisz w pliku
/etc/puppet/code/packages.pp
Zainstaluj następujące pakiety za pomocą Puppet:
nmap
htop
git
Upewnij się by Puppet wykonał polecenie
apt update
na początku
Hostname change
- English
Create manifest in
/etc/puppet/code/hostname.pp
Using manifest change the hostname to
ecosystem.local
Make sure that command
hostname
returns valid outputMake sure that
hostname
do not restores to default after reboot
- Polish
Manifest do tego zadania zapisz w pliku
/etc/puppet/code/hostname.pp
Za pomocą manifestu zmień hostname maszyny na
ecosystem.local
Upewnij się, że po wpisaniu polecenia
hostname
będzie ustawiona na odpowiednią wartość(jeżeli korzystasz z Vagrant) Upewnij się, że hostname nie przywróci się do domyślnej wartości po ponownym uruchomieniu
Hostname zmienia się na dwa sposoby:
podmiana zawartości pliku
/etc/hostname
i uruchomieniehostname -F /etc/hostname
uruchomienie polecenia
hostnamectl set-hostname ...
Users, Groups and Directories management
- English
Create manifest in
/etc/puppet/code/users.pp
Make sure group
mygroup
exists and hasgid=1337
Make sure user
myuser
exists and hasuid=1337
and belongs tomygroup
Make sure:
Directory
/home/myuser
existsOwner is set to
myuser
Group is set to
mygroup
Has
rwxr-xr-x
permissions
- Polish
Manifest do tego zadania zapisz w pliku
/etc/puppet/code/users.pp
Upewnij się, że użytkownik
myuser
istnieje, mauid=1337
i należy do grupymygroup
Upewnij się, że grupa
mygroup
istnieje i magid=1337
Upewnij się, że:
Katalog
/home/myuser
istniejeWłaścicielem jego jest user
myuser
Właścicielem jego jest grupa
mygroup
Ma uprawnienia
rwxr-xr-x
Puppet Apache2 installation
- English
Create manifest in
/etc/puppet/manifests/apache.pp
Install and confugure Apache 2 using Puppet module
Using terminal generate self-signed OpenSSL certificates and put them in
/etc/ssl/
:/etc/ssl/ssl.example.com.cert
/etc/ssl/ssl.example.com.key
Using Puppet create two vhosts:
insecure.example.com
using port80
and with document root in/var/www/insecure.example.com
ssl.example.com` using port ``443
with document root in/var/www/ssl.example.com
using certificates from/etc/ssl/
Create file:
/var/www/insecure.example.com/index.html
with contentEhlo World! - Insecure
/var/www/ssl.example.com/index.html
with contentEhlo World! - SSL!
Run browser on your localhost:
- Polish
Za pomocą Puppet upewnij się by był użytkownik
www-data
i miałuid=33
Za pomocą Puppet upewnij się by była grupa
www-data
i miałagid=33
Upewnij się że katalog
/var/www
istnieje i właścicielem jego są userwww-data
i grupawww-data
i że ma uprawnieniarwxr-xr-x
Zainstaluj i skonfiguruj
nginx
wykorzystując moduł PuppetZ terminala wygeneruj certyfikaty self signed OpenSSL (
.cert
i.key
) (za pomocą i umieść je w/etc/ssl/
)Za pomocą Puppet Stwórz dwa vhosty:
insecure.example.com
na porcie 80 i z katalogiem domowym/var/www/insecure-example-com
ssl.example.com
na porcie 443 i z katalogiem domowym/var/www/ssl-example-com
+ używanie certyfikatów SSL wcześniej wygenerowanych
Stwórz pliki z treścią:
/var/www/insecure-example-com/index.html
z treściąEhlo World! - Insecure
/var/www/ssl-example-com/index.html
z treściąEhlo World! - SSL!
W przeglądarce na komputerze lokalnym wejdź na stronę:
Puppet MySQL installation and configuration
- English
Create manifest in
/etc/puppet/code/mysql.pp
Install MySQL database using Puppet module
Set
root
password tomypassword
Set
mysqld
to listen on all interfaces (0.0.0.0
)Create database
mydb
withutf-8
Create user
myusername
with passwordmypassword
Grant all privileges to
myusername
formydb
Setup database backup to
/tmp/mysql-backup
- Polish
Manifest do tego zadania zapisz w pliku
/etc/puppet/code/mysql.pp
Zainstaluj bazę danych MySQL wykorzystując moduł Puppet
Ustaw hasło dla użytkownika
root
namypassword
Ustaw nasłuchiwanie serwera
mysqld
na wszystkich interfejsach (0.0.0.0
)Stwórz bazę danych
mydb
zutf-8
Stwórz usera
myusername
z hasłemmypassword
Nadaj wszystkie uprawnienia dla usera
myusername
dla bazymydb
Ustaw backupowanie bazy danych do
/tmp/mysql-backup
Puppet Tomcat installation and configuration
- English
Create manifest in
/etc/puppet/manifests/mysql.pp
Install Java using Puppet module
Install Tomcat 8 using Puppet module in
/opt/tomcat8
Configure to Tomcat instances running simultanously on your hostname:
One instance is running on default ports
Another instance is using
8006
port for connector and8081
to redirect to8443
On the first instance deploy WAR from
/opt/tomcat8/webapps/docs/appdev/sample/sample.war
- Polish
Manifest do tego zadania zapisz w pliku
/etc/puppet/code/tomcat.pp
Zainstaluj język Java za pomocą modułu Puppet
Zainstaluj Tomcat 8 za pomocą Puppet w katalogu
/opt/tomcat8
Skonfiguruj dwie instancje Tomcat działające jednocześnie:
Jedna uruchamiana na domyślnych portach
Druga uruchamiana na
8006
a connector z portu8081
przekierowywał na8443
Na pierwszej uruchom
war
z lokacji/opt/tomcat8/webapps/docs/appdev/sample/sample.war