Mein Ubuntu-Standard-Server – via Vagrant und Ansible

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, provisionieren
  • vagrant reload -> VM neu starten (nicht via reboot in der VM)
  • vagrant halt -> VM herunterfahren (ausschalten)
  • vagrant destroy -> VM komplett löschen
  • vagrant ssh -> login in die VM via ssh
  • vagrant 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:

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

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.