<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Madalin Ignisca</title>
    <description>The latest articles on DEV Community by Madalin Ignisca (@madalinignisca).</description>
    <link>https://dev.to/madalinignisca</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F87285%2F759e96c8-903c-4a17-9124-cc8a5aa0fb30.jpeg</url>
      <title>DEV Community: Madalin Ignisca</title>
      <link>https://dev.to/madalinignisca</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/madalinignisca"/>
    <language>en</language>
    <item>
      <title>How to configure Apache MPM Prefork with mod_php for performance and stability</title>
      <dc:creator>Madalin Ignisca</dc:creator>
      <pubDate>Sun, 26 Mar 2023 09:43:33 +0000</pubDate>
      <link>https://dev.to/madalinignisca/how-to-configure-apache-mpm-prefork-with-modphp-for-performance-and-stability-5a8f</link>
      <guid>https://dev.to/madalinignisca/how-to-configure-apache-mpm-prefork-with-modphp-for-performance-and-stability-5a8f</guid>
      <description>&lt;p&gt;Why you should never use Apache with mod_php in the frontend? Because PHP can take your services to Out Of Memory when somebody really wants to take your website down. Read next about this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YKV6RMRh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://source.unsplash.com/-6SmukZ_w6s" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YKV6RMRh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://source.unsplash.com/-6SmukZ_w6s" alt="Elephants family" width="880" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, I am not against Prefork. I find it actually a decent way of running PHP, but only if it is for handling PHP and no static files. When it is used in combination with another web server (Apache included) and proxying to it only PHP requests, can help to make the usage of many popular web applications even easier, as for example WordPress with some plugins will always set extra directives in .htaccess (consider W3 Total Cache for example).&lt;/p&gt;

&lt;p&gt;But read the following to understand what you are facing leaving it with defaults and allowing it to be hit directly from the Web.&lt;/p&gt;

&lt;p&gt;Next are the default values of how Prefork will be configured.&lt;/p&gt;

&lt;p&gt;StartServers 5&lt;br&gt;
Number of child server processes created at startup&lt;/p&gt;

&lt;p&gt;MinSpareServers 5&lt;br&gt;
Minimum number of idle child server processes&lt;br&gt;
If only &amp;lt;= 5 concurent requests happen, max 5 processes will live, but if serving static files, you’ll see this up after just a few minutes, hours.&lt;/p&gt;

&lt;p&gt;MaxSpareServers 100&lt;br&gt;
Maximum number of idle child server processes&lt;br&gt;
If you are trying to set the value equal to or lower than MinSpareServers, Apache HTTP Server will automatically adjust it to MinSpareServers + 1.&lt;br&gt;
As concurent requests happen, this will become current idle number of processes, and all above 100 idling will be terminated. Why waste cpu time on handling this? I would set it to MaxRequestWorkers value.&lt;/p&gt;

&lt;p&gt;MaxRequestWorkers 256&lt;br&gt;
Maximum number of connections that will be processed simultaneously&lt;br&gt;
Put this on a heavy Magento website and cry if you don’t have  64GB of RAM only for Apache + mod_php 😂&lt;/p&gt;

&lt;p&gt;ListenBacklog 511&lt;br&gt;
Maximum length of the queue of pending connections&lt;br&gt;
Will be useful for queuing more requests if your requests finish really fast (&amp;lt;50ms), but consider going horizontal scalling if you do more than 2-4 concurent requests per real cpu core.&lt;/p&gt;

&lt;p&gt;ServerLimit 256&lt;br&gt;
Upper limit on configurable number of processes&lt;br&gt;
Use this directive only if you need to set MaxRequestWorkers higher than 256 (default). Do not set the value of this directive any higher than what you might want to set MaxRequestWorkers to.&lt;br&gt;
Human explanation: DON’T YOU EVER F**KING THINK TO SET THIS VALUE VERY HIGH!&lt;/p&gt;

&lt;p&gt;What it means is if you are hit on 256 HTTP requests in PHP, you will have 256 processes trying to concurrently do work.&lt;/p&gt;

