Browse Tag: Linux Arch

Odroid-HC2 mit Ansible für das GlusterFS vorbereiten

Nachdem Ansible nun auf die Nodes zugreifen kann, folgt der nächste Schritt. Ich werde mich Stück für Stück an die notwendigen Bestandteile eines Playbooks an nähern. Ziel ist es, ein Playbook zu erhalten, welches mir die Nodes so Aufbaut, dass sie in das GlusterFS integriert werden können.

Zuerst schaue ich mir mal an, wie das von https://archlinuxarm.org/platforms/armv7/samsung/odroid-hc2 erzeugte Setup aussieht. Mich interessiert vor allem, wie das Arm Team die microSD Karte gemountet hat und wo diese und die eingebaute HDD im /dev Baum sitzt.

[ansible@alarm ~]$ cat /etc/fstab 
# Static information about the filesystems.
# See fstab(5) for details.

# <file system> <dir> <type> <options> <dump> <pass>
[ansible@alarm ~]$ sudo cat /etc/fstab
# Static information about the filesystems.
# See fstab(5) for details.

# <file system> <dir> <type> <options> <dump> <pass>
[ansible@alarm ~]$ 

Okay, kann man so machen.. seufz, damit hält man sich natürlich alle Freiheitsgrade offen, indem man die Medien gar nicht erst mountet.

Bin ich persönlich jetzt keine Freund davon etwas ungemountet zu booten, aber ich kann es nachvollziehen, dass das Team es so gemacht hat. Dadurch können die drei verschiedenen Arten von Mount-Points nach dem ersten Booten bedient werden. (/dev/sdX versus Label versus UUID). Ich selber nutze sehr gerne zum Mounten die Label, weil meiner Meinung nach dadurch in einem späteren Update sich verändernde Mount-Points a’la /dev/sda1 abgefangen werden und diese schwer handlebaren UUIDs umgangen werden können. Aber wie schon erwähnt, was für mich war ist, mag noch lange nicht für jedes Szenario gelten.

Na dann schau ich mir mal die Optionen zum Mounten im /dev/disk Baum genauer an.

[ansible@alarm ~]$ sudo tree /dev/disk/
/dev/disk/
|-- by-id
|   |-- ata-SAMSUNG_HD203WI_S1UYJ1KZ100183 -> ../../sda
|   |-- ata-SAMSUNG_HD203WI_S1UYJ1KZ100183-part1 -> ../../sda1
|   |-- mmc-SC16G_0xb48f4515 -> ../../mmcblk0
|   |-- mmc-SC16G_0xb48f4515-part1 -> ../../mmcblk0p1
|   |-- wwn-0x50024e9002b89270 -> ../../sda
|   `-- wwn-0x50024e9002b89270-part1 -> ../../sda1
|-- by-label
|   `-- ROOT -> ../../mmcblk0p1
<snip>

Am Ende steht mein gesetztes Label ROOT für die mmcblk0p1 Partition. Diese Partition möchte ich jetzt auch gerne fest in der /etc/fstab stehen haben, damit nach späteren Software-Updates nichts ins Schlingern gerät.

Ich baue mir das Zielplaybook aus einzelnen kleinen Playbook-Configlets zusammen, deshalb prüfe ich jetzt erstmal, ob das Mounten von einem / auf einem Node geht. Das Playbook-Configlets habe ich mir wie folgt ausgedacht.

[ansible@barney ~]$ cat label_root_mount.yml 
---
- name: All mount / by label ROOT
  hosts: glusterfs
  become: yes

  tasks:
   - name: generate LABEL=ROOT entry
     mount:
       path: /
       src: LABEL=ROOT
       fstype: ext4
       opts: defaults,noatime
       state: present

[ansible@barney ~]$ 

Damit ich das Playbook jetzt nicht gleich gegen alle Nodes lasse, habe ich mir die /etc/ansible/host aufgeteilt, so daß ich einzelne Nodes gezielt ansprechen kann. Die /etc/ansible/host Textdatei sieht wie folgt aus.

[root@barney ansible]# cat /etc/ansible/hosts
[glusterfs]
192.168.2.143
192.168.2.144
192.168.2.145
192.168.2.146
192.168.2.25

[g1]
192.168.2.143

[g2]
192.168.2.144

[g3]
192.168.2.145

[g4]
192.168.2.146

[g5]
192.168.2.25
[root@barney ansible]# 

Ich vermute, in größeren Installationen wird wohl eher mit dem DNS Namen der Nodes hantiert als mit der IPv4 Adresse, ist mir jetzt aber erstmal Wurst..

Okay, dann lassen wir mal das Playbook-Configlets gegen den Node g5 laufen und schauen uns an, wie das Ergebnis aussieht.

[ansible@barney ~]$ ansible-playbook label_root_mount.yml --limit g5

PLAY [All mount / by label ROOT] ***************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.2.25]

TASK [generate LABEL=ROOT entrie] **************************************************************************************************************************
changed: [192.168.2.25]

PLAY RECAP *************************************************************************************************************************************************
192.168.2.25               : ok=2    changed=1    unreachable=0    failed=0   

[ansible@barney ~]$ 

Spannend war es jetzt für mich, da ich mir nicht ganz sicher war, was der Node denn nun wirklich als Ergebnis in der /etc/fstab stehen hat. Ich seh mal nach.

[ansible@alarm ~]$ cat /etc/fstab 
# Static information about the filesystems.
# See fstab(5) for details.

# <file system> <dir> <type> <options> <dump> <pass>
LABEL=ROOT / ext4 defaults,noatime 0 0
[ansible@alarm ~]$ 

Sieht gut aus, allerdings gibt es noch ein Delta zwischen /etc/fstab und gelebter /etc/mtab. Dafür werde ich den Node jetzt mach rebooten und dann sollten die beiden symmetrisch sein.

[ansible@barney ~]$ ansible-playbook reboot.yml --limit g5

PLAY [All hosts reboot] ************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.2.25]

TASK [Unconditionally reboot the machine with all defaults] ************************************************************************************************
changed: [192.168.2.25]

PLAY RECAP *************************************************************************************************************************************************
192.168.2.25               : ok=2    changed=1    unreachable=0    failed=0   

[ansible@barney ~]$ 

Nach dem kleinen Klaps sieht es jetzt so aus, wie ich es haben wollte. Die microSD Karte ist über das Label gemountet.

[ansible@alarm ~]$ lsblk -Tf
NAME        FSTYPE LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINT
sda                                                                          
`-sda1      xfs          03df1da5-89d0-4fd3-8bf4-69cbbe8d87ce                
mmcblk0                                                                      
`-mmcblk0p1 ext4   ROOT  5ab9cbdc-0a5f-4fe8-8910-afdda9eafb81   12.4G    10% /

Nun wird es Zeit, die verbaute HDD der Nodes für die Nutzung im GlusterFS einzurichten. Dafür verwerfe ich das auf der HDD existierende Setup erst einmal weg.

Ich hab jetzt eine gute Stunde gebraucht, um festzustellen, dass auf meinen Nodes alle Befehle vom Modul parted nicht ausgeführt werden. Ich stell da mal eine steile Hypothese auf, das Modul parted braucht auf dem Nodes das Programm parted. Ich werde das also mal Installieren und gucken ob das Playbook dann sauber durch läuft.

