P mint performancia (és mint PHP)

Néha úgy hozza a sors, hogy az embernek be kell piszkolnia a kezét. Apache-al, PHP-val, és mindennel ami ezzel jár.

Egy web szerver készítése általában egyszerű feladat.

apt install apache2
apt install php

A bonyodalmak általában ez után következnek, mivel ami ez után történik produkcióban az meglehetősen lassú, és erőforrás igényes. Mint régi új szerver tulajdonos meglepődve konstatáltam, hogy egy normális weboldal betöltése eltart 3 másodpercig, ami bár nem sok idő (főleg egy kazettás töltéshez képest a c64-en), de általában ezzel már nem vagyok túl elégedett.

Hogy ez ne így legyen ahhoz az alábbi dolgokat kell tenni:

Kezdjük az apache-al, ami alapesetben preforkol, de az lassú, főleg ha virtualizált környezetekről van szó, célszerűbb előre létrehozni kiszolgáló szálakat, és thread-eket. Ehhez mpm módot kell váltani. Itt lehet event-et is használni, de nekem a worker alapú megoldás optimálisabb, mivel egyszerűbb és nem számítok nagy egyidejű lekérdezésre:

a2dismod mpm_prefork
a2enmod mpm_worker

Ez előtt a php modult is ki kell kapcsolni. A worker alapú megoldással az alap php modul általában nem működik, mivel nem thread safe (Apache is running a threaded MPM, but your PHP Module is not compiled to be threadsafe. You need to recompile PHP). De ez nem is akkora nagy probléma, mivel php terén is érdemes már a kezdetek kezdetén perspektívát váltani, és telepíteni a php-fpm-et:

apt install php-fpm
a2enmod proxy_fcgi
a2enconf php8.2-fpm

Ezek után a php beállításokat már az fpm-es php.ini-ben érdemes szerkeszteni (/etc/php/8.2/fpm/php.ini ). Ha ezzel megvagyunk akkor még egy opcache telepítést is érdemes megtenni.

apt install php-opcache

A telepítés után a beállítások itt találhatók: /etc/php/8.2/fpm/conf.d/10-opcache.ini

Az én beállításaim valahogy így néznek ki a teszt site-on:

zend_extension=opcache.so
opcache.jit=on
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
opcache.revalidate_freq=240
opcache.validate_timestamps=1

Érdemes továbbá a PHP által kezelt objektumokat perzisztens módon tárolni, pl. memcached-ben.

apt install memcached

A konfiguráció itt található: /etc/memcached.conf de nagyon nem kell bántani, talán a memória méretét érdmes megemelni 127 mb-ra (alapból 64)

service restart memcached
apt install php-memcached
apt-get install php-pear php-dev zlib1g-dev
pecl channel-update pecl.php.net
pecl install memcache

Php.ini-be fel kell venni: extension=memcache.so majd újra kell indítani az fpm php-t.

WordPress plugin-ből érdemes telepíteni az alábbit:

Memcached Object Cache

Így majd kapunk értesítést a wordpres-sen belül, ha esetleg új verzió lenne. Sikeres telepítés után kézzel kell átmásolni a object-cache.php fájlt a /wp-content/plugins/memcached könyvtárból a wp-content könyvtárba. Majd felvenni a WP_CACHE_KEY_SALT változót a wp-config.php-be (a plugin leáírsa – readme.txt – alapján)

Ha működik a dolog, akkor a Query monitor Overview fülén tudjuk majd ellenőrizni:

Ha ezzel mind megvagyunk nincs más dolgunk, csak a wordpress oldalon is körülnézni egy kicsit. Itt nem érdemes kézzel matatni a dolgokban, nagyszerű plugin-ek vannak, amik megteszik ezeket helyettünk. Én az alábbiakra tettem le a voksom:

Query Monitor – hogy lásd mi történik

Memcached Object Cache – a php objektumok memcached-ben történő tárolásához