&lt;p&gt;If you are on a BIG server, saying you have 32, 64 real cores, that means no VCPU as that is already 1 core with 2 threads fighting for resources, it might play nice, as long as you have the amount of RAM that your php request will need to be handled.&lt;/p&gt;

&lt;p&gt;For example, if you have a Symfony app that on each request uses in average 40mb of ram, to be safe, you should have your server with 10 GB of ram only for Apache + PHP and some extra for the OS to behave, so actually over 12 is safe. This is considering that the server will do only Apache + mod_php and not extra databases or others.&lt;/p&gt;

&lt;p&gt;If Apache + mod_php is a must, you should calculate your MaxRequestWorkers to the max it can be handled.&lt;/p&gt;

&lt;p&gt;You must benchmark your application for memory usage first. Speed is not the main issue here, as you must calculate how much RAM will be used on max concurent requests.&lt;/p&gt;

&lt;p&gt;From personal observation, for common popular PHP apps, I observed that WordPress for example averages around 40MB, when using just few plugins (contact form 7 with an extra storage plugin) and a theme like Divi.&lt;/p&gt;

&lt;p&gt;I tweaked many WordPress websites to ditch plugins and move in the theme a lot of extras, like meta directives in the header, Google Analytics etc. directly as html/js/css things as necessary. This always helped getting rid of about 10MB in total from all the extra plugins per each request.&lt;/p&gt;

&lt;p&gt;If you use WooCommerce, you should raise your expectations to 60MB if you avoid any useless plugin (again, try to build in theme templates all you really need, avoiding PHP as much as possible).&lt;/p&gt;

&lt;p&gt;On an plugins abusive WordPress website, think towards 90-120MB per request and accept high costs for hosting it.&lt;/p&gt;

&lt;p&gt;Don’t forget that the the Admin of WordPress in a few operations might hit you with 200-220MB per request, and for an WooCommerce shop, you should already consider separate instance for handling the admin.&lt;/p&gt;

&lt;p&gt;If you are going to have a correctly only PHP handling Apache + mod_php, with values at the limit of your hardware resources dedicated to it, you will actually mimic the same that PHP-FPM does and keeps your app much more safe. PHP-FPM gives you the possibility to use Apache, with Event MPM, and set on both sides limits to protect PHP going OOM (Out Of Memory).&lt;/p&gt;

&lt;p&gt;To be safe you always end up in using a Web Server to access static files and Proxy to the PHP server, either Apache mod_php or PHP-FPM. You decide which one.&lt;/p&gt;

&lt;p&gt;An example for a very small budget WordPress website, on 2GB ram, 2 vcpu:&lt;/p&gt;

&lt;p&gt;Use Nginx to deliver static files and proxy to Varnish (give 100m to it), which will proxy to Apache.&lt;br&gt;
Set MaxRequestWorkers to 4 (keep it safe for performance of requests, use 2 per vcpu or 4 per real core cpu)&lt;br&gt;
Set MaxSpareServers to 4&lt;br&gt;
Set MinSpareServers to 3&lt;br&gt;
Set StartServers to 4&lt;br&gt;
ListenBacklog to max 16 if your HTTP response is &amp;gt; 1s. Adjust depending on how fast are your responses from PHP (not cache)&lt;br&gt;
Set Mysql/MaridDB innodb_buffer_pool_size higher if you have loads of posts (10K+ articles or heavy commented blog) but don’t allocate more than 768MB as it will take over 70% of ram.&lt;br&gt;
Move sessions to Memcached in php settings.&lt;br&gt;
Use any plugin that will set correctly HTTP cache headers so Varnish will serve instant cachable pages. Best might be you making a small plugin that controls cache headers.&lt;br&gt;
All the above might need the hand of an experienced system engineer, but will make any WordPress website incredebly performant at a 5-10$ monthly hosting fee for a cloud server. Might be a good deal paying somebody a few hundreds to save thousands over next couple of years on expensive hosting.&lt;/p&gt;