Also schraub ich mir ein Playbook zum Ausrollen von parted.

[ansible@barney ~]$ cat parted.yml
---
- name: All hosts install parted
  hosts: glusterfs
  become: yes
  
  tasks:
    - name: install parted
      pacman:
        name: parted
        update_cache: yes
[ansible@barney ~]$ 

Und läuft es durch?

[ansible@barney ~]$ ansible-playbook parted.yml --limit g5

PLAY [All hosts install parted] ****************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.2.25]

TASK [install parted] **************************************************************************************************************************************
changed: [192.168.2.25]

PLAY RECAP *************************************************************************************************************************************************
192.168.2.25               : ok=2    changed=1    unreachable=0    failed=0   
[ansible@barney ~]$

Okay, jetzt ist auf Node g5 also das Programm parted ausgerollt und jetzt lass ich nochmal das Playbook zum Löschen der Partition auf Node g5 los.

---
- name: All remove all partitions on sda
  hosts: glusterfs
  become: yes

  tasks:
   - parted:
       device: /dev/sda
       number: 1
       state: absent

Diese Partition stammt aus meinen Versuchen mit unterschiedlichen Softgware Defined Storages (Ceph/Hadoop/GlusterFs). Für mich stellt sich GlusterFS als am leichtesten handlebar dar, auch hier mag jeder zu seiner eigenen Sichtweisekommen auf Basis seines Bedarfs.

Nun gut, was macht denn nun das Playbok zum Löschen der Partion #1 auf SDA?

[ansible@barney ~]$ ansible-playbook remove_all_partitions_on_hdd.yml --limit g5

PLAY [All remove all partitions on sda] ********************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.2.25]

TASK [parted] **********************************************************************************************************************************************
changed: [192.168.2.25]

PLAY RECAP *************************************************************************************************************************************************
192.168.2.25               : ok=2    changed=1    unreachable=0    failed=0   

[ansible@barney ~]$ 

Jetzt noch flott eon prüfender Blick direkt ins Filesystem des Node g5.