WP Rocket – a legenerált oldalak gyorsítása, és további optimalizálások (css, js minify, stb.)

Smush – kép tömörítés és konverzió webp formátumra (és igény esetén átméretezés adott limitre)

Ha mindent jól csináltunk, akkor egy viszonylag gyenge szerveren (Intel Pentium Processor E6700 és 1 Gb memória), az alábbit kellene látunk egy Elementor által generált oldal esetén.

Ui: eredetileg egy oldalbetöltés 3s volt…

Docker windows alatt

Szerintem operációs rendszerek esetén jó taktika, ha csak az fut rajta, amit az ember használ is. A docker is egy ilyen dolog, amit csak ritkán használok, és ha használom, akkor csak linux alatt, ami ugye az esetemben WSL ubuntu (windows subsystem for linux).

Ubuntut viszonylag egyszerű telepíteni windows alá, de nekünk olyan ubuntu kell, ami WSL2-t használ.

Akit érdekel mi is az a wsl2 járjon utána

A telepített wsl példányokat az alábbi módon tudjuk kilistázni:

wsl -l -v

A WSL2-nek van pár peremfeltétele, aminek meg kell felelni, és amin a többség elvérzik:

  • Top verziót használjunk a windows 10 vagy 11-ből (Windows update)
  • Biosban be legyen kapcsolva a virtualizácó
  • Windows alatt telepítve legyen a Virtual Machine feature
  • Le legyen töltve a wsl2 kernel frissítés (külön exe)

Hogy ezeket az előfeltételeket hogy lehet megteremteni, arról van egy jó leírás itt:

https://s1gr1d.medium.com/how-to-set-up-linux-on-windows-with-wsl-2-debe2a64d20d

Ha ezek megvannak (a restart fontos, én pl. kihagytam), akkor két út van:

Még nincs WSL linux telepítve, és szeretnénk, ez esetben nincs más dolgunk, mint alapértelmezetté tenni a wsl2-t

wsl --set-default-version 2

majd telepíteni a microsoft app store-ból.

Ha már van WSL linuxunk, de nem WSL2 akkor frissíthetjük az alábbi módon:

wsl --set-version Ubuntu 2

Ahol az ubuntu az a név, amit a fenti wsl -l -es parancs kimeneténél a name mezőben látsz.

Ha a frissítés hiba nélkül lefut, akkor a wsl -l már kettes verziót fog kiírni. Ha nem fut le hiba nélkül akkor vagy az előfeltételek közül hiányzik valami (nem írja ki, hogy mi, nálam a restart volt 🙂 ), vagy valami egyéb hiba van, amiből azért lehet bőségesen

Ha ez megvan, és rendelkezünk egy WSL2-es linuxal, akkor már csak a dockert-t kell életre kelteni. Ennek a módja az alábbi:

Győződjünk meg róla, hogy semmilyen docker maradvány nincs a rendszeren:

apt remove docker docker-engine docker.io containerd runcsudo apt remove docker docker-engine docker.io containerd runc

Telepítsük az előfeltételeket, és adjuk hozzá a docker-t a source listához:

sudo apt install --no-install-recommends apt-transport-https ca-certificates curl gnupg2
source /etc/os-release
curl -fsSL https://download.docker.com/linux/${ID}/gpg | sudo apt-key add -
echo "deb [arch=amd64] https://download.docker.com/linux/${ID} ${VERSION_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/docker.list
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io
sudo usermod -aG docker $USER

Ezek után még szükség lesz egy docker specifikus könyvtárra, ahol a socket elérhető a dockerd számára. Ehhez kell egy config:

sudo mkdir /etc/docker
sudo nano /etc/docker/daemon.json

A fájl tartalma legyen:

{
   "hosts": ["unix:///var/run/docker.sock"]
}

Ha ez megvan már csak indítani kell a docker démont a használat előtt:

sudo dockerd

És utána működni fognak a docker-es utasítások.

Java classpath