&lt;p&gt;Conclusion&lt;br&gt;
Always set MaxRequestWorkers to a value you will not go above physical memory available (consider 20% for OS, or 5% if running in a container, Docker or Kubernetes).&lt;/p&gt;

&lt;p&gt;Consider setting MaxSpareServers close to MaxRequestWorkers.&lt;/p&gt;

&lt;p&gt;Set ListenBacklog to a value that it is realistic to queue requests.&lt;/p&gt;

&lt;p&gt;Consider StartServers to a higher value, as forking new processes is also a bit time consuming and hits the cpu.&lt;/p&gt;

&lt;p&gt;A great benfit of using it, is when monitoring system understand Apache HTTPd, but has not idea about PHP-FPM.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>What do I need to be able to develop a web application</title>
      <dc:creator>Madalin Ignisca</dc:creator>
      <pubDate>Thu, 09 Mar 2023 11:00:00 +0000</pubDate>
      <link>https://dev.to/madalinignisca/what-do-i-need-to-be-able-to-develop-a-web-application-4cgl</link>
      <guid>https://dev.to/madalinignisca/what-do-i-need-to-be-able-to-develop-a-web-application-4cgl</guid>
      <description>&lt;p&gt;ChatGPT answered in a way I think it is OK for beginners, but for me sounds a bit outdated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oo6U3Om8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://source.unsplash.com/U7lWyAV-aM8" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oo6U3Om8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://source.unsplash.com/U7lWyAV-aM8" alt="Woman with laptop sitting on a couch" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To develop a web application, you will need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Programming skills: You should have a strong understanding of programming concepts and be able to write code in programming languages commonly used for web development such as HTML, CSS, JavaScript, PHP, and Python.&lt;/li&gt;
&lt;li&gt;Familiarity with web development frameworks: You should know how to use web development frameworks such as AngularJS, React, Vue.js, Spring MVC, Ruby on Rails and Django.&lt;/li&gt;
&lt;li&gt;Knowledge of databases: You need to know how to create, manipulate, and query databases such as MySQL, PostgreSQL, Oracle or MongoDB, that can store and retrieve data for your web application.&lt;/li&gt;
&lt;li&gt;Understanding of web hosting: You should have a basic understanding of web hosting and the various hosting services such as VPS, Cloud Hosting, and Shared Hosting.&lt;/li&gt;
&lt;li&gt;Familiarity with version control: Knowing how to use Git, Mercurial, or SVN for version control will help you manage your code base and collaborate with other developers.&lt;/li&gt;
&lt;li&gt;Web server knowledge: Understanding how web servers work and how you can configure them for your web application’s needs is essential.&lt;/li&gt;
&lt;li&gt;Security: Knowing how to secure your web application, protect data, and prevent attacks is critical to web development.&lt;/li&gt;
&lt;li&gt;Testing and debugging skills: You need to have good testing and debugging skills to ensure the application works as expected and is free of errors or issues.&lt;/li&gt;
&lt;li&gt;Integrated Development Environment (IDE): You will need a development environment that helps you write and test your code. Popular choices are Eclipse, Visual Studio Code, and JetBrains.&lt;/li&gt;
&lt;li&gt;Front-end frameworks and libraries: There are many front-end frameworks and libraries available that make it easy to create user interfaces. Some popular ones include Bootstrap, Foundation, Materialize, and Semantic-UI.&lt;/li&gt;
&lt;li&gt;APIs and web services: If your application needs to interact with other web applications or services, you may need to use APIs or create your web services.&lt;/li&gt;
&lt;li&gt;Agile development methodology: Agile is a popular software development methodology that emphasizes collaboration, flexibility, and incremental development. Knowing how to work in an agile environment can be helpful.&lt;/li&gt;
&lt;li&gt;Project Management: You will need to manage your project, schedule tasks, collaborate with others, and track progress. Popular project management tools include Trello, Asana and Jira.&lt;/li&gt;
&lt;li&gt;Continuous Integration/Continuous Deployment: You may want to automate the deployment of your application, which involves integrating code changes and automatically deploying them into a production environment. Some popular tools for CI/CD are Jenkins, Travis CI, and CircleCI.&lt;/li&gt;
&lt;li&gt;Communication Tools: To work effectively with other developers, you may need to use communication tools like Slack or Microsoft Teams to share updates, code snippets, and collaborate in real-time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What do you think? Twit your opinion: &lt;a href="https://twitter.com/16nsk/status/1633793019978698753?s=61&amp;amp;t=j5oKpW8iw9HLOGOAEu0EbA"&gt;https://twitter.com/16nsk/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>qanda</category>
      <category>web</category>
      <category>application</category>
      <category>development</category>
    </item>
    <item>
      <title>How to reset correct directories and files permissions recursive in linux</title>
      <dc:creator>Madalin Ignisca</dc:creator>
      <pubDate>Wed, 01 Mar 2023 13:30:00 +0000</pubDate>
      <link>https://dev.to/madalinignisca/how-to-reset-correct-directories-and-files-permissions-recursive-in-linux-22j3</link>
      <guid>https://dev.to/madalinignisca/how-to-reset-correct-directories-and-files-permissions-recursive-in-linux-22j3</guid>
      <description>&lt;p&gt;Here is what you need to do to reset the right way all files and directories in Linux. Works also on Macos and FreeBSD.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsource.unsplash.com%2FLUYD2b7MNrg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsource.unsplash.com%2FLUYD2b7MNrg" alt="Sad eggs" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the terminal. Change directory to the directory containing all you want to fix permissions.&lt;/p&gt;

