Einen Ubuntu-Standard-Server installiere neuerdings ich so:
Überblick:
- Mit Vagrant eine lokale VM erstellen.
- Dabei wird Ansible zum einrichten der Minimal-Anforderung benutzt (automatische Upgrades, Mail etc.).
- Danach verschiebe ich die Disk (vmdk) auf meinen ESX-Storage und binde sie dort in eine VM ein.
(Leider ist dieser letzte Schritt lästig und irgendwie überflüssig, wenn man weiß, dass es einen vagrant vsphere provider gibt. Da fehlte mir aber noch das passende Template… kommt noch.) - Alternative, um den letzten Schritt des Kopierens zu sparen: Automatische Ubuntu-Installation via Kickstart und dann genauso mit Ansible weiter.
Vorgehen:
- Ansible installieren. Auf dem Mac z.B. mit
brew install ansible
- Vagrant installieren. Download hier.
- Notwendige Roles installieren mit
ansible-galaxy install -r requirements.yml
- VM lokal erstellen und einrichten mit
DSTK_HOSTNAME=dirkvm3 vagrant up
- BTW: Die ssh-keys, um nachher auf die VM drauf zu kommen werden gleich mit kopiert.
- Kopieren des vmdk (1:1) auf meinen ESXi-Datastore. Ich mache das so:
rsync -av --progress /Users/stk/VirtualBox\ VMs/dirkstest/box-disk1.vmdk \
root@martini:/data/vmware2/docker3
wichtigste Vagrant-Befehle
vagrant up
-> VM, aufsetzen, installieren, provisionierenvagrant reload
-> VM neu starten (nicht via reboot in der VM)vagrant halt
-> VM herunterfahren (ausschalten)vagrant destroy
-> VM komplett löschenvagrant ssh
-> login in die VM via sshvagrant provision
-> startet die Provisionierung (hier: Ansible) neu
wichtige Ansible-Befehle
- Installiert alle Pakete aus dem File:
ansible-galaxy install -r requirements.yml
- Führt explizite Befehle auf dem host aus (hier ls -la als Beispiel):
ansible all -i hosts -u root -m raw -a 'ls -la'
- Führt einzelnen Ansible-Modul aus (hier: Installiert traceroute als Beispiel):
ansible all -i hosts -u root -m apt -a 'pkg=traceroute state=installed update_cache=true'
- Führt ein Playbook aus. Das kann jedes einzelne Playbook sein oder das „umfassende“ baseserver.yml:
ansible-playbook -i hosts -u root --extra-vars='{"dstk_hostname":{"name":"dirkvm","domain":"steinkopf.net"}}' ansible/baseserver.yml
ansible-playbook -i hosts -u root ansible/docker.yml
Links:
- Hier kann man sein yaml online prüfen lassen: http://www.yamllint.com/.
- Großer Schatz an Ansible-Resources: https://galaxy.ansible.com/ – v.a. Roles.
- Tutorial, das ich ganz gut fand: https://serversforhackers.com/an-ansible-tutorial.
- Noch ein Tutorial zu Vagrant + Ansible: https://adamcod.es/2014/09/23/vagrant-ansible-quickstart-tutorial.html
- Ansible in Vagrant einbinden: http://docs.ansible.com/ansible/guide_vagrant.html
Files:
Hier noch die Files aus meinem Vorgehen:
requirements.txt
:
- src: jnv.unattended-upgrades
- src: Yannik.enable-standard-cronjobs
- src: kosssi.apt
- src: tersmitten.postfix
- src: holms.fqdn
- src: tersmitten.ssh-keys
- Das
Vagrantfile
übernimmt die Umgebungsvariable für den Hostnamen und gibt ihn an Ansible weiter:
Vagrant.configure(2) do |config|
config.vm.box = "ubuntu/trusty64"
config.vm.provider "virtualbox" do |v|
v.memory = 2048
v.cpus = 2
v.name = "dirkstest"
v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
end
config.vm.provision "ansible" do |ansible|
ansible.verbose = "v"
ansible.playbook = "ansible/baseserver.yml"
ansible.extra_vars = {
dstk_hostname: {
name: ENV['STK_HOSTNAME'] || 'dirkvm',
domain: 'steinkopf.net'
}
}
end
end
ansible/baseserver.yml
enthält i.W. Includes der weiteren Ansible-Playfiles zur besseren Strukturierung. Auf diese Weise kann man jedes includierte File auch einzeln via ansible direkt benutzen:
- name: setup base server
hosts: all
become: true
become_user: root
roles:
- role: Yannik.enable-standard-cronjobs
tasks:
#- debug: msg="System {{ inventory_hostname }} has uuid {{ ansible_product_uuid }}"
- include: hostname.yml
- include: timezone.yml
- include: postfix.yml # must be after hostname
- include: apt.yml
- include: unattended-upgrades.yml
- include: ssh-keys.yml
- In
ansible/hostname.yml
ist wird die o.g. Umgebungsvariable zum Hostnamen (Die Anführungszeichen sind hier nötig, weil der Wert der Vaiablen fqdn und hostname mit einer Klammer beginnt, was yml sonst für den Beginn einer Map halten würde):
- name: set hostname
hosts: all
become: true
become_user: root
# see https://galaxy.ansible.com/holms/fqdn/
roles:
- role: holms.fqdn
fqdn: "{{ dstk_hostname.name }}.{{ dstk_hostname.domain }}"
hostname: "{{ dstk_hostname.name }}"
- In
ansible/timezone.yml
gibt es die kleine Besonderheit, dass das Setzen der Timezone explizit nur gemacht wird, falls nicht schon geschehen:
- name: Set timezone to CET
hosts: all
become: true
become_user: root
# see https://gist.github.com/garethrees/5591027#gistcomment-1583883
tasks:
- name: Set timezone to Europe/Berlin
when: ansible_date_time.tz != 'CET'
command: timedatectl set-timezone 'Europe/Berlin'
ansible/postfix.yml
. Auch hier könnte man die Mailadresse für root und die User-Daten herausziehen, bei mir wird das aber bei allen Maschinen gleich bleiben. Daher fest drin:
- name: Install and config postfix
hosts: all
become: true
become_user: root
# see https://galaxy.ansible.com/tersmitten/postfix/
roles:
- tersmitten.postfix
vars:
postfix_aliases: # Aliases to ensure present in /etc/aliases
- { user: root, alias: dirk@xxx.xxx }
postfix_relayhost: mail.xxx.xxx
postfix_relayhost_port: 25
postfix_relaytls: true
postfix_sasl_user: xxx
postfix_sasl_password: xxx
ansible/apt.yml
:
- name: Install several apt packages
hosts: all
become: true
become_user: root
# see https://github.com/kosssi/ansible-role-apt
roles:
- role: kosssi.apt # execute apt-get update and install
tags: apt
vars:
apt_install:
- traceroute
- open-vm-tools
- locate
- ntp
ansible/unattended-upgrades.yml
– fast am Wichtigsten:
- name: Setup unattended upgrades
hosts: all
become: true
become_user: root
# see https://github.com/jnv/ansible-role-unattended-upgrades.git
roles:
- role: jnv.unattended-upgrades
unattended_origins_patterns:
- 'origin=Ubuntu,archive=${distro_codename}-security'
- 'o=Ubuntu,a=${distro_codename}-updates'
unattended_mail: 'root'
unattended_remove_unused_dependencies: true
unattended_automatic_reboot: true
tasks:
- name: remove unnecessary file 10periodic
file: state=absent path=/etc/apt/apt.conf.d/10periodic
ansible/ssh-keys.yml
:
- name: Install ssh keys
hosts: all
become: true
become_user: root
# see https://galaxy.ansible.com/tersmitten/ssh-keys/
roles:
- role: tersmitten.ssh-keys
ssh_keys_authorized_keys:
- owner: root
src: ssh-keys/stk-sprizz.pub
- owner: root
src: ssh-keys/stk-martini.pub
- owner: root
src: ssh-keys/root-martini.pub
- owner: root
src: ssh-keys/backuppc-martini.pub
ansible/docker.yml
:
- name: Install docker and docker-compose
hosts: all
become: true
become_user: root
# see https://galaxy.ansible.com/angstwad/docker_ubuntu/
roles:
- role: angstwad.docker_ubuntu
docker_pkg_name: docker-engine
update_docker_package: yes
install_kernel_extras: yes
kernel_update_and_reboot_permitted: yes
docker_opts: "--storage-driver=aufs --icc=false --iptables"
tasks:
- name: Fetch docker_compose_version
shell: docker-compose --version | awk 'NR==1{print $NF}'
register: docker_compose_version
- name: Install Bash completion for Docker Compose
get_url:
url='https://raw.githubusercontent.com/docker/compose/{{ docker_compose_version['stdout'] }}/contrib/completion/bash/docker-compose'
dest=/etc/bash_completion.d/docker-compose
force=yes # always overwrite in case docker version changed
when: docker_compose_version|success
requirements.yml
:
# ansible-galaxy install -r requirements.yml
- src: jnv.unattended-upgrades
- src: Yannik.enable-standard-cronjobs
- src: kosssi.apt
- src: tersmitten.postfix
- src: holms.fqdn
- src: tersmitten.ssh-keys