Just enough Linux to shine in society
Quasiment tous les outils utilisés pour le DevOps ont d'abord été développés pour Linux puis portéssur Windows, souvent avec un décalage important :
- Docker Linux : 2013,
- Docker for Windows : 2016.
Exemple
- Ansible ne tourne pas sur Windows de façon native, mais peut tourner sur WSL2.
- Kubernetes ne tourne pas sur Windows.
Working with Shell
/home/
sweet /home/
Dès que l'on lance un terminal Linux, le premier répertoire dans lequel vous vous trouverez sera le répertoire principal.
Si vous avez comme nom d'utilisateur vorphus
, votre terminal sera directement ouvert au répertoire /home/vorphus
.
De façon générique, l'utilisateur se nomme user
dans les docs. Pour voir le nom d'utilisateur, on peut soit taper la commande whoami
dans le terminal, soit savoir que le nom d'utilisateur est stocké dans la variable d'environnement USER
, on peut alors taper echo $USER
pour afficher son contenu.
Le répertoire /home/$USER
est unique à chaque utilisateur.
On peut taper la commande pwd
dans le terminal pour voir dans quel répertoire l'on se trouve.
Commandes basiques
On a deux types de commandes dans le Shell :
- Les commandes internes :
echo
,cd
,pwd
,mkdir
,set e.t.c
, qui sont fournies avec le Shell. - Les commandes externes :
mv
,date
,uptime
,cp
, qui sont des fichiers binaires ou scripts distincts qui sont appelées par le Shell.
Pour déterminer si une commande est interne ou externe, on peut taper type
suivi de la commande.
Commande | Résultat |
---|---|
echo |
print a line of text |
ls |
list files and folders |
cd |
change directory |
mkdir |
create a directory |
touch |
create a file |
mv new_file.txt sample_file.txt |
move new_file.txt to sample_file.txt |
pwd |
print the present working directory |
cat file.txt |
show the content of file.txt |
Des commandes successives peuvent être lancées avec le point virgule.
Commandes successives | |
---|---|
Création de plusieurs répertoires en une seule fois | |
---|---|
Les commandes du type
peuvent se simplifier en une seule ligne via l'argument-p
permettant de créer de façon récurrente les répertoires parents. On a ainsi la commande suivante.
Commande simplifée, création directe des dossiers parents | |
---|---|
De même pour la suppression/copie récursive des données avec -r
.
suppression récursive | |
---|---|
copie récursive | |
---|---|
La commande tree /home/vorph/test_dir
permet de voir toute l'arborescence de test_dir
, ie l'ensemble des fichiers et dossiers, sous la forme d'un arbre.
Chemins absolus et relatifs
-
/home/vorphus/test
est un chemin absolu. Un chemin absolu spécifie le chemin du répertoire, ou du dossier, depuis le dossier root/
. -
Si on est dans le "home directory"
/home/vorphus
, alorstest
est un chemin relatif au répertoire dans lequel on se trouve déjà.
Protip : pushd
& popd
La commande pushd
permet de mettre en cache l'adresse du répertoire où l'on se trouve actuellement avant de changer de répertoire. On peut alors y revenir avec popd
.
Si l'on est dans /home/vorph
, pushd /etc
nous amène au répertoire /etc
, tout en mettant /home/vorph
en haut de la pile des répertoires.
La commande popd
nous permet alors de revenir au répertoire en haut de la pile, eg ici /home/vorph
.
Obtenir de l'aide sur les commandes
On a plusieurs moyen d'obtenir de l'aide sur les commandes Linux et ce qu'elles sont sensées faire.
- On peut utiliser la commande
whatis
pour une décription succinte.
- On peut lire le manuel de la commande avec
man
.
- Certaines commandes fournissent une aide via la commande
--help
.
apropos regex
fera une recherche dans toutes les pages deman
pour trouver les commandes contenant la regex donnée.
Shell types
Définition
En informatique, le terme shell désigne un logiciel fournissant une interface à l'utilisateur pour des composantes d'un ensemble informatique plus grand. Bien qu'il puisse aussi désigner une interface graphique, shell est plus généralement employé pour désigner un interpréteur de lignes de commandes pouvant accéder aux services et interagir avec le noyau d'un système d'exploitation. Dans le cas d'Ubuntu, un shell interagit avec le noyau Linux. Source
Il existe différents types de shell :
- Bourne Shell (Sh Shell)
- C Shell (csh ou tcsh)
- Korn Shell (ksh)
- Z Shell (zsh)
- Bourne again Shell (bash)
Si l'ensemble des shells cités au dessus ont une base de commandes communes (cp
, cd
, mv
, ls
, etc.), il existe des commandes ou des synthaxes spécifiques à chacun, comme on le verra avec le shebang dans les scripts shell. On se concentre ici sur le plus utilisé, Bash.
Info
Pour voir quel shell est utilisé dans un terminal : echo $SHELL
.
La commande alias
permet de créer des raccourcis permettant d'appeler d'autres commandes. Par exemple, pour renommer la commande date
en dt
, on utilisealias dt=date
.
La commande history
permet elle d'avoir un historique des commandes déjà tapées dans le terminal. Commande fort utile pour se rappeler de la synthaxe de certaine commandes, à utiliser en conjonction avec grep
.
Les variables d'environnement
Définition
Une variable d'environnement est une valeur dynamique, chargée en mémoire, pouvant être utilisée par plusieurs processus fonctionnant simultanément. Sur la plupart des systèmes d'exploitation, les emplacement de certaines librairies, voire des principaux exécutables du système peuvent avoir un emplacement différent selon l'installation.
Ainsi, grâce aux variables d'environnement, il est possible, à partir d'un programme, de faire référence à un emplacement en s'appuyant sur les variables d'environnement définissant ces données. Source
Beaucoup de softwares, par exemple lorsque l'on déploie des conteneurs, on besoin de variables d'environnement pour fonctionner. On peut citer par exemple :
- L'adresse d'une DB,
- Le mot de passe d'une DB,
- Un port réseau;
- etc.
La commande env
permet de lister l'ensemble des variables d'environnement définies dans votre shell. Lorsque l'on tape cette commande, on peut par exemple avoir les résultats suivants.
Une variable d'environnement est par convention toujours définie en majuscule.
Pour en défnir une, rien de plus simple, il suffit de la définir dans le terminal, on peut alors y accéder pour la voir par exemple en tapant la commande echo
suivant de la variable.
export
En fait, non. Ce que nous avons créer là est une variable de shell, ce qui veut dire qu'elle ne sera valable que dans le shell dans lequel vous travailler. Pour vraiment définir une variable d'environnement, il faut utiliser la commande export
.
Différence entre variable de shell et d'environnemnt, en passant de zsh à bash | |
---|---|
Définir une variable de shell en simplement spécifiant son nom dans zsh
ne la fera pas persister dans bash
, alors que définir une variable d'environnement via la commande export
la fera persister peut importe le shell que vous utiliser.
Question
Y a-t-il une différence de lieu de stockage de stockage entre les variables de shell et les variables d'environnement ?
Attention
Les variables d'environnement définies par vos soins ne persisteront que jusqu'à ce que vous fermiez le terminal dans lequel vous les avez défini.
Pour que les variables d'environnement persistent, il faut les rentrer dans ~/.profile
ou ~/.pam_environment
.
La variable PATH
Une variable d'environnement particulières est la variable PATH
.
Définition
La variable d'environnement PATH gouverne les chemins d'exécution des logiciels ubuntu. Cette variable PATH permet d'installer et d’utiliser en local un logiciel sans avoir fait appel à l'administration système. Source
Ainsi, lorsque vous tapez par exemple la commande python
, pour avoir accès à un terminal python, le fichier binaire éxecuté se trouve dans une adresse déterminée dans l'ensemble des chemins définies dans la variable PATH
.
Pour avoir accès à l'ensemble des chemins définis dans PATH
, on fait comme précédemment.
:
, ainsi /home/vorph/.cargo/bin:/home/vorph/miniconda3/bin:/home/vorph/miniconda3/condabin
constituent 3 chemins différents :
/home/vorph/.cargo/bin
,/home/vorph/miniconda3/bin
,/home/vorph/miniconda3/condabin
.
which
Simplement taper echo $PATH
ne renseigne pas beaucoup, tant il y a de chemins. Pour voir le chemin d'un logiciel spécifique on peut utiliser la commande which
.
Si l'on regarde attentivement le résultat de la commande echo $PATH
, on verra que l'on ne voit pas le chemin vers /home/vorph/miniconda3/bin/python
, en revanche on a bien le chemin vers /home/vorph/miniconda3/bin
. Les chemins répertoriés dans la variables PATH sont les chemins racines vers d'autres logiciels.
export PATH
Pour ajouter un chemin à la variable PATH
, on utilise la commande export PATHH=$PATH:chemin
.
Par exemple, si notre logiciel se trouve dans /opt/...
on tape la commande suivante.
Linux Core concepts
Le noyau Linux
Le noyau Linux (Linux kernel) est la composante principale du système d'exploitation, le noyau fait l'interface principale entre la partie hardware et software.
graph LR
subgraph Software
B[Applications/Processus]
end
A[Linux Kernel]
subgraph Hardware
C1[Mémoire]
C2[CPU/GPU]
C3[Devices]
end
A<-..->B
A<-..->C1 & C2 & C3
Le noyau Linux est responsable des 4 tâches principales suivantes :
- gestion de la mémoire,
- gestion des processus : quel processus peut utiliser le CPU/GPU, comment, quand, et pour combien de temps,
- drivers des périphériques,
- sécurité et gestion des appels systèmes.
Le noyau Linux est monolithique, ces tâches sont faites par lui même et non déléguées.
Le noyau Linux est aussi modulaire, ces compétences peuvent être étendues par l'ajout de modules.
Info
Pour l'exemple, des kernels qui ne sont pas monolithiques sont les suivants :
- QNX,
- Symbian,
- L4Linux,
- Singularity,
- K42,
- Mac OS X,
- Integrity,
- PikeOS,
- HURD,
- Minix,
- Coyotos.
Version du noyau
Pour avoir le nom du noyau, on peut taper la commande uname
, qui ne produit que peu d'informations.
Pour voir la version du noyau utilisé, taper uname -r
ou uname -a
.
- 5 = Version du noyau,
- 13 = Version majeure,
- 0 = Version mineure,
- 40 = patch release,
- generic = information spécifique à la distribution.
Pour avoir toutes les informations sur la version de l'OS qui est utilisée, on peut aller voir dans le répertoire /etc
. A l'intérieur devrait se trouver un fichier nommé os-release
. On peut trouver ce fichier via ls /etc/*release*
.
Pour plus d'informations, aller voir sur kernel.org qui recense le code source de toutes les versions du noyau Linux disponibles.
Espace kernel et espace utilisateur
Voyons maintenant comment la mémoire est gérée dans un OS Linux.
La mémoire est divisée en deux espaces séparés, l'espace kernel et l'espace utilisateur.
L'espace kernel est la partie de la mémoire dans laquelle le noyau provisionne et éxecute ses services, un processus tournant dans l'espace kernel à un accès illimité au hardware.
Tous les processus tournant hors de l'espace kernel tournent dans l'espace utilisateur, qui a un accès restreint au CPU/GPU et à la mémoire.
graph LR
subgraph Kernel space
A[Linux Kernel]
B[Device drivers]
end
subgraph User space
C[Applications/Processus]
end
L'espace kernel contient :
- kernel code,
- kernel extensions,
- devices drivers.
L'espace utilisateur lui contient entre autre les programmes codés dans le languages suivants :
- C,
- Java,
- Python,
- Ruby,
- Docker containers,
- etc.
Lorsqu'une application dans l'espace utilisateur tourne et qu'elle a besoin d'accéder au hardware pour par exemple :
- ouvrir un fichier,
- écrire dans un fichier,
- définir une variable,
- etc,
l'espace utilisateur produit un "system call" à l'espace kernel qui lui fournit les ressources nécessaires via les drivers.
graph LR
subgraph Kernel space
A[Linux Kernel]
B[Device drivers]
end
subgraph User space
C[Applications/Processus]
end
D[Hardware]
C-.->|System Call|B-.->D
Linux et hardware
Comment Linux identifie le hardware dans son OS.
Prenons l'exemple d'une clé usb branchée sur un pc avec un OS Linux.
- Dès que la clé usb est branchée, le driver correspondant dans l'espace kernel détecte un changement d'état et génère un évènement, appelé
uevent
. - Cet évènement est envoyé au "user space device manager daemon", appelé
udev
. udev
crée alors de façon dynamique un noeud de device correspondant à la clé usb se trouvant dans le système de fichier/dev
et lui assigne le nomsdb1
(par exemple).- Une fois ces étapes faites, la clé usb et son contenu seront listés comme
/dev/sdb1
.
Attention
/dev/sdb1
n'est pas un répertoire, c'est l'adresse que l'OS Linux assigne à la clé usb. Tenter de faire un cd
vous donnera l'erreur suivante.
graph LR
subgraph Extérieur
A[Clé USB]
B[PC]
end
subgraph Kernel space
C[Device driver]
end
subgraph User space
D[udev]
end
E[/ /dev/sdb1 /]
A-.->B-.->C
C-.->|uevents|D-.->E
Comment avoir des infos sur les composants hardware ?
-
dmesg
(pour l'anglais "display message") est une commande sur les systèmes d'exploitation de type Unix qui affiche la mémoire tampon des messages du noyau. Quand un système Linux boot, il y a de nombreux messages qui peuvent ou non s'afficher (suivant votre OS), ces messages contiennent des logs du hardware -
udevadm info
requète la db deudev
pour des infos concernant les périphériques. -
udevadm monitor
est à l'écoute de nouveauxuevent
, et les affichera dans le terminal.
Exemple
Voici ce qui se passe avant et après avoir branché un disque usb avec udevadm monitor
.
avant | |
---|---|
lspci
liste tous les périphériques du "bus pci" du pc, ie les fameuses cartes qui s'enfichent dans la carte mère (GPU, carte réseau, RAM etc.)
Vi
Vi est un éditeur de texte minimaliste installé par défaut dans la plupart des distributions Linux.
Il possède deux modes :
- mode commande : copier, coller, suppression, mais pas d'écriture possible,
- mode insertion : nécessaire pour écrire du contenu dans un fichier.
Info
Pour activer le mode insertion dans Vi, appuyez sur la touche i
. Pour revenir au mode commande, il faut appuyer sur la touche echap esc
.
installer Vim plutôt que Vi, pour un expérience plus user friendly | |
---|---|
Commandes basiques
Commande | Résultat |
---|---|
x |
supprime un caractère |
dd |
supprime la ligne entière |
yy |
copie la ligne |
p |
colle le ligne |
ctrl + u |
scrolle ver le haut |
ctrl + d |
scrolle vers le bas |
: |
affiche l'invite de commande |
:w |
sauvegarde le fichier |
:w filename |
sauvegarde le fichier sous le nom filename |
:q |
quitte l'éditeur vi |
:wq |
sauvegarder et quitter |
/string |
recherche le string dans le texte |
n |
va à l'occurence suivante du string |
More Linux commands
whoami
id
su
: switch user-
ssh user@hostname
-
sudo ...
toujours un utilisateur classique, mais avec des privilèges root, liste dansetc/sudoers
wget http://www.url.com/some-file.txt -O some-file.txt
curl http://www.url.com/some-file.txt -O
Package Management
CentOS
CentOS utilise RPM (Red Hat Package Manager), les softwares sont alors packagés sous la forme telnet.rpm
, pour installer un tel package on tape alors la commande suivante.
l'argument -i
signifiant que l'on veut installer quelque chose. Pour supprimer un package, on tape
et pour requêter la db sur un package particuliers, on a la commande suivante.
rpm
ne fait qu'installer le package qu'on lui a pointé, il n'installe aucune de ses dépendances ! Pour installer un package et l'ensemble de ses dépendances, on utilise yum
qui est un surcouche de rpm
.
Exemple
yum install ansible
installera ansible
et l'ensemble de ses dépendances, par exemple python
, pyYAML
, sshpass
.
Comment yum
sait où sont localisées, ie dans quel repo, les dépendances d'un package ? L'ensemble des dépendances classiques sont listées dans /etc/yum.repos.d
, c'est là que yum
cherche en premier.
Pour voir la liste des repos disponibles sur un système CentOS, on tape la commande yum repolist
.
ls /etc/yum.repos.d
cat /etc/yum.repos.d/CentOS-Base.repo
Pour l'ensemble des packages, par exemple ansible
, disponible à l'installation, on peut taper la commande suivante.
yum list ansible
,yum remove ansible
supprime le package,yum --showduplicates list ansible
pour voir les différentes versions disponibles.yum install ansible-2.4.2.0
Ubuntu
Services
Dès qu'un software comme une DB, un webserveur ou Docker est installé sur une machine, il est configuré comme un service, pour que ce service marche, il doit être lancé.
service nom_du_service start
est la commande permattant de le lancer.
Exemple
service httpd start
lance un serveur apache.
Une méthode plus moderne de lancer un service est d'utiliser la commande systemctl
.
systemctl start httpd
systemctl
est la commande utilisée pour manager les services sur un serveur managé par systemd
.
-
systemctl stop httpd
-
systemctl status httpd
-
systemctl enable httpd
lance le service automatiquement quand le serveur boot. -
systemctl disable httpd
désactive le service automatiquement quand le serveur boot.
Exemple
Exemple
Supposons que l'on souhaite configurer l'API suivante comme un service.
si on la lance via la commande suivante.
GET
, par exemple avec httpie.
Remarque
Le fais d'utiliser un chemin absolu /home/vorph/miniconda3/envs/api/bin/python
pour lancer python est important et sera utilisé par la suite, pour connaître le chemin complet de votre éxecutable python, vous pouvez utiliser la commande suivante dans un terminal.
Il sera aussi utile par la suite de connaître le chemin absolu de my_app.py
, on peut le connaître en utilisant la commande readlink
.
Evidemment, la commande readlink
nécessite que vous soyez dans le répertoire où se trouve my_app.py
pour pouvoir lire le chemin absolu.
Remarque
Il n'est pas nécessaire de laisser les lignes suivantes à la fin de my_app.py
.
On pourrait très bien les enlever, mais dans ce cas là, il faudrait activer le bon environnement virtuel puis lancer la commande suivante.
Ce qui fait deux actions plutôt qu'une, cela sera plus pratique pour la suite.
Autres références :
L'idée de la configurer comme un service est que l'on pourra alors utiliser systemctl
pour la lancer et l'arrêter via les commandes systemctl start my_app
et systemctl stop my_app
.
De cette façon l'administrateur du serveur n'a pas besoin de se soucier du chemin où se trouve l'API, ou même du langage dans lequel est codée cette API, il sait que c'est un service qu'il peut lancer et stopper à sa guise.
Il sera même possible de la lancer de façon automatique au début de chaque démarrage du serveur, ou de la relancer si le serveur crash.
Pour pouvoir lancer un script python comme un service, par exemple avec les commande systemctl start my_app
, systemctl stop my_app
, my_app
faisant référence au nom que l'on souhaite assigner au service, on doit alors configurer ce script comme un service systemd
en définissant un "systemd unit file" dans /etc/systemd/system
.
On définit le fichier systemd suivant.
Une fois le fichier créé dans /etc/systemd/system
, il est nécessaire de redémarrer le processus systemd en lançant la commande suivante.
systemctl daemon-reload
Seulement de cette façon le nouveau service configuré sera pris en compte dans /etc/systemd/system
, il suffit alors de lancer le nouveau service lancer via systemctl start my_app
pour qu'il démarre. On peut alors vérifier qu'il fonctionne en faisant la réquête suivante.
Attention
Les chemins utilisés dans un ficheier .service
doivent toujours être des chemins absolus. Les chemins relatifs ne fonctionnent pas.
Pour la stopper, il suffit de lancer systemctl stop my_app
.
Comment configurer le service pour qu'il se lance automatiquement au démarrage du serveur ?
C'est la partie [Install]
du fichier my_app.service
qui le définit. La partie WantedBy=multi-user.target
désigne que se service doit être lancé dès le démarrage. Pour que ce paramètre soit pris en compte, il est alors nécessaire de lancer la commande suivante.
systemctl enable my_app
Approfondir :
- Why do most systemd examples contain WantedBy=multi-user.target?
- Systemd service - what is
multi-user.target
- Systemd Services 101
- systemd 101
- Systemd – Easy as 1, 2, 3
- Comment utiliser Systemctl pour gérer les services et les unités de Systemd
- systemd.service — Service unit configuration
Placer votre code dans /opt/
Dans la plupart des systèmes d'exploitations Linux, il existe un répertoire nommé /opt
. Ici /opt
peut se comprendre comme "option" ou "optionnal", pour citer la réponse StackOverflow de What does "opt" mean (as in the "opt" directory)? Is it an abbreviation? :
Quote
In the old days, /opt
was used by UNIX vendors like AT&T, Sun, DEC and 3rd-party vendors to hold "Option" packages; i.e. packages that you might have paid extra money for. I don't recall seeing /opt
on Berkeley BSD UNIX. They used /usr/local
for stuff that you installed yourself.
But of course, the true "meaning" of the different directories has always been somewhat vague. That is arguably a good thing, because if these directories had precise (and rigidly enforced) meanings you'd end up with a proliferation of different directory names.
The Filesystem Hierarchy Standard says this about /opt/*
:
- "/opt is reserved for the installation of add-on application software packages."
By contrast it says this about /usr/local/*
:
- "The /usr/local hierarchy is for use by the system administrator when installing software locally."
These days, /usr/local/*
is typically used for installing software that has been built locally, possibly after tweaking configuration options, etcetera.
En d'autres termes :
- Si votre programme est programmé dans un langage compilé, par exemple le C++ ou le Rust, et que vous le compilez, alors vous devriez le placer dans
/usr/local/*
. - Votre application est un binaire unique, alors vous le copierez dans
/usr/local
. - Vous voulez utiliser une alternative d'un programme système existant construit à partir des sources en utilisant
make
. Dans ce cas, vous l'installerez dans/usr/local
. - Si vous déployez une application, et que par design, tous ses fichiers sont dans le même répertoire, alors on la déploiera dans un répertoire
/opt/my_app/
.
Cela ne reste que des conventions, mais elles sont largement utilisées et cela évite de se poser trop de questions sur où est tel application.
Dans le cas qui nous interesse ici, déployer notre API basique comme un service, il ne faudrait donc pas mettre
ExecStart=/home/vorph/miniconda3/envs/api/bin/python /media/vorph/datas/formation-Deep-MLOps/includes/my_app.py
Dans notre fichier my_app.service
, mais copier notre api et toutes ses dépendances (eg Dockerfile, docker-compose, etc) dans un répertoire /opt/code/
par exemple, et mettre
ExecStart=/home/vorph/miniconda3/envs/api/bin/python /opt/code/my_app.py
dans my_app.service
.
TLDR
Pour créer un service à partir d'une application my_app.py
:
- Mettre l'application dans un répertoire
/opt/code/my_app.py
. - Définir un "systemd unit file"
/etc/systemd/system/my_app.service
. - Relancer le démon
systemd
viasystemctl daemon-reload
. - Lancer le service avec
systemctl start my_app
. - Faire la configuration pour le lancement du service de façon automatique, si nécessaire.