&lt;p&gt;We will fix also the owner, optionally.&lt;/p&gt;

&lt;p&gt;I am setting OTHERS to 0, but if some services need read access to your directories or files, use 755, respectively 644. I am just a bit more restrictive and set correct the groups depending on how they will be used.&lt;/p&gt;

&lt;p&gt;Fix directories:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;find . -type d -exec chmod 750 {} \;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Fix the files:&lt;code&gt;find . -type f -exec chmod 640 {} \;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Fix owner:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;chmod -R [replace with user]:[replace with group] .&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If this are my local files, usually I replace user and group with my username.&lt;/p&gt;

&lt;p&gt;If this are in a web project, on a web server, I set user for the user running the application and group for a group the web server belongs to. I normally ensure both belong to that group. Make note you must set your application platform to do an umask of &lt;code&gt;027&lt;/code&gt;. PHP it is in the ini settings ;-)&lt;/p&gt;

</description>
      <category>linux</category>
      <category>files</category>
      <category>directories</category>
    </item>
    <item>
      <title>The minimal AWS IAM policy for using a bucket with an application</title>
      <dc:creator>Madalin Ignisca</dc:creator>
      <pubDate>Wed, 01 Mar 2023 13:30:00 +0000</pubDate>
      <link>https://dev.to/madalinignisca/the-minimal-aws-iam-policy-for-using-a-bucket-with-an-application-4ncg</link>
      <guid>https://dev.to/madalinignisca/the-minimal-aws-iam-policy-for-using-a-bucket-with-an-application-4ncg</guid>
      <description>&lt;p&gt;This is the minimal policy for an application to access only an AWS S3 bucket in which it would upload / download files and generate signed urls for public access.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AOkqM93F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://source.unsplash.com/BrunIOLQMfQ" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AOkqM93F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://source.unsplash.com/BrunIOLQMfQ" alt="Sad eggs" width="880" height="660"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:GetObject",
                "s3:DeleteObject",
                "s3:GetObjectAcl",
                "s3:PutObjectAcl",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::bucket-name",
                "arn:aws:s3:::bucket-name/*"
            ]
        }
    ]
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a IAM user. Attach the above policy with &lt;code&gt;bucket-name&lt;/code&gt; replaced.&lt;/p&gt;

&lt;p&gt;Enjoy and remember to ignore all people that suggest you attach a give all permissions policy. You don’t give your house keys to strangers, right?&lt;/p&gt;

