Raspbery Pi – UBUNTU GPIO problem

Jeżeli chcemy się dostać do GPIO za pomocą Python i biblioteki RPi.GPIO, jako zwykły użytkownik, może pokazać się nam błąd „RuntimeError: Not running on a RPi!”. Ten błąd wynika z braku uprawnień do /dev/gpiomem.

# dodajemy użytkownika na którym pracujemy do grupy dialout
$ sudo usermod -aG dialout tomeks
# ustawiamy na stałe uprawnienia do /dev/gpiomem dla grupy dialout
sudo nano /etc/udev/rules.d/99-gpiomem.rules

KERNEL=="gpiomem", OWNER="root", GROUP="dialout"

# uruchamiamy ponownie
$ sudo reboot

Systemd – usługi użytkownika

Systemd umożliwia zarządzania usługami przez użytkownika.

Aby konto mogło korzystać z usług systemd bez konieczności logowania musimy włączyć funkcję „linger”

$ loginctl enable-linger nazwa_użytkownika

Logujemy się na użytkownika i w katalogu domowym tworzymy katalog i plik usługi. Przy konfiguracji nie podajemy User w sekcji [Service]

$ mkdir -p ~/.config/systemd/user/
$ vi ~/.config/systemd/user/myapp.service

[Unit]
Description=My demo application

[Service]
ExecStart=plikUslugi
WorkingDirectory=/home/myapp/html

[Install]
WantedBy=default.target

Najpierw musisz przeładować systemd, aby mógł pobrać plik jednostki.

$ systemctl --user daemon-reload

Uruchamiamy usługę i włączamy aby uruchamiała się przy starcie sytemu

$ systemctl --user start myapp.service
$ systemctl --user enable myapp.service

SSH – Generowanie kluczy autoryzujących, połączenie bez hasła

Domyślnie do autoryzacji użytkownika w połączeniu ssh wykorzystujemy hasło. Znacznym ułatwieniem jest wygenerowanie pary kluczy asymetrycznych po stronie klienta i zainstalowanie klucza publicznego na serwerze ssh z którym chcemy się łączyć.

Generowanie kluczy rsa na komputerze kliencie:

$ ssh-keygen -t rsa -b 4096

Generating public/private rsa key pair.
Enter file in which to save the key (/home/user/.ssh/id_rsa): 
Created directory '/home/user/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/user/.ssh/id_rsa
Your public key has been saved in /home/user/.ssh/id_rsa.pub
...

Przesłanie klucza publicznego ~/.ssh/id_rsa.pub na serwer ssh, z którym będziemy się łączyć:

$ ssh-copy-id -i ~/.ssh/id_rsa.pub user@sshserver

Teraz już możemy zestawić połączenie z ssh bez hasła.

$ ssh user@sshserver

W czasie przesyłania klucza, program poprosi nas o podanie hasła do konta na serwerze ssh.

Serwer może nie być skonfigurowany do akceptowania uwierzytelniania klucza publicznego. Upewnij się, że /etc/ssh/sshd_config na serwerze zawiera PubkeyAuthentication yes. Pamiętaj o ponownym uruchomieniu procesu sshd na serwerze.

Docker – polecenia

Uruchomienie nowego kontenera

# docker run <nazwaKontenera>

$ docker run ubuntu

Uruchomienie kontenera w trybie interaktywnym z shellem

$ docker run -it ubuntu bash
root@a52191c360d2:/#

Wyświetlanie wszystkich kontenerów

$ docker container -a # opcja -a pokaże również kontenery nie uruchomione
lub
$ docker ps -a

Uruchomienie wyłączonego kontenera

# docker start <CONTAINER ID> | <NAMES>

$ docker ps -a
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS                      PORTS     NAMES
a52191c360d2   ubuntu                 "bash"                   9 minutes ago    Exited (0) 9 minutes ago              magical_shtern

$ docker start a52191c360d2
lub   
$ docker start magical_shtern

Zatrzymanie kontenera pracującego w tle

# docker stop <CONTAINER ID> | <NAMES>

$ docker stop a52191c360d2

Kopiowanie plików między kontenerem a lokalnym systemem plików

# docker cp <[NAZWA KONTENERA | CONTAINER ID]>:<SCIEZKA DO PLIKU W KONTENERZE> <LOKALNA SCIEZKA DO PLIKU>
lub
# docker cp <LOKALNA SCIEZKA DO PLIKU> <[NAZWA KONTENERA | CONTAINER ID]>:<SCIEZKA DO PLIKU W KONTENERZE> 

Wyświetlenie listy obrazów

$ docker image ls
REPOSITORY                           TAG       IMAGE ID       CREATED          SIZE
ubuntu                               latest    daab1c66d467   6 days ago       65.6MB

Tworzenie nowego obrazu na podstawie kontenera

# docker commit <CONTAINER ID> <NAZWA_NOWEGO_OBRAZU>

$ docker commit a52191c360d2 nowy_obraz
sha256:534f2be1cee938f127815d93323d07e02ecd8f723c4df052ed8241ecb7922511
$ docker image ls
REPOSITORY                           TAG       IMAGE ID       CREATED          SIZE
nowy_obraz                           latest    534f2be1cee9   14 seconds ago   65.6MB
ubuntu                               latest    daab1c66d467   6 days ago       65.6MB

