Ansible: instalacja pakietu

2022-03-25

Definiując w playbooku instalację pakietu przez ansible można to zrobić na RedHat, Centos, Fedora o tak:

  tasks:
  - name: install nginx
    yum: 
      name: vsftpd 
      state: present

no ale, jeśli masz Ubuntu, to należałoby użyć apt:

  tasks:
  - name: install vsftpd
    apt: 
      name: nginx 
      state: present

Co kraj to obyczaj, co edycja to inna instalacja… ale można również skorzystać z modułu packages – jedna uniwersalna metoda:

  tasks:
  - name: Install nginx
    package:
      name: nginx
      state: present

 

 

 

By Rafał Kraik in Linuxy

Linux: Krótki przewodnik instalacji nginx i ufw firewall

2022-03-25

nginx na dobre wyparł już ze świata Linuxa starego poczciwego httpd/Apache (zdanie z przymróżeniem oka). Oto krótki przewodnik instalacji nginx na Ubuntu

Zaczynamy od instalacji samego pakietu:

sudo apt update
sudo apt install nginx

Podczas instalacji pakietu dzieje się jedna fajna rzecz. Nginx dodaje reguły aplikacyjne do firewalla ufw. Można je wylistować korzystając z:

sudo ufw app list

Są trzy domyślne zestawy: HTTP, HTTPS i Full (zawierający zarówno http jak i https). Można je włączyć uruchamiając np:

sudo ufw allow 'Nginx Full'

Jeśli chcesz zachować kontrolę nad zmianami w firewall możesz też otwierać zamykać porty samodzielnie korzystając z nazw protokołów lub nawet numerów portów:

sudo ufw allow https 
sudo ufw allow 443

Status firewalla można sprawdzić poleceniem

sudo ufw status

a spodziewany wynik to np coś w tym stylu:

Status: active

To Action From
-- ------ ----
443/tcp ALLOW Anywhere
22/tcp ALLOW Anywhere
80/tcp ALLOW Anywhere
Nginx Full ALLOW Anywhere
443/tcp (v6) ALLOW Anywhere (v6)
22/tcp (v6) ALLOW Anywhere (v6)
80/tcp (v6) ALLOW Anywhere (v6)
Nginx Full (v6) ALLOW Anywhere (v6)

Gdyby status ufw był inactive, to włączenie firewalla wykonasz przez

sudo ufw enable

Jednak przed włączeniem ufw, warto sprawdzić, czy np. zezwalamy na połączenia na porcie 22 (ssh). Niestety nie ma komendy, która by pozwoliła to zzrobić za prośrednictem ufw. Można za to wszystkie reguły podejrzeć w pliku:

sudo cat /etc/ufw/user.rules

Wróćmy do konfiguracji nginx. Warto by było, żeby usługa startowała automatycznie po uruchomieniu systemu:

systemctl enable nginx
systemctl start nginx
systemctl status nginx

Domyślna konfiguracja nginx znajduje się w /etc/nginx/nginx.conf. Domyślnie serwer nasłuchuje tylko na porcie 80 i root wskazuje na pliki w katalogu /var/www/html

Konfigurację można (i należy) docelowo zmienić tak, aby mogła obsługiwać multi-site.

Całkiem ładny opis instalacji ingx jest też tu

https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-20-04

Manual do ufw tutaj:

https://ubuntu.com/server/docs/security-firewall

i tu:

How To Setup a Firewall with UFW on an Ubuntu and Debian Cloud Server | DigitalOcean

By Rafał Kraik in Linuxy

PostgreSQL: Sequences – co to jest i jak ich używać?

2022-03-16

Jest wiele sytuacji, w których w bazie danych trzeba zapisać unikalne informacje, oznaczone unikalnym identyfikatorem. Tak jest zresztą nie tylko w bazach danych. Idziesz do urzędu, a na wejściu musisz pobrać numerek, dzięki czemu od razu jesteś zakolejkowany 🙂

Obiektem, który w bazie danych generuje kolejne wartości jest tzw. SEQUENCE. Bardzo podstawowa definicja sequence może wyglądać tak:

CREATE SEQUENCE ticket_id INCREMENT 1 START 101;

Ilekroć będziesz odwoływać się do ticket_id, z wykorzystaniem funkcji nextvalue, będziesz otrzymywać kolejne numerki począwszy od 101. Sqeunce ma mnóstwo parametrów dotyczących tego, jak ma wyglądać generowanie kolejnych wartości i znajdziesz je w helpie.