</description>
      <category>devops</category>
      <category>amazon</category>
      <category>aws</category>
      <category>iam</category>
    </item>
    <item>
      <title>How to: permanent private ip on Multipass on Windows with Hyper-V</title>
      <dc:creator>Madalin Ignisca</dc:creator>
      <pubDate>Tue, 13 Sep 2022 08:22:08 +0000</pubDate>
      <link>https://dev.to/madalinignisca/how-to-permanent-private-ip-on-multipass-on-windows-with-hyper-v-14k6</link>
      <guid>https://dev.to/madalinignisca/how-to-permanent-private-ip-on-multipass-on-windows-with-hyper-v-14k6</guid>
      <description>&lt;p&gt;If you use &lt;a href="https://multipass.run"&gt;Multipass&lt;/a&gt; on Windows with Hyper-V, when running MicroK8S or possible other services, you will notice that on restart of the instance or of the host, MicroK8S will not work for you.&lt;/p&gt;

&lt;p&gt;The issue is that the assignated IP by the default V-switch of Hyper-V that provides also internet connectivity to your instances will always assign a different ip address to the instance.&lt;/p&gt;

&lt;p&gt;Services like Kubernetes, MicroK8S, K3S, expect that the main IP used will not change. Working with changing IP addresses is more complex, but doable.&lt;/p&gt;

&lt;p&gt;For Hyper-V on Windows, there is an easy to use solution: add a secondary V-Swith of type internal. Private can be used, but there will be some additional complexity unnecesarry for using Multipass and MicroK8S.&lt;/p&gt;

&lt;p&gt;So open Hyper-V manager and in the V-Swith section add a new switch called for example &lt;code&gt;multipass&lt;/code&gt;. Keep the name simple, one word and lower case. If not, you will understand yourself later why I advise so.&lt;/p&gt;

&lt;p&gt;Now, when you want to start a new Multipass instance, add the network param. Example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;multipass launch -n microk8s-3 -c 2 -m 4G -d 40G --network name=multipass,mode=manual&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will give us in the instance a secondary ETH1 network interface that waits for us to configure it.&lt;/p&gt;

&lt;p&gt;Do not try to find a way to do dhcpd with the new v-switch, as 99% of cases you will have identical same situation like with main default V-Switch, random ip addresses.&lt;/p&gt;

&lt;p&gt;Now, shell in the instance and add a prepare netplan file:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ip l&lt;/code&gt; --- note the MAC address of ETH1 (I copy it in notepad, to be able to copy paste it later)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo nano /etc/netplan/99-multipass.yaml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Paste the following template and change the MAC and the IP address including netmask to fit your requirements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;network:
    ethernets:
        eth1:
            dhcp4: false
            match:
                macaddress: 52:54:00:d4:44:41
            set-name: eth1
            addresses: [10.0.1.1/16]
    version: 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only important thing is that your machines use identical netmask, so the ip class is not important if it is different from groups of instances to others, as the added vswith is like a dump simple hardware network switch in this case.&lt;/p&gt;