[ansible@alarm ~]$ lsblk -Tf
NAME        FSTYPE LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINT
sda                                                                          
mmcblk0                                                                      
`-mmcblk0p1 ext4   ROOT  5ab9cbdc-0a5f-4fe8-8910-afdda9eafb81   12.4G    10% /
[ansible@alarm ~]$ 

Es hat funktioniert, die Partition sda1 ist gelöscht. Sicherlich geistern die Testdaten jetzt noch auf der HDD rum, aber was solls.

Als nächstes möchte ich jetzt eine Partition auf /dev/sda anlegen mit den Label=BRICK. Das Filesystem dieser BRICK Partition muss XFS sein. Das Playbook dafür habe ich mir wiefolgt vorgestellt.

[ansible@barney ~]$ cat make_sda1_partition.yml 
---
- name: All make one partition on sda1 with xfs
  hosts: glusterfs
  become: yes

  tasks:
   - name: Create sda1 partition
     parted:
       device: /dev/sda
       label: gpt
       number: 1
       state: present
       part_start: 0%
       part_end: 100%

   - name: Create a xfs filesystem on /dev/sda1
     filesystem:
       fstype: xfs
       dev: /dev/sda1
       opts: -i size=512

   - name: Label sda1 partition with LABEL=BRICK
     command: "xfs_admin -L BRICK /dev/sda1"
     become: true

[ansible@barney ~]$ 

Der Knackpunkt in diesem Ablauf ist, dass ich es auf Biegen und Brechen nicht hinbekommen habe mit den standard Ansible Modulen ein Label für die neue Partition zu setzen. Deshalb steht da im Playbook so ein hässliches Bash Command drin, aber nun ja. Dann lass ich mal mein erstes Playbook mit drei Tasks ausrollen. Mal sehen, ob das so geht, wie ich es mir vorstelle.

[ansible@barney ~]$ ansible-playbook make_sda1_partition.yml --limit g5

PLAY [All make one partition on sda1 with xfs] *************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.2.25]

TASK [Create sda1 partition] *******************************************************************************************************************************
changed: [192.168.2.25]

TASK [Label sda1 partition with LABEL=BRICK] ***************************************************************************************************************
changed: [192.168.2.25]

TASK [Create a xfs filesystem on /dev/sda1] ****************************************************************************************************************
ok: [192.168.2.25]

PLAY RECAP *************************************************************************************************************************************************
192.168.2.25               : ok=4    changed=2    unreachable=0    failed=0   

[ansible@barney ~]$

Ausgerollt ist es jedenfalls. Bei Filesystemveränderungen sehe ich lieber genau nach, bevor ich weiter mache.

[ansible@alarm ~]$ ls -l /dev/disk/by-label/
total 0
lrwxrwxrwx 1 root root 10 Mar 31 15:52 BRICK -> ../../sda1
lrwxrwxrwx 1 root root 15 Mar 31 09:56 ROOT -> ../../mmcblk0p1


[ansible@alarm ~]$ lsblk -Tf
NAME        FSTYPE LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINT
sda                                                                          
`-sda1      xfs    BRICK cdc27797-15eb-4e4d-b58e-3e6ad3315112                
mmcblk0                                                                      
`-mmcblk0p1 ext4   ROOT  5ab9cbdc-0a5f-4fe8-8910-afdda9eafb81   12.4G    10% /

[ansible@alarm ~]$ sudo xfs_db -c info /dev/sda1
meta-data=/dev/sda1              isize=512    agcount=4, agsize=122091705 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=0
data     =                       bsize=4096   blocks=488366820, imaxpct=5
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=238460, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0

ansible@alarm ~]$ 

Das sieht doch nach einem gültigen Versuch aus. Es gibt eine Partition /dev/sda1 mit dem Label BRICK. Die orginale Anleitung zum GlusterFS sieht vor, dass die XFS Storage-Partitionen nach /export/sdX1 gemountet wird. Also lassen wir Ansible mal ein Verzeichnis /export erzeugen, worin danach /export/sda1 erzeugt wird. das Playbook dafür stelle ich mir wie folgt vor.

[ansible@barney ~]$ cat mkdir_export_mount_label_brick.yml 
---
- name: Generate /export/sdb1 and mount into fstab
  hosts: glusterfs
  become: yes

  tasks:
  - name: Creates directory and subdirectory
    file:
      path: /export/sda1
      state: directory

  - name: generate LABEL=BRICK entry
    mount:
      path: /export/sda1
      src: LABEL=BRICK
      fstype: xfs
      opts: defaults
      state: present

[ansible@barney ~]$ 

Wenn ich mir das Ergebnis des Playbooks ansehe, bin ich zufrieden. Nach einem Reboot sollte dann die Partition sauber gemountet sein.

[ansible@alarm ~]$ tree -dfugA /export/
/export
└── [root     root    ]  /export/sda1

1 directory

[ansible@alarm ~]$ cat /etc/fstab 
# Static information about the filesystems.
# See fstab(5) for details.

# <file system> <dir> <type> <options> <dump> <pass>
LABEL=ROOT / ext4 defaults,noatime 0 0
LABEL=BRICK /export/sda1 xfs defaults 0 0

[ansible@alarm ~]$ 

Also steht als letzes für die Vorbereitung der GlusterFS installation noch ein Reboot an, dafür nehme ich das oben bereits beschriebene Playbook reboot.yml nochmal zur Hand und lasse es auf Node g5 los und sehe mir nachdem Reboot das Ergebnis auf dem Node g5 an.

[ansible@alarm ~]$ lsblk -Tf 
NAME        FSTYPE LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINT
sda                                                                          
`-sda1      xfs    BRICK cdc27797-15eb-4e4d-b58e-3e6ad3315112    1.8T     0% /export/sda1
mmcblk0                                                                      
`-mmcblk0p1 ext4   ROOT  5ab9cbdc-0a5f-4fe8-8910-afdda9eafb81   12.4G    10% /

[ansible@alarm ~]$ 

Das sieht genau so aus, wie ich es haben wollte. Damit sind die Vorbereitung des Dateisystems und der Partitonen abgeschlossen. Die einzelnen Code-Schnipsel baue ich jetzt noch in ein zusammenhängendes Playbook zusammen und lasse es dann über das gesammte Setup laufen.

Das finale Plabook lief fast zufriedenstellend durch. 😉

[ansible@barney ~]$ ansible-playbook preinstallation_glusterfs.yml

PLAY [Vorbereiten der Nodes vor der GlusterFS installation] ************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.2.146]
ok: [192.168.2.144]
ok: [192.168.2.145]
ok: [192.168.2.143]
ok: [192.168.2.25]
ok: [192.168.2.137]

TASK [Installiere parted] **********************************************************************************************************************************
ok: [192.168.2.25]
changed: [192.168.2.145]
changed: [192.168.2.144]
changed: [192.168.2.143]
changed: [192.168.2.146]
ok: [192.168.2.137]

TASK [Installiere glusterfs] *******************************************************************************************************************************
changed: [192.168.2.25]
changed: [192.168.2.144]
changed: [192.168.2.145]
changed: [192.168.2.146]
changed: [192.168.2.143]
ok: [192.168.2.137]

TASK [Erzeuge einen FSTAB Eintrag für LABEL=ROOT] **********************************************************************************************************
changed: [192.168.2.145]
changed: [192.168.2.143]
changed: [192.168.2.144]
ok: [192.168.2.25]
changed: [192.168.2.146]
ok: [192.168.2.137]

TASK [Lösche existierende Partitionen SDA1 auf SDA] ********************************************************************************************************
fatal: [192.168.2.25]: FAILED! => {"changed": false, "err": "Warning: Partition /dev/sda1 is being used. Are you sure you want to continue?\n", "msg": "Error while running parted script: /usr/bin/parted -s -m -a optimal /dev/sda -- rm 1", "out": "", "rc": 1}
changed: [192.168.2.143]
changed: [192.168.2.144]
changed: [192.168.2.146]
changed: [192.168.2.145]
fatal: [192.168.2.137]: FAILED! => {"changed": false, "err": "Warning: Partition /dev/sda1 is being used. Are you sure you want to continue?\n", "msg": "Error while running parted script: /usr/bin/parted -s -m -a optimal /dev/sda -- rm 1", "out": "", "rc": 1}

TASK [Erzeuge eine SDA1 Partition] *************************************************************************************************************************
changed: [192.168.2.143]
changed: [192.168.2.144]
changed: [192.168.2.145]
changed: [192.168.2.146]

TASK [Formatiere SDA1 mit XFS] *****************************************************************************************************************************
changed: [192.168.2.143]
changed: [192.168.2.145]
changed: [192.168.2.144]
changed: [192.168.2.146]

TASK [Umlabeln der SDA1 mit LABEL=BRICK] *******************************************************************************************************************
changed: [192.168.2.145]
changed: [192.168.2.146]
changed: [192.168.2.144]
changed: [192.168.2.143]

TASK [Erzeuge Verzeichnis /export/sda1] ********************************************************************************************************************
changed: [192.168.2.143]
changed: [192.168.2.144]
changed: [192.168.2.146]
changed: [192.168.2.145]

TASK [Erzeuge einen FSTAB Eintrag für LABEL=BRICK] *********************************************************************************************************
changed: [192.168.2.143]
changed: [192.168.2.144]
changed: [192.168.2.145]
changed: [192.168.2.146]

TASK [Durchbooten um die FSTAB Einträge zu aktivieren] *****************************************************************************************************
changed: [192.168.2.144]
changed: [192.168.2.146]
changed: [192.168.2.143]
changed: [192.168.2.145]
        to retry, use: --limit @/home/ansible/preinstallation_glusterfs.retry

PLAY RECAP *************************************************************************************************************************************************
192.168.2.137              : ok=4    changed=0    unreachable=0    failed=1   
192.168.2.143              : ok=11   changed=10   unreachable=0    failed=0   
192.168.2.144              : ok=11   changed=10   unreachable=0    failed=0   
192.168.2.145              : ok=11   changed=10   unreachable=0    failed=0   
192.168.2.146              : ok=11   changed=10   unreachable=0    failed=0   
192.168.2.25               : ok=4    changed=1    unreachable=0    failed=1   

[ansible@barney ~]$ 

Ansible auf Linux Arch installieren

Die Installation von Ansible auf Linux Arch ist weitestgehend ein No-Brainer. Für das Ansible selber ist das Paket “ansible” notwendig. Wer, wie ich, seine Playbooks checken lassen möchte, installiert das Paket “ansible-lint” einfach mit.

[root@barney ansible]# pacman -S ansible ansible-lint
Löse Abhängigkeiten auf...
Suche nach in Konflikt stehenden Paketen...

Pakete (3) python-ruamel-yaml-0.15.89-1  ansible-2.7.9-1  ansible-lint-4.1.0-1

Gesamtgröße des Downloads:            0,33 MiB
Gesamtgröße der installierten Pakete:  81,56 MiB

:: Installation fortsetzen? [J/n] 
:: Empfange Pakete...
 python-ruamel-yaml-0.15.89-1-x86_64                                                                                                                                  279,1 KiB  5,80M/s 00:00 [#######################################################################################################################] 100%
 ansible-lint-4.1.0-1-any                                                                                                                                              60,9 KiB  0,00B/s 00:00 [#######################################################################################################################] 100%
(3/3) Prüfe Schlüssel im Schlüsselring                                                                                                                                                         [#######################################################################################################################] 100%
(3/3) Überprüfe Paket-Integrität                                                                                                                                                               [#######################################################################################################################] 100%
(3/3) Lade Paket-Dateien                                                                                                                                                                       [#######################################################################################################################] 100%
(3/3) Prüfe auf Dateikonflikte                                                                                                                                                                 [#######################################################################################################################] 100%
(3/3) Überprüfe verfügbaren Festplattenspeicher                                                                                                                                                [#######################################################################################################################] 100%
:: Verarbeite Paketänderungen...
(1/3) Installiere ansible                                                                                                                                                                      [#######################################################################################################################] 100%
Optionale Abhängigkeiten für ansible
    sshpass: for ssh connections with password
    python-passlib: crypt values for vars_prompt
    python-pyopenssl: openssl modules
    python-netaddr: for the ipaddr filter
    python-systemd: log to journal
    python-pywinrm: connect to Windows machines
    python-dnspython: for dig lookup
    python-ovirt-engine-sdk: ovirt support
    python-boto3: aws_s3 module
    python-jmespath: json_query support
    acme-tiny: openssl_certificate module
(2/3) Installiere python-ruamel-yaml                                                                                                                                                           [#######################################################################################################################] 100%
(3/3) Installiere ansible-lint                                                                                                                                                                 [#######################################################################################################################] 100%
:: Starte post-transaction hooks...
(1/1) Arming ConditionNeedsUpdate...
[root@barney ansible]# 

Nachdem Installieren ist es wichtig seine zu managenden Nodes in der /etc/ansible/hosts einzutragen oder wer eine erheblich größere Infrastruktur hat, nutzt die Variante mit den Unterverzeichnissen. Für mich reicht die Variante mit den Textdateien allemale aus. Na dann trage ich mal die Nodes ein.

[root@barney bak]# nano /etc/ansible/hosts 

[glusterfs]
192.168.2.143
192.168.2.144
192.168.2.145
192.168.2.146
192.168.2.25

Damit sind die fünf Nodes des zu bauenden GlusterFS eingetragen und ich prüfe auch gleich mal, ob sie für Ansible erreichbar sind, in dem in den klassichen PING Test mache.

[ansible@barney ~]$ ansible all -m ping
192.168.2.144 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.2.146 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.2.143 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.2.145 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.2.25 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
[ansible@barney ~]$ 

Läuft ja wie geschmiert. Alle fünf Odroid-HC2 melden sich, ergo kann sich Ansible über SSH einloggen und Python Befehle absetzen. Dann werde ich den fünf mal ein kleines Playbook vorspielen lassen, was sie zum Software-Update animieren sollte. Das Playbook ist ganz kurz und stößt nur lokal den Update selbst an.

[ansible@barney ~]$ cat syu.yml 
---
- name: All hosts up-to-date
  hosts: glusterfs
  become: yes
  
  tasks:
    - name: full system upgrade
      pacman:
        update_cache: yes
        upgrade: yes

[ansible@barney ~]$ 

Fertig ist das Playbook. Ich habe das Playbook einmal mittels ansible-lint geprüft.

[ansible@barney ~]$ ansible-lint syu.yml 
[201] Trailing whitespace
syu.yml:5
  

[ansible@barney ~]$

Ja okay, es sind hinter dem Text tatäschlich noch Leerzeichen drin, kann ich mir verkneifen – ist aber funktional irrelevant. Na dann mal sehen, was die Nodes zu dem Playbook sagen..

[ansible@barney ~]$ ansible-playbook syu.yml

PLAY [All hosts up-to-date] ******************************************************

TASK [Gathering Facts] ***********************************************************
ok: [192.168.2.146]
ok: [192.168.2.143]
ok: [192.168.2.144]
ok: [192.168.2.145]
ok: [192.168.2.25]

TASK [full system upgrade] **************************************************************************************************************************************************************************************************************************************************************************************************
changed: [192.168.2.146]
changed: [192.168.2.144]
changed: [192.168.2.143]
changed: [192.168.2.145]
changed: [192.168.2.25]

PLAY RECAP ******************************************************************************************************************************************************************************************************************************************************************************************************************
192.168.2.143              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.144              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.145              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.146              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.25               : ok=2    changed=1    unreachable=0    failed=0   

[ansible@barney ~]$ 

Ich freue mich, dass alle fünf sich neue Software geholt haben und der Software Update gut durch lief. Natürlich kann man da noch deutlich mehr steuern, aber für den ersten Test reicht mir das aus. Nach dem Software Update wollte ich auch noch probieren, ob ich alle fünf gleichzeitig Rebooten kann. Dafür habe ich ebenso ein Playbook geschrieben. Auch kein weltbewegendes Zeug.

[ansible@barney ~]$ cat reboot.yml 
---
- name: All hosts reboot
  hosts: glusterfs
  become: yes
  
  tasks:
    - name: Unconditionally reboot the machine with all defaults
      reboot:

[ansible@barney ~]$ 

Und das Playbook werfe ich jetzt den fünf Nodes mal vor die Füße und wenn ich das richtig Verstanden habe, rebooten alle fünf Nodes.

[ansible@barney ~]$ ansible-playbook reboot.yml 

PLAY [All hosts reboot] **********************************************************

TASK [Gathering Facts] ***********************************************************
ok: [192.168.2.146]
ok: [192.168.2.143]
ok: [192.168.2.144]
ok: [192.168.2.145]
ok: [192.168.2.25]

TASK [Unconditionally reboot the machine with all defaults] **********************
changed: [192.168.2.143]
changed: [192.168.2.145]
changed: [192.168.2.146]
changed: [192.168.2.144]
changed: [192.168.2.25]

PLAY RECAP ***********************************************************************
192.168.2.143              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.144              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.145              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.146              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.25               : ok=2    changed=1    unreachable=0    failed=0   

[ansible@barney ~]$ 

Ist schon ein merkwürdiges Gefühl, wenn da fünf Nodes gleichzeitig rebooten und man das Anlaufen ihrer HDDs hören kann.

Für meinen ersten Ansible Test bin ich zufrieden. In dem nächsten Beitrag werde ich mich Stück für Stück an das automatische Aufsetzen des GlusterFS machen. Denn wenn später weitere Nodes dazu kommen, möchte ich das ja genau mit Playbooks ausführen.

Linux Arch Installation auf den SDS Odroid-HC2 Nodes

Nachdem das PowerSupply und das Chassis für die Odroid-HC2 Bricks fertig ist, habe ich mich jetzt dem Software Defined Storage selbst zugewendet. Beim Formatieren der 10 microSD Karten viel mir sofort auf, das ist ein Anwendungsfall für eine Automatisierung. Leider ist das Installieren bis zum Aufsatzpunkt einer Automatisierung sehr Zeit intensiv.

Also fange ich mal an 10 microSD Karten platt zu machen, damit sie jungfräulich an den Start gehen.

[eric@Wintermute ~]$ sudo dd if=/dev/zero of=/dev/mmcblk0 bs=1M count=8
8+0 Datensätze ein
8+0 Datensätze aus
8388608 bytes (8,4 MB, 8,0 MiB) copied, 0,131692 s, 63,7 MB/s
[eric@Wintermute ~]$ sync

Als nächstes hole ich mir ein Linux Arch Image für die kleinen Bricks. Dankenswerterweise hat sich das Team von Arch Linux ARM die Mühe gemacht, Images für die Installation von Linux Arch im monitorless Mode zu bauen, so daß ich die Installation nicht von Hand machen muss. Das hat bei Linux Arch immer etwas von Tierquälerei..

Das Image und die genaue Erläuterung in englisch findet ihr unter:

https://archlinuxarm.org/platforms/armv7/samsung/odroid-hc2

Allerdings werde ich die 10 microSD Karten nicht einzeln nach dem Verfahren generieren und dann fertig machen bis Ansible sich einloggen kann. Da sitze ich ja in Tagen noch hier. Ich werde eine microSD Karte fertig machen und diese dann clonen. Also hole ich mir mal ein aktuelles Linux Arch Image für eine Installation ohne Monitor!

[eric@Wintermute ~]$ wget http://os.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz
--2019-03-25 19:00:32--  http://os.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz
Auflösen des Hostnamens os.archlinuxarm.org (os.archlinuxarm.org)… 50.116.36.110
Verbindungsaufbau zu os.archlinuxarm.org (os.archlinuxarm.org)|50.116.36.110|:80 … verbunden.
HTTP-Anforderung gesendet, auf Antwort wird gewartet … 302 Found
Platz: http://de4.mirror.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz [folgend]
--2019-03-25 19:00:32--  http://de4.mirror.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz
Auflösen des Hostnamens de4.mirror.archlinuxarm.org (de4.mirror.archlinuxarm.org)… 213.202.193.253
Verbindungsaufbau zu de4.mirror.archlinuxarm.org (de4.mirror.archlinuxarm.org)|213.202.193.253|:80 … verbunden.
HTTP-Anforderung gesendet, auf Antwort wird gewartet … 200 OK
Länge: 405576169 (387M) [application/octet-stream]
Wird in »ArchLinuxARM-odroid-xu3-latest.tar.gz« gespeichert.

ArchLinuxARM-odroid-xu3-latest.tar.gz 100%[======================================================================>] 386,79M  5,65MB/s    in 69s      

2019-03-25 19:01:42 (5,59 MB/s) - »ArchLinuxARM-odroid-xu3-latest.tar.gz« gespeichert [405576169/405576169]

Jetzt noch flott checken, ob mir da niemand Müll angedreht hat. Für mich reicht da ein md5 check, wer es genauer mag und ganz sicher sein möchte, findet auf dem Server auch gpg .tar.gz.sig File zu dem Archiv.

[eric@Wintermute ~]$ wget http://os.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz.md5
--2019-03-25 19:08:44--  http://os.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz.md5
Auflösen des Hostnamens os.archlinuxarm.org (os.archlinuxarm.org)… 50.116.36.110
Verbindungsaufbau zu os.archlinuxarm.org (os.archlinuxarm.org)|50.116.36.110|:80 … verbunden.
HTTP-Anforderung gesendet, auf Antwort wird gewartet … 302 Found
Platz: http://de6.mirror.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz.md5 [folgend]
--2019-03-25 19:08:44--  http://de6.mirror.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz.md5
Auflösen des Hostnamens de6.mirror.archlinuxarm.org (de6.mirror.archlinuxarm.org)… 2a01:4f8:c17:5ade::f00, 138.201.244.244
Verbindungsaufbau zu de6.mirror.archlinuxarm.org (de6.mirror.archlinuxarm.org)|2a01:4f8:c17:5ade::f00|:80 … verbunden.
HTTP-Anforderung gesendet, auf Antwort wird gewartet … 200 OK
Länge: 72 [application/octet-stream]
Wird in »ArchLinuxARM-odroid-xu3-latest.tar.gz.md5« gespeichert.

ArchLinuxARM-odroid-xu3-latest.tar.gz 100%[======================================================================>]      72  --.-KB/s    in 0s      

2019-03-25 19:08:44 (4,79 MB/s) - »ArchLinuxARM-odroid-xu3-latest.tar.gz.md5« gespeichert [72/72]

Jetzt noch eben md5sum gegen das Archiv laufen lassen und auf ein OK warten.

[eric@Wintermute ~]$ md5sum -c ArchLinuxARM-odroid-xu3-latest.tar.gz.md5
ArchLinuxARM-odroid-xu3-latest.tar.gz: OK
[eric@Wintermute ~]$ 

Md5sum hat den gepackten Tarball für verdaulich erachtet. Es kann also weiter gehen mit der Installation auf einer microSD Karte. Diese werde ich dann wie gesagt clonen.

Ich richte mich dabei nach der orginalen Anleitung, auf der oben verlinkten Website. Dann formatiere ich also mal die microSD Karte.

[eric@Wintermute ~]$ sudo fdisk /dev/mmcblk0

Willkommen bei fdisk (util-linux 2.33.1).
Änderungen werden vorerst nur im Speicher vorgenommen, bis Sie sich
entscheiden, sie zu schreiben.
Seien Sie vorsichtig, bevor Sie den Schreibbefehl anwenden.

Gerät enthält keine erkennbare Partitionstabelle.
Eine neue DOS-Festplattenbezeichnung 0x5c6a4f4f wurde erstellt.

Befehl (m für Hilfe): o
Eine neue DOS-Festplattenbezeichnung 0xf4792cc4 wurde erstellt.

Befehl (m für Hilfe): p
Festplatte /dev/mmcblk0: 14,9 GiB, 15931539456 Bytes, 31116288 Sektoren
Einheiten: Sektoren von 1 * 512 = 512 Bytes
Sektorgröße (logisch/physikalisch): 512 Bytes / 512 Bytes
E/A-Größe (minimal/optimal): 512 Bytes / 512 Bytes
Festplattenbezeichnungstyp: dos
Festplattenbezeichner: 0xf4792cc4

Befehl (m für Hilfe): n
Partitionstyp
   p   Primär (0 primär, 0 erweitert, 4 frei)
   e   Erweitert (Container für logische Partitionen)
Wählen (Vorgabe p): p
Partitionsnummer (1-4, Vorgabe 1): 1
Erster Sektor (2048-31116287, Vorgabe 2048): 4096
Last sector, +/-sectors or +/-size{K,M,G,T,P} (4096-31116287, Vorgabe 31116287): 

Eine neue Partition 1 des Typs „Linux“ und der Größe 14,9 GiB wurde erstellt.

Befehl (m für Hilfe): w
Die Partitionstabelle wurde verändert.
ioctl() wird aufgerufen, um die Partitionstabelle neu einzulesen.
Festplatten werden synchronisiert.

[eric@Wintermute ~]$ 

Sehen wir uns das mal genauer an, was FDisk da draus gemacht hat. Meine Erwartung ist ein nicht gemountet Blockdevice unter /dev/mmcblk0 mit einer Partition unter /dev/mmcblk0p1.

[eric@Wintermute ~]$ lsblk -TJ /dev/mmcblk0
{
   "blockdevices": [
      {"name":"mmcblk0", "maj:min":"179:0", "rm":false, "size":"14,9G", "ro":false, "type":"disk", "mountpoint":null,
         "children": [
            {"name":"mmcblk0p1", "maj:min":"179:1", "rm":false, "size":"14,9G", "ro":false, "type":"part", "mountpoint":null}
         ]
      }
   ]
}

Schaut soweit gut aus, die Partition ist genau da, wo ich sie erwartet habe. Dann geht es jetzt daran ein Filesystem auf der Partition zu installieren. Nach der Anleitung wird es ein ext4fs, womit ich für “/” leben kann. Allerdings arbeite ich persönlich lieber mit Labeln bei den Partitionen anstelle ihrer Position im /dev Baum. Meine Root Partion bekommt folglich auch das Label ROOT.

[eric@Wintermute ~]$ sudo mkfs.ext4 -v /dev/mmcblk0p1 -L ROOT
mke2fs 1.45.0 (6-Mar-2019)
Dateisystemtypen für das Aufschlüsseln von mke2fs.conf: 'ext4'
Geräteblöcke werden verworfen: erledigt                        
Dateisystembezeichnung=ROOT
OS-Typ: Linux
Blockgröße=4096 (log=2)
Fragmentgröße=4096 (log=2)
Stride=0 Blöcke, Stripebreite=0 Blöcke
972944 Inodes, 3889024 Blöcke
194451 Blöcke (5.00%) reserviert für den Superuser
Erster Datenblock=0
Maximale Dateisystem-Blöcke=2151677952
119 Blockgruppen
32768 Blöcke pro Gruppe, 32768 Fragmente pro Gruppe
8176 Inodes pro Gruppe
UUID des Dateisystems: 5ab9cbdc-0a5f-4fe8-8910-afdda9eafb81
Superblock-Sicherungskopien gespeichert in den Blöcken: 
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208

beim Anfordern von Speicher für die Gruppentabellen: erledigt                        
Inode-Tabellen werden geschrieben: erledigt                        
Das Journal (16384 Blöcke) wird angelegt: erledigt
Die Superblöcke und die Informationen über die Dateisystemnutzung werden
geschrieben: erledigt

[eric@Wintermute ~]$ 

Na das lief doch wie gewünscht gut durch, es gibt jetzt also auf /dev/mmcblk0 eine Partition /dev/mmcblk0p1 mit ext4 Filesystem. Dann mounten wir die mal ins bestehende Filesystem hinein, um Daten darauf schreiben zu können.

[eric@Wintermute ~]$ mkdir root
[eric@Wintermute ~]$ sudo mount /dev/mmcblk0p1 root

Ist ohne Wiederworte eingehängt. Jetzt kommt der Teil mit dem Daten aus dem gezippten Tarball auspacken. Dabei ist es wichtig wirklich ROOT zu sein, ein sudo hilft hier nicht.

[eric@Wintermute ~]$ su
Passwort: ALIGATOR3 

root@Wintermute eric]# bsdtar -vxpf ArchLinuxARM-odroid-xu3-latest.tar.gz -C root
x ./bin
x ./boot/
x ./boot/tzsw.bin
x ./boot/dtbs/
x ./boot/initramfs-linux.img
x ./boot/sd_fusing.sh
x ./boot/mkscr
x ./boot/boot.txt
x ./boot/zImage
x ./boot/u-boot.bin
x ./boot/bl2.bin
<snip>
x ./var/db/nscd/
x ./var/db/Makefile
x ./var/log/lastlog
x ./var/log/wtmp
x ./var/log/journal/
x ./var/log/audit/
x ./var/log/btmp
x ./var/log/private/
x ./var/log/old/
x ./var/log/journal/remote/
[root@Wintermute eric]# 

Nicht erschrecken, wenn du das selber machst, aus dem Tarball plumpst ein ganzes Linuxsystem raus. 🙂

Damit der Odroid-HC2 von der microSD Karte booten kann, ist noch etwas Bootcode im Bootsektor der microSD Karte notwendig. Den schaufeln wir da per mitgeliefertem Script hin.

[root@Wintermute eric]# cd root
[root@Wintermute root]# cd boot

[root@Wintermute boot]# pwd
/home/eric/root/boot

[root@Wintermute boot]# sh sd_fusing.sh /dev/mmcblk0
/dev/mmcblk0 reader is identified.
BL1 fusing
30+0 Datensätze ein
30+0 Datensätze aus
15360 bytes (15 kB, 15 KiB) copied, 0,0379681 s, 405 kB/s
BL2 fusing
28+1 Datensätze ein
28+1 Datensätze aus
14592 bytes (15 kB, 14 KiB) copied, 0,111764 s, 131 kB/s
u-boot fusing
1123+1 Datensätze ein
1123+1 Datensätze aus
575173 bytes (575 kB, 562 KiB) copied, 1,65022 s, 349 kB/s
TrustZone S/W fusing
512+0 Datensätze ein
512+0 Datensätze aus
262144 bytes (262 kB, 256 KiB) copied, 0,574115 s, 457 kB/s
U-boot image is fused successfully.
[root@Wintermute boot]# 

Selbst nach etlichen Jahren mit Linux mag ich so ein Bootcode rumgeschraube nicht wirklich..

Nach dem Bootcode Übertragen ist das Linux Arch auf der microSD Karte drauf und ich nehme sie wieder aus dem Filesystem raus.

[root@Wintermute boot]# cd ../..
[root@Wintermute eric]# pwd
/home/eric
[root@Wintermute eric]# umount root
[root@Wintermute eric]# rmdir root
[root@Wintermute eric]# 

Noch ein letzter prüfender Blick auf die microSD Karte.

[root@Wintermute eric]# lsblk -f /dev/mmcblk0
NAME        FSTYPE LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINT
mmcblk0                                                                      
└─mmcblk0p1 ext4   ROOT  5ab9cbdc-0a5f-4fe8-8910-afdda9eafb81                
[root@Wintermute eric]# 

Es sieht gut aus und die microSD Karte kann in einen Odroid-HC2, um dann das Linux Arch von der Karte das erstemal zu Booten und dem System alle notwendigen Pakete für eine weiteres Setup mit Ansible zu verpassen.

Nachdem das Lampenspiel an dem Odroid-HC2 darauf schließen liess, das er online ist, habe ich auf meinem DHCP Server mal nachgesehen und tatsächlich, da ist eine 192.168.2.146 aufgetaucht. Eine kurze Kontrolle, ob die IPv4 Adresse auch lebt.

[root@Wintermute eric]# ping 192.168.2.146
PING 192.168.2.146 (192.168.2.146) 56(84) bytes of data.
64 bytes from 192.168.2.146: icmp_seq=1 ttl=64 time=1.44 ms
64 bytes from 192.168.2.146: icmp_seq=2 ttl=64 time=0.566 ms
64 bytes from 192.168.2.146: icmp_seq=3 ttl=64 time=0.733 ms
64 bytes from 192.168.2.146: icmp_seq=4 ttl=64 time=0.619 ms
^X^C
--- 192.168.2.146 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 24ms
rtt min/avg/max/mdev = 0.566/0.840/1.442/0.352 ms
[root@Wintermute eric]# 

Es scheint dem Odroid-HC2 gut zu gehen, zumindest antwortet jemand auf das Geklopfe an der Netzwerkkarte. Laut Anleitung ist er per SSH unter dem User alarm, mit dem Passwort alarm erreichbar. Prüfen wir das mal und sehen auch gleich mal nach der Linux Version und den CPU Kernen des Odroid-HC2.

[root@Wintermute eric]# ssh -l alarm 192.168.2.146
alarm@192.168.2.146's password: alarm

[alarm@alarm ~]$ cat /proc/version
Linux version 4.14.102-1-ARCH (builduser@leming) (gcc version 8.2.1 20181127 (GCC)) #1 SMP PREEMPT Sat Feb 23 19:59:09 UTC 2019

[alarm@alarm ~]$ cat /proc/cpuinfo 
processor       : 0
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 90.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc07
CPU revision    : 3

processor       : 1
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 90.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc07
CPU revision    : 3

processor       : 2
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 90.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc07
CPU revision    : 3

processor       : 3
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 90.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc07
CPU revision    : 3

processor       : 4
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 120.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x2
CPU part        : 0xc0f
CPU revision    : 3

processor       : 5
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 120.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x2
CPU part        : 0xc0f
CPU revision    : 3

processor       : 6
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 120.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x2
CPU part        : 0xc0f
CPU revision    : 3

processor       : 7
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 120.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x2
CPU part        : 0xc0f
CPU revision    : 3

Hardware        : SAMSUNG EXYNOS (Flattened Device Tree)
Revision        : 0100
Serial          : 0000000000000000
[alarm@alarm ~]$ 

Es ist schon sehr verblüffend, dass dieses kleine Board in dem Odroid-HC2 mit 8 Kernen daher kommt. Eine wirklich sehr potente Plattform!

Weiter geht es mit dem Anlegen und Abgleichen des Pacman Schlüsselrings, in welchem die Vertrauenswürdigkeiten zu den Softwareentwicklern liegen.

[alarm@alarm ~]$ pacman-key --init
==> ERROR: pacman-key needs to be run as root for this operation.

[alarm@alarm ~]$ su
Password: root

[root@alarm alarm]# pacman-key --init
gpg: /etc/pacman.d/gnupg/trustdb.gpg: trustdb created
gpg: no ultimately trusted keys found
gpg: starting migration from earlier GnuPG versions
gpg: porting secret keys from '/etc/pacman.d/gnupg/secring.gpg' to gpg-agent
gpg: migration succeeded
gpg: Generating pacman keyring master key...
gpg: key 2AC1C92126FEB3C8 marked as ultimately trusted
gpg: directory '/etc/pacman.d/gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/etc/pacman.d/gnupg/openpgp-revocs.d/3189C9A002F783227C42CD222AC1C92126FEB3C8.rev'
gpg: Done
==> Updating trust database...
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u

[root@alarm alarm]# pacman-key --populate archlinuxarm
==> Appending keys from archlinuxarm.gpg...
==> Locally signing trusted keys in keyring...
  -> Locally signing key 69DD6C8FD314223E14362848BF7EEF7A9C6B5765...
  -> Locally signing key 02922214DE8981D14DC2ACABBC704E86B823CD25...
  -> Locally signing key 9D22B7BB678DC056B1F7723CB55C5315DCD9EE1A...
==> Importing owner trust values...
gpg: setting ownertrust to 4
gpg: inserting ownertrust of 4
gpg: setting ownertrust to 4
==> Updating trust database...
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   3  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: depth: 1  valid:   3  signed:   1  trust: 0-, 0q, 0n, 3m, 0f, 0u
gpg: depth: 2  valid:   1  signed:   0  trust: 1-, 0q, 0n, 0m, 0f, 0u
[root@alarm alarm]# 

Wenn ich schonmal auf dem Odroid-HC2 bin, mache ich auch gleich einen Softwareupdate. Vorweg sei gesagt, dass ich erstaunt bin wie wenig er sich aus dem Repository abholt. Die Images scheinen sehr gut gepflegt zu werden.

[root@alarm alarm]# pacman -Syu
:: Synchronizing package databases...
 core                                                            215.4 KiB  2.50M/s 00:00 [####################################################] 100%
 extra                                                             2.2 MiB  5.96M/s 00:00 [####################################################] 100%
 community                                                         5.1 MiB  4.70M/s 00:01 [####################################################] 100%
 alarm                                                           117.3 KiB  3.18M/s 00:00 [####################################################] 100%
 aur                                                               5.2 KiB  0.00B/s 00:00 [####################################################] 100%
:: Starting full system upgrade...
resolving dependencies...
looking for conflicting packages...

Packages (15) bash-5.0.002-1  ca-certificates-mozilla-3.43-1  coreutils-8.31-1  e2fsprogs-1.45.0-1  glib2-2.60.0-1  gnupg-2.2.14-1
              iana-etc-20190308-1  iproute2-5.0.0-1  libgpg-error-1.36-1  libseccomp-2.4.0-1  libssh2-1.8.1-1  linux-firmware-20190313.efd2c1c-1
              linux-odroid-xu3-4.14.107-1  s-nail-14.9.13-2  texinfo-6.6-1

Total Download Size:   107.30 MiB
Total Installed Size:  559.27 MiB
Net Upgrade Size:        1.78 MiB

:: Proceed with installation? [Y/n] 
:: Retrieving packages...
 iana-etc-20190308-1-any                                         

<snip>

==> Creating gzip-compressed initcpio image: /boot/initramfs-linux.img
==> Image generation successful
(2/7) Reloading system manager configuration...
(3/7) Creating temporary files...
(4/7) Reloading device manager configuration...
(5/7) Arming ConditionNeedsUpdate...
(6/7) Updating the info directory file...
(7/7) Rebuilding certificate stores...
[root@alarm alarm]# 

Damit Ansible zum Fliegen kommt, benötige ich noch die Pakete Python und Sudo. Welche ich gleich installiere, dabei wird Pacman die Abhängigkeiten automatisch Auflösen und die notwendigen weiteren Pakete selbstständig nachinstallieren.

[root@alarm alarm]# pacman -S sudo python
resolving dependencies...
looking for conflicting packages...

Packages (3) libnsl-1.2.0-1  python-3.7.2-3  sudo-1.8.27-1

Total Download Size:    16.42 MiB
Total Installed Size:  111.02 MiB

:: Proceed with installation? [Y/n] 
:: Retrieving packages...
 sudo-1.8.27-1-armv7h                                            715.7 KiB  10.9M/s 00:00 [####################################################] 100%
 libnsl-1.2.0-1-armv7h                                            51.2 KiB  2.50M/s 00:00 [####################################################] 100%
 python-3.7.2-3-armv7h                                            15.7 MiB  12.2M/s 00:01 [####################################################] 100%
(3/3) checking keys in keyring                                                            [####################################################] 100%
(3/3) checking package integrity                                                          [####################################################] 100%
(3/3) loading package files                                                               [####################################################] 100%
(3/3) checking for file conflicts                                                         [####################################################] 100%
(3/3) checking available disk space                                                       [####################################################] 100%
:: Processing package changes...
(1/3) installing sudo                                                                     [####################################################] 100%
(2/3) installing libnsl                                                                   [####################################################] 100%
(3/3) installing python                                                                   [####################################################] 100%
Optional dependencies for python
    python-setuptools
    python-pip
    sqlite [installed]
    mpdecimal: for decimal
    xz: for lzma [installed]
    tk: for tkinter
:: Running post-transaction hooks...
(1/2) Creating temporary files...
(2/2) Arming ConditionNeedsUpdate...
[root@alarm alarm]# 

Ich prüfe lieber nach, was für ein Python da genau installiert wurde. Dabei geht es mir darum, ob es sich um Python2 oder Python3 handelt.

[root@alarm alarm]# ls -la /usr/bin/python*
lrwxrwxrwx 1 root root    7 Jan 13 15:56 /usr/bin/python -> python3
lrwxrwxrwx 1 root root   14 Jan 13 15:56 /usr/bin/python-config -> python3-config
lrwxrwxrwx 1 root root    9 Jan 13 15:56 /usr/bin/python3 -> python3.7
lrwxrwxrwx 1 root root   16 Jan 13 15:56 /usr/bin/python3-config -> python3.7-config
-rwxr-xr-x 2 root root 5540 Jan 13 15:56 /usr/bin/python3.7
lrwxrwxrwx 1 root root   17 Jan 13 15:56 /usr/bin/python3.7-config -> python3.7m-config
-rwxr-xr-x 2 root root 5540 Jan 13 15:56 /usr/bin/python3.7m
-rwxr-xr-x 1 root root 3180 Jan 13 15:56 /usr/bin/python3.7m-config
[root@alarm alarm]# 

Sehr schön, es ist ein aktuelles Python3 geworden, was mich beruhigt – denn ich mag einem neuen System auch gerne ein frisches Python3. 😉

Damit Ansible auch auf dem System rumfuhrwerken kann, bedarf es eines Users mit sudo-Rechten ohne Passwort abfrage. Ich für meinen Teil nehme gerne den User ansible und verteile den SSH Schlüssel des Users ansible vom Control Server (master) auf die zu betreuenden Systeme (nodes), aber da gibt es verschiedene Ansätze.

[root@alarm alarm]# useradd -m -g users -s /bin/bash ansible
       
[root@alarm alarm]# passwd ansible
New password: SUPERGEHEIM 
Retype new password: SUPERGEHEIM

passwd: password updated successfully
[root@alarm alarm]# 

Okay, für den ein oder anderen stellt sich die Frage wie kriege ich diese SSH-Keys vom Master zu den Nodes. Hier eine kurze Darstellung des Ablaufs auf dem Master, ab der Stelle… es gibt keinen User ansible auf der Master. 😉

eric@barney ~]$ sudo useradd -m -g users -s /bin/bash ansible
[sudo] Passwort für eric: 

[eric@barney ~]$ sudo passwd ansible
Geben Sie ein neues Passwort ein: SUPERGEHEIM 
Geben Sie das neue Passwort erneut ein: SUPERGEHEIM
passwd: Passwort erfolgreich geändert

[ansible@barney ~]$ ssh-keygen -t ed25519
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/ansible/.ssh/id_ed25519): 
Created directory '/home/ansible/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/ansible/.ssh/id_ed25519.
Your public key has been saved in /home/ansible/.ssh/id_ed25519.pub.
The key fingerprint is:
SHA256:B4A2txMe0ho2TcF0S4gMH3BIxJhvrIjra1JpuWF4WYw ansible@barney
The key's randomart image is:
+--[ED25519 256]--+
<snip>
+----[SHA256]-----+


[ansible@barney ~]$ ls -la .ssh/
insgesamt 16
drwx------ 2 ansible users 4096 25. Mär 19:58 .
drwx------ 3 ansible users 4096 25. Mär 19:57 ..
-rw------- 1 ansible users  411 25. Mär 19:58 id_ed25519
-rw-r--r-- 1 ansible users   96 25. Mär 19:58 id_ed25519.pub

[ansible@barney ~]$ ssh-copy-id -i ~/.ssh/id_ed25519.pub ansible@192.168.2.146
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/ansible/.ssh/id_ed25519.pub"
The authenticity of host '192.168.2.146 (192.168.2.146)' can't be established.
ECDSA key fingerprint is SHA256:tatuetata.
Are you sure you want to continue connecting (yes/no)? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
ansible@192.168.2.146's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'ansible@192.168.2.146'"
and check to make sure that only the key(s) you wanted were added.

[ansible@barney ~]$ ssh ansible@192.168.2.146

[ansible@alarm ~]$ ls -la
total 24
drwx------ 3 ansible users 4096 Mar 25 20:01 .
drwxr-xr-x 4 root    root  4096 Mar 25 19:50 ..
-rw-r--r-- 1 ansible users   21 Mar 16 15:27 .bash_logout
-rw-r--r-- 1 ansible users   57 Mar 16 15:27 .bash_profile
-rw-r--r-- 1 ansible users  141 Mar 16 15:27 .bashrc
drwx------ 2 ansible users 4096 Mar 25 20:01 .ssh
[ansible@alarm ~]$ 

Der User ansible kann sich ohne Passwort vom Master im Node, mittels SSH einloggen. Jetzt fehlen nur noch die sudo Rechte für den User ansible, damit der Ansible Master auf dem Node auch Änderungen am System vornehmen kann.

[ansible@alarm ~]$ su
Password: GEHEIM

[root@alarm ansible]# visudo

ansible ALL=(ALL) NOPASSWD: ALL

[root@alarm ansible]# visudo -c    
/etc/sudoers: parsed OK
[root@alarm ansible]#       

Wie immer erfolgt danach eine kurze Kontrolle, ob der User sich einloggen und auch tatsächlich ohne Passwortabfrage sudo ausführen darf.

[ansible@barney ~]$ ssh ansible@192.168.2.146
Last login: Mon Mar 25 20:15:11 2019 from 192.168.2.10

[ansible@alarm ~]$ ls -lah /root/
ls: cannot open directory '/root/': Permission denied

[ansible@alarm ~]$ sudo ls -lah /root/
total 16K
drwxr-x---  3 root root 4.0K Mar 25 19:55 .
drwxr-xr-x 16 root root 4.0K Mar 25 18:57 ..
-rw-------  1 root root  305 Mar 25 20:15 .bash_history
drwx------  3 root root 4.0K Mar  9 01:57 .gnupg
[ansible@alarm ~]$ 

Des gewünschte Setup ist erreicht – JUPIEEE.

Die Replikation von der nun fertigen microSD Karte auf die restlichen neuen Karten kann starten. Fahren wir also sanft unseren ersten Node runter.

[ansible@alarm ~]$ sudo shutdown -P now
Connection to 192.168.2.146 closed by remote host.
Connection to 192.168.2.146 closed.
[ansible@barney ~]$ 

Anschließend ziehe ich mir von der microSD Karte ein Image mittels dd. Der Ouput beim Image ziehen sieht dann wiefolgt aus.

[root@Wintermute GlusterFS-Image]# sudo dd if=/dev/mmcblk0 of=20190325_GlusterFS-Node.img bs=4M status=progress
8824815616 bytes (8,8 GB, 8,2 GiB) copied, 110 s, 80,2 MB/s
[root@Wintermute GlusterFS-Image]#

Auf eine leere microSD Karte Schreiben sieht dann auch nicht wesentlich anders aus. Nur dann halt neunmal nach einander.

[root@Wintermute GlusterFS-Image]# sudo dd if=20190325_GlusterFS-Node.img of=/dev/mmcblk0 bs=4M status=progress
4970250240 bytes (5,0 GB, 4,6 GiB) copied, 6 s, 825 MB/s
3798+1 Datensätze ein
3798+1 Datensätze aus
15931539456 bytes (16 GB, 15 GiB) copied, 1163,94 s, 13,7 MB/s
[root@Wintermute GlusterFS-Image]#

Das einzig Blöde ist nur, dass diese microSD Karten nicht gerade Raketen beim Schreiben sind. Am Ende dauert es pro microSD Karte ~20Minuten. Zeit etwas Grand Tour zu sehen und nebenbei die ganzen Karten fertig zu machen. Mehr zum Aufsetzen des GlusterFS kommt dann im nächsten Beitrag.