Podgląd historii warstw w obrazie

# docker history <NAZWA OBRAZU>

$ docker history nowy_obraz

Pobranie obrazu z docker hub. Polecenie docker run <nazwa obrazu> pobiera obraz jeżeli go nie posiadamy i uruchamia kontener. Możemy pobrać obraz bez uruchamiania kontenera.

# docker push <NAZWA OBRAZU>

$ docker run alpine

Zmiana nazwy obrazu. Źródłowy obraz nie zostanie usunięty.

# docker tag <NAZWA OBRAZU> <NAZWA KONTA DOCKER>/<NOWA NAZWA OBRAZU>

Wysyłanie obrazu do docker hub. Aby to zrobić musimy posiadać na docker hub konto i się do niego zalogować. Nazwa obrazu musi być podana w formacie <NAZWA KONTA DOCKER>/<NOWA NAZWA OBRAZU>

# docker login
# docker push <NAZWA KONTA DOCKER>/<NOWA NAZWA OBRAZU>

Docker – Dockerfile

Dockerfile to plik, który pomaga utworzyć własne obrazy Dockerowe.

Dla każdego polecenia z Dockerfile jest uruchamiany nowy kontener i zapisywana nowa warstwa obrazu.

$ mcedit Dockerfile
FROM ubuntu # bazowy obraz
COPY plik.txt . # skopiuje plik do kontenera
RUN apt-get -y update && apt-get -y install nano # zainstaluje w kontenerze edytor nano

Budowanie obrazu na podstawie Dockerfile.

# docker build [--tag name:tag] <ścieżka do kontekstu> 

$ docker build --tag moj_ubuntu:1.0 . 
$ docker image ls
REPOSITORY                           TAG       IMAGE ID       CREATED              SIZE
moj_ubuntu                           1.0       de40b0f082a5   0 days ago           93.4MB

$ docker run de40b0f082a5 cat plik.txt # uruchomi nasz nowy obraz i wyświetli zawartość skopiowanego pliku plik.txt 

Ścieżka do kontekstu to folder w którym mogą się znajdować pliki, które w procesie budowy obrazu zostaną wysłane do demona Dockera. W kontekście powinny znajdować się tylko niezbędne elementy do budowy, aby nie wydłużać procesu przesyłania ich demona.

Plik Dockerfile domyślnie jest wyszukiwany w folderze kontekstu. Możemy podać inną lokalizację pliku za pomocą parametru -f.

Docker – Wolumeny i mapowanie katalogów

Przechowywanie danych w kontenerze nie jest zbyt wygodne, gdy usuniemy kontener (docker rm <id_kontenera>), zapisane dane zostaną skasowane. Aby przechowywać dane aplikacji możemy stworzyć wolumen lub mapować katalog z hosta do kontenera.

Wolumeny

# docker volume create <nazwa_wolumenu>
$ docker volume create ubuntu_data
ubuntu_data
$ docker volume ls
DRIVER    VOLUME NAME
local     ubuntu_data

Usuwanie wolumenu

$ docker volume rm ubuntu_data
ubuntu_data

Podłączenie wolumenu do kontenera.

docker run --volume <nazwa_wolumenu>:<katalog_wewnątrz_kontenera> <nazwa_obrazu> 

Podpinamy wolumen 'ubuntu_data’ do katalogu 'data’ wewnątrz kontenera. Wszystkie dane zapisane wewnątrz kontenera do katalogu 'data’, zostaną zapisane w wolumenie 'ubuntu_data’. Jeżeli wolumen nie będzie istniał zostanie utworzony przy starcie kontenera.

$ docker run --volume ubuntu_data:/data ubuntu

Mapowanie katalogu

Mapujemy katalog z hosta do kontenera

# docker run --volume <sciezka_bezwzgledna_katalogu>:<katalog_wewnątrz_kontenera> <nazwa_obrazu>
$ docker run --volume /home/user/docker:/data ubuntu

Mapujemy katalog '/home/user/docker’ do katalogu 'data’ wewnątrz kontenera. Wszystkie dane zapisane wewnątrz kontenera do katalogu 'data’, zostaną zapisane do katalogu ’ /home/user/docker’ na hoscie.

Klaster Kubernetes na Raspberry PI

Instalacja oparta jest o co najmniej dwa Raspberry PI 4, min. 4G RAM z zainstalowanym obrazem Ubuntu 20.04

Większość ustawień musi być wykonywane na każdym urządzeniu, które stworzą klaster.

Każde Raspberry powinno posiadać statyczny adres lub przydzielony na stałe DHCP.

Ustawienie nazwy na kontrolerze

$ sudo nano /etc/hostname
k8s-controller1

$ sudo nano /etc/hosts 
192.168.XX.XX k8s-controller1.jakas-domena.com.pl k8s-controller1 # nazwa domeny nie jest wymagana

$ sudo reboot

Zmiana nazwy na pozostałych PI które będą pracowały jako węzły (nody)

$ sudo nano /etc/hostname
k8s-node-01