&lt;p&gt;I used 10.0.0.0/16 for the instances, as for some experiments with no tunnel networks for pods Microk8s does 10.1.0.0/16 and I am matching this setup in Hetzner with their private network and automatic routes creation using the hetzner ccm for kubernetes (this is another topic, but for me this way of using Hyper-V allowed me to replicate the Hetzner Cloud locally and experiment without expenses).&lt;br&gt;
Any other projects outside of MicroK8S, I use other ip address classes.&lt;/p&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;code&gt;sudo netplan apply&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will freeze the CLI. If u leave it like this a couple of minutes it will return to your CMD prompt, or if u are impatient, close current windows terminal tab, open another one and shell in the instance again. Test now with :&lt;br&gt;
ip a and you should see ETH1 configured.&lt;/p&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://www.madalin.me/devops/2022/250/multipass-permanent-ip-private-network-hyperv-windows.html"&gt;https://www.madalin.me/devops/2022/250/multipass-permanent-ip-private-network-hyperv-windows.html&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>multipass</category>
      <category>microk8s</category>
      <category>windows</category>
      <category>hyperv</category>
    </item>
    <item>
      <title>OpenEBS on MicroK8S on Hetzner</title>
      <dc:creator>Madalin Ignisca</dc:creator>
      <pubDate>Tue, 13 Sep 2022 08:19:49 +0000</pubDate>
      <link>https://dev.to/madalinignisca/openebs-on-microk8s-on-hetzner-2g63</link>
      <guid>https://dev.to/madalinignisca/openebs-on-microk8s-on-hetzner-2g63</guid>
      <description>&lt;p&gt;Last few months I experimented more and more with all &lt;a href="https://openebs.io/"&gt;OpenEBS&lt;/a&gt; solutions that fit small Kubernetes cluster, using &lt;a href="https://microk8s.io/"&gt;MicroK8S&lt;/a&gt; and &lt;a href="https://hetzner.cloud/?ref=Aj8JqsPS0zhy"&gt;Hetzner Cloud&lt;/a&gt; for a real experience.&lt;/p&gt;

&lt;p&gt;Initially I thought that &lt;a href="https://openebs.io/docs/concepts/jiva"&gt;Jiva&lt;/a&gt; is the best choice, as this backend is easy to maintain and ignoring &lt;a href="https://openebs.io/docs/concepts/cstor"&gt;cStor&lt;/a&gt; as long time maintanance seemed to be too complex for less experienced people.&lt;/p&gt;

&lt;p&gt;So my discoverings made me realise that both cStor and Jiva have their good cases and a decision which one to use from my point of view is simple:&lt;/p&gt;

&lt;p&gt;If you want only a small minimum 3 nodes cluster, with expected storage requirements, possible with no expansion in the future, Jiva is the main choice, as on future upgrades, any manual steps will not be needed.&lt;/p&gt;

&lt;p&gt;Jiva Storage Classes are defined to use for the storage replicas other dynamic storage classes (defined in Jiva Policies), and stright forward is to use &lt;a href="https://openebs.io/docs/concepts/localpv"&gt;OpenEBS localpath&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On Hetzner on most cases I add each of the 3 nodes an expected block volume with estimated initial size and create the Normal Storage Class. I create also a Fast Storage Class that uses the nodes local storage, which Hetzner provides local NVMe drives with best possible IOPS in all the public cloud industry. Anyway, the attached block storage have higher IOPS than premium storage on any other public cloud, and can be used for almost all cases also.&lt;/p&gt;

&lt;p&gt;The idea with attached volumes on Hetzner is that you can increase size on the fly. I manage myself the initial formating with EXT4 and resize in realtime is easy using parted and resize2fs. Usually I use Ansible and run this in parallel on all 3 nodes, but if you are going to try, do it manually a few times than automate, to be fully confident in what you learn.&lt;/p&gt;

&lt;p&gt;For the future, as I do Helm updates to the OpenEBS deployment, with Jiva I don't have any changes to do, volumes being on localpaths in reality, files are natively written to EXT4 file system, it is the easyest mode possible from my point of view.&lt;/p&gt;

&lt;p&gt;Now on Cstor, which on my first attemps, looked more complicated, and I could not find a real business case to use it, as Jiva is fitting my clients and projects almost in all cases. But one day after re-reading the Cstor documentation again and again, &lt;strong&gt;I saw it&lt;/strong&gt;. I can have POOLS of attached block volumes and the out of the box setup of Cstore with &lt;a href="https://openebs.io/docs/concepts/ndm"&gt;OpenEBS Node Disk Manager&lt;/a&gt; made me realise that Cstor is the best choice for a project I started recently, and to which sold storage for applications is a promise to the consumer that is THICK allocated to them, and I don't do oversell (Jiva with localpath would be that by default as it allows overprovisioning by default).&lt;/p&gt;

