Kapitel 4. Nützliche Linux-Dienstprogramme
Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com
Die Kommandozeile und ihre Werkzeuge waren einer der Hauptgründe, warum sich Alfredo zu Beginn seiner Karriere mit Linux-Servern verbunden fühlte. Einer seiner ersten Aufträge als Systemadministrator in einem mittelständischen Unternehmen bestand darin, sich um alles zu kümmern, was mit Linux zu tun hatte. Die kleine IT-Abteilung konzentrierte sich auf die Windows-Server und -Desktops, und die Verwendung der Kommandozeile war ihnen zutiefst zuwider. Irgendwann sagte der IT-Manager zu ihm, dass er grafische Benutzeroberflächen (GUIs), die Installation von Dienstprogrammen und Werkzeuge im Allgemeinen verstehe, um Probleme zu lösen: "Ich bin kein Programmierer, wenn es keine grafische Benutzeroberfläche gibt, kann ich sie nicht benutzen", sagte er.
Alfredo wurde als Auftragnehmer eingestellt, um bei den wenigen Linux-Servern des Unternehmens auszuhelfen. Zu dieser Zeitwar Subversion (SVN) der letzte Schrei in Sachen Versionskontrolle, und die Entwickler waren auf diesen einen SVN-Server angewiesen, um ihre Arbeit zu veröffentlichen. Anstelle des zentralen Identitätsservers, der von zwei Domänencontrollern bereitgestellt wurde, wurde ein textbasiertes Authentifizierungssystem verwendet, das einen Benutzer einem Hash zuordnete, der das Passwort repräsentierte. Das bedeutete, dass die Benutzernamen nicht unbedingt mit denen des Domänencontrollers übereinstimmten und dass die Passwörter beliebig sein konnten. Oft bat ein Entwickler darum, das Passwort zurückzusetzen, und jemand musste die Textdatei mit dem Hash bearbeiten. Ein Projektmanager bat Alfredo, die SVN-Authentifizierung in den Domänencontroller (Microsofts Active Directory) zu integrieren. Die erste Frage, die er stellte, war, warum die IT-Abteilung das nicht schon längst getan hatte. "Sie sagen, es sei nicht möglich, aber Alfredo, das ist eine Lüge, SVN kann sich in Active Directory integrieren."
Er hatte noch nie einen Authentifizierungsdienst wie Active Directory benutzt und verstand kaum etwas von SVN, aber er war fest entschlossen, dies zum Laufen zu bringen. Alfredo machte sich daran, alles über SVN und Active Directory zu lesen, bastelte an einer virtuellen Maschine mit einem SVN-Server herum und versuchte, diese Authentifizierung zum Laufen zu bringen. Er brauchte etwa zwei Wochen, um sich über alle Einzelheiten zu informieren und sie zum Funktionieren zu bringen. Am Ende war er erfolgreich und konnte das System in die Produktion bringen. Er fühlte sich unglaublich stark; er hatte sich einzigartiges Wissen angeeignet und war nun in der Lage, die volle Verantwortung für dieses System zu übernehmen. Der IT-Manager und der Rest der Abteilung waren überglücklich. Alfredo versuchte, sein neu erworbenes Wissen mit anderen zu teilen, aber er bekam immer eine Ausrede zu hören: "keine Zeit " , "zu beschäftigt " , "andere Prioritäten" und "vielleicht ein anderes Mal - vielleicht nächste Woche".
Eine treffende Beschreibung für Technologen ist: Wissensarbeiter. Deine Neugier und dein unermüdliches Streben nach Wissen werden dich und die Umgebungen, an denen du arbeitest, immer besser machen. Lass dich niemals von einem Kollegen oder einer Kollegin (oder einer ganzen IT-Abteilung, wie in Alfredos Fall) davon abhalten, deine Systeme zu verbessern. Wenn es eine Möglichkeit gibt, etwas Neues zu lernen, dann nimm sie wahr! Das Schlimmste, was passieren kann, ist, dass du dir Wissen angeeignet hast, das du vielleicht nicht oft nutzen wirst, das aber deine berufliche Laufbahn verändern könnte.
Linux verfügt zwar über Desktop-Umgebungen, aber seine wahre Stärke liegt im Verständnis und in der Nutzung der Kommandozeile und letztlich in deren Erweiterung. Wenn es keine vorgefertigten Tools gibt, um ein Problem zu lösen, entwickeln erfahrene DevOps-Leute ihre eigenen. Dieser Gedanke, Lösungen zu finden, indem man die wichtigsten Teile zusammensetzt, ist unglaublich mächtig und das ist es auch, was bei diesem Job passiert ist, bei dem es sich produktiv anfühlte, Aufgaben zu erledigen, ohne Software von der Stange installieren zu müssen, um Dinge zu beheben.
In diesem Kapitel gehen wir einige gängige Muster in der Shell durch und stellen einige nützliche Python-Befehle vor, die die Interaktion mit einer Maschine verbessern sollen. Wir finden, dass das Erstellen von Aliasen und Einzeilern am meisten Spaß bei der Arbeit macht, und manchmal sind sie so nützlich, dass sie als Plug-ins oder eigenständige Software landen.
Disk Utilities
Es gibt verschiedene Dienstprogramme, mit denen du Informationen über Geräte in einem System erhalten kannst. Viele von ihnen haben überlappende Funktionen, und einige haben eine interaktive Sitzung, um mit Festplattenoperationen umzugehen, wie fdisk
und parted
.
Es ist wichtig, die Festplattendienstprogramme gut zu beherrschen, nicht nur um Informationen abzurufen und Partitionen zu manipulieren, sondern auch um die Leistung genau zu messen. Vor allem die Leistung ist eine der schwierigsten Aufgaben, die es zu bewältigen gilt. Die beste Antwort auf die Frage "Wie messe ich die Leistung eines Geräts?" lautet: Es kommt darauf an, denn es ist schwierig, die gewünschte Kennzahl zu ermitteln.
Leistung messen
Wenn wir in einer isolierten Umgebung mit einem Server arbeiten müssen, der keinen Zugang zum Internet hat oder den wir nicht kontrollieren und daher keine Pakete installieren können, würde das Tool dd
(das auf allen gängigen Linux-Distributionen verfügbar sein sollte ) helfen, einige Antworten zu finden. Wenn möglich, solltest du es mit iostat
kombinieren, um den Befehl, der das Gerät angreift, von dem Befehl zu trennen, der den Bericht liefert.
Wie ein erfahrener Leistungsingenieur einmal sagte, kommt es darauf an, was gemessen wird und wie. Zum Beispiel ist dd
ein Single-Thread-System und kann nicht mehrere zufällige Lese- und Schreibvorgänge durchführen; außerdem misst es den Durchsatz und nicht die Ein- und Ausgabevorgänge pro Sekunde (IOPS). Was misst du? Durchsatz oder IOPS?
Vorsicht
Ein Wort der Warnung zu diesen Beispielen. Sie können dein System zerstören, also folge ihnen nicht blind und verwende Geräte, die gelöscht werden können.
Dieser einfache Einzeiler lässt dd
laufen, um einige Zahlen für ein brandneues Gerät( in diesem Fall/dev/sdc ) zu erhalten:
$ dd if=/dev/zero of=/dev/sdc count=10 bs=100M 10+0 records in 10+0 records out 1048576000 bytes (1.0 GB, 1000 MiB) copied, 1.01127 s, 1.0 GB/s
Er schreibt 10 Datensätze von 100 Megabyte mit einer Rate von 1 GB/s. Das ist der Durchsatz. Ein einfacher Weg, um mit dd
IOPS zu erhalten, ist die Verwendung von iostat
. In diesem Beispiel wird iostat
nur auf dem Gerät ausgeführt, das mit dd
überlastet wird, wobei das Flag -d
nur dazu dient, das Gerät zu informieren, und das Intervall eine Sekunde beträgt:
$ iostat -d /dev/sdc 1 Device tps kB_read/s kB_wrtn/s kB_read kB_wrtn sdc 6813.00 0.00 1498640.00 0 1498640 Device tps kB_read/s kB_wrtn/s kB_read kB_wrtn sdc 6711.00 0.00 1476420.00 0 1476420
Die Ausgabe iostat
wiederholt sich jede Sekunde, bis eine Ctrl-C
ausgegeben wird, um den Vorgang abzubrechen. Die zweite Spalte in der Ausgabe ist tps
, was für Transaktionen pro Sekunde steht und dasselbe ist wie IOPS. Eine schönere Art, die Ausgabe zu visualisieren und das Durcheinander zu vermeiden, das ein sich wiederholender Befehl verursacht, ist, das Terminal bei jedem Durchlauf zu löschen:
$ while true; do clear && iostat -d /dev/sdc && sleep 1; done
Genaue Tests mit fio
Wenn dd
und iostat
nicht ausreichen, ist das am häufigsten verwendete Tool für Leistungstests fio
. Es kann dabei helfen, das Leistungsverhalten eines Geräts in einer lese- oder schreiblastigen Umgebung zu verdeutlichen (und sogar die Anteile von Lese- und Schreibvorgängen anzupassen).
Die Ausgabe von fio
ist ziemlich ausführlich. Das folgende Beispiel hebt die IOPS bei Lese- und Schreibvorgängen hervor:
$ fio --name=sdc-performance --filename=/dev/sdc --ioengine=libaio \ --iodepth=1 --rw=randrw --bs=32k --direct=0 --size=64m sdc-performance: (g=0): rw=randwrite, bs=(R) 32.0KiB-32.0KiB, (W) 32.0KiB-32.0KiB, (T) 32.0KiB-32.0KiB, ioengine=libaio, iodepth=1 fio-3.1 Starting 1 process sdc-performance: (groupid=0, jobs=1): err= 0: pid=2879: read: IOPS=1753, BW=54.8MiB/s (57.4MB/s)(31.1MiB/567msec) ... iops : min= 1718, max= 1718, avg=1718.00, stdev= 0.00, samples=1 write: IOPS=1858, BW=58.1MiB/s (60.9MB/s)(32.9MiB/567msec) ... iops : min= 1824, max= 1824, avg=1824.00, stdev= 0.00, samples=1
Die im Beispiel verwendeten Flags geben dem Auftrag den Namen sdc-performance
, verweisen direkt auf das Gerät /dev/sdc (erfordert Superuser-Berechtigungen), verwenden die native Linux-Bibliothek für asynchrone E/A, setzen iodepth
auf 1
(Anzahl der gleichzeitig zu sendenden sequentiellen E/A-Anfragen) und definieren zufällige Lese- und Schreibvorgänge von 32 Kilobyte für die Puffergröße unter Verwendung von gepufferter E/A (kann auf 1 gesetzt werden, um ungepufferte E/A zu verwenden) auf eine 64-Megabyte-Datei. Das ist ein ziemlich langer Befehl!
Das Tool fio
bietet eine Vielzahl zusätzlicher Optionen, die in fast allen Fällen, in denen genaue IOPS-Messungen erforderlich sind, hilfreich sind. Zum Beispiel kann es den Test auf viele Geräte gleichzeitig ausdehnen, ein I/O-Warm-up durchführen und sogar I/O-Schwellenwerte für den Test festlegen, wenn eine bestimmte Grenze nicht überschritten werden soll. Schließlich können die vielen Optionen in der Befehlszeile mit INI-Dateien konfiguriert werden, so dass die Ausführung von Aufträgen gut geskriptet werden kann.
Trennwände
In der Regel verwenden wir fdisk
mit seiner interaktiven Sitzung zum Erstellen von Partitionen, aber in manchen Fällen funktioniert fdisk
nicht gut, z. B. bei großen Partitionen (zwei Terabyte oder mehr). In diesen Fällen solltest du auf parted
zurückgreifen.
Eine kurze interaktive Sitzung zeigt, wie man eine primäre Partition mit fdisk
erstellt, mit dem Standard-Startwert und einer Größe von vier Gibibytes. Am Ende wird der Schlüssel w
gesendet, um die Änderungen zu schreiben:
$ sudo fdisk /dev/sds Command (m for help): n Partition type: p primary (0 primary, 0 extended, 4 free) e extended Select (default p): p Partition number (1-4, default 1): First sector (2048-22527999, default 2048): Using default value 2048 Last sector, +sectors or +size{K,M,G} (2048-22527999, default 22527999): +4G Partition 1 of type Linux and of size 4 GiB is set Command (m for help): w The partition table has been altered! Calling ioctl() to re-read partition table. Syncing disks.
parted
erreicht das Gleiche, allerdings mit einer anderen Schnittstelle:
$ sudo parted /dev/sdaa GNU Parted 3.1 Using /dev/sdaa Welcome to GNU Parted! Type 'help' to view a list of commands. (parted) mklabel New disk label type? gpt (parted) mkpart Partition name? []? File system type? [ext2]? Start? 0 End? 40%
Zum Schluss beendest du das Programm mit der Taste q
. Für die programmatische Erstellung von Partitionen auf der Kommandozeile ohne interaktive Eingabeaufforderungen erreichst du das gleiche Ergebnis mit ein paar Befehlen:
$ parted --script /dev/sdaa mklabel gpt $ parted --script /dev/sdaa mkpart primary 1 40% $ parted --script /dev/sdaa print Disk /dev/sdaa: 11.5GB Sector size (logical/physical): 512B/512B Partition Table: gpt Disk Flags: Number Start End Size File system Name Flags 1 1049kB 4614MB 4613MB
Abrufen von spezifischen Geräteinformationen
Wenn bestimmte Informationen zu einem Gerät benötigt werden, sind entweder lsblk
oder blkid
gut geeignet. fdisk
arbeitet nicht gerne ohne Superuser-Rechte. Hier fdisk
listet die Informationen über das Gerät /dev/sda auf:
$ fdisk -l /dev/sda fdisk: cannot open /dev/sda: Permission denied $ sudo fdisk -l /dev/sda Disk /dev/sda: 42.9 GB, 42949672960 bytes, 83886080 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk label type: dos Disk identifier: 0x0009d9ce Device Boot Start End Blocks Id System /dev/sda1 * 2048 83886079 41942016 83 Linux
blkid
ist insofern ähnlich, als dass es auch Superuser-Rechte benötigt:
$ blkid /dev/sda $ sudo blkid /dev/sda /dev/sda: PTTYPE="dos"
lsblk
ermöglicht es, Informationen ohne höhere Berechtigungen zu erhalten, und liefert unabhängig davon die gleiche Informationsausgabe:
$ lsblk /dev/sda NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 40G 0 disk └─sda1 8:1 0 40G 0 part / $ sudo lsblk /dev/sda NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 40G 0 disk └─sda1 8:1 0 40G 0 part /
Dieser Befehl, der das -p
Flag für Low-Level-Device-Probing verwendet, ist sehr gründlich und sollte dir genügend Informationen über ein Gerät liefern:
$ blkid -p /dev/sda1 UUID="8e4622c4-1066-4ea8-ab6c-9a19f626755c" TYPE="xfs" USAGE="filesystem" PART_ENTRY_SCHEME="dos" PART_ENTRY_TYPE="0x83" PART_ENTRY_FLAGS="0x80" PART_ENTRY_NUMBER="1" PART_ENTRY_OFFSET="2048" PART_ENTRY_SIZE="83884032"
lsblk
hat einige Standardeigenschaften, nach denen du suchen kannst:
$ lsblk -P /dev/nvme0n1p1 NAME="nvme0n1p1" MAJ:MIN="259:1" RM="0" SIZE="512M" RO="0" TYPE="part"
Du kannst aber auch bestimmte Flaggen setzen, um eine bestimmte Eigenschaft abzufragen:
lsblk -P -o SIZE /dev/nvme0n1p1 SIZE="512M"
Der Zugriff auf eine Eigenschaft auf diese Weise macht es einfach, Skripte zu erstellen und sogar von der Python-Seite aus zu konsumieren.
Netzwerk-Dienstprogramme
Die Netzwerk-Tools werden immer besser, da immer mehr Server miteinander verbunden werden müssen. Viele der Dienstprogramme in diesem Abschnitt befassen sich mit nützlichen Einzeilern wie Secure Shell (SSH)-Tunneling, aber einige andere gehen ins Detail, wenn es darum geht, die Netzwerkleistung zu testen, z. B. mit dem Apache Bench Tool.
SSH-Tunneling
Hast du schon einmal versucht, einen HTTP-Dienst zu erreichen, der auf einem entfernten Server läuft, der nur über SSH erreichbar ist? Diese Situation tritt ein, wenn der HTTP-Dienst aktiviert ist, aber nicht öffentlich benötigt wird. Das letzte Mal haben wir das bei einer RabbitMQ-Produktionsinstanz gesehen, bei der das Management Plug-in aktiviert war, das einen HTTP-Dienst auf Port 15672 startet. Der Dienst ist nicht öffentlich zugänglich, und das aus gutem Grund: Es besteht keine Notwendigkeit, ihn öffentlich zugänglich zu machen, da er nur selten genutzt wird, und außerdem kann man die Tunneling-Funktionen von SSH nutzen.
Dazu wird eine SSH-Verbindung mit dem entfernten Server hergestellt und der entfernte Port (in meinem Fall 15672) an einen lokalen Port auf dem Ursprungsrechner weitergeleitet. Der entfernte Rechner hat einen eigenen SSH-Port, was den Befehl etwas komplizierter macht. So sieht es aus:
$ ssh -L 9998:localhost:15672 -p 2223 adeza@prod1.rabbitmq.ceph.internal -N
Es gibt drei Flaggen, drei Zahlen und zwei Adressen. Zerlegen wir den Befehl, um zu verdeutlichen, was hier vor sich geht. Das Flag -L
signalisiert, dass die Weiterleitung aktiviert ist und ein lokaler Port (9998) an einen Remote-Port (RabbitMQs Standardwert 15672) gebunden werden soll. Als Nächstes gibt das -p
Flag an, dass der benutzerdefinierte SSH-Port des Remote-Servers 2223 ist, und dann werden der Benutzername und die Adresse angegeben. Schließlich bedeutet -N
, dass wir nicht zu einer entfernten Shell gelangen und die Weiterleitung vornehmen sollen.
Wenn der Befehl korrekt ausgeführt wird, scheint er zu hängen, aber er ermöglicht es dir, http://localhost:9998/ aufzurufen und die Anmeldeseite für die entfernte RabbitMQ-Instanz zu sehen. Ein nützliches Flag beim Tunneln ist -f
: Es schickt den Prozess in den Hintergrund, was hilfreich ist, wenn die Verbindung nicht nur vorübergehend ist, so dass das Terminal bereit und sauber ist, um weitere Arbeit zu erledigen.
Benchmarking von HTTP mit Apache Benchmark (ab)
Wir lieben es, die Server, mit denen wir arbeiten, auf Herz und Nieren zu prüfen, um sicherzustellen, dass sie mit der Last richtig umgehen, vor allem bevor sie in die Produktion gehen. Manchmal versuchen wir sogar, eine seltsame Wettlaufsituation auszulösen, die unter hoher Last auftreten kann. Das Apache Benchmark Tool (ab
in der Kommandozeile) ist eines dieser kleinen Tools, das dich mit nur ein paar Flags schnell ans Ziel bringt.
Mit diesem Befehl werden jeweils 100 Anfragen an eine lokale Instanz, auf der Nginx läuft, erstellt, insgesamt also 10.000 Anfragen:
$ ab -c 100 -n 10000 http://localhost/
Das ist ziemlich brutal für ein System, aber dies ist ein lokaler Server, und die Anfragen sind nur ein HTTP GET
. Die detaillierte Ausgabe von ab
ist sehr umfangreich und sieht wie folgt aus (der Kürze halber gekürzt):
Benchmarking localhost (be patient) ... Completed 10000 requests Finished 10000 requests Server Software: nginx/1.15.9 Server Hostname: localhost Server Port: 80 Document Path: / Document Length: 612 bytes Concurrency Level: 100 Time taken for tests: 0.624 seconds Complete requests: 10000 Failed requests: 0 Total transferred: 8540000 bytes HTML transferred: 6120000 bytes Requests per second: 16015.37 [#/sec] (mean) Time per request: 6.244 [ms] (mean) Time per request: 0.062 [ms] (mean, across all concurrent requests) Transfer rate: 13356.57 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 3 0.6 3 5 Processing: 0 4 0.8 3 8 Waiting: 0 3 0.8 3 6 Total: 0 6 1.0 6 9
Diese Art von Informationen und wie sie präsentiert werden, ist enorm. Auf einen Blick kannst du schnell erkennen, ob ein Produktionsserver Verbindungen abbricht (im Feld Failed requests
) und wie hoch die Durchschnittswerte sind. Es wird eine GET
Anfrage verwendet, aber mit ab
kannst du auch andere HTTP-Verben verwenden, wie z. B. POST
, und sogar eine HEAD
Anfrage stellen. Mit dieser Art von Tool musst du vorsichtig sein, da es einen Server leicht überlasten kann. Im Folgenden findest du realistischere Zahlen von einem HTTP-Dienst in der Produktion (der Kürze halber gekürzt):
... Benchmarking prod1.ceph.internal (be patient) Server Software: nginx Server Hostname: prod1.ceph.internal Server Port: 443 SSL/TLS Protocol: TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256 Server Temp Key: ECDH P-256 256 bits TLS Server Name: prod1.ceph.internal Complete requests: 200 Failed requests: 0 Total transferred: 212600 bytes HTML transferred: 175000 bytes Requests per second: 83.94 [#/sec] (mean) Time per request: 1191.324 [ms] (mean) Time per request: 11.913 [ms] (mean, across all concurrent requests) Transfer rate: 87.14 [Kbytes/sec] received ....
Jetzt sehen die Zahlen anders aus, es trifft auf einen Dienst mit aktiviertem SSL, und ab
listet auf, welche Protokolle das sind. Mit 83 Anfragen pro Sekunde könnte es besser laufen, aber dies ist ein API-Server, der JSON produziert und normalerweise nicht so viel Last auf einmal bekommt, wie gerade erzeugt wurde.
Belastungstests mit molotov
Das Molotov-Projekt ist ein interessantes Projekt, das auf Lasttests ausgerichtet ist. Einige seiner Funktionen ähneln denen von Apache Benchmark, aber da es ein Python-Projekt ist, bietet es eine Möglichkeit, Szenarien mit Python und dem Modul asyncio
zu schreiben.
So sieht das einfachste Beispiel für molotov
aus:
import
molotov
@molotov.scenario
(
100
)
async
def
scenario_one
(
session
):
async
with
session
.
get
(
"http://localhost:5000"
)
as
resp
:
assert
resp
.
status
==
200
Speichere die Datei als load_test.py, erstelle eine kleine Flask-Anwendung, die sowohl POST
als auch GET
Anfragen an ihre Haupt-URL bearbeitet, und speichere sie als small.py:
from
flask
import
Flask
,
redirect
,
request
app
=
Flask
(
'basic app'
)
@app.route
(
'/'
,
methods
=
[
'GET'
,
'POST'
])
def
index
():
if
request
.
method
==
'POST'
:
redirect
(
'https://www.google.com/search?q=
%s
'
%
request
.
args
[
'q'
])
else
:
return
'<h1>GET request from Flask!</h1>'
Starte die Flask-Anwendung mit FLASK_APP=small.py flask run
und führe dann molotov
mit der zuvor erstellten Datei load_test.py aus:
$ molotov -v -r 100 load_test.py **** Molotov v1.6. Happy breaking! **** Preparing 1 worker... OK SUCCESSES: 100 | FAILURES: 0 WORKERS: 0 *** Bye ***
Einhundert Anfragen auf einem einzigen Worker liefen gegen die lokale Flask-Instanz. Das Tool glänzt wirklich, wenn die Lasttests erweitert werden, um mehr pro Anfrage zu tun. Es verfügt über Konzepte, die den Unit-Tests ähneln, wie z. B. Setup, Teardown und sogar Code, der auf bestimmte Ereignisse reagieren kann. Da die kleine Flask-Anwendung mit einer POST
umgehen kann, die auf eine Google-Suche umleitet, füge ein weiteres Szenario in die Datei load_test.py_ ein. Diesmal änderst du die Gewichtung so, dass 100% der Anfragen eine POST
ausführen:
@molotov.scenario
(
100
)
async
def
scenario_post
(
session
):
resp
=
await
session
.
post
(
"http://localhost:5000"
,
params
=
{
'q'
:
'devops'
})
redirect_status
=
resp
.
history
[
0
]
.
status
error
=
"unexpected redirect status:
%s
"
%
redirect_status
assert
redirect_status
==
301
,
error
Führe dieses neue Szenario für eine einzelne Anfrage aus, um das Folgende zu zeigen:
$ molotov -v -r 1 --processes 1 load_test.py **** Molotov v1.6. Happy breaking! **** Preparing 1 worker... OK AssertionError('unexpected redirect status: 302',) File ".venv/lib/python3.6/site-packages/molotov/worker.py", line 206, in step **scenario['kw']) File "load_test.py", line 12, in scenario_two assert redirect_status == 301, error SUCCESSES: 0 | FAILURES: 1 *** Bye ***
Eine einzige Anfrage (mit -r 1
) reichte aus, um das Ganze fehlschlagen zu lassen. Die Assertion muss so aktualisiert werden, dass sie auf 302
statt auf 301
prüft. Sobald dieser Status aktualisiert ist, ändere die Gewichtung des Szenarios POST
in 80
, damit weitere Anfragen (mit GET
) an die Flask-Anwendung gesendet werden. So sieht die Datei am Ende aus:
import
molotov
@molotov.scenario
()
async
def
scenario_one
(
session
):
async
with
session
.
get
(
"http://localhost:5000/"
)
as
resp
:
assert
resp
.
status
==
200
@molotov.scenario
(
80
)
async
def
scenario_two
(
session
):
resp
=
await
session
.
post
(
"http://localhost:5000"
,
params
=
{
'q'
:
'devops'
})
redirect_status
=
resp
.
history
[
0
]
.
status
error
=
"unexpected redirect status:
%s
"
%
redirect_status
assert
redirect_status
==
301
,
error
Führe load_test.py für 10 Anfragen aus, um die Anfragen zu verteilen, zwei für eine GET
und den Rest mit einer POST
:
127.0.0.1 - - [04/Sep/2019 12:10:54] "POST /?q=devops HTTP/1.1" 302 - 127.0.0.1 - - [04/Sep/2019 12:10:56] "POST /?q=devops HTTP/1.1" 302 - 127.0.0.1 - - [04/Sep/2019 12:10:57] "POST /?q=devops HTTP/1.1" 302 - 127.0.0.1 - - [04/Sep/2019 12:10:58] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [04/Sep/2019 12:10:58] "POST /?q=devops HTTP/1.1" 302 - 127.0.0.1 - - [04/Sep/2019 12:10:59] "POST /?q=devops HTTP/1.1" 302 - 127.0.0.1 - - [04/Sep/2019 12:11:00] "POST /?q=devops HTTP/1.1" 302 - 127.0.0.1 - - [04/Sep/2019 12:11:01] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [04/Sep/2019 12:11:01] "POST /?q=devops HTTP/1.1" 302 - 127.0.0.1 - - [04/Sep/2019 12:11:02] "POST /?q=devops HTTP/1.1" 302 -
Wie du siehst, ist molotov
mit reinem Python leicht erweiterbar und kann an andere, komplexere Bedürfnisse angepasst werden. Diese Beispiele kratzen nur an der Oberfläche dessen, was das Tool leisten kann.
CPU-Dienstprogramme
Es gibt zwei wichtige CPU-Utilities: top
und htop
. top
ist heute in den meisten Linux-Distributionen vorinstalliert, aber wenn du in der Lage bist, Pakete zu installieren, ist htop
fantastisch zu bedienen und wir ziehen seine anpassbare Oberfläche top
vor. Es gibt noch ein paar andere Tools, die eine CPU-Visualisierung und vielleicht sogar eine Überwachung ermöglichen, aber keines davon ist so vollständig und so weit verbreitet wie top
und htop
. Es ist zum Beispiel durchaus möglich, die CPU-Auslastung mit dem Befehl ps
zu ermitteln:
$ ps -eo pcpu,pid,user,args | sort -r | head -10 %CPU PID USER COMMAND 0.3 719 vagrant -bash 0.1 718 vagrant sshd: vagrant@pts/0 0.1 668 vagrant /lib/systemd/systemd --user 0.0 9 root [rcu_bh] 0.0 95 root [ipv6_addrconf] 0.0 91 root [kworker/u4:3] 0.0 8 root [rcu_sched] 0.0 89 root [scsi_tmf_1]
Der Befehl ps
benötigt einige benutzerdefinierte Felder. Das erste ist pcpu
, das die CPU-Auslastung angibt, gefolgt von der Prozess-ID, dem Benutzer und schließlich dem Befehl. Das führt zu einer umgekehrten Sortierung, da die CPU-Auslastung standardmäßig von weniger zu mehr geht und du die höchste CPU-Auslastung an der Spitze haben musst. Da der Befehl diese Informationen für jeden einzelnen Prozess anzeigt, filtert er die 10 besten Ergebnisse mit dem Befehl head
.
Aber der Befehl ist ein ziemlicher Zungenbrecher, an den man sich nur schwer erinnern kann und der nicht ständig aktualisiert wird. Selbst wenn du einen Alias hast, bist du mit top
oder htop
besser dran. Wie du sehen wirst, haben beide umfangreiche Funktionen.
Prozesse mit htop ansehen
Das Tool htop
ist genau wie top
(ein interaktiver Prozessbetrachter), aber es ist plattformübergreifend (funktioniert unter OS X, FreeBSD, OpenBSD und Linux), bietet Unterstützung für bessere Visualisierungen (siehe Abbildung 4-1) und ist angenehm zu bedienen. Unter https://hisham.hm/htop findest du einen Screenshot von htop
, der auf einem Server läuft. Einer der größten Nachteile von htop
ist, dass alle Shortcuts, die du vielleicht von top
kennst, nicht kompatibel sind. Du musst also dein Gehirn neu programmieren, um sie zu verstehen und für htop
zu verwenden.
Die Informationen in Abbildung 4-1 sehen sofort anders aus und fühlen sich anders an. CPU, Speicher und Swap werden oben links angezeigt und bewegen sich, wenn sich das System ändert. Mit den Pfeiltasten kannst du nach oben oder unten und sogar von links nach rechts scrollen und so den gesamten Befehl des Prozesses sehen.
Willst du einen Prozess beenden? Bewege dich mit den Pfeiltasten zu ihm oder drücke /
, um den Prozess schrittweise zu suchen (und zu filtern), und drücke dann k
. In einem neuen Menü werden alle Signale angezeigt, die an den Prozess gesendet werden können, z. B. SIGTERM
anstelle von SIGKILL
. Es ist möglich, mehr als einen Prozess zum Töten zu "markieren". Drücke die Leertaste, um den ausgewählten Prozess zu markieren und ihn mit einer anderen Farbe hervorzuheben. Du hast einen Fehler gemacht und möchtest die Markierung aufheben? Drücke erneut die Leertaste. Das ist alles sehr intuitiv.
Ein Problem mit htop
ist, dass es viele Aktionen gibt, die den Tasten F
zugeordnet sind, und du vielleicht keine hast. F1
steht zum Beispiel für Hilfe. Die Alternative ist, wenn möglich die entsprechenden Zuordnungen zu verwenden. Um das Hilfemenü aufzurufen, benutze die Taste h
; um das Setup aufzurufen, benutze Shift s
anstelle von F2.
Die t
(auch hier, wie intuitiv!) aktiviert (schaltet) die Prozessliste als Baum. Die wahrscheinlich am häufigsten verwendete Funktion ist die Sortierung. Drücke >
und es erscheint ein Menü, in dem du die Art der Sortierung auswählen kannst: PID, Benutzer, Speicher, Priorität und CPU-Prozent sind nur einige davon. Es gibt auch Tastenkombinationen, um direkt (ohne Menüauswahl) nach Speicher (Shift i
), CPU (Shift p
) und Zeit (Shift t
) zu sortieren.
Zum Schluss noch zwei unglaubliche Funktionen: Du kannst strace
oder lsof
direkt in dem ausgewählten Prozess ausführen, solange diese installiert und für den Benutzer verfügbar sind. Wenn die Prozesse Superuser-Rechte benötigen, meldet htop
dies und verlangt, dass sudo
als privilegierter Benutzer ausgeführt wird. Um strace
in einem ausgewählten Prozess auszuführen, verwendest du die Taste s
; für lsof
verwendest du die Taste l
.
Wenn entweder strace
oder lsof
verwendet wird, sind die Such- und Filteroptionen auch mit dem Zeichen /
verfügbar. Was für ein unglaublich nützliches Tool! Hoffentlich werden eines Tages auch andere Zuordnungen alsF
möglich sein, auch wenn die meiste Arbeit mit den alternativen Zuordnungen erledigt werden kann .
Tipp
Wenn htop
über die interaktive Sitzung angepasst wird, werden die Änderungen in einer Konfigurationsdatei gespeichert, die sich normalerweise in ~/.config/htop/htoprc befindet. Wenn du dort Konfigurationen definierst und sie später in der Sitzung änderst, überschreibt die Sitzung alles, was zuvor in der htoprc-Datei definiert wurde.
Arbeiten mit Bash und ZSH
Alles beginnt mit der Anpassung. Sowohl Bash als auch ZSH werden in der Regel mit einem "Dotfile" ausgeliefert , einer Datei mit einem vorangestellten Punkt, die Konfigurationsdaten enthält, aber standardmäßig ausgeblendet wird, wenn Verzeichnisinhalte aufgelistet werden, und die sich im Heimatverzeichnis des Benutzers befindet. Bei der Bash ist das die Datei .bashrc und bei der ZSH die Datei .zshrc. Beide Shells unterstützen mehrere Ebenen, die in einer vordefinierten Reihenfolge geladen werden, die in der Konfigurationsdatei für den Benutzer endet.
Wenn ZSH installiert wird, wird normalerweise keine .zshrc
erstellt. So sieht eine Minimalversion davon in einer CentOS-Distribution aus (alle Kommentare wurden der Kürze halber entfernt):
$
cat /etc/skel/.zshrc
autoload -U compinit
compinit
setopt COMPLETE_IN_WORD
In der Bash gibt es ein paar zusätzliche Punkte, aber nichts Überraschendes. Zweifellos wirst du an den Punkt kommen, an dem du dich über ein Verhalten oder eine Sache, die du auf einem anderen Server gesehen hast und die du nachmachen willst, extrem ärgerst. Wir können ohne Farben im Terminal nicht leben, also muss in jeder Shell die Farbe aktiviert sein. Ehe du dich versiehst, steckst du tief in den Konfigurationen und willst einen Haufen nützlicher Aliase und Funktionen hinzufügen.
Bald darauf kommen die Texteditor-Konfigurationen hinzu, und das Ganze fühlt sich auf verschiedenen Rechnern oder wenn neue hinzukommen unhandlich an, und all diese nützlichen Aliase sind nicht eingerichtet, und es ist unglaublich, aber niemand hat irgendwo die Farbunterstützung aktiviert. Jeder hat einen Weg gefunden, dieses Problem auf eine nicht übertragbare, ad hoc Weise zu lösen: Alfredo benutzt irgendwann ein Makefile, und seine Kollegen benutzen entweder gar nichts oder ein Bash-Skript. Ein neues Projekt namens Dotdrop bietet viele Funktionen, um all diese Dotfiles in Ordnung zu bringen, z. B. Kopieren, Symlinking und getrennte Profile für Entwicklungs- und andere Rechner - sehr nützlich, wenn du von einem Rechner auf einen anderen umziehst.
Du kannst Dotdrop für ein Python-Projekt verwenden. Obwohl du es über die regulären Tools virtualenv
und pip
installieren kannst, ist es empfehlenswert, es als Submodul in dein Repository mit Dotfiles einzubinden. Falls du das noch nicht getan hast, ist es sehr praktisch, alle deine Dotfiles in der Versionskontrolle zu haben, damit du die Änderungen verfolgen kannst. Die Dotfiles von Alfredo sind öffentlich zugänglich und er versucht, sie so aktuell wie möglich zu halten.
Unabhängig davon, was verwendet wird, ist es eine gute Strategie, die Änderungen über die Versionskontrolle zu verfolgen und sicherzustellen, dass alles immer auf dem neuesten Stand ist.
Anpassen der Python-Shell
Du kannst die Python-Shell mit Helfern anpassen und nützliche Module in eine Python-Datei importieren, die dann als Umgebungsvariable exportiert werden muss. Ich bewahre meine Konfigurationsdateien in einem Repository namens dotfiles auf. In meiner Shell-Konfigurationsdatei( bei mir$HOME/.zshrc ) definiere ich also den folgenden Export:
export
PYTHONSTARTUP
=
$HOME
/dotfiles/pythonstartup.py
Um das auszuprobieren, erstellst du eine neue Python-Datei mit dem Namen pythonstartup.py (sie kann aber jeden beliebigen Namen haben), die so aussieht:
import types
import uuid
helpers = types.ModuleType('helpers')
helpers.uuid4 = uuid.uuid4()
Öffne nun eine neue Python-Shell und gib die neu erstellte pythonstartup.py an:
$ PYTHONSTARTUP=pythonstartup.py python Python 3.7.3 (default, Apr 3 2019, 06:39:12) [GCC 8.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> helpers <module 'helpers'> >>> helpers.uuid4() UUID('966d7dbe-7835-4ac7-bbbf-06bf33db5302')
Das Objekt helpers
ist sofort verfügbar. Da wir die Eigenschaft uuid4
hinzugefügt haben, können wir es als helpers.uuid4()
aufrufen. Wie du vielleicht schon gemerkt hast, werden alle Importe und Definitionen in der Python-Shell verfügbar sein. Dies ist eine bequeme Möglichkeit, das Verhalten zu erweitern, das in der Standard-Shell nützlich ist.
Rekursives Globbing
Rekursives Globbing ist in ZSH standardmäßig aktiviert, aber in der Bash (Version 4 und höher) muss es unter shopt
eingestellt werden. Rekursives Globbing ist eine coole Einstellung, die es dir ermöglicht, einen Pfad mit der folgenden Syntax zu durchlaufen:
$ ls **/*.py
Dieses Snippet würde jede Datei und jedes Verzeichnis rekursiv durchgehen und jede einzelne Datei auflisten, die auf .py
endet. So aktivierst du es in Bash 4:
$ shopt -s globstar
Suchen und Ersetzen durch Eingabeaufforderungen
Vim hat eine nette Funktion in seiner Such- und Ersetzungsmaschine, die eine Eingabeaufforderung enthält, um die Ersetzung durchzuführen oder zu überspringen. Das ist besonders nützlich, wenn du nicht genau den regulären Ausdruck findest, den du brauchst, aber einige andere naheliegende Übereinstimmungen ignorieren willst. Wir kennen uns mit regulären Ausdrücken aus, aber wir haben versucht, keine Experten darin zu sein, denn es wäre sehr verlockend, sie für alles zu verwenden. Meistens willst du eine einfache Suche und Ersetzung durchführen und nicht mit dem Kopf gegen die Wand schlagen, um die perfekte Regex zu finden.
Das Flag c
muss am Ende des Befehls angehängt werden, um die Eingabeaufforderung in Vim zu aktivieren:
:%s/original term/replacement term/gc
Das bedeutet: Suche nach dem ursprünglichen Begriff in der ganzen Datei und ersetze ihn durch den Ersatzbegriff, aber bei jeder Eingabeaufforderung kann man sich entscheiden, ihn zu ändern oder zu überspringen. Wenn eine Übereinstimmung gefunden wird, zeigt Vim eine Meldung wie diese an:
replace with replacement term (y/n/a/q/l/^E/^Y)?
Der gesamte Bestätigungs-Workflow mag albern erscheinen, aber er ermöglicht es dir, die Beschränkungen für den regulären Ausdruck zu lockern oder sogar gar keinen zu verwenden, um ein einfacheres Vergleichen und Ersetzen zu erreichen. Ein kurzes Beispiel dafür ist eine kürzliche API-Änderung in einem Produktionswerkzeug, bei der das Attribut eines Objekts für eine Callable geändert wurde. Der Code gab True
oder False
zurück, um anzugeben, ob Superuser-Berechtigungen erforderlich waren oder nicht. Die tatsächliche Ersetzung in einer einzelnen Datei würde wie folgt aussehen:
:%s/needs_root/needs_root()/gc
Die zusätzliche Schwierigkeit besteht darin, dass needs_root
auch in Kommentaren und Doku-Strings vorkommt. Daher war es nicht einfach, einen regulären Ausdruck zu finden, der die Ersetzung in einem Kommentarblock oder in einem Teil eines Doku-Strings überspringt. Mit dem c
Flag kannst du einfach Y
oder N
eingeben und weitermachen. Es wird überhaupt kein regulärer Ausdruck benötigt!
Wenn rekursives Globbing aktiviert ist (shopt -s globstar
in Bash 4), durchläuft dieser leistungsstarke Einzeiler alle übereinstimmenden Dateien, führt die Suche durch und ersetzt das Element entsprechend der Eingabeaufforderung, wenn das Muster in den Dateien gefunden wird:
vim -c "bufdo! set eventignore-=Syntax | %s/needs_root/needs_root()/gce" **/*.py
Es gibt hier viel zu entpacken, aber das obige Beispiel sucht rekursiv nach allen Dateien, die auf .py
enden, lädt sie in Vim und führt das Suchen und Ersetzen mit Bestätigung nur dann durch, wenn es eine Übereinstimmung gibt. Wenn es keine Übereinstimmung gibt, wird die Datei übersprungen. Das set eventignore-=Syntax
wird verwendet, weil Vim sonst die Syntaxdateien nicht lädt, wenn er auf diese Weise ausgeführt wird; wir mögen die Syntaxhervorhebung und erwarten, dass sie funktioniert, wenn diese Art der Ersetzung verwendet wird. Der nächste Teil nach dem |
-Zeichen ist die Ersetzung mit dem Bestätigungsflag und dem e
-Flag, das dazu beiträgt, Fehler zu ignorieren, die einen reibungslosen Arbeitsablauf mit Fehlern verhindern würden.
Tipp
Es gibt zahlreiche andere Flags und Variationen, mit denen du den Ersetzungsbefehl erweitern kannst. Um mehr über die speziellen Flags beim Suchen und Ersetzen in Vim zu erfahren, wirf einen Blick auf :help substitute
, insbesondere auf den Abschnitt s_flags
.
Mach den komplizierten Einzeiler leichter zu merken mit einer Funktion, die zwei Parameter (Such- und Ersetzungsbegriffe) und den Pfad benötigt:
vsed()
{
search
=
$1
replace
=
$2
shift
shift
vim -c
"bufdo! set eventignore-=Syntax| %s/
$search
/
$replace
/gce"
$*
}
Nenne es vsed
, als eine Mischung aus Vim und dem Tool sed
, damit du es dir leichter merken kannst. Im Terminal sieht es übersichtlich aus und ermöglicht es dir, Änderungen an mehreren Dateien einfach und sicher vorzunehmen, da du jede Ersetzung akzeptieren oder ablehnen kannst:
$ vsed needs_root needs_root() **/*.py
Entfernen von temporären Python-Dateien
Pythons pyc
, und neuerdings auch seine pycache
Verzeichnisse, können manchmal im Weg sein. Dieser einfache Einzeiler mit dem Alias pyclean
verwendet den Befehl find
, um pyc
zu entfernen, und sucht dann nach pycache
Verzeichnisse und löscht sie rekursiv mit dem eingebauten Delete-Flag des Tools:
alias
pyclean
=
'find . \
\( -type f -name "*.py[co]" -o -type d -name "__pycache__" \) -delete &&
echo "Removed pycs and __pycache__"'
Auflistung und Filterung von Prozessen
Das Auflisten von Prozessen, um zu sehen, was auf einem Rechner läuft, und das anschließende Filtern, um eine bestimmte Anwendung zu überprüfen, gehört zu den Dingen, die du mindestens mehrmals am Tag machen wirst. Es ist nicht verwunderlich, dass jeder eine Variation der Flags oder der Reihenfolge der Flags für das Tool ps
hat (wir verwenden normalerweise aux
). Das ist etwas, das du so oft am Tag machst, dass sich die Reihenfolge und die Flaggen in deinem Gehirn festsetzen und es schwer ist, es anders zu machen.
Ein guter Ausgangspunkt für die Auflistung der Prozesse und einiger Informationen, wie z. B. der Prozess-IDs, ist Folgendes:
$ ps auxw
Dieser Befehl listet alle Prozesse mit den BSD-üblichen Flags auf (Flags, denen kein Bindestrich vorangestellt ist -
), unabhängig davon, ob sie ein Terminal (tty) haben oder nicht, und gibt den Benutzer an, dem der Prozess gehört. Außerdem wird der Benutzer, dem der Prozess gehört, mit angegeben. Außerdem wird mehr Platz für die Ausgabe zur Verfügung gestellt (w
).
In den meisten Fällen filterst du mit grep
, um Informationen über einen bestimmten Prozess zu erhalten. Wenn du zum Beispiel überprüfen willst, ob Nginx läuft, leitest du die Ausgabe in grep und gibst nginx
als Argument an:
$ ps auxw | grep nginx root 29640 1536 ? Ss 10:11 0:00 nginx: master process www-data 29648 5440 ? S 10:11 0:00 nginx: worker process alfredo 30024 924 pts/14 S+ 10:12 0:00 grep nginx
Das ist toll, aber es ist ärgerlich, dass der Befehl grep
enthalten ist. Das ist besonders ärgerlich, wenn es außer dem grep
keine Ergebnisse gibt:
$ ps auxw | grep apache alfredo 31351 0.0 0.0 8856 912 pts/13 S+ 10:15 0:00 grep apache
Es wird kein apache
Prozess gefunden, aber die visuelle Darstellung könnte dich zu der Annahme verleiten, dass dies der Fall ist, und die doppelte Überprüfung, ob es sich tatsächlich nur um grep
handelt, das wegen des Arguments eingeschlossen wurde, kann ziemlich schnell ermüdend werden. Eine Möglichkeit, dieses Problem zu lösen, besteht darin, eine weitere Pipe zu grep
hinzuzufügen, um sich selbst aus der Ausgabe zu filtern:
$ ps auxw | grep apache | grep -v grep
Wenn du dich immer daran erinnern musst, das zusätzliche grep
hinzuzufügen, kann das genauso lästig sein, also hilft ein Alias:
alias pg='ps aux | grep -v grep | grep $1'
Der neue Alias filtert die erste Zeile von grep
heraus und lässt nur die interessante Ausgabe übrig (falls vorhanden):
$ pg vim alfredo 31585 77836 20624 pts/3 S+ 18:39 0:00 vim /home/alfredo/.zshrc
Unix Zeitstempel
Es ist sehr einfach, den weit verbreiteten Unix-Zeitstempel in Python zu erhalten:
In [1]: import time
In [2]: int(time.time())
Out[2]: 1566168361
Aber in der Shell kann es ein bisschen komplizierter sein. Dieser Alias funktioniert unter OS X, wo es die BSD-Version des Tools date
gibt:
alias timestamp='date -j -f "%a %b %d %T %Z %Y" "`date`" "+%s"'
OS X kann mit seinen Werkzeugen sehr umständlich sein, und es kann verwirrend sein, sich nie daran zu erinnern, warum sich ein bestimmtes Dienstprogramm (wie in diesem Fall date
) völlig anders verhält. In der Linux-Version von date
funktioniert ein viel einfacherer Ansatz auf die gleiche Weise:
alias timestamp='date "+%s"'
Python mit Bash und ZSH mischen
Es ist uns nie in den Sinn gekommen, Python mit einer Shell wie ZSH oder Bash zu kombinieren. Das widerspricht zwar dem gesunden Menschenverstand, aber es gibt ein paar gute Beispiele, die du fast täglich nutzen kannst. Generell gilt die Faustregel, dass 10 Zeilen Shell-Skript die Obergrenze sind. Alles, was darüber hinausgeht, ist ein Fehler, der nur darauf wartet, dass du Zeit verschwendest, weil die Fehlermeldungen dir nicht weiterhelfen.
Zufallsgenerator für Passwörter
Die Anzahl der Konten und Passwörter, die du von Woche zu Woche brauchst, wird immer größer, selbst für Wegwerfkonten, für die du Python verwenden kannst, um robuste Passwörter zu generieren. Erstelle einen nützlichen, zufälligen Passwortgenerator, der den Inhalt in die Zwischenablage sendet, damit du ihn einfach einfügen kannst:
In
[
1
]:
import
os
In
[
2
]:
import
base64
In
[
3
]:
(
base64
.
b64encode
(
os
.
urandom
(
64
))
.
decode
(
'utf-8'
))
gHHlGXnqnbsALbAZrGaw
+
LmvipTeFi3tA
/
9
uBltNf9g2S9qTQ8hTpBYrXStp
+
i
/
o5TseeVo6wcX2A
==
Die Übertragung auf eine Shell-Funktion, die eine beliebige Länge annehmen kann (nützlich, wenn eine Website die Länge auf eine bestimmte Zahl beschränkt), sieht so aus:
mpass()
{
if
[
$1
]
;
then
length
=
$1
else
length
=
12fi
_hash
=
`
python3 -c"
import os,base64
exec('print(base64.b64encode(os.urandom(64))[:
${
length
}
].decode(\'utf-8\'))')
"
`
echo
$_hash
|
xclip -selection clipboardecho
"new password copied to the system clipboard"
}
Jetzt generiert die Funktion mpass
standardmäßig 12-stellige Passwörter, indem sie die Ausgabe zerschneidet und den Inhalt der generierten Zeichenfolge an xclip
sendet, damit sie in die Zwischenablage kopiert und einfach eingefügt werden kann.
Hinweis
xclip
ist in vielen Distributionen nicht standardmäßig installiert, du musst also sicherstellen, dass es installiert ist, damit die Funktion richtig funktioniert. Wenn xclip
nicht verfügbar ist, funktioniert auch jedes andere Dienstprogramm, das die Zwischenablage des Systems verwalten kann.
Existiert mein Modul?
Finde heraus, ob ein Modul existiert, und wenn ja, erhalte den Pfad zu diesem Modul. Dies ist nützlich, wenn du es für andere Funktionen wiederverwendest, die diese Ausgabe zur Verarbeitung verwenden können:
try()
{
python -c"
exec('''
try:
import
${
1
}
as _
print(_.__file__)
except Exception as e:
print(e)
''')"
}
Verzeichnisse in den Pfad eines Moduls ändern
"Wo befindet sich dieses Modul?" wird oft gefragt, wenn man Bibliotheken und Abhängigkeiten debuggt oder sogar im Quellcode von Modulen herumstöbert. Die Art und Weise, wie Python Module installiert und verteilt, ist nicht ganz einfach, und in den verschiedenen Linux-Distributionen sind die Pfade völlig unterschiedlich und haben eigene Konventionen. Du kannst den Pfad eines Moduls herausfinden, wenn du es importierst und dann print
verwendest:
In
[
1
]:
import
os
In
[
2
]:
(
os
)
<
module
'os'
from
'.virtualenvs/python-devops/lib/python3.6/os.py'
>
Das ist unpraktisch, wenn du nur den Pfad brauchst, damit du in ein anderes Verzeichnis wechseln und dir das Modul ansehen kannst. Diese Funktion versucht, das Modul als Argument zu importieren, es auszudrucken (das ist eine Shell, also tut return
nichts für uns) und dann in das Verzeichnis zu wechseln:
cdp()
{
MODULE_DIRECTORY
=
`
python -c"
exec('''
try:
import os.path as _,
${
module
}
print(_.dirname(_.realpath(
${
module
}
.__file__)))
except Exception as e:
print(e)
''')"
`
if
[[
-d$MODULE_DIRECTORY
]]
;
then
cd
$MODULE_DIRECTORY
else
echo
"Module
${
1
}
not found or is not importable:
$MODULE_DIRECTORY
"
fi
}
Für den Fall, dass der Paketname einen Bindestrich enthält und das Modul einen Unterstrich verwendet, fügen wir noch etwas hinzu:
module
=
$(
sed's/-/_/g'
<<<
$1
)
Wenn die Eingabe einen Bindestrich enthält, kann die kleine Funktion dies im Handumdrehen lösen und uns an das gewünschte Ziel bringen:
$ cdp pkg-resources $ pwd /usr/lib/python2.7/dist-packages/pkg_resources
Konvertierung einer CSV-Datei in JSON
Python verfügt über einige integrierte Funktionen, die dich überraschen werden, wenn du dich noch nie mit ihnen beschäftigt hast. Python kann sowohl JSON als auch CSV-Dateien nativ verarbeiten. Es sind nur ein paar Zeilen nötig, um eine CSV-Datei zu laden und ihren Inhalt als JSON auszugeben. Verwende die folgende CSV-Datei(addresses.csv), um den Inhalt zu sehen, wenn JSON in der Python-Shell gedumpt wird:
John,Doe,120 Main St.,Riverside, NJ, 08075 Jack,Jhonson,220 St. Vernardeen Av.,Phila, PA,09119 John,Howards,120 Monroe St.,Riverside, NJ,08075 Alfred, Reynolds, 271 Terrell Trace Dr., Marietta, GA, 30068 Jim, Harrison, 100 Sandy Plains Plc., Houston, TX, 77005
>>>
import
csv
>>>
import
json
>>>
contents
=
open
(
"addresses.csv"
)
.
readlines
()
>>>
json
.
dumps
(
list
(
csv
.
reader
(
contents
)))
'[["John", "Doe", "120 Main St.", "Riverside", " NJ", " 08075"],
[
"Jack"
,
"Jhonson"
,
"220 St. Vernardeen Av."
,
"Phila"
,
" PA"
,
"09119"
],
[
"John"
,
"Howards"
,
"120 Monroe St."
,
"Riverside"
,
" NJ"
,
"08075"
],
[
"Alfred"
,
" Reynolds"
,
" 271 Terrell Trace Dr."
,
" Marietta"
,
" GA"
,
" 30068"
],
[
"Jim"
,
" Harrison"
,
" 100 Sandy Plains Plc."
,
" Houston"
,
" TX"
,
" 77005"
]]
'
Portiere die interaktive Sitzung auf eine Funktion, die dies auf der Kommandozeile erledigen kann:
csv2json()
{
python3 -c"
exec('''
import csv,json
print(json.dumps(list(csv.reader(open(\'
${
1
}
\')))))
''')
"
}
Das ist viel einfacher, als sich an alle Aufrufe und Module zu erinnern:
$ csv2json addresses.csv [["John", "Doe", "120 Main St.", "Riverside", " NJ", " 08075"], ["Jack", "Jhonson", "220 St. Vernardeen Av.", "Phila", " PA", "09119"], ["John", "Howards", "120 Monroe St.", "Riverside", " NJ", "08075"], ["Alfred", " Reynolds", " 271 Terrell Trace Dr.", " Marietta", " GA", " 30068"], ["Jim", " Harrison", " 100 Sandy Plains Plc.", " Houston", " TX", " 77005"]]
Python Einzeiler
Im Allgemeinen gilt es nicht als gute Praxis, eine lange, einzelne Python-Zeile zu schreiben. Der PEP 8-Leitfaden rät sogar davon ab, Anweisungen mit einem Semikolon zu verbinden (es ist möglich, Semikolons in Python zu verwenden!). Aber schnelle Debug-Anweisungen und Aufrufe eines Debuggers sind in Ordnung. Sie sind schließlich nur vorübergehend.
Debugger
Einige Programmierer da draußen schwören auf die Anweisung print()
als beste Strategie zum Debuggen von laufendem Code. In manchen Fällen mag das gut funktionieren, aber meistens verwenden wir den Python-Debugger (mit dem Modul pdb
) oder ipdb
, der IPython als Backend verwendet. Wenn du einen Haltepunkt einrichtest, kannst du in den Variablen herumstochern und den Stack rauf und runter gehen. Diese einzeiligen Anweisungen sind so wichtig, dass du sie auswendig lernen solltest:
Setze einen Haltepunkt und wechsle in den Python-Debugger (pdb
):
import
pdb
;
pdb
.
set_trace
()
Setze einen Haltepunkt und wechsle zu einem Python-Debugger, der auf IPython (ipdb
) basiert:
import
ipdb
;
ipdb
.
set_trace
()
Obwohl es sich technisch gesehen nicht um einen Debugger handelt (du kannst dich im Stack nicht vorwärts oder rückwärts bewegen), kannst du mit diesem Einzeiler eine IPython-Sitzung starten, wenn die Ausführung sie erreicht:
import
IPython
;
IPython
.
embed
()
Hinweis
Jeder scheint ein Lieblings-Debugger-Tool zu haben. Wir finden, dass pdb
zu grob ist (keine Autovervollständigung, keine Syntaxhervorhebung), deshalb gefällt uns ipdb
besser. Sei nicht überrascht, wenn jemand mit einem anderen Debugger daherkommt! Letztendlich ist es nützlich zu wissen, wie pdb
funktioniert, denn das ist die Grundlage, die man braucht, um unabhängig vom Debugger gut zurechtzukommen. In Systemen, die du nicht kontrollieren kannst, solltest du pdb
direkt verwenden, da du keine Abhängigkeiten installieren kannst; das mag dir vielleicht nicht gefallen, aber du kannst dich trotzdem zurechtfinden.
Wie schnell ist dieses Snippet?
Python verfügt über ein Modul, mit dem du einen Code mehrmals ausführen und Leistungskennzahlen erhalten kannst. Viele Nutzerinnen und Nutzer wollen wissen, ob es effiziente Wege gibt, eine Schleife zu bearbeiten oder ein Wörterbuch zu aktualisieren, und es gibt viele sachkundige Leute, die das Modul timeit
lieben, um die Leistung zu beweisen.
Wie du wahrscheinlich schon gesehen hast, sind wir Fans von IPython, und seine interaktive Shell verfügt über eine "magische" Spezialfunktion für das Modul timeit
. "Magische" Funktionen haben das Zeichen %
vorangestellt und führen eine bestimmte Operation innerhalb der Shell aus. Ein beliebtes Thema in Bezug auf die Leistung ist die Frage, ob das Verstehen von Listen schneller ist als das einfache Anhängen an eine Liste. Die beiden folgenden Beispiele verwenden das Modul timeit
, um das herauszufinden:
In
[
1
]:
def
f
(
x
):
...
:
return
x
*
x
...
:
In
[
2
]:
%
timeit
for
x
in
range
(
100
):
f
(
x
)
100000
loops
,
best
of
3
:
20.3
us
per
loop
In der Standard-Python-Shell (oder dem Interpreter) importierst du das Modul und rufst es direkt auf. Der Aufruf sieht in diesem Fall ein bisschen anders aus:
>>>
array
=
[]
>>>
def
appending
():
...
for
i
in
range
(
100
):
...
array
.
append
(
i
)
...
>>>
timeit
.
repeat
(
"appending()"
,
"from __main__ import appending"
)
[
5.298534262983594
,
5.32031941099558
,
5.359099322988186
]
>>>
timeit
.
repeat
(
"[i for i in range(100)]"
)
[
2.2052824340062216
,
2.1648171059787273
,
2.1733458579983562
]
Die Ausgabe ist etwas merkwürdig, aber das liegt daran, dass sie von einem anderen Modul oder einer Bibliothek verarbeitet werden soll und nicht für die menschliche Lesbarkeit gedacht ist. Die Durchschnittswerte bevorzugen das Listenverständnis. So sieht es in IPython aus:
In
[
1
]:
def
appending
():
...
:
array
=
[]
...
:
for
i
in
range
(
100
):
...
:
array
.
append
(
i
)
...
:
In
[
2
]:
%
timeit
appending
()
5.39
µs
±
95.1
ns
per
loop
(
mean
±
std
.
dev
.
of
7
runs
,
100000
loops
each
)
In
[
3
]:
%
timeit
[
i
for
i
in
range
(
100
)]
2.1
µs
±
15.2
ns
per
loop
(
mean
±
std
.
dev
.
of
7
runs
,
100000
loops
each
)
Da IPython timeit
als speziellen Befehl ausgibt (beachte das Präfix mit %
), ist die Ausgabe für Menschen lesbar und hilfreicher und erfordert nicht den seltsamen Import wie in der Standard-Python-Shell.
strace
Die Fähigkeit, herauszufinden, wie ein Programm mit dem Betriebssystem interagiert, ist von entscheidender Bedeutung, wenn Anwendungen die interessanten Teile nicht protokollieren oder überhaupt nicht protokollieren. Die Ausgabe von strace
kann grob sein, aber wenn du die Grundlagen verstehst, wird es einfacher zu verstehen, was in einer problematischen Anwendung vor sich geht. Einmal versuchte Alfredo zu verstehen, warum der Zugriff auf eine Datei verweigert wurde. Diese Datei befand sich in einem Symlink, der alle Rechte zu haben schien. Was war da los? Es war schwierig, das herauszufinden, wenn man sich nur die Logs ansah, da diese nicht besonders hilfreich waren, wenn es darum ging, die Berechtigungen für den Zugriff auf Dateien anzuzeigen.
strace
diese beiden Zeilen in die Ausgabe aufgenommen:
stat("/var/lib/ceph/osd/block.db", 0x7fd) = -1 EACCES (Permission denied) lstat("/var/lib/ceph/osd/block.db", {st_mode=S_IFLNK|0777, st_size=22}) = 0
Das Programm legte die Eigentumsrechte für das übergeordnete Verzeichnis fest, das zufällig ein Link war, und für block.db, das in diesem Fall ebenfalls ein Link zu einem Blockgerät war. Das Blockgerät selbst hatte die richtigen Berechtigungen, wo lag also das Problem? Es stellte sich heraus, dass der Link im Verzeichnis ein Sticky Bit hatte, das andere Links daran hinderte, den Pfad zu ändern - einschließlich des Blockgeräts. Das Tool chown
hat ein spezielles Flag (-h
oder --no-dereference
), um anzuzeigen, dass die Änderung der Besitzverhältnisse auch die Links betreffen sollte.
Diese Art der Fehlersuche wäre ohne etwas wie strace
schwierig (wenn nicht sogar unmöglich). Um es auszuprobieren, erstelle eine Datei namens follow.py mit folgendem Inhalt:
import
subprocess
subprocess
.
call
([
'ls'
,
'-alh'
])
Es importiert das Modul subprocess
, um einen Systemaufruf durchzuführen. Er gibt den Inhalt des Systemaufrufs an ls
aus. Anstelle eines direkten Aufrufs mit Python kannst du dem Befehl strace
voranstellen und sehen, was passiert:
$ strace python follow.py
Das Terminal sollte mit einer Menge Ausgaben gefüllt sein, und wahrscheinlich wird das meiste davon sehr fremd aussehen. Zwinge dich, jede Zeile durchzugehen, unabhängig davon, ob du verstehst, was vor sich geht. Einige Zeilen werden leichter zu unterscheiden sein als andere. Es gibt viele read
und fstat
Aufrufe; du siehst die tatsächlichen Systemaufrufe und was der Prozess bei jedem Schritt macht. Es gibt auch open
und close
Operationen für einige Dateien und es gibt einen bestimmten Abschnitt, in dem einige stat
Aufrufe zu sehen sind:
stat("/home/alfredo/go/bin/python", 0x7ff) = -1 ENOENT (No such file) stat("/usr/local/go/bin/python", 0x7ff) = -1 ENOENT (No such file) stat("/usr/local/bin/python", 0x7ff) = -1 ENOENT (No such file) stat("/home/alfredo/bin/python", 0x7ff) = -1 ENOENT (No such file) stat("/usr/local/sbin/python", 0x7ff) = -1 ENOENT (No such file) stat("/usr/local/bin/python", 0x7ff) = -1 ENOENT (No such file) stat("/usr/sbin/python", 0x7ff) = -1 ENOENT (No such file) stat("/usr/bin/python", {st_mode=S_IFREG|0755, st_size=3691008, ...}) = 0 readlink("/usr/bin/python", "python2", 4096) = 7 readlink("/usr/bin/python2", "python2.7", 4096) = 9 readlink("/usr/bin/python2.7", 0x7ff, 4096) = -1 EINVAL (Invalid argument) stat("/usr/bin/Modules/Setup", 0x7ff) = -1 ENOENT (No such file) stat("/usr/bin/lib/python2.7/os.py", 0x7ffd) = -1 ENOENT (No such file) stat("/usr/bin/lib/python2.7/os.pyc", 0x7ff) = -1 ENOENT (No such file) stat("/usr/lib/python2.7/os.py", {st_mode=S_IFREG|0644, ...}) = 0 stat("/usr/bin/pybuilddir.txt", 0x7ff) = -1 ENOENT (No such file) stat("/usr/bin/lib/python2.7/lib-dynload", 0x7ff) = -1 ENOENT (No such file) stat("/usr/lib/python2.7/lib-dynload", {st_mode=S_IFDIR|0755, ...}) = 0
Dieses System ist ziemlich alt, und python
in der Ausgabe bedeutet python2.7
, also stöbert es im Dateisystem herum, um die richtige ausführbare Datei zu finden. Er geht einige durch, bis er /usr/bin/python erreicht, das ein Link ist, der auf /usr/bin/python2 verweist, was wiederum ein weiterer Link ist, der den Prozess zu /usr/bin/python2.7 schickt. Dann ruft er stat
auf /usr/bin/Modules/Setup auf, von dem wir als Python-Entwickler noch nie etwas gehört haben, um dann zum Modul os
weiterzugehen.
Es geht weiter zu pybuilddir.txt und lib-dynload. Was für ein Trip. Ohne strace
hätten wir wahrscheinlich versucht, den Code zu lesen, um herauszufinden, wohin er als Nächstes führt. Aber strace
macht das alles viel einfacher, indem es alle interessanten Schritte auf dem Weg mit nützlichen Informationen für jeden Aufruf aufführt.
Das Tool hat viele Flags, die einen Blick wert sind; zum Beispiel kann es sich an eine PID anhängen. Wenn du die PID eines Prozesses kennst, kannst du strace
anweisen, eine Ausgabe darüber zu erstellen, was genau mit dem Prozess passiert.
Eines dieser nützlichen Flags ist -f
; es verfolgt die Kindprozesse, die vom Startprogramm erstellt werden. In der Python-Beispieldatei wird ein Aufruf an subprocess
gemacht, der ls
aufruft. Wenn der Befehl an strace
geändert wird, um -f
zu verwenden, wird die Ausgabe umfangreicher und enthält Details über diesen Aufruf.
Wenn follow.py im Home-Verzeichnis ausgeführt wird, gibt es einige Unterschiede mit dem -f
Flag. Du kannst Aufrufe an lstat
und readlink
für die Dotfiles sehen (von denen einige symlinked sind):
[pid 30127] lstat(".vimrc", {st_mode=S_IFLNK|0777, st_size=29, ...}) = 0 [pid 30127] lgetxattr(".vimrc", "security.selinux", 0x55c5a36f4720, 255) [pid 30127] readlink(".vimrc", "/home/alfredo/dotfiles/.vimrc", 30) = 29 [pid 30127] lstat(".config", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0
Es werden nicht nur die Aufrufe dieser Dateien angezeigt, sondern auch die PID wird in der Ausgabe vorangestellt, was dabei hilft, zu erkennen, welcher (untergeordnete) Prozess was tut. Ein Aufruf von strace
ohne das Flag -f
würde zum Beispiel keine PID anzeigen.
Um die Ausgabe im Detail zu analysieren, kann es hilfreich sein, sie in einer Datei zu speichern. Das ist mit dem Flag -o
möglich:
$ strace -o output.txt python follow.py
Übungen
-
Definiere, was IOPS ist.
-
Erkläre, was der Unterschied zwischen Durchsatz und IOPS ist.
-
Nenne eine Einschränkung bei
fdisk
für die Erstellung von Partitionen, dieparted
nicht hat. -
Nenne drei Tools, die Festplatteninformationen liefern können.
-
Was kann ein SSH-Tunnel leisten? Wann ist er nützlich?
Get Python für DevOps now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.