Kérdés: melyik jvm verziónál fog szöget ütni valaki fejében a gondolat, hogy a classpath paraméterében az egyik platformon pontosvesszővel, a másikon kettőspont az elválasztó karakter?

Linux: java -cp “.:lib/*”

Windows: -cp “.;lib/*”

Mondjuk így legalább mindenki gyorsan megtanulja, mit értenek a fejlesztők platformfüggetlen megoldások alatt.

Flutter 2 install

Nekem is úgy mondták (öregszem), de már egy napja megjelent a Flutter 2, ami mindennél is jobb. Mindig is szerettem a UI absztrakciókat, és azokat a durvábbnál durvább hackeket amibe aztán mindenki belebonyolódik az absztrakciók következményeként (SWT és társai ugye). Gondoltam felrakom, és jól megnézem, 2021-ben mit sikerült összehozni.

A telepítés (ami másolás) gond nélkül lement, aztán azt javasolták futtassam a flutter doctort, ami azt mondta, fogadjam el az android licenszeket egy újabb flutter doctor futtatással. Mégpedig ezzel:

flutter doctor --android-licenses

Lelkes voltam, futtattam és mint lenni szokott, megint egy szuper kis kaland vette kezdetét. Ugyanis a várt nyomj Y-t és fogadd el a licenszek helyett ezt kaptam:

C:\Users\voji\AppData\Local\Android\Sdk\tools\bin>flutter doctor --android-licenses
Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlSchema
        at com.android.repository.api.SchemaModule$SchemaModuleVersion.<init>(SchemaModule.java:156)
        at com.android.repository.api.SchemaModule.<init>(SchemaModule.java:75)
        at com.android.sdklib.repository.AndroidSdkHandler.<clinit>(AndroidSdkHandler.java:81)
        at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:73)
        at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:48)
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlSchema
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:606)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:168)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
        ... 5 more

Nem igazán értettem a dolgot. Na jó értettem, ez a class ugyanis a jaxb része, ami már java 9 óta deprecated 11 óta meg benne sincs a JDK-ban. Elég sokan, elég sokat szívtak már ez miatt a nagyvilágban.

Szóval vagy átírogatom mindig a környezeti változókat, hogy java8-java15-java8 vagy keresek valami értelmesebb megoldást. Az utóbbit preferáltam, így legalább lehetőségem nyílt kicsit megismerkedni a dart nyelvvel is, ami állítólag flutterhez nem hátrány.

Szóval az történt, hogy flutter doki megpróbálja nyaggatni az android sdk parancssoros kezelőjét, az sdkmanager.bat-ot. Ha ezt önmagában lefuttatjuk, akkor ugyanezt a hibát fogjuk kapni, mert ott még valaki nem vette észre, hogy egy 2017 óta deprecated dolgot használ.

A megoldás innen már egyszerű: le kell tölteni egy jaxb-t, és megoldani, hogy a classpath-on legyen.

A letöltés viszonylag egyszreű, innen lehet megtenni. FYI a jobb oldali Download felirat nem egy menü, hanem funkció. Lehet a JDK fejlesztők se találták elsőre a letöltés gombot, azért maradt ki a jaxb a JDK-ból. Tanúság: tessék jól látható letöltés gombokat használni, lehetőleg az oldal közepén.

Na de visszatérve a problémára, azt az sdkmanager.bat generálta, célszerű itt is megoldani. Ez alapesetben itt található:

%USERPROFILE%\AppData\Local\Android\Sdk\tools\bin\sdkmanager.bat

A problémás rész pedig itt (ahol látható, hogy szépen felülírják a classpath-ot, pedig az első próbálkozásom az volt, hogy oda felteszem a dependenciákat mint környezeti változó):

set CLASSPATH=%APP_HOME%\lib\dvlib-26.0.0-dev.jar;%APP_HOME%\lib\jimfs-1.1.jar;%APP_HOME%...

Ha belematatjuk ezt a kis kiegészítést (ide lett kitömörítve a csomag: d:\work\libs\jaxb-ri):

set CLASSPATH=d:\work\libs\jaxb-ri\mod\jaxb-api.jar;d:\work\libs\jaxb-ri\mod\jaxb-runtime.jar;d:\work\libs\jaxb-ri\mod\istack-commons-runtime.jar;d:\work\libs\jaxb-ri\mod\javax.activation-api.jar;%APP_HOME%\lib\dvlib-26.0.0...

Máris boldogabb lesz egy flutter pingvin a google főhadiszállásán…

C:\Users\voji>flutter doctor --android-licenses
                                                                                
7 of 7 SDK package licenses not accepted. 100% Computing updates...
Review licenses that have not been accepted (y/N)?

Gradle parametrizált task

A gradle task-ok nem tudnak paramétereket fogadni by design. Globális build paramétereket lehet olvasni belőlük, de hát azért a gányolásnak is van határa…

És bár paramétert nem lehet task-nak adni, task-ot viszonylag egyszerűen lehet létrehozni. És igény esetén kicsit bonyolultabban is.

Tegyük fel angular-t szeretnénk fordítani, különböző build target-ekkel. Vagy másolunk sokat, vagy csinálunk egy ilyet:

def createAngularTask(build, language) {
    return tasks.create("buildAngular_${build}_${language}", Exec) {
        group = "generated"
        workingDir = project.ANGULAR_APP_PATH
        inputs.dir(Paths.get(workingDir.toString(), "/src"))
        outputs.dir(Paths.get(workingDir.toString(), "/dist_hu"))
        def cmdArray = ["npm", "run", "${build}:${language}"]
        if (System.getProperty('os.name').toUpperCase().contains('WINDOWS')) {
            cmdArray.addAll(0, ["cmd", "/c"])
        }
        commandLine cmdArray
    }
}

Kicsit emlékeztet ez a C-s define varázslásokra, de úgy tűnik a groovy-s csapatot ez nem rettentette el. Szóval ha csináltunk egy ilyet, akkor már csinálhatunk ilyesmiket is:

createAngularTask("aembuild", "hu")
createAngularTask("aembuild", "en")
createAngularTask("jarbuild", "hu")
createAngularTask("jarbuild", "en")

És ha ilyeneket csináltunk, akkor már csinálhatunk csoportos build task-okat copy paste nélkül:

task buildAngularAem {
    group = BasePlugin.BUILD_GROUP
    dependsOn('buildAngular_aembuild_hu')
    dependsOn('buildAngular_aembuild_en')
}

A fenti játékot el lehet játszani akár string tömbökből is, ha nagyon sok task lenne. Így a task-oknak, csak a képzeletünk szab határt…

Securing apache

Ha esélyt szeretnénk adni Apache szerverünknek az életre, érdemes az alap beállításokon módosítani pár dolgot.

A legfontosabb szabály, a szerverünk mindig legyen naprakész (apt update, apt upgrade, esetleg a paranoiásaknak chkrootkit)

Aztán érdemes meggyőzni az Apache-ot, hogy ne mondja el mindenkinek a verziószámát, így talán nehezebb lesz expolit-ot keresni benne:

ServerSignature Off
ServerTokens Prod

A második a mod_security. Ezt sajnos kicsit macerásabb beállítani, de megéri. Ehhez az alábbi lépésekre van szükség:

apt-get install libapache2-mod-security2
cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf

Ezután érdemes bekapcsolni a security használatát az újonnan létrehozott (modsecurity.conf) konfig fájlban (alapból csak detektálásra van állítva)

SecRuleEngine On

Ha ez megvan, már nincs másra szükségünk, csak friss biztonsági szabályokra (persze csak egy gyors backup után).

mv /usr/share/modsecurity-crs /usr/share/modsecurity-crs.bak
git clone https://github.com/coreruleset/coreruleset.git /usr/share/modsecurity-crs

Majd ezt lehet frissítgetni időnként az alábbi paranccsal:

cd /usr/share/modsecurity-crs
git pull

Ezután még példányosítani kell a configot:

cp /usr/share/modsecurity-crs/crs-setup.conf.example /usr/share/modsecurity-crs/crs-setup.conf

Ha ez megvan, és a szerveren futtatunk valamilyen speciális dolgot (Pl. WordPress) akkor érdemes azt kivételként megadni. Az új configunkban (crs-setup.conf) az alábbi sorokat kell kicommentelni, és a megfelelő rendszerhez 1-est írni:

 SecAction \
  "id:900130,\
   phase:1,\
   nolog,\
   pass,\
   t:none,\
   setvar:tx.crs_exclusions_cpanel=0,\
   setvar:tx.crs_exclusions_dokuwiki=0,\
   setvar:tx.crs_exclusions_drupal=0,\
   setvar:tx.crs_exclusions_nextcloud=0,\
   setvar:tx.crs_exclusions_phpbb=0,\
   setvar:tx.crs_exclusions_wordpress=1,\
   setvar:tx.crs_exclusions_xenforo=0"

És már meg is tettük az első lépéseket egy biztonságosabb Apache szerver felé.

Sose feledjétek: biztonságos rendszer nincs, max. biztonságosabb…

JDK on Ubuntu

A Java telepítés nem egy nagy varázslat. Rendszerint arról szól, hogy kitömörítjük valahova a fájlokat, és elérjük, hogy amikor beírjuk hogy java, akkor az általunk kitömörített cucc induljon el.

Ez windows alatt egy download, total commander, és a PATH környezeti változó módosításával elérhető.

Ubuntu alatt főként csak terminált használok, szóval beírtam a google-be, hogy jdk install ubuntu, és vadabbnál vadabb dolgokat láttam, és nagyjából semmi se működött. Szóval ha valaki jdk-t szeretne telepíteni ubuntura, akkor az alábbit kell tenni:

Ellátogatni az OpenJdk oldalára, és kimásolni a nekünk tetsző JDK csomag URL-jét

Letölteni a kívánt csomagot:

wget https://download.java.net/java/GA/jdk15.0.1/51f4f36ad4ef43e39d0dfdbaf6549e32/9/GPL/openjdk-15.0.1_linux-x64_bin.tar.gz

Ki kell tömöríteni a cuccot amit letöltöttünk:

sudo tar -xvf openjdk-15.0.1_linux-x64_bin.tar.gz -C /usr/lib/jvm/

Fel kell venni az újonnan létrehozott JDK-a az alternatives közé:

sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk-15.0.1/bin/java 1151

Utána pedig beállíthatjuk a használni kívánt java verziót az alábbi paranccsal:

sudo update-alternatives --config java

Netflix magyar content

A netflix már egy ideje hivatalosan is elérhető Magyarországon. Aztán lokalizálták a honlapot is magyarra, és az utóbbi időben elszaporodtak a magyar feliratok, és ami meglepett a magyar szinkronos filmek is. Bár ezekre az embedded app-okon belül nem nagyon lehet keresni, és a webes felületen se triviális, az alábbi linket megnyitva szépen le lehet szűrni a magyar nyelvű contentet:

https://www.netflix.com/browse/audio

Spotify számcímek a lejátszási listákból

Ha valaki ki szeretné másolni a spotify playlist-ből az előadók és számok címeit, akkor az alábbi javascript kóddal érdemes próbálkozni:

var tracks = document.getElementsByClassName("track-name-wrapper");
for(var i = 0; i < tracks.length; i++)
{
   var trackTitle = tracks[i].getElementsByClassName("tracklist-name")[0].innerHTML;
   var trackArtist = tracks[i].getElementsByClassName("tracklist-row__artist-name-link")[0].innerHTML;
   console.log(trackArtist + " - " + trackTitle);
}