Załóżmy, że została utworzona taka oto tabela:

create table tickets(id integer, title text);

Typowe wykorzystanie sequence wygląda tak:

insert into tickets(id, title) values(nextval('ticket_id'),'issue1'), (nextval('ticket_id'),'issue2');

W efekcie, w tabeli znajdują się teraz następujące rekordy:

 select * from tickets;
 id  | title 
-----+--------
 101 | issue1
 102 | issue2

Pięknie… ale…. dla danego sequence nie wiadomo, gdzie on jest wykorzystywany. Można by było oczywiście dochodzić do tego po nazwie. Skoro ktoś nazwał sequence ticket_id, to może jest jakaś tabela z ticket-ami?

Tabele można też zdefiniować tak:

CREATE TABLE color (
 color_id INT GENERATED BY DEFAULT AS IDENTITY (START WITH 10 INCREMENT BY 10),
 color_name VARCHAR NOT NULL
 );

Zasada pracy tej tabeli jest podobna, jak w przpadku sequence, a gdyby słowo DEFAULT zamienić na ALWAYS, to wartości dla color_id nie tylko, że nie trzeba by podawać – nie można jej podać, bo będzie ona generowana przez PostgreSQL samodzielnie.

insert into color (color_name) values ('pink'), ('green'), ('yellow');
INSERT 0 3

select * from color;
 color_id | color_name 
----------+------------
 10       | pink
 20       | green
 30       | yellow

Jest jednak pewna różnica. Jeśli zajrzymy do widoku  information_schema.sequences, to znajdziemy tam tylko informacje o pierwszym sequence (tym stworzonym jawnie i osobno). Jeśli jednak zajrzymy do tabeli systemowej  pg_sequences, to będą tam już obie!

No ale skoro utworzenie sequence nastąpiło automatycznie podczas tworzenia tabeli – to nie ma siły. Gdzieś musiał zostać ślad po tym, z jakim sequence jest powiązana kolumna color_id. Rzeczywiście:

 select pg_get_serial_sequence('public.color', 'color_id');

W efekcie dostaniemy:

 pg_get_serial_sequence 
---------------------------
 public.color_color_id_seq

A jak zidentyfikować, te ręcznie tworzone sequence? Niestety – chyba odkryliśmy regułę, jak nie należy korzystać z sequence. Jeśli sequence jest obiektem „na boku” i nie podlega przypisaniu do tabeli, jest to tylko maszyna wydająca numerki, stojąca gdzieś w  portierni naszej bazy danych, to po prostu nie jest przypisana do żadnej tabeli/kolumny w tabeli. Logikę generowania wartości id w tabeli tickets, zna w tym przypadku tylko programista aplikacji.

Co ciekawe, jeśli tabela została utworzona poleceniem:

CREATE TABLE Towns (
 id SERIAL UNIQUE NOT NULL,
 code VARCHAR(10) NOT NULL, -- not unique
 article TEXT,
 name TEXT NOT NULL, -- not unique
 department VARCHAR(4) NOT NULL,
 UNIQUE (code, department)
);

to poniższe zapytanie również wykryje relację między kolumną id, a automatycznie utworzonym sequence:

SELECT d.refobjid::regclass, a.attname
FROM pg_sequences s
JOIN pg_depend d ON d.objid = CONCAT(s.schemaname,'.', s.sequencename)::regclass
JOIN pg_attribute a ON a.attrelid = d.refobjid AND a.attnum = d.refobjsubid;

A tak BTW, skąd PostgreSQL wie, jak się ze sobą te obiekty wiążą? Jest jeszcze jedna tabela systemowa o nazwie pg_sequence, gdzie dla każdego sequence można odnaleźć jego identyfikator. Dzięki temu, działa następujące zapytanie, które – przyjmijmy – odpowiada na pytanie – gdzie jest używany obiekt sequence:

SELECT seqclass.relname AS sequence_name,
     seqclass.relfilenode AS sequenceref,
     dep.refobjid AS depobjref,
     depclass.relname AS table_name
FROM pg_class AS seqclass
 JOIN pg_sequence AS seq ON seq.seqrelid = seqclass.relfilenode
 JOIN pg_depend AS dep ON seq.seqrelid = dep.objid
 JOIN pg_class AS depclass ON dep.refobjid = depclass.relfilenode;

[ Referece: https://sadique.io/blog/2019/05/07/viewing-sequence-ownership-information-in-postgres/ ]

By Rafał Kraik in PostgreSQL

PostgreSQL: Liczba rekordów w każdej tabeli

2022-03-16

Jeden z ostatnich kroków migracji danych, to sprawdzenie, czy migracja „niczego nie zgubiła”. Potencjalnie można się np. spodziewać, że tuż po zakończeniu migracji, jakiś zbłąkany użytkownik dopisał swoje rekordy do źródłowej bazy danych, a my tego rekordu nie przenieśliśmy. Jeden z „topornych” sposobów sprawdzenia, czy żaden rekord nie zostanie utracony, jest po prostu policzenie rekordów w źródłowej i docelowej bazie danych.

Najpierw wypadałoby ustalić, jakie mamy schematy w bazie. Robimy to głównie po to, żeby wykluczyć z raportu schematy systemowe, względnie inne, które mają być z jakiegoś powodu ominięte. psql pozwala na uruchomienie skróconego polecenia \dn, ale ta właśnie pomija schematy systemowe. Dlatego proponuję:

SELECT schema_name FROM information_schema.schemata;

Oto możliwy wynik:

 schema_name 
--------------------
 public
 information_schema
 pg_catalog
 pg_toast_temp_1
 pg_temp_1
 pg_toast

Znając schematy bazy danych, pora na sztuczkę. Poniższe zapytanie automatycznie zbierze ze wszystkich tabel z wybranych schematów liczbę rekordów. Ponieważ zapytanie jest uruchamiane na faktycznych danych (a nie np. ze statystyk), jego wykonanie może zająć chwilę czasu. Zapytanie jest sprytne, bo korzysta z możliwości wykonania XML w tym zapytaniu. Poniekąd więc dla każdego rekordu zwracanego z tabel systemowych uruchomi się jeszcze jedno zapytanie XML, zliczające liczbę rekordów:

select table_schema, 
 table_name, 
 (xpath('/row/cnt/text()', xml_count))[1]::text::int as row_count
from (
 select table_name, 
 table_schema, 
 query_to_xml(format('select count(*) as cnt from %I.%I', table_schema, table_name), false, true, '') as xml_count
 from information_schema.tables
 where table_schema NOT LIKE 'pg_%' and table_schema != 'information_schema'
) t

U mnie zwrcony wynik wygląda mniej więcej tak:

 table_schema | table_name         | row_count 
--------------+--------------------+-----------
 public       | pg_stat_statements | 0
 public       | towns4             | 1000000
 public       | pg_buffercache     | 262144
 public       | towns5             | 1000000
 public       | towns              | 1000000
 public       | towns2             | 1000000
 public       | towns3             | 1000000
 public       | towns6             | 1000000
 public       | towns7             | 1000000
 public       | towns8             | 1000000
 public       | towns9             | 1000000
 public       | towns0             | 1000000
 public       | large_test         | 20000000
 public       | a_table            | 1013
(14 rows)

Powyższe zapytanie można by uruchomić na obu bazach danych i po prostu porównać uzyskane wyniki – czy to excelu, czy pythonie, czy palcem na ekranie – obojętnie.

Istnieją też inne propozycje rozwiązania tego problemu, ale niekoniecznie zliczające rekordy co do jednego, np. zapytanie:

SELECT relname, 
       n_tup_ins - n_tup_del as rowcount 
FROM pg_stat_all_tables 
WHERE schemaname NOT LIKE 'pg_%' and schemaname != 'information_schema';

zwraca:

 relname    | rowcount 
------------+----------
 towns      | 1044898
 towns5     | 1000000
 towns6     | 1000000
 a_table    | 1016
 towns4     | 1000000
 large_test | 20000000
 towns7     | 1000000
 towns2     | 1000000
 towns0     | 1000000
 towns9     | 1000000
 towns8     | 1000000
 towns3     | 1000000
(12 rows)

ale już dla tabeli a_table, widać różnice… Jeśli więc, chcesz mieć dokładne wyniki, to musisz liczyć wszystko. Z pomocą powyższych zapytań będzie to i tak rozwiązanie automatyczne, tylko może z punktu widzenia PostgreSQL, jakby trochę na piechotę.

By Rafał Kraik in PostgreSQL

PostgreSQL: Wykrywanie tabel bez primary key i dodawanie primary key

2022-03-16

Niektóre mechanizmy PostgreSQL, jak np. replikacja logiczna wymagają, aby tabele posiadały primary key. Dlatego przyda się wiedzieć, czy baza danych spełnia wymogi dla replikacji logicznej. Oto polecenie, które wyświetli informację o tabelach bez primary key:

select tab.table_schema,
       tab.table_name
from information_schema.tables tab
left join information_schema.table_constraints tco 
     on tab.table_schema = tco.table_schema
        and tab.table_name = tco.table_name 
        and tco.constraint_type = 'PRIMARY KEY'
where tab.table_type = 'BASE TABLE'
      and tab.table_schema not in ('pg_catalog', 'information_schema')
      and tco.constraint_name is null
order by table_schema, table_name;

I co jeśli takie tabele się znajdą? Ogólnie masz kłopot, ale w najlepszym przypadku, znajdzie się w tabelach kolumna, która może być potraktowana jako primary key. Zaczynamy więc od sprawdzenia struktur tabel:

mytest=> \d towns
 Table "public.towns"
 Column     | Type                  | Collation | Nullable | Default 
------------+-----------------------+-----------+----------+-----------------------------------
 id         | integer               |           | not null | nextval('towns_id_seq'::regclass)
 code       | character varying(10) |           | not null | 
 article    | text                  |           |          | 
 name       | text                  |           | not null | 
 department | character varying(4)  |           | not null | 
Indexes:
 "towns_code_department_key" UNIQUE CONSTRAINT, btree (code, department)
 "towns_id_key" UNIQUE CONSTRAINT, btree (id)

Dodanie indeksu opartego o kolumnę id wyglądałoby tak:

alter table towns add primary key (id);

I gotowe!

 

By Rafał Kraik in PostgreSQL

Python: Grupowanie po dacie z sumą – redukcja liczby wierszy

2022-03-12

Problem:

Dane pochodzące z wielu plików, zostały zaimportowane do Pandas Data Frame w następującej postaci:

Chcielibyśmy uniknąć wielokrotnie powtarzanej daty, wartości numeryczne znajdujące się w col1, col2 i col3 powinny się nasumować, dając taki efekt:

Rozwiązanie:

Wydaje się, że funkcjonalnością stworzoną do takiego celu jest grupowanie. Wystarczy na rzecz obiektu data frame wywołać metodę groupby wskazując, że grupowanie ma się odbyć ze względu na datę, a dla kolumn numerycznych chcemy zobaczyć sumę. Oto propozycja rozwiązania:

 

import pandas as pd
data = ({
 'date' :['2000-01','2000-02','2000-01','2000-02','2000-01','2000-02'],
 'col1' :[22000,25000,0,0,0,0],
 'col2' :[0,0,23000,24000,0,0],
 'col3' :[0,0,0,0,26000,25000]
 })
df = pd.DataFrame(data, columns=['date','col1','col2','col3'])
print(df)

df2 = df.groupby('date').sum()
print(df2)

 

By Rafał Kraik in Python

Azure: ustalenie minimalnych uprawnień wymaganych do pewnej czynności

2022-03-05

Zadanie z jakim się zmierzam, to ustalenie minimalnych uprawnień jakie powinien posiadać użytkownik, aby wykonać pewną czynność. W tym przypadku chodziło o budowanie specyficznej infrastruktury z wykorzystaniem skryptu Terraform, ale metoda sprawdzi się też w innych scenariuszach.

Zacząłem od stworzenia service principal, bo w moim przypadku skrypt miał być uruchamiany na service principal. Gdyby miało to być zwykłe konto użytkownika, to oczywiście należało by stworzyć konto:

az ad sp create-for-rbac --name myserviceprincipal

W wyniku tego polecenia jest tworzony service principal, a  w zwróconym output można znaleźć coś w tym stylu:

{
  "appId": "6...XXX...",
  "displayName": "myserviceprincipal",
  "password": "y...XXX...",
  "tenant": "6...XXX..."
}

Mamy tu: appId, bo jest on wykorzystywany przez dalsze polecenia i można go utożsamiać z określonym service principal i password, bo to tajna część utworzonego właśnie service principal i wreszcie tenant określający, gdzie tego service principal można używać (w jakiej organizacji). Czytaj dalej »

By Rafał Kraik in Azure