&lt;p&gt;Both Cstor and Jiva can allow over provisioning or not, depedning on how you tweak it, but in my business case, Cstor would provide "block storage" to Deployments or Stateful Apps that the "disks" can be formated with needed filesystems (some require XFS, some EXT4, some clients kindly asked to have it BTRFS). As choice of filesystem did not bother me, I ended up implementing Cstor as this made clients happy (you do look better when you are flexible and provide the features clients demand, if that doesn't complicates your work).&lt;/p&gt;

&lt;p&gt;The last experiment went with the recent &lt;a href="https://openebs.io/docs/concepts/mayastor"&gt;Mayastor&lt;/a&gt; engine, which I still want to experiment more, but the first real blocker to use it, is that it's minimal requirements only to run are very high, and in most projects using MicroK8S to deploy a Highly Available ecommerce shop for example, to have Mayastor already increases the hardware requirements visible in public cloud costs (yes, for many clients even 100 euros extra per month must be really well justified). As initial benchmarks on how Woocommerce or Magento runs in a MicroK8S environment on Hetzner, I did not see a viable justification for using Mayastor, as with minimal caching done well on both WordPress and Magento side, both load tests provided same results. In both cases, MySQL was deployed using Percona Operator which uses hostpath, so DB access speed was at native NVMe speed of the local disks, and assets were cached by Nginx with cache path set to also a temporary localpath mount. Keeping all uploaded images of products on Mayastor provided zero advantange, simply increased the price for larger cloud servers.&lt;/p&gt;

&lt;p&gt;My conclusion is use Jiva for most cases, Cstor when you want to expand by adding disk nodes. Avoid Mayastor if you are not in the high pay niche.&lt;/p&gt;

&lt;p&gt;If you want to find out how I do all the stuff described above, don't hesitate to buy my book &lt;a href="https://leanpub.com/wp-microk8s/overview"&gt;&lt;strong&gt;WordPress on MicroK8s&lt;/strong&gt;&lt;/a&gt;. At this moment I'm finishing the chapter on storage and soon to publish a new update describe both using Jiva and Cstor on MicroK8S in Hetzner cloud.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://www.madalin.me/devops/2022/249/openebs-on-microk8s-in-hetzner.html"&gt;https://www.madalin.me/devops/2022/249/openebs-on-microk8s-in-hetzner.html&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>openebs</category>
      <category>microk8s</category>
      <category>hetzner</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Let's encrypt certificates, HTTPS on MicroK8s</title>
      <dc:creator>Madalin Ignisca</dc:creator>
      <pubDate>Sat, 20 Feb 2021 05:23:11 +0000</pubDate>
      <link>https://dev.to/madalinignisca/let-s-encrypt-certificates-https-on-microk8s-j7f</link>
      <guid>https://dev.to/madalinignisca/let-s-encrypt-certificates-https-on-microk8s-j7f</guid>
      <description>&lt;p&gt;Took me two days, well, not full days, but two rounds of my 1h hour per day to work on the &lt;a href="https://leanpub.com/wp-microk8s"&gt;WordPress on MicroK8s book&lt;/a&gt;, to figure out how to get Let's encrypt working with default setup of &lt;a href="https://microk8s.io"&gt;MicroK8s&lt;/a&gt; and provision a real certificate for a real domain I use.&lt;/p&gt;

&lt;p&gt;First, examples from &lt;a href="https://cert-manager.io/docs/"&gt;cert-manager&lt;/a&gt; did not work out of the box, but were perfect to get started.&lt;/p&gt;

&lt;p&gt;1st, MicroK8s by enabling dns and ingress addons, simply will give you a well configured Nginx ingress controller and a compatible expected internal dns for network communication between pods.&lt;/p&gt;

&lt;p&gt;To install cert-manager simply running: &lt;code&gt;kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.2.0/cert-manager.yaml&lt;/code&gt; worked, and if you follow along, make sure to look at current latest tag and replace &lt;code&gt;v1.2.0&lt;/code&gt; with what is right now.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you use RBAC on MicroK8s, at this moment I have not yet experimented as it is not in my scope for the moment, but when I will experiment with it, I will update on this.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Next, you need to setup Issuers, and for MicroK8s the example is slightly different from the official docs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make sure you will edit according to your needs the &lt;strong&gt;email&lt;/strong&gt;,&lt;br&gt;
&lt;strong&gt;privateKeySecretRef/name&lt;/strong&gt;!!!&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cert-manager.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIssuer&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;letsencrypt-staging&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;acme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# You must replace this email address with your own.&lt;/span&gt;
    &lt;span class="c1"&gt;# Let's Encrypt will use this to contact you about expiring&lt;/span&gt;
    &lt;span class="c1"&gt;# certificates, and issues related to your account.&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;user@example.com&lt;/span&gt;
    &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://acme-staging-v02.api.letsencrypt.org/directory&lt;/span&gt;
    &lt;span class="na"&gt;privateKeySecretRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Secret resource that will be used to store the account's private key.&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example-issuer-account-key&lt;/span&gt;
    &lt;span class="c1"&gt;# Add a single challenge solver, HTTP01 using nginx&lt;/span&gt;
    &lt;span class="na"&gt;solvers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http01&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;public&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference is in the &lt;em&gt;class&lt;/em&gt;, which needs to be public and not nginx. I guess that nginx is default on official installation of nginx ingress and MicroK8s is configuring it to public. I noticed that in the logs of the nginx ingress pod, the first lines. Also the other difference is in the &lt;em&gt;kind&lt;/em&gt; which on MicroK8s only ClusterIssuer worked, and for the official Issuer example in documentation, simply errors that ingress is unknown for http01.&lt;br&gt;
Not sure on the error, but other examples in the official documentation do use ClusterIssuer, so it might need some updates.&lt;/p&gt;

&lt;p&gt;The above example will configure it for Let's encrypt staging so you can experiment without getting your domain/subdomain blocked while experimenting. To go for production, create one for production. You can and should have both staging and production Issuers.&lt;/p&gt;

&lt;p&gt;Here the production equivalent you need to adapt and apply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cert-manager.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIssuer&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;letsencrypt-prod&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;acme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# You must replace this email address with your own.&lt;/span&gt;
    &lt;span class="c1"&gt;# Let's Encrypt will use this to contact you about expiring&lt;/span&gt;
    &lt;span class="c1"&gt;# certificates, and issues related to your account.&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;user@example.com&lt;/span&gt;
    &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://acme-v02.api.letsencrypt.org/directory&lt;/span&gt;
    &lt;span class="na"&gt;privateKeySecretRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Secret resource that will be used to store the account's private key.&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example-issuer-account-key&lt;/span&gt;
    &lt;span class="c1"&gt;# Add a single challenge solver, HTTP01 using nginx&lt;/span&gt;
    &lt;span class="na"&gt;solvers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http01&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;public&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the Ingress definition, all you need to do is add the annotation like: &lt;code&gt;cert-manager.io/cluster-issuer: letsencrypt-staging&lt;/code&gt; or &lt;code&gt;cert-manager.io/cluster-issuer: letsencrypt-prod&lt;/code&gt;. Use staging to test change to prod if all is ok for you.&lt;/p&gt;

&lt;p&gt;Here is an example for a basic service I use for healthcheck of my home server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;healthcheck-ingress&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;nginx.ingress.kubernetes.io/rewrite-target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/$1&lt;/span&gt;
    &lt;span class="s"&gt;cert-manager.io/cluster-issuer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;letsencrypt-prod"&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;healthcheck.home.madalin.me&lt;/span&gt;
    &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;healthcheck-home-tls&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;healthcheck.home.madalin.me&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
            &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;healthcheck&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;The service can be any web server that responds with HTTP 200 and any content you wish&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Major benefit: it will self renew the cert with 15 days before expiration.&lt;/p&gt;

&lt;p&gt;Also, you can use cert-manager to manage other types of certificates and has some Cloudflare integration which I want to explore next.&lt;/p&gt;

</description>
      <category>microk8s</category>
      <category>kubernetes</category>
      <category>letsencrypt</category>
      <category>certmanager</category>
    </item>
  </channel>
</rss>