$ sudo nano /etc/hosts 
192.168.XX.XX k8s-node-01.jakas-domena.com.pl k8s-node-01 # nazwa domeny nie jest wymagana

$ sudo reboot

Dodajemy nowe opcje bootowania na końcu linii w pliku

$ sudo nano  /boot/firmware/cmdline.txt
cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1 swapaccount=1

Aktualizacja pakietów

$ sudo apt update && sudo apt dist-upgrade -y
$ sudo reboot

Dla wygody możemy dodać swojego użytkownika na którym będziemy pracować

$ sudo adduser mojaNazwaUzytkownika
# Dodajemy go do grupy sudo
$ sudo usermod -aG sudo mojaNazwaUzytkownika

Instalacja Dockera i dodanie własnego utworzonego użytkownika do grupy docker

$ curl -sSL get.docker.com | sh
$ sudo usermod -aG docker mojaNazwaUzytkownika

Dodając do pliku opcje konfigurujemy demona Docker.

$ sudo nano /etc/docker/daemon.json

{
   "exec-opts": ["native.cgroupdriver=systemd"],
   "log-driver": "json-file",
   "log-opts": {
     "max-size": "100m"
   },
   "storage-driver": "overlay2"
 }

Włączamy routing odkomentowując linijkę #net.ipv4.ip_forward=1 w pliku /etc/sysctl.conf

$ sudo nano /etc/sysctl.conf
$ sudo reboot

Sprawdzamy czy Docker działa prawidłowo

$  sudo systemctl status docker
...
 Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
     Active: active (running) since Sat 2021-10-23 17:24:11 UTC; 59s ago
TriggeredBy: ● docker.socket
...

$ docker run hello-world
...
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
...
Hello from Docker!
This message shows that your installation appears to be working correctly.
...

Dodajemy repozytoria Kubernetes i dodajemy klucz GPG

 $ sudo nano /etc/apt/sources.list.d/kubernetes.list

deb http://apt.kubernetes.io/ kubernetes-xenial main

$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
OK

Instalacja Kubernetes

$ sudo apt update
 && sudo apt install kubeadm kubectl kubelet -y

Podczas instalacji czasem zdarzają się błędy. Gdy tak się zdarza powtarzamy ostatnie polecenie.

Teraz możemy zainicjalizować klaster wykonując poniższe na kontrolerze

$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16

Po uruchomieniu, na końcu otrzymasz dane wyjściowe, które będą zawierać polecenie kubeadm join wraz z tokenem. Zapisz je gdzieś. Będzie potrzebne przy dołączaniu węzłów. Jeżeli nasz token gdzieś się zagubi możemy go odzyskać wukonując na kontrolerze polecenie

kubeadm token create --print-join-command

Nadal tylko na kontrolerze uruchamiamy polecenia

$ mkdir -p ~.kube 
$ sudo cp /etc/kubernetes/admin.conf ~/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

Instalujemy sterowniki sieci na kontrolerze

$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

po instalacji sterowników czekamy, czy wszystkie pody zostały uruchomione

$ kubectl get pods --all-namespaces

Wszystkie powinny mieć status Running. W przeciwnym wypadku czekamy chwile i powtarzamy powyższy test.

Możemy dołączać węzły robocze. Na każdym PI, który będzie pełnił taką funkcję uruchamiamy polecenie kubeadm join, które zapisaliśmy w czasie inicjalizacji klastra.

$ sudo kubeadm join 192.168.XX.XX:6443 --token xxxxxxx --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Sprawdzamy na kontrolerze czy nasze węzły robocze się dołączyły

$ kubectl get nodes
NAME              STATUS   ROLES                  AGE    VERSION
k8s-controller1   Ready    control-plane,master   65d    v1.22.1
k8s-node-01       Ready    <none>                 65d    v1.22.1
k8s-node-02       Ready    <none>                 2m1s   v1.22.2

Linux – Zadania pracujące w tle

Jeżeli na terminalu mamy uruchomione jakieś zadanie np.: edytor vi i chcemy jednocześnie sprawdzić coś, nie zamykając edytora w którym nie zakończyliśmy pracy, możemy otworzyć nowy terminal lub przenieś vi w tło.

Ctrl + z
[1]+  Stopped                 vi

Uruchamiamy dowolne inne zadanie a następnie powracamy do edytora poleceniem:

$ fg

Możemy również uruchomić zadanie, które będzie pracowało w tle, dodając na końcu znak '&’.

$  vi /etc/resolv.conf &
[2] 2982789

Teraz 2 nasze zadania pracują w tle. Wyświetlamy ich listę:

$ jobs
[1]-  Stopped                 vi
[2]+  Stopped                 vi /etc/resolv.conf

Aby przywrócić konkretne zadanie na pierwszy plan wpisujemy fg i idetyfikator podany w wynikach polecenia jobs.

 $ fg 2

Systemctl – polecenia

Pokaż serwisy

$ systemctl --type=service
$ systemctl --type=service --state=active
$ systemctl --type=service --state=running

Wyświetli wszystkie zainstalowane pliki jednostek. Również te nie aktywne.

$ systemctl list-unit-files