<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[DevOps Dynamics]]></title><description><![CDATA[DevOps Dynamics]]></description><link>https://dhairya137.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1679418784836/cZbv1zyg_.png</url><title>DevOps Dynamics</title><link>https://dhairya137.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 01 Nov 2026 21:30:26 GMT</lastBuildDate><atom:link href="https://dhairya137.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Comprehensive Guide to Ubuntu Server Hardening]]></title><description><![CDATA[Comprehensive Guide for Ubuntu Servers on AWS]]></description><link>https://dhairya137.hashnode.dev/comprehensive-guide-to-ubuntu-server-hardening</link><guid isPermaLink="true">https://dhairya137.hashnode.dev/comprehensive-guide-to-ubuntu-server-hardening</guid><category><![CDATA[server]]></category><category><![CDATA[Security]]></category><category><![CDATA[hardening]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Ubuntu]]></category><category><![CDATA[best practices]]></category><category><![CDATA[serverless]]></category><dc:creator><![CDATA[Dhairya Patel]]></dc:creator><pubDate>Mon, 16 Dec 2024 04:30:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1734273888783/bc3ac456-760e-4a60-b351-84f76aceb6a8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-what-is-server-hardening">What is Server Hardening?</h2>
<p>Server hardening is a comprehensive security strategy that involves reducing a server's vulnerability surface and strengthening its defenses against potential cyber attacks. It's like fortifying a castle – you're building multiple layers of security to protect your valuable assets.</p>
<p>Key aspects of server hardening include:</p>
<ol>
<li><p><strong>Attack Surface Reduction</strong>: Removing or disabling unnecessary services, ports, and software that could be exploited by attackers.</p>
</li>
<li><p><strong>Access Control</strong>: Implementing strict authentication and authorization mechanisms to ensure only legitimate users can access the system.</p>
</li>
<li><p><strong>System Hardening</strong>: Configuring system parameters, file permissions, and kernel settings to enhance security.</p>
</li>
<li><p><strong>Network Security</strong>: Implementing firewalls, intrusion detection systems, and network access controls.</p>
</li>
<li><p><strong>Monitoring and Logging</strong>: Setting up comprehensive logging and monitoring systems to detect and respond to security incidents.</p>
</li>
</ol>
<p>Why is server hardening important?</p>
<ul>
<li><p>Protects against common cyber attacks (brute force, DDoS, etc.)</p>
</li>
<li><p>Helps meet compliance requirements (PCI-DSS, HIPAA, etc.)</p>
</li>
<li><p>Reduces the risk of data breaches</p>
</li>
<li><p>Maintains system integrity and availability</p>
</li>
<li><p>Protects customer data and business reputation</p>
</li>
</ul>
<h2 id="heading-introduction">Introduction</h2>
<p>This guide provides detailed steps to harden an Ubuntu server deployed on AWS, making it more resilient against common security threats. Each section includes not just commands but detailed explanations of why we're implementing specific measures and how they contribute to overall security.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>An AWS account with necessary permissions</p>
</li>
<li><p>A newly created Ubuntu server instance</p>
</li>
<li><p>Basic understanding of Linux commands</p>
</li>
<li><p>SSH access to your server</p>
</li>
</ul>
<h2 id="heading-1-initial-server-setup-and-access-control">1. Initial Server Setup and Access Control</h2>
<h3 id="heading-11-update-system-packages">1.1 Update System Packages</h3>
<p>First and foremost, update all system packages to their latest versions:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Update package index</span>
sudo apt update

<span class="hljs-comment"># Upgrade installed packages</span>
sudo apt upgrade -y

<span class="hljs-comment"># Perform distribution upgrade (includes kernel updates)</span>
sudo apt dist-upgrade -y

<span class="hljs-comment"># Remove unnecessary packages</span>
sudo apt autoremove
</code></pre>
<p><strong>Command Explanation:</strong></p>
<ul>
<li><p><code>apt update</code>: Updates the package index files from repositories, ensuring we know about the latest available versions</p>
</li>
<li><p><code>apt upgrade</code>: Installs newer versions of installed packages while respecting dependencies</p>
</li>
<li><p><code>apt dist-upgrade</code>: Similar to upgrade but handles changing dependencies intelligently and can remove obsolete packages</p>
</li>
<li><p><code>apt autoremove</code>: Removes packages that were installed as dependencies but are no longer needed</p>
</li>
</ul>
<p><strong>Why This Matters:</strong> Outdated software often contains known vulnerabilities that attackers can exploit. Regular updates are your first line of defense against security threats. Many major security breaches have occurred due to unpatched systems.</p>
<h3 id="heading-12-configure-ssh-security">1.2 Configure SSH Security</h3>
<p>SSH (Secure Shell) is your primary access point to the server. As it's often the only public-facing service, securing it properly is crucial for overall server security.</p>
<ol>
<li>Create SSH key pair (on your local machine):</li>
</ol>
<pre><code class="lang-bash"><span class="hljs-comment"># Generate a strong SSH key (Ed25519 is recommended for modern systems)</span>
ssh-keygen -t ed25519 -a 100 -C <span class="hljs-string">"your_email@example.com"</span>

<span class="hljs-comment"># Copy the public key to server</span>
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@your-server-ip
</code></pre>
<ol start="2">
<li>Edit SSH configuration:</li>
</ol>
<pre><code class="lang-bash">sudo nano /etc/ssh/sshd_config
</code></pre>
<ol start="3">
<li>Implement these security measures:</li>
</ol>
<pre><code class="lang-bash"><span class="hljs-comment"># Disable root login - prevents direct root access</span>
PermitRootLogin no

<span class="hljs-comment"># Use SSH Protocol 2 (more secure than Protocol 1)</span>
Protocol 2

<span class="hljs-comment"># Disable password authentication (use keys only)</span>
PasswordAuthentication no

<span class="hljs-comment"># Use only SSH key-based authentication</span>
PubkeyAuthentication yes

<span class="hljs-comment"># Change default port (reduces automated attacks)</span>
Port 2222

<span class="hljs-comment"># Limit login attempts</span>
MaxAuthTries 3

<span class="hljs-comment"># Set idle timeout interval (in seconds)</span>
ClientAliveInterval 300
ClientAliveCountMax 2

<span class="hljs-comment"># Restrict SSH access to specific users</span>
AllowUsers your_username

<span class="hljs-comment"># Disable empty passwords</span>
PermitEmptyPasswords no

<span class="hljs-comment"># Disable X11 forwarding if not needed</span>
X11Forwarding no

<span class="hljs-comment"># Use strong ciphers only</span>
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com

<span class="hljs-comment"># Limit SSH version displayed</span>
DebianBanner no
</code></pre>
<ol start="4">
<li>Restart SSH service:</li>
</ol>
<pre><code class="lang-bash">sudo systemctl restart sshd
</code></pre>
<p><strong>Security Measures Explained:</strong></p>
<ul>
<li><p><strong>Changing Default Port</strong>: While security through obscurity isn't perfect, moving SSH from port 22 reduces automated scanning attacks</p>
</li>
<li><p><strong>Key-based Authentication</strong>: Much stronger than passwords, virtually impossible to brute force</p>
</li>
<li><p><strong>Login Restrictions</strong>: Limiting attempts and timeouts helps prevent brute force attacks</p>
</li>
<li><p><strong>Strong Ciphers</strong>: Using modern encryption algorithms ensures secure communication</p>
</li>
<li><p><strong>User Restrictions</strong>: Limiting SSH access to specific users reduces attack surface</p>
</li>
</ul>
<p><strong>Important Note:</strong> When changing the SSH port:</p>
<ol>
<li><p>Update your AWS security group to allow the new port</p>
</li>
<li><p>Keep your current SSH session open and test the new configuration in a new session before logging out</p>
</li>
<li><p>Update any automation tools or scripts that use SSH</p>
</li>
</ol>
<h2 id="heading-2-firewall-configuration">2. Firewall Configuration</h2>
<h3 id="heading-21-configure-ufw-uncomplicated-firewall">2.1 Configure UFW (Uncomplicated Firewall)</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Install UFW if not present</span>
sudo apt install ufw

<span class="hljs-comment"># Set default policies</span>
sudo ufw default deny incoming
sudo ufw default allow outgoing

<span class="hljs-comment"># Allow SSH (use your custom port if changed)</span>
sudo ufw allow 2222/tcp

<span class="hljs-comment"># Enable UFW</span>
sudo ufw <span class="hljs-built_in">enable</span>

<span class="hljs-comment"># Check status</span>
sudo ufw status verbose
</code></pre>
<h3 id="heading-22-aws-security-group-configuration">2.2 AWS Security Group Configuration</h3>
<ol>
<li><p>Allow only necessary ports in your AWS Security Group</p>
</li>
<li><p>Limit SSH access to specific IP ranges</p>
</li>
<li><p>Remove default 'Allow All' rules</p>
</li>
</ol>
<h2 id="heading-3-system-hardening">3. System Hardening</h2>
<h3 id="heading-31-configure-system-security-parameters">3.1 Configure System Security Parameters</h3>
<p>Edit <code>/etc/sysctl.conf</code>:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Network security</span>
net.ipv4.conf.all.rp_filter=1
net.ipv4.conf.default.rp_filter=1
net.ipv4.icmp_echo_ignore_broadcasts=1
net.ipv4.conf.all.accept_redirects=0
net.ipv4.conf.default.accept_redirects=0
net.ipv4.conf.all.secure_redirects=0
net.ipv4.conf.default.secure_redirects=0
net.ipv6.conf.all.accept_redirects=0
net.ipv6.conf.default.accept_redirects=0
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.default.send_redirects=0
net.ipv4.icmp_ignore_bogus_error_responses=1

<span class="hljs-comment"># Disable IPv6 if not needed</span>
net.ipv6.conf.all.disable_ipv6=1
net.ipv6.conf.default.disable_ipv6=1
net.ipv6.conf.lo.disable_ipv6=1
</code></pre>
<p>Apply changes:</p>
<pre><code class="lang-bash">sudo sysctl -p
</code></pre>
<h3 id="heading-32-secure-user-authentication">3.2 Secure User Authentication</h3>
<ol>
<li>Configure password policies in <code>/etc/login.defs</code>:</li>
</ol>
<pre><code class="lang-bash">PASS_MAX_DAYS 90
PASS_MIN_DAYS 7
PASS_WARN_AGE 7
</code></pre>
<ol start="2">
<li>Install and configure PAM for password complexity:</li>
</ol>
<pre><code class="lang-bash">sudo apt install libpam-pwquality
</code></pre>
<p>Edit <code>/etc/security/pwquality.conf</code>:</p>
<pre><code class="lang-bash">minlen = 12
minclass = 4
maxrepeat = 2
enforce_for_root
</code></pre>
<h3 id="heading-33-file-system-security">3.3 File System Security</h3>
<ol>
<li>Set secure mount options in <code>/etc/fstab</code>:</li>
</ol>
<pre><code class="lang-bash"><span class="hljs-comment"># Add these options to relevant mount points</span>
defaults,noexec,nosuid,nodev
</code></pre>
<ol start="2">
<li>Secure shared memory:</li>
</ol>
<pre><code class="lang-bash"><span class="hljs-built_in">echo</span> <span class="hljs-string">"tmpfs /run/shm tmpfs defaults,noexec,nosuid,nodev 0 0"</span> &gt;&gt; /etc/fstab
</code></pre>
<h2 id="heading-4-monitoring-and-logging">4. Monitoring and Logging</h2>
<h3 id="heading-41-configure-audit-system">4.1 Configure Audit System</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Install auditd</span>
sudo apt install auditd

<span class="hljs-comment"># Configure basic audit rules</span>
cat &lt;&lt; EOF | sudo tee /etc/audit/rules.d/audit.rules
<span class="hljs-comment"># Delete all existing rules</span>
-D

<span class="hljs-comment"># Set buffer size</span>
-b 8192

<span class="hljs-comment"># Monitor file system for changes</span>
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k identity

<span class="hljs-comment"># Monitor network configuration changes</span>
-w /etc/network/ -p wa -k network
-w /etc/sysconfig/ -p wa -k network

<span class="hljs-comment"># Monitor command execution</span>
-w /usr/bin/ -p wa -k commands
-w /usr/sbin/ -p wa -k commands
EOF

<span class="hljs-comment"># Restart audit daemon</span>
sudo systemctl restart auditd
</code></pre>
<h3 id="heading-42-configure-system-logging">4.2 Configure System Logging</h3>
<ol>
<li>Install and configure fail2ban:</li>
</ol>
<pre><code class="lang-bash">sudo apt install fail2ban
</code></pre>
<ol start="2">
<li>Create custom jail configuration:</li>
</ol>
<pre><code class="lang-bash">sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
</code></pre>
<p>Edit <code>/etc/fail2ban/jail.local</code>:</p>
<pre><code class="lang-bash">[sshd]
enabled = <span class="hljs-literal">true</span>
port = 2222
filter = sshd
logpath = /var/<span class="hljs-built_in">log</span>/auth.log
maxretry = 3
bantime = 3600
</code></pre>
<h2 id="heading-5-advanced-security-measures">5. Advanced Security Measures</h2>
<h3 id="heading-51-install-and-configure-rootkit-detection">5.1 Install and Configure Rootkit Detection</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Install rkhunter</span>
sudo apt install rkhunter

<span class="hljs-comment"># Update rkhunter database</span>
sudo rkhunter --update

<span class="hljs-comment"># Perform initial system scan</span>
sudo rkhunter --check
</code></pre>
<h3 id="heading-52-configure-automatic-security-updates">5.2 Configure Automatic Security Updates</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Install unattended-upgrades</span>
sudo apt install unattended-upgrades

<span class="hljs-comment"># Configure automatic updates</span>
sudo dpkg-reconfigure unattended-upgrades
</code></pre>
<h3 id="heading-53-remove-unnecessary-services-and-packages">5.3 Remove Unnecessary Services and Packages</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># List all running services</span>
systemctl list-units --<span class="hljs-built_in">type</span>=service

<span class="hljs-comment"># Disable unnecessary services</span>
sudo systemctl <span class="hljs-built_in">disable</span> &lt;service_name&gt;
sudo systemctl stop &lt;service_name&gt;

<span class="hljs-comment"># Remove unnecessary packages</span>
sudo apt purge telnet rsh-client rsh-server xinetd nis yp-tools tftpd atftpd tftpd-hpa telnetd rsh-server

<span class="hljs-comment"># Remove unnecessary ports and services</span>
sudo apt purge openssh-server vsftpd apache2 samba

<span class="hljs-comment"># Only reinstall services you actually need</span>
sudo apt install openssh-server

<span class="hljs-comment"># Remove unnecessary compilers if not needed</span>
sudo apt purge gcc g++ make automake
</code></pre>
<p><strong>Why Remove Services:</strong></p>
<ul>
<li><p>Each running service is a potential attack vector</p>
</li>
<li><p>Unnecessary services consume system resources</p>
</li>
<li><p>Reduces complexity in security auditing</p>
</li>
<li><p>Minimizes potential vulnerabilities</p>
</li>
</ul>
<h3 id="heading-54-implement-port-knocking">5.4 Implement Port Knocking</h3>
<p>Port knocking adds an additional layer of security by requiring a specific sequence of connection attempts before opening a port:</p>
<ol>
<li>Install port knocking service:</li>
</ol>
<pre><code class="lang-bash">sudo apt install knockd
</code></pre>
<ol start="2">
<li>Configure knockd (/etc/knockd.conf):</li>
</ol>
<pre><code class="lang-bash">[options]
    UseSyslog
    LogFile = /var/<span class="hljs-built_in">log</span>/knockd.log

[openSSH]
    sequence    = 7000,8000,9000
    seq_timeout = 5
    <span class="hljs-built_in">command</span>     = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
    tcpflags    = syn

[closeSSH]
    sequence    = 9000,8000,7000
    seq_timeout = 5
    <span class="hljs-built_in">command</span>     = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
    tcpflags    = syn
</code></pre>
<h3 id="heading-55-secure-shared-memory">5.5 Secure Shared Memory</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Add to /etc/fstab</span>
none /run/shm tmpfs rw,noexec,nosuid,nodev 0 0

<span class="hljs-comment"># Remount all</span>
sudo mount -a
</code></pre>
<h3 id="heading-56-implement-file-integrity-monitoring">5.6 Implement File Integrity Monitoring</h3>
<p>Install and configure AIDE (Advanced Intrusion Detection Environment):</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Install AIDE</span>
sudo apt install aide

<span class="hljs-comment"># Initialize AIDE database</span>
sudo aideinit

<span class="hljs-comment"># Move the new database to the proper location</span>
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db

<span class="hljs-comment"># Configure daily checks</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">'0 5 * * * root /usr/bin/aide --check'</span> | sudo tee -a /etc/crontab
</code></pre>
<h2 id="heading-6-regular-maintenance-and-monitoring">6. Regular Maintenance and Monitoring</h2>
<h3 id="heading-61-implement-regular-security-checks">6.1 Implement Regular Security Checks</h3>
<p>Create a maintenance schedule for:</p>
<ul>
<li><p>System updates</p>
</li>
<li><p>Log review</p>
</li>
<li><p>User account audit</p>
</li>
<li><p>Security scan with tools like rkhunter</p>
</li>
<li><p>Firewall rule review</p>
</li>
<li><p>SSL certificate renewal (if applicable)</p>
</li>
</ul>
<h3 id="heading-62-document-changes">6.2 Document Changes</h3>
<p>Maintain a changelog of:</p>
<ul>
<li><p>System modifications</p>
</li>
<li><p>Security updates</p>
</li>
<li><p>Configuration changes</p>
</li>
<li><p>Incident responses</p>
</li>
</ul>
<h2 id="heading-best-practices-and-tips">Best Practices and Tips</h2>
<ol>
<li><p><strong>Principle of Least Privilege</strong></p>
<ul>
<li><p>Grant minimal necessary permissions</p>
</li>
<li><p>Regularly review and revoke unnecessary access</p>
</li>
<li><p>Use sudo instead of root</p>
</li>
</ul>
</li>
<li><p><strong>Regular Backups</strong></p>
<ul>
<li><p>Implement automated backups</p>
</li>
<li><p>Test backup restoration regularly</p>
</li>
<li><p>Store backups in separate secure location</p>
</li>
</ul>
</li>
<li><p><strong>Monitoring and Alerts</strong></p>
<ul>
<li><p>Set up monitoring for system resources</p>
</li>
<li><p>Configure alerts for suspicious activities</p>
</li>
<li><p>Regularly review security logs</p>
</li>
</ul>
</li>
<li><p><strong>Documentation</strong></p>
<ul>
<li><p>Maintain detailed documentation of configurations</p>
</li>
<li><p>Keep recovery procedures updated</p>
</li>
<li><p>Document incident response plans</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Server hardening is an ongoing process rather than a one-time task. Regular reviews and updates of security measures are essential to maintain a robust security posture. While this guide covers fundamental hardening steps, security requirements may vary based on specific use cases and compliance needs.</p>
<p>Remember to:</p>
<ul>
<li><p>Regularly test security measures</p>
</li>
<li><p>Keep systems and packages updated</p>
</li>
<li><p>Monitor security advisories</p>
</li>
<li><p>Maintain proper documentation</p>
</li>
<li><p>Conduct regular security audits</p>
</li>
</ul>
<h2 id="heading-additional-resources">Additional Resources</h2>
<ul>
<li><p>AWS Security Best Practices Documentation</p>
</li>
<li><p>Ubuntu Security Guide</p>
</li>
<li><p>CIS Benchmarks for Ubuntu Linux</p>
</li>
<li><p>NIST Security Guidelines</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Run a Minecraft Server with Docker Compose]]></title><description><![CDATA[Running minecraft with docker]]></description><link>https://dhairya137.hashnode.dev/run-a-minecraft-server-with-docker-compose</link><guid isPermaLink="true">https://dhairya137.hashnode.dev/run-a-minecraft-server-with-docker-compose</guid><category><![CDATA[Docker]]></category><category><![CDATA[Devops]]></category><category><![CDATA[server]]></category><category><![CDATA[minecraft]]></category><category><![CDATA[Devops articles]]></category><category><![CDATA[self-hosted]]></category><dc:creator><![CDATA[Dhairya Patel]]></dc:creator><pubDate>Sun, 15 Dec 2024 14:26:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1734272738209/1b65c959-ea31-44ab-8c4b-2a150cc3def2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This post will show how to setup your own minecraft game server on docker using docker-compose.</p>
<h2 id="heading-requirements">Requirements</h2>
<p>As we will run minecraft as a docker container, we require docker and docker-compose. You can follow the links to their website for instructions to install it.</p>
<p>In summary it will be the following to install docker:</p>
<pre><code class="lang-bash">curl https://get.docker.com | bash
sudo usermod -aG docker <span class="hljs-variable">$USER</span>
</code></pre>
<p>And the following to install docker-compose:</p>
<pre><code class="lang-bash">sudo curl -L <span class="hljs-string">"https://github.com/docker/compose/releases/download/1.28.5/docker-compose-<span class="hljs-subst">$(uname -s)</span>-<span class="hljs-subst">$(uname -m)</span>"</span> -o /usr/<span class="hljs-built_in">local</span>/bin/docker-compose
sudo chmod +x /usr/<span class="hljs-built_in">local</span>/bin/docker-compose
sudo ln -s /usr/<span class="hljs-built_in">local</span>/bin/docker-compose /usr/bin/docker-compose
</code></pre>
<p>Note that I installed version 1.28.5, in future this may be outdated, so I recommend getting the latest version from their website.</p>
<p>You can verify if docker and docker-compose was installed by running:</p>
<pre><code class="lang-bash">docker --version
docker-compose --version
</code></pre>
<h2 id="heading-configuration">Configuration</h2>
<p>Create the project directory:</p>
<pre><code class="lang-bash">mkdir -p ~/minecraft
</code></pre>
<p>Change to the directory:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> ~/minecraft
</code></pre>
<p>Create the docker-compose.yml file and open it with your editor of choice, then provide this content:</p>
<pre><code class="lang-bash">version: <span class="hljs-string">"3.8"</span>

services:
  minecraft-server:
    image: itzg/minecraft-server:latest
    container_name: minecraft-server
    ports:
      - 25565:25565
    environment:
      SERVER_NAME: <span class="hljs-string">"Minecraft-Server"</span>
      MOTD: <span class="hljs-string">"Forge Minecraft Server"</span>
      EULA: <span class="hljs-string">"TRUE"</span>
      TYPE: <span class="hljs-string">"FORGE"</span>
      VERSION: <span class="hljs-string">"1.17.1"</span>
      MODE: <span class="hljs-string">"survival"</span>
      MEMORY: <span class="hljs-string">"1G"</span>
      LEVEL_TYPE: <span class="hljs-string">"DEFAULT"</span>
      ENABLE_RCON: <span class="hljs-string">"true"</span>
      RCON_PASSWORD: password
      RCON_PORT: 28016
      SERVER_PORT: 25565
      ENABLE_WHITELIST: <span class="hljs-string">"true"</span>
      WHITELIST: <span class="hljs-string">"<span class="hljs-variable">${WHITELISTED_PLAYERS}</span>"</span>
      OPS: <span class="hljs-string">"<span class="hljs-variable">${OPS_PLAYERS}</span>"</span>
      MAX_PLAYERS: 20
      ANNOUNCE_PLAYER_ACHIEVEMENTS: <span class="hljs-string">"true"</span>
      SPAWN_ANIMALS: <span class="hljs-string">"true"</span>
      SPAWN_MONSTERS: <span class="hljs-string">"true"</span>
      PVP: <span class="hljs-string">"true"</span>
      LEVEL: <span class="hljs-string">"cold"</span>
      TZ: <span class="hljs-string">"Asia/Kolkata"</span>
      GUI: <span class="hljs-string">"FALSE"</span>
      MODS_FILE: /extras/mods.txt
      REMOVE_OLD_MODS: <span class="hljs-string">"true"</span>
    restart: unless-stopped
    user: <span class="hljs-string">"<span class="hljs-variable">${UID}</span>:<span class="hljs-variable">${GID}</span>"</span>
    volumes:
      - ./data:/data
      - ./mods.txt:/extras/mods.txt:ro
    logging:
      driver: <span class="hljs-string">"json-file"</span>
      options:
        max-size: <span class="hljs-string">"1m"</span>
</code></pre>
<p>We want the user creating the data directory to be the same user that we are using to avoid permission issues, so we need to create a .env file and set the UID and GID values to the userid and groupid of our user:</p>
<pre><code class="lang-bash">bashCopyecho UID=$(id -u) &gt; .env
<span class="hljs-built_in">echo</span> GID=$(id -g) &gt;&gt; .env
</code></pre>
<p>Yours will most probably differ, but mine look like this:</p>
<pre><code class="lang-bash">cat .env
UID=501
GID=20
</code></pre>
<p>Next we need to set the whitelisted and ops players in WHITELISTED_PLAYERS and OPS_PLAYERS so the end result will look something like this:</p>
<pre><code class="lang-bash">UID=501
GID=20
WHITELISTED_PLAYERS=player2,player3
OPS_PLAYERS=tag1
</code></pre>
<p>If you don't want mods, you can remove MODS_FILE and REMOVE_OLD_MODS entry, but in this case I will be installing the Fast Leaf Decay mod from curseforge.com.</p>
<p>Create the file ~/minecraft/mods.txt and add the jar that you want to add to the file, as example:</p>
<pre><code class="lang-bash">https://media.forgecdn.net/files/3399/353/FastLeafDecay-26.jar
</code></pre>
<h2 id="heading-start-the-minecraft-server">Start the Minecraft Server</h2>
<p>Once that is in place, start the minecraft server:</p>
<pre><code class="lang-bash">docker-compose up -d
</code></pre>
<p>While the server is booting, you can follow the logs:</p>
<pre><code class="lang-bash">docker-compose logs -f minecraft-server
</code></pre>
<p>Once the server is booted, we can verify by using the healthcheck command:</p>
<pre><code class="lang-bash">docker <span class="hljs-built_in">exec</span> -it minecraft-server mc-monitor status
</code></pre>
<p>You should see output similar to:</p>
<pre><code class="lang-bash">localhost:25565 : version=1.17.1 online=0 max=20 motd=<span class="hljs-string">'Forge Minecraft Server'</span>
</code></pre>
<h2 id="heading-minecraft-command-line">Minecraft Command Line</h2>
<p>We can access the minecraft command line with the following:</p>
<pre><code class="lang-bash">docker-compose <span class="hljs-built_in">exec</span> minecraft-server rcon-cli
</code></pre>
<p>To list our whitelisted players:</p>
<pre><code class="lang-bash">/whitelist list
</code></pre>
<p>To restart the server:</p>
<pre><code class="lang-bash">/stop
</code></pre>
<h2 id="heading-install-minecraft">Install Minecraft</h2>
<p>Install minecraft java edition on your PC from: <a target="_blank" href="https://www.minecraft.net/en-us/download">https://www.minecraft.net/en-us/download</a></p>
<p>Once installed, install the forge client: <a target="_blank" href="https://files.minecraftforge.net/net/minecraftforge/forge/">https://files.minecraftforge.net/net/minecraftforge/forge/</a></p>
<p>Open minecraft and you should see the forge under our installations. When we run our forge installation, you'll be able to add your server in the multiplayer section.</p>
<h2 id="heading-note-on-forge-mods">Note on Forge Mods</h2>
<p>For forge mods to work properly you need the mods on the server and client side, for more info, see this issue: <a target="_blank" href="https://github.com/itzg/docker-minecraft-server/issues/1086">https://github.com/itzg/docker-minecraft-server/issues/1086</a></p>
<h2 id="heading-backups">Backups</h2>
<p>Create the directories for the script and backup locations:</p>
<pre><code class="lang-bash">sudo mkdir -p /opt/backups /opt/scripts
</code></pre>
<p>Change the permissions of those directories:</p>
<pre><code class="lang-bash">sudo chown -R <span class="hljs-string">"<span class="hljs-subst">$(id -u)</span>:<span class="hljs-subst">$(id -g)</span>"</span> /opt
</code></pre>
<p>Create the backup script in /opt/scripts/backup_minecraft.sh with the following content:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/usr/bin/env bash</span>
tar -zcvf /opt/backups/minecraft/backup-$(date +%F).tar.gz ~/minecraft
find /opt/backups/minecraft/ -<span class="hljs-built_in">type</span> f -name <span class="hljs-string">"backup-*.tar.gz"</span> -mtime +7 -<span class="hljs-built_in">exec</span> rm {} \;
</code></pre>
<p>This script will compress the minecraft project directory in a archive and delete any backups older than 7 days, which we will run under a cronjob.</p>
<p>Change the permissions of our backup script to make it executable:</p>
<pre><code class="lang-bash">chmod +x /opt/scripts/backup_minecraft.sh
</code></pre>
<p>Open crontab with crontab -e and add the cron expression to backup daily at 00:00:</p>
<pre><code class="lang-bash">00 00 * * * /opt/scripts/backup_minecraft.sh
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Deploying a Django WebSocket Application with Uvicorn, Nginx, and PostgreSQL]]></title><description><![CDATA[Step-by-step guide to deploy Django WebSocket applications in production using Nginx, Uvicorn, and PostgreSQL. Includes SSL setup and best practices.]]></description><link>https://dhairya137.hashnode.dev/deploying-a-django-websocket-application-with-uvicorn-nginx-and-postgresql</link><guid isPermaLink="true">https://dhairya137.hashnode.dev/deploying-a-django-websocket-application-with-uvicorn-nginx-and-postgresql</guid><category><![CDATA[Django]]></category><category><![CDATA[deployment]]></category><category><![CDATA[Devops]]></category><category><![CDATA[production]]></category><category><![CDATA[SocketIO]]></category><category><![CDATA[server]]></category><category><![CDATA[nginx]]></category><category><![CDATA[#unicorn]]></category><category><![CDATA[Gunicorn]]></category><category><![CDATA[Python]]></category><category><![CDATA[AWS]]></category><dc:creator><![CDATA[Dhairya Patel]]></dc:creator><pubDate>Sat, 14 Dec 2024 09:43:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1734169269636/f6827cde-9fdd-49f3-b61b-941db24e3f3b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this comprehensive guide, we'll walk through the process of deploying a Django application that uses WebSockets. We'll set up a production-ready environment using Uvicorn as the ASGI server, Nginx as a reverse proxy, and PostgreSQL as the database.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>An Ubuntu 22.04 server (or similar Linux distribution)</p>
</li>
<li><p>A domain name pointing to your server</p>
</li>
<li><p>Basic knowledge of Django, ASGI, and WebSockets</p>
</li>
<li><p>Root or sudo access to your server</p>
</li>
</ul>
<h2 id="heading-1-initial-server-setup">1. Initial Server Setup</h2>
<p>First, let's update our system and install required packages:</p>
<pre><code class="lang-bash">sudo apt update &amp;&amp; sudo apt upgrade -y
sudo apt install python3-pip python3-venv nginx postgresql postgresql-contrib -y
</code></pre>
<h2 id="heading-2-setting-up-postgresql">2. Setting Up PostgreSQL</h2>
<p>Let's configure our database:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a database user</span>
sudo -u postgres createuser --interactive
<span class="hljs-comment"># Enter username: djangouser</span>
<span class="hljs-comment"># Make as superuser? y</span>

<span class="hljs-comment"># Create a database</span>
sudo -u postgres createdb djangodb

<span class="hljs-comment"># Set password for the user</span>
sudo -u postgres psql
postgres=<span class="hljs-comment"># ALTER USER djangouser WITH PASSWORD 'your_secure_password';</span>
postgres=<span class="hljs-comment"># \q</span>
</code></pre>
<h2 id="heading-3-project-setup">3. Project Setup</h2>
<p>Create a directory for your project and set up a virtual environment:</p>
<pre><code class="lang-bash">mkdir ~/django_websocket
<span class="hljs-built_in">cd</span> ~/django_websocket
python3 -m venv venv
<span class="hljs-built_in">source</span> venv/bin/activate

<span class="hljs-comment"># Install required packages</span>
pip install django channels uvicorn psycopg2-binary <span class="hljs-string">"uvicorn[standard]"</span> websockets
</code></pre>
<h2 id="heading-step-4-creating-and-configuring-a-new-django-project">Step 4 — Creating and Configuring a New Django Project</h2>
<p>With the Python components installed, you can create the actual Django project files.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a new Django project</span>
django-admin startproject socket_demo
</code></pre>
<p>Now, let's configure the project:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Navigate to the project directory</span>
<span class="hljs-built_in">cd</span> socket_demo

<span class="hljs-comment"># Create a Django app</span>
python manage.py startapp chat
</code></pre>
<p>Make changes in your chatbot project now. For demo purpose you can clone my project and start doing deployment.</p>
<p><a target="_blank" href="https://github.com/dhairya137/django-websocket">Django-WebSocket</a></p>
<h2 id="heading-5-asgi-configuration">5. ASGI Configuration</h2>
<p>Create or update your <a target="_blank" href="http://asgi.py"><code>asgi.py</code></a>:</p>
<pre><code class="lang-python"><span class="hljs-comment"># asgi.py</span>

<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> django.core.asgi <span class="hljs-keyword">import</span> get_asgi_application
<span class="hljs-keyword">from</span> channels.routing <span class="hljs-keyword">import</span> ProtocolTypeRouter, URLRouter
<span class="hljs-keyword">from</span> channels.auth <span class="hljs-keyword">import</span> AuthMiddlewareStack
<span class="hljs-keyword">from</span> socket_demo.routing <span class="hljs-keyword">import</span> websocket_urlpatterns

os.environ.setdefault(<span class="hljs-string">'DJANGO_SETTINGS_MODULE'</span>, <span class="hljs-string">'socket_demo.settings'</span>)

application = ProtocolTypeRouter({
    <span class="hljs-string">"http"</span>: get_asgi_application(),
    <span class="hljs-string">"websocket"</span>: AuthMiddlewareStack(
        URLRouter(
            websocket_urlpatterns
        )
    ),
})
</code></pre>
<h2 id="heading-6-test-the-asgi-configuration">6. Test the ASGI Configuration\</h2>
<p>Run this command in socket_demo directory</p>
<pre><code class="lang-bash">uvicorn socket_demo.asgi:application --reload
</code></pre>
<p>Now, open your browser and navigate to <a target="_blank" href="http://localhost:8000">http://localhost:8000</a></p>
<p>You will see chatbot page in your browser. You will see connnected in green box means you have connected to websocket server.</p>
<p>Open in different browser and navigate to <a target="_blank" href="http://localhost:8000">http://localhost:8000</a> and test socket.</p>
<h2 id="heading-6-setting-up-uvicorn-service">6. Setting Up Uvicorn Service</h2>
<p>Create a systemd service file for Uvicorn:</p>
<pre><code class="lang-bash">sudo nano /etc/systemd/system/django-websocket.service
</code></pre>
<p>Add the following content:</p>
<pre><code class="lang-ini"><span class="hljs-section">[Unit]</span>
<span class="hljs-attr">Description</span>=Django WebSocket Application
<span class="hljs-attr">After</span>=network.target

<span class="hljs-section">[Service]</span>
<span class="hljs-attr">User</span>=ubuntu
<span class="hljs-attr">WorkingDirectory</span>=/home/ubuntu/socket_demo
<span class="hljs-attr">Environment</span>=<span class="hljs-string">"PATH=/home/ubuntu/socket_demo/venv/bin"</span>
<span class="hljs-attr">ExecStart</span>=/home/ubuntu/socket_demo/venv/bin/uvicorn socket_demo.asgi:application --host <span class="hljs-number">0.0</span>.<span class="hljs-number">0.0</span> --port <span class="hljs-number">8000</span>
<span class="hljs-attr">Restart</span>=always

<span class="hljs-section">[Install]</span>
<span class="hljs-attr">WantedBy</span>=multi-user.target
</code></pre>
<p>Enable and start the service:</p>
<pre><code class="lang-bash">sudo systemctl <span class="hljs-built_in">enable</span> django-websocket
sudo systemctl start django-websocket
</code></pre>
<h2 id="heading-7-nginx-configuration">7. Nginx Configuration</h2>
<p>Create a new Nginx configuration file:</p>
<pre><code class="lang-bash">sudo nano /etc/nginx/sites-available/django-websocket
</code></pre>
<p>Add the following configuration:</p>
<pre><code class="lang-nginx"><span class="hljs-section">server</span> {
    <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;
    <span class="hljs-attribute">server_name</span> your_domain.com;

    <span class="hljs-attribute">location</span> = /favicon.ico { <span class="hljs-attribute">access_log</span> <span class="hljs-literal">off</span>; <span class="hljs-attribute">log_not_found</span> <span class="hljs-literal">off</span>; }
    <span class="hljs-attribute">location</span> /static/ {
        <span class="hljs-attribute">root</span> /home/ubuntu/django_websocket;
    }

    <span class="hljs-attribute">location</span> / {
        <span class="hljs-attribute">proxy_pass</span> http://127.0.0.1:8000;
        <span class="hljs-attribute">proxy_http_version</span> <span class="hljs-number">1</span>.<span class="hljs-number">1</span>;
        <span class="hljs-attribute">proxy_set_header</span> Host <span class="hljs-variable">$host</span>;
        <span class="hljs-attribute">proxy_set_header</span> X-Real-IP <span class="hljs-variable">$remote_addr</span>;
        <span class="hljs-attribute">proxy_set_header</span> X-Forwarded-For <span class="hljs-variable">$proxy_add_x_forwarded_for</span>;
        <span class="hljs-attribute">proxy_set_header</span> X-Forwarded-Proto <span class="hljs-variable">$scheme</span>;
    }
}
</code></pre>
<p>Enable the site and restart Nginx:</p>
<pre><code class="lang-bash">sudo ln -s /etc/nginx/sites-available/django-websocket /etc/nginx/sites-enabled/
sudo systemctl restart nginx
</code></pre>
<h2 id="heading-8-setting-up-ssl-with-lets-encrypt">8. Setting Up SSL with Let's Encrypt</h2>
<p>Install Certbot and get an SSL certificate:</p>
<pre><code class="lang-bash">sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d your_domain.com
</code></pre>
<h2 id="heading-10-testing-the-websocket-connection">10. Testing the WebSocket Connection</h2>
<p>You can test your WebSocket connection using JavaScript:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> socket = <span class="hljs-keyword">new</span> WebSocket(<span class="hljs-string">'wss://your_domain.com/ws/chat/'</span>);

socket.onopen = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Connection established'</span>);
    socket.send(<span class="hljs-built_in">JSON</span>.stringify({
        <span class="hljs-string">'message'</span>: <span class="hljs-string">'Hello, Server!'</span>
    }));
};

socket.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Message received:'</span>, e.data);
};

socket.onclose = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Connection closed'</span>);
};
</code></pre>
<h2 id="heading-view-logs-of-your-django-application">View logs of your django application</h2>
<pre><code class="lang-bash">sudo journalctl -u django-websocket -f
</code></pre>
<h2 id="heading-restart-your-django-application">Restart your django application</h2>
<pre><code class="lang-bash">sudo systemctl restart django-websocket
</code></pre>
<h2 id="heading-common-issues-and-solutions">Common Issues and Solutions</h2>
<ol>
<li><p><strong>WebSocket Connection Failed</strong></p>
<ul>
<li><p>Verify Nginx WebSocket configuration</p>
</li>
<li><p>Check firewall settings</p>
</li>
<li><p>Ensure SSL is properly configured</p>
</li>
</ul>
</li>
<li><p><strong>Static Files Not Loading</strong></p>
<ul>
<li><p>Run <code>python</code> <a target="_blank" href="http://manage.py"><code>manage.py</code></a> <code>collectstatic</code></p>
</li>
<li><p>Check Nginx static files configuration</p>
</li>
</ul>
</li>
</ol>
<p>If you have any issues or doubts feel free to react out to me!!</p>
]]></content:encoded></item><item><title><![CDATA[Achieving Zero-Downtime Live Migration for Stateful Workloads with Drafter: An In-Depth Guide]]></title><description><![CDATA[Learn to achieve zero-downtime live migration of stateful workloads using Drafter, optimizing cloud resources and minimizing disruptions]]></description><link>https://dhairya137.hashnode.dev/achieving-zero-downtime-live-migration-for-stateful-workloads-with-drafter-an-in-depth-guide</link><guid isPermaLink="true">https://dhairya137.hashnode.dev/achieving-zero-downtime-live-migration-for-stateful-workloads-with-drafter-an-in-depth-guide</guid><category><![CDATA[live migration]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Devops articles]]></category><category><![CDATA[migration]]></category><category><![CDATA[AWS]]></category><category><![CDATA[GCP]]></category><category><![CDATA[Cloud]]></category><category><![CDATA[Cloud Computing]]></category><category><![CDATA[cloud native]]></category><category><![CDATA[Redis]]></category><category><![CDATA[automation]]></category><category><![CDATA[spot instances]]></category><category><![CDATA[ec2]]></category><category><![CDATA[vm]]></category><category><![CDATA[virtual machine]]></category><dc:creator><![CDATA[Dhairya Patel]]></dc:creator><pubDate>Tue, 03 Dec 2024 05:27:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1733203529426/39381760-7bac-4579-abb1-48fa3c3988c8.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-the-challenge-with-stateful-workloads-in-cloud">The Challenge with Stateful Workloads in Cloud</h2>
<p>In today's cloud-native world, managing stateful workloads like databases presents unique challenges. While solutions like Kubernetes excel at handling stateless applications, managing stateful services—especially when it comes to live migration between nodes or even different cloud providers—remains complex.</p>
<p>One particular pain point is running databases on spot instances. While spot instances offer significant cost savings (often 60-90% cheaper than regular instances), their temporary nature poses a major challenge for stateful applications. When a cloud provider reclaims a spot instance, you typically have just minutes to migrate your stateful workload without losing data or experiencing downtime.</p>
<h2 id="heading-enter-drafter-a-new-approach-to-vm-migration">Enter Drafter: A New Approach to VM Migration</h2>
<p><a target="_blank" href="https://github.com/loopholelabs/drafter">Drafter</a>, an open-source project by Loophole Labs, offers a novel solution to this challenge. It's a compute primitive that enables live migration of virtual machines between heterogeneous nodes with effectively zero downtime. Think of it as a way to "lift and shift" running applications—including their entire state—between different machines or even different cloud providers.</p>
<h3 id="heading-key-features-that-make-drafter-special">Key Features That Make Drafter Special</h3>
<ol>
<li><p><strong>Fast Migration</strong>: Drafter achieves migrations in under 100ms within the same data center and around 500ms for cross-continental migrations</p>
</li>
<li><p><strong>Heterogeneous Support</strong>: Works across different cloud providers and CPU models (with PVM support)</p>
</li>
<li><p><strong>No Hardware Virtualization Requirement</strong>: Using PVM (Platform Virtualization Module), Drafter can run on instances without hardware virtualization support</p>
</li>
<li><p><strong>OCI Image Support</strong>: Can run container images directly as VMs without nested virtualization overhead</p>
</li>
</ol>
<h2 id="heading-a-practical-example-migrating-redis-between-gcp-instances">A Practical Example: Migrating Redis Between GCP Instances</h2>
<p>Let's walk through a real-world example of using Drafter to migrate a Redis instance between two Google Cloud Platform (GCP) virtual machines with zero downtime.</p>
<h3 id="heading-the-setup">The Setup</h3>
<h3 id="heading-creating-gcp-instances-in-different-regions">Creating GCP Instances in Different Regions</h3>
<p>First, let's create two instances in different regions: one in us-east1 (South Carolina) and another in europe-west2 (London).</p>
<h4 id="heading-through-gcp-console">Through GCP Console</h4>
<ol>
<li><p><strong>Source Instance (US East)</strong>:</p>
<ul>
<li><p>Go to Google Cloud Console → Compute Engine → VM Instances</p>
</li>
<li><p>Click "CREATE INSTANCE"</p>
</li>
<li><p>Configure:</p>
<pre><code class="lang-plaintext">  Name: redis-source
  Region: us-east1 (South Carolina)
  Zone: us-east1-b
  Machine configuration: c2-standard-4 (4 vCPU, 16 GB memory)
  Boot disk:
    - Operating System: Rocky Linux
    - Version: Rocky Linux 9
    - Size: 20 GB
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Destination Instance (London)</strong>:</p>
<ul>
<li><p>Same process but with:</p>
<pre><code class="lang-plaintext">  Name: redis-destination
  Region: europe-west2 (London)
  Zone: europe-west2-a
</code></pre>
</li>
<li><p>Keep all other settings identical to ensure compatibility</p>
</li>
</ul>
</li>
<li><p><strong>Networking</strong>:</p>
<ul>
<li><p>Create a Firewall rule for migration:</p>
<pre><code class="lang-plaintext">  Name: allow-drafter-migration
  Network: default
  Targets: Specified target tags
  Target tags: drafter-migration
  Source IP ranges: [Source instance IP]/32
  Protocols and ports: tcp:1337
</code></pre>
</li>
<li><p>Add network tags to both instances:</p>
<ul>
<li><p>Edit each instance</p>
</li>
<li><p>Add network tag: <code>drafter-migration</code></p>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<h4 id="heading-using-gcloud-cli">Using gcloud CLI</h4>
<p>For those who prefer command line:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create source instance in US East</span>
gcloud compute instances create redis-source \
    --project=your-project-id \
    --zone=us-east1-b \
    --machine-type=c2-standard-4 \
    --network-interface=network-tier=PREMIUM,subnet=default \
    --maintenance-policy=MIGRATE \
    --provisioning-model=STANDARD \
    --service-account=your-service-account \
    --scopes=https://www.googleapis.com/auth/cloud-platform \
    --tags=drafter-migration \
    --create-disk=auto-delete=yes,boot=yes,device-name=redis-source,image=rocky-linux-9-optimized-gcp,mode=rw,size=20,<span class="hljs-built_in">type</span>=pd-balanced \
    --no-shielded-secure-boot \
    --shielded-vtpm \
    --shielded-integrity-monitoring \
    --reservation-affinity=any

<span class="hljs-comment"># Create destination instance in London</span>
gcloud compute instances create redis-destination \
    --project=your-project-id \
    --zone=europe-west2<span class="hljs-_">-a</span> \
    --machine-type=c2-standard-4 \
    --network-interface=network-tier=PREMIUM,subnet=default \
    --maintenance-policy=MIGRATE \
    --provisioning-model=STANDARD \
    --service-account=your-service-account \
    --scopes=https://www.googleapis.com/auth/cloud-platform \
    --tags=drafter-migration \
    --create-disk=auto-delete=yes,boot=yes,device-name=redis-destination,image=rocky-linux-9-optimized-gcp,mode=rw,size=20,<span class="hljs-built_in">type</span>=pd-balanced \
    --no-shielded-secure-boot \
    --shielded-vtpm \
    --shielded-integrity-monitoring \
    --reservation-affinity=any

<span class="hljs-comment"># Create firewall rule for migration</span>
gcloud compute firewall-rules create allow-drafter-migration \
    --direction=INGRESS \
    --priority=1000 \
    --network=default \
    --action=ALLOW \
    --rules=tcp:1337 \
    --source-tags=drafter-migration \
    --target-tags=drafter-migration
</code></pre>
<h4 id="heading-verifying-connectivity">Verifying Connectivity</h4>
<p>After creating the instances:</p>
<ol>
<li><p><strong>SSH into source instance</strong>:</p>
<pre><code class="lang-bash"> gcloud compute ssh redis-source --zone=us-east1-b
</code></pre>
</li>
<li><p><strong>Test connectivity to destination</strong>:</p>
<pre><code class="lang-bash"> <span class="hljs-comment"># From source instance</span>
 ping $(gcloud compute instances describe redis-destination \
     --zone=europe-west2<span class="hljs-_">-a</span> \
     --format=<span class="hljs-string">'get(networkInterfaces[0].networkIP)'</span>)
</code></pre>
</li>
<li><p><strong>Check latency</strong>:</p>
<pre><code class="lang-bash"> <span class="hljs-comment"># This will help you estimate migration time</span>
 time nc -zv $(gcloud compute instances describe redis-destination \
     --zone=europe-west2<span class="hljs-_">-a</span> \
     --format=<span class="hljs-string">'get(networkInterfaces[0].networkIP)'</span>) 1337
</code></pre>
</li>
</ol>
<p>Now we have two GCP instances running Rocky Linux 9:</p>
<p>Both instances use PVM for cross-machine compatibility and Drafter for orchestrating the migration.</p>
<h3 id="heading-step-1-setting-up-pvm-support">Step 1: Setting Up PVM Support</h3>
<p>First, we need to prepare our instances with PVM support. On both instances:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Update system packages</span>
sudo dnf update -y

<span class="hljs-comment"># Add the PVM repository for Rocky Linux 9 on GCP</span>
sudo dnf config-manager --add-repo <span class="hljs-string">'https://loopholelabs.github.io/linux-pvm-ci/rocky/gcp/repodata/linux-pvm-ci.repo'</span>

<span class="hljs-comment"># Install the PVM kernel package</span>
sudo dnf install -y kernel-6.7.12_pvm_host_rocky_gcp-1.x86_64

<span class="hljs-comment"># Set the PVM kernel as default and configure kernel parameters</span>
sudo grubby --set-default /boot/vmlinuz-6.7.12-pvm-host-rocky-gcp
sudo grubby --copy-default --args=<span class="hljs-string">"pti=off nokaslr lapic=notscdeadline"</span> --update-kernel /boot/vmlinuz-6.7.12-pvm-host-rocky-gcp

<span class="hljs-comment"># Regenerate initramfs</span>
sudo dracut --force --kver 6.7.12-pvm-host-rocky-gcp

<span class="hljs-comment"># Configure kernel module blacklisting</span>
sudo tee /etc/modprobe.d/kvm-intel-amd-blacklist.conf &lt;&lt;EOF
blacklist kvm-intel
blacklist kvm-amd
EOF

<span class="hljs-comment"># Enable PVM module</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"kvm-pvm"</span> | sudo tee /etc/modules-load.d/kvm-pvm.conf

<span class="hljs-comment"># Reboot the instance</span>
sudo reboot

<span class="hljs-comment">#After reboot, verify PVM installation:</span>
<span class="hljs-comment"># Check if PVM module is loaded</span>
lsmod | grep pvm

<span class="hljs-comment"># Expected output should show:</span>
<span class="hljs-comment"># kvm_pvm               57344  0</span>
<span class="hljs-comment"># kvm                 1388544  1 kvm_pvm</span>
```
</code></pre>
<h3 id="heading-step-2-installing-drafter-components">Step 2: Installing Drafter Components</h3>
<p>Drafter consists of several components that work together:</p>
<pre><code class="lang-bash">mkdir -p /tmp/drafter-install

<span class="hljs-comment"># Download and install Drafter binaries</span>
<span class="hljs-keyword">for</span> BINARY <span class="hljs-keyword">in</span> drafter-nat drafter-forwarder drafter-snapshotter drafter-packager drafter-runner drafter-registry drafter-mounter drafter-peer drafter-terminator; <span class="hljs-keyword">do</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Downloading <span class="hljs-variable">$BINARY</span>..."</span>
    curl -L -o <span class="hljs-string">"/tmp/drafter-install/<span class="hljs-variable">${BINARY}</span>"</span> <span class="hljs-string">"https://github.com/loopholelabs/drafter/releases/latest/download/<span class="hljs-variable">${BINARY}</span>.linux-<span class="hljs-subst">$(uname -m)</span>"</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Installing <span class="hljs-variable">$BINARY</span>..."</span>
    sudo install -v <span class="hljs-string">"/tmp/drafter-install/<span class="hljs-variable">${BINARY}</span>"</span> /usr/<span class="hljs-built_in">local</span>/bin
<span class="hljs-keyword">done</span>

<span class="hljs-comment"># Download and install Firecracker with PVM support</span>
<span class="hljs-keyword">for</span> BINARY <span class="hljs-keyword">in</span> firecracker jailer; <span class="hljs-keyword">do</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Downloading <span class="hljs-variable">$BINARY</span>..."</span>
    curl -L -o <span class="hljs-string">"/tmp/drafter-install/<span class="hljs-variable">${BINARY}</span>"</span> <span class="hljs-string">"https://github.com/loopholelabs/firecracker/releases/download/release-main-live-migration-pvm/<span class="hljs-variable">${BINARY}</span>.linux-<span class="hljs-subst">$(uname -m)</span>"</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Installing <span class="hljs-variable">$BINARY</span>..."</span>
    sudo install -v <span class="hljs-string">"/tmp/drafter-install/<span class="hljs-variable">${BINARY}</span>"</span> /usr/<span class="hljs-built_in">local</span>/bin
<span class="hljs-keyword">done</span>

<span class="hljs-comment"># Configure sudo path to include /usr/local/bin</span>
sudo tee /etc/sudoers.d/preserve_path &lt;&lt; EOF
Defaults    secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/<span class="hljs-built_in">local</span>/bin:/usr/<span class="hljs-built_in">local</span>/sbin
EOF

<span class="hljs-comment"># Load NBD module with increased device count</span>
sudo modprobe nbd nbds_max=4096

<span class="hljs-comment"># Clean up temporary files</span>
rm -rf /tmp/drafter-install

<span class="hljs-comment"># Verify installations</span>
<span class="hljs-keyword">for</span> CMD <span class="hljs-keyword">in</span> drafter-nat drafter-forwarder drafter-snapshotter drafter-packager drafter-runner drafter-registry drafter-mounter drafter-peer drafter-terminator firecracker jailer; <span class="hljs-keyword">do</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Checking <span class="hljs-variable">$CMD</span> version..."</span>
    <span class="hljs-variable">$CMD</span> --version || <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$CMD</span> version check not supported"</span>
<span class="hljs-keyword">done</span>
</code></pre>
<h3 id="heading-step-3-preparing-the-redis-vm">Step 3: Preparing the Redis VM</h3>
<p>On the source instance:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Install redis cli</span>
sudo dnf install -y redis

<span class="hljs-comment">### Preparing the Environment</span>
<span class="hljs-comment"># Create working directories</span>
mkdir -p out/blueprint out/package out/instance-0/{overlay,state}

<span class="hljs-comment"># Download DrafterOS with PVM support</span>
curl -Lo out/drafteros-oci.tar.zst <span class="hljs-string">"https://github.com/loopholelabs/drafter/releases/latest/download/drafteros-oci-<span class="hljs-subst">$(uname -m)</span>_pvm.tar.zst"</span>
curl -Lo out/oci-valkey.tar.zst <span class="hljs-string">"https://github.com/loopholelabs/drafter/releases/latest/download/oci-valkey-<span class="hljs-subst">$(uname -m)</span>.tar.zst"</span>

<span class="hljs-comment"># Extract DrafterOS blueprint</span>
drafter-packager --package-path out/drafteros-oci.tar.zst --extract --devices <span class="hljs-string">'[
  {
    "name": "kernel",
    "path": "out/blueprint/vmlinux"
  },
  {
    "name": "disk",
    "path": "out/blueprint/rootfs.ext4"
  }
]'</span>

sudo drafter-packager --package-path out/oci-valkey.tar.zst --extract --devices <span class="hljs-string">'[
  {
    "name": "oci",
    "path": "out/blueprint/oci.ext4"
  }
]'</span>
</code></pre>
<h3 id="heading-step-4-the-migration-process">Step 4: The Migration Process</h3>
<p>The actual migration is remarkably simple:</p>
<ol>
<li><h3 id="heading-on-source-instance"><strong>On source instance:</strong></h3>
</li>
</ol>
<p><strong>Network Setup and Snapshot Creation</strong></p>
<pre><code class="lang-bash"><span class="hljs-comment"># Open new terminal. This will start NAT for network connectivity</span>
sudo drafter-nat --host-interface eth0 <span class="hljs-comment"># Replace eth0 with the network interface you want to route outgoing traffic from the VMs to</span>
</code></pre>
<pre><code class="lang-bash"><span class="hljs-comment"># Open new terminal and run it. This will create initial snapshot</span>
sudo drafter-snapshotter --netns ark0 --cpu-template T2 --devices <span class="hljs-string">'[
  {
    "name": "state",
    "output": "out/package/state.bin"
  },
  {
    "name": "memory",
    "output": "out/package/memory.bin"
  },
  {
    "name": "kernel",
    "input": "out/blueprint/vmlinux",
    "output": "out/package/vmlinux"
  },
  {
    "name": "disk",
    "input": "out/blueprint/rootfs.ext4",
    "output": "out/package/rootfs.ext4"
  },
  {
    "name": "config",
    "output": "out/package/config.json"
  },
  {
     "name": "oci",
     "input": "out/blueprint/oci.ext4",
     "output": "out/package/oci.ext4"
   }
]'</span>
</code></pre>
<p><strong>Starting Redis with Migration Support</strong></p>
<pre><code class="lang-bash"><span class="hljs-comment">#  Open new terminal and run it. This will start Redis with migration enabled</span>
sudo drafter-peer --netns ark0 --raddr <span class="hljs-string">''</span> --laddr <span class="hljs-string">':1337'</span> --devices <span class="hljs-string">'[
  {
    "name": "state",
    "base": "out/package/state.bin",
    "overlay": "out/instance-0/overlay/state.bin",
    "state": "out/instance-0/state/state.bin",
    "blockSize": 65536,
    "expiry": 1000000000,
    "maxDirtyBlocks": 200,
    "minCycles": 5,
    "maxCycles": 20,
    "cycleThrottle": 500000000,
    "makeMigratable": true,
    "shared": false
  },
  {
    "name": "memory",
    "base": "out/package/memory.bin",
    "overlay": "out/instance-0/overlay/memory.bin",
    "state": "out/instance-0/state/memory.bin",
    "blockSize": 65536,
    "expiry": 1000000000,
    "maxDirtyBlocks": 200,
    "minCycles": 5,
    "maxCycles": 20,
    "cycleThrottle": 500000000,
    "makeMigratable": true,
    "shared": false
  },
  {
    "name": "kernel",
    "base": "out/package/vmlinux",
    "overlay": "out/instance-0/overlay/vmlinux",
    "state": "out/instance-0/state/vmlinux",
    "blockSize": 65536,
    "expiry": 1000000000,
    "maxDirtyBlocks": 200,
    "minCycles": 5,
    "maxCycles": 20,
    "cycleThrottle": 500000000,
    "makeMigratable": true,
    "shared": false
  },
  {
    "name": "disk",
    "base": "out/package/rootfs.ext4",
    "overlay": "out/instance-0/overlay/rootfs.ext4",
    "state": "out/instance-0/state/rootfs.ext4",
    "blockSize": 65536,
    "expiry": 1000000000,
    "maxDirtyBlocks": 200,
    "minCycles": 5,
    "maxCycles": 20,
    "cycleThrottle": 500000000,
    "makeMigratable": true,
    "shared": false
  },
  {
    "name": "config",
    "base": "out/package/config.json",
    "overlay": "out/instance-0/overlay/config.json",
    "state": "out/instance-0/state/config.json",
    "blockSize": 65536,
    "expiry": 1000000000,
    "maxDirtyBlocks": 200,
    "minCycles": 5,
    "maxCycles": 20,
    "cycleThrottle": 500000000,
    "makeMigratable": true,
    "shared": false
  },
  {
    "name": "oci",
    "base": "out/package/oci.ext4",
    "overlay": "out/instance-0/overlay/oci.ext4",
    "state": "out/instance-0/state/oci.ext4",
    "blockSize": 65536,
    "expiry": 1000000000,
    "maxDirtyBlocks": 200,
    "minCycles": 5,
    "maxCycles": 20,
    "cycleThrottle": 500000000,
    "makeMigratable": true,
    "shared": false
  }
]'</span>
</code></pre>
<p><strong>Start port forwarding on source vm for redis</strong></p>
<pre><code class="lang-bash"><span class="hljs-comment"># Open new terminal and run it. This will set up port forwarding for Redis</span>
sudo drafter-forwarder --port-forwards <span class="hljs-string">'[
  {
    "netns": "ark0",
    "internalPort": "6379",
    "protocol": "tcp",
    "externalAddr": "127.0.0.1:3333"
  }
]'</span>
</code></pre>
<p><strong>Connect to redis and add dummy data</strong></p>
<pre><code class="lang-bash">redis-cli -p 3333 ping
redis-cli -p 3333

<span class="hljs-comment"># Once connected, you can run these commands:</span>
SET message <span class="hljs-string">"Hello from Rocky Linux!"</span>
SET user <span class="hljs-string">"rockyuser"</span>
SET counter 42

<span class="hljs-comment"># Check the data</span>
KEYS *
GET message
GET user
GET counter
</code></pre>
<h3 id="heading-destination-vm">Destination VM</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Open new terminal. This will start NAT for network connectivity</span>
sudo drafter-nat --host-interface eth0 <span class="hljs-comment"># Replace eth0 with the network interface you want to route outgoing traffic from the VMs to</span>
</code></pre>
<p><strong>Migration Process</strong></p>
<pre><code class="lang-bash"><span class="hljs-comment"># Open new terminal and run it. This will Start migration receiver (replace FIRST_INSTANCE_IP)</span>
sudo drafter-peer --netns ark1 --raddr <span class="hljs-string">'SOURCE_VM_IP:1337'</span> --laddr <span class="hljs-string">''</span> --devices <span class="hljs-string">'[
  {
    "name": "state",
    "base": "out/package/state.bin",
    "overlay": "out/instance-1/overlay/state.bin",
    "state": "out/instance-1/state/state.bin",
    "blockSize": 65536,
    "expiry": 1000000000,
    "maxDirtyBlocks": 200,
    "minCycles": 5,
    "maxCycles": 20,
    "cycleThrottle": 500000000,
    "makeMigratable": true,
    "shared": false
  },
  {
    "name": "memory",
    "base": "out/package/memory.bin",
    "overlay": "out/instance-1/overlay/memory.bin",
    "state": "out/instance-1/state/memory.bin",
    "blockSize": 65536,
    "expiry": 1000000000,
    "maxDirtyBlocks": 200,
    "minCycles": 5,
    "maxCycles": 20,
    "cycleThrottle": 500000000,
    "makeMigratable": true,
    "shared": false
  },
  {
    "name": "kernel",
    "base": "out/package/vmlinux",
    "overlay": "out/instance-1/overlay/vmlinux",
    "state": "out/instance-1/state/vmlinux",
    "blockSize": 65536,
    "expiry": 1000000000,
    "maxDirtyBlocks": 200,
    "minCycles": 5,
    "maxCycles": 20,
    "cycleThrottle": 500000000,
    "makeMigratable": true,
    "shared": false
  },
  {
    "name": "disk",
    "base": "out/package/rootfs.ext4",
    "overlay": "out/instance-1/overlay/rootfs.ext4",
    "state": "out/instance-1/state/rootfs.ext4",
    "blockSize": 65536,
    "expiry": 1000000000,
    "maxDirtyBlocks": 200,
    "minCycles": 5,
    "maxCycles": 20,
    "cycleThrottle": 500000000,
    "makeMigratable": true,
    "shared": false
  },
  {
    "name": "config",
    "base": "out/package/config.json",
    "overlay": "out/instance-1/overlay/config.json",
    "state": "out/instance-1/state/config.json",
    "blockSize": 65536,
    "expiry": 1000000000,
    "maxDirtyBlocks": 200,
    "minCycles": 5,
    "maxCycles": 20,
    "cycleThrottle": 500000000,
    "makeMigratable": true,
    "shared": false
  },
  {
    "name": "oci",
    "base": "out/package/oci.ext4",
    "overlay": "out/instance-1/overlay/oci.ext4",
    "state": "out/instance-1/state/oci.ext4",
    "blockSize": 65536,
    "expiry": 1000000000,
    "maxDirtyBlocks": 200,
    "minCycles": 5,
    "maxCycles": 20,
    "cycleThrottle": 500000000,
    "makeMigratable": true,
    "shared": false
  }
]'</span>
</code></pre>
<p><strong>After migration completes</strong></p>
<pre><code class="lang-bash"><span class="hljs-comment"># Open new terminal and run it. This will  Set up port forwarding on second instance</span>
sudo drafter-forwarder --port-forwards <span class="hljs-string">'[
  {
    "netns": "ark1",
    "internalPort": "6379",
    "protocol": "tcp",
    "externalAddr": "127.0.0.1:3333"
  }
]'</span>
</code></pre>
<p>By doing this you will see in source VM logs that migration is started.</p>
<p><strong>Verify Data on destination VM</strong></p>
<pre><code class="lang-bash"><span class="hljs-comment">### Open new terminal and connect to redis and check instance 1 data is there or not</span>
```bash
redis-cli -p 3333 ping
redis-cli -p 3333

<span class="hljs-comment"># Check the data</span>
KEYS *
GET message
GET user
GET counter
```
</code></pre>
<p>During migration, Drafter:</p>
<ol>
<li><p>Creates a snapshot of the running VM</p>
</li>
<li><p>Transfers the VM state between instances</p>
</li>
<li><p>Resumes the VM on the destination</p>
</li>
<li><p>All while keeping Redis running and accepting connections</p>
</li>
</ol>
<h2 id="heading-the-technical-magic-behind-it">The Technical Magic Behind It</h2>
<p>Drafter achieves this feat through several innovative approaches:</p>
<ol>
<li><p><strong>Custom Firecracker Fork</strong>: Uses an optimized version of AWS's Firecracker VMM</p>
</li>
<li><p><strong>PVM Integration</strong>: Enables migration between different CPU models</p>
</li>
<li><p><strong>Hybrid Migration Strategy</strong>: Uses both pre-copy and post-copy strategies for optimal performance</p>
</li>
<li><p><strong>Block-Level Synchronization</strong>: Efficiently transfers only changed memory blocks</p>
</li>
</ol>
<h2 id="heading-real-world-applications">Real-World Applications</h2>
<p>This technology opens up several interesting possibilities:</p>
<ol>
<li><p><strong>Spot Instance Optimization</strong>: Run databases on spot instances, migrating before termination</p>
</li>
<li><p><strong>Cross-Cloud Migration</strong>: Move workloads between cloud providers without downtime</p>
</li>
<li><p><strong>Maintenance Windows</strong>: Eliminate downtime during hardware maintenance</p>
</li>
<li><p><strong>Geographic Optimization</strong>: Move workloads closer to users during peak times</p>
</li>
</ol>
<h2 id="heading-benchmarks-and-performance">Benchmarks and Performance</h2>
<p>In our Redis migration example:</p>
<ul>
<li><p>Local data center migration: ~100ms</p>
</li>
<li><p>Cross-regional migration: ~500ms</p>
</li>
<li><p>Zero data loss during migration</p>
</li>
<li><p>Continuous availability of Redis service</p>
</li>
</ul>
<h2 id="heading-security-considerations">Security Considerations</h2>
<p>When implementing Drafter in production:</p>
<ol>
<li><p><strong>Network Security</strong>:</p>
<ul>
<li><p>Use TLS for migration traffic</p>
</li>
<li><p>Implement proper firewall rules</p>
</li>
<li><p>Consider VPC peering for cross-region migrations</p>
</li>
</ul>
</li>
<li><p><strong>Access Control</strong>:</p>
<ul>
<li><p>Use proper file permissions for VM images</p>
</li>
<li><p>Implement authentication for migration endpoints</p>
</li>
<li><p>Follow principle of least privilege</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-future-possibilities">Future Possibilities</h2>
<p>The implications of this technology are significant:</p>
<ol>
<li><p><strong>Multi-Cloud Orchestration</strong>: Seamless workload movement between clouds</p>
</li>
<li><p><strong>Cost Optimization</strong>: Dynamic workload placement based on spot pricing</p>
</li>
<li><p><strong>Geographic Redundancy</strong>: Easy movement of stateful services between regions</p>
</li>
<li><p><strong>Development Workflows</strong>: Instant environment replication with state</p>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Drafter represents a significant advancement in managing stateful workloads in cloud environments. By enabling true zero-downtime migration of entire VMs, it solves one of the most challenging aspects of cloud computing: managing state across heterogeneous infrastructure.</p>
<p>Whether you're looking to optimize costs with spot instances, implement cross-cloud strategies, or simply need a better way to manage stateful workloads, Drafter provides a powerful new tool in your cloud architecture toolkit.</p>
<h2 id="heading-resources">Resources</h2>
<ul>
<li><p><a target="_blank" href="https://github.com/loopholelabs/drafter">Drafter GitHub Repository</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/loopholelabs/linux-pvm-ci">PVM Documentation</a></p>
</li>
<li><p><a target="_blank" href="https://youtu.be/HrtX0JrjekE?si=8xyfFzfhxhoCVfF2">KubeCon Talk Demo</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/loopholelabs/drafter#tutorial">Implementation Guide</a></p>
</li>
</ul>
<p>My <a target="_blank" href="https://github.com/dhairya137">Github</a>. Feel free to comment or connect with me for any issue.</p>
<p>Remember: While the setup might seem complex, the benefits of zero-downtime migration and the ability to run stateful workloads on spot instances can lead to significant cost savings and operational improvements in your cloud infrastructure.</p>
]]></content:encoded></item><item><title><![CDATA[Implementing CI/CD with GitHub Actions: A Comprehensive Beginner's Guide: Part 2]]></title><description><![CDATA[Implementing CI/CD with GitHub Actions: A Comprehensive Beginner's Guide]]></description><link>https://dhairya137.hashnode.dev/implementing-cicd-with-github-actions-a-comprehensive-beginners-guide-part-2</link><guid isPermaLink="true">https://dhairya137.hashnode.dev/implementing-cicd-with-github-actions-a-comprehensive-beginners-guide-part-2</guid><category><![CDATA[github-actions]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[ci-cd]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Dhairya Patel]]></dc:creator><pubDate>Sun, 28 May 2023 06:12:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1685254274964/b71b1e24-4574-414e-baa9-183ac19f5272.avif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-introduction-picking-up-where-we-left-off"><strong>Introduction: Picking Up Where We Left Off</strong></h3>
<p>In the first part of our deep dive into GitHub Actions, we laid the foundation by exploring the basics of creating workflows, dissecting workflow files, and getting to grips with the core concepts. With that groundwork in place, we're now ready to explore more advanced topics that will help us make the most out of this powerful tool.</p>
<p>If you're joining us for the first time, I recommend going back to read <a target="_blank" href="https://blog.dhairyapatel.in/implementing-cicd-with-github-actions-a-comprehensive-beginners-guide-part-1"><strong>Part 1</strong></a> for a refresher on GitHub Actions fundamentals. For those who have been following along, get ready to delve deeper!</p>
<p>In this second part of our series, we're going to dive into advanced concepts such as action versioning, job dependencies, the matrix strategy for environment testing, and the handling of environment variables and secrets. After that, we'll guide you through the steps to set up a complete CI/CD pipeline using GitHub Actions, and explore ways to optimize these workflows for better performance and cost-effectiveness.</p>
<p>But that's not all – we'll also look at how you can utilize community actions from the GitHub Marketplace to extend the functionality of your workflows, and guide you through managing and monitoring your workflow runs.</p>
<p>So buckle up, and let's continue our journey to master GitHub Actions!</p>
<h3 id="heading-advanced-concepts-in-github-actions"><strong>Advanced Concepts in GitHub Actions</strong></h3>
<ol>
<li><p><strong>Action Versioning</strong>: Actions in GitHub Actions are versioned, similar to how software packages are versioned. This allows you to use a specific version of an action in your workflow. Typically, an action's version corresponds to a Git tag in the action's repository. Using a specific version of an action can be crucial in maintaining the consistency and reliability of your workflow. For example, if you were using <code>actions/checkout@v2</code>, this means you are using version 2 of the <code>checkout</code> action.</p>
</li>
<li><p><strong>Job Dependencies</strong>: In GitHub Actions, jobs run in parallel by default. However, you can configure jobs to run sequentially by defining dependencies between them. This is done using the <code>needs</code> keyword in the workflow file. If job B needs job A to complete successfully before it can run, you would add <code>needs: A</code> under the definition for job B.</p>
</li>
<li><p><strong>Matrix Strategy</strong>: The matrix strategy in GitHub Actions allows you to run a job on multiple versions of an operating system or programming language at the same time. This is particularly useful when you want to test your application against different environments. The matrix is defined using the <code>strategy.matrix</code> syntax in your workflow file.</p>
</li>
<li><p><strong>Environment Variables and Secrets</strong>: Environment variables are used in GitHub Actions to customize your workflows. They can be set at the workflow, job, or step level. For example, you might set an environment variable to specify the path to a file that one of your steps needs to access.</p>
<ul>
<li><p>Secrets in GitHub Actions are encrypted environment variables that you create in a repository or organization. They are used to store and manage sensitive information, like API tokens or credentials. In a workflow file, you can reference a secret using the <code>secrets</code> context.</p>
</li>
<li><pre><code class="lang-yaml">  <span class="hljs-attr">jobs:</span>
    <span class="hljs-attr">my_job:</span>
      <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
      <span class="hljs-attr">env:</span>
        <span class="hljs-attr">MY_ENV_VAR:</span> <span class="hljs-string">my_value</span>
      <span class="hljs-attr">steps:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">My</span> <span class="hljs-string">step</span>
          <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.MY_SECRET</span> <span class="hljs-string">}}</span>
</code></pre>
<p>  In this example, <code>MY_ENV_VAR</code> is an environment variable with the value "my_value", and <code>MY_SECRET</code> is a secret. Note that secrets are not printed in logs or exposed to untrusted users, making them safe for storing sensitive data.</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-working-with-community-actions">Working with Community Actions</h3>
<p>The GitHub Actions marketplace is a hub where developers can create, share, and utilize actions created by others, which can provide capabilities ranging from integrating with third-party APIs to automating common tasks. Using these actions can save you time and effort, and allow you to leverage the experience and knowledge of other developers.</p>
<p>Here's how you might use a community action in your workflow:</p>
<ol>
<li><p><strong>Finding Actions</strong></p>
<p> You can find actions by browsing the <a target="_blank" href="https://github.com/marketplace?type=actions"><strong>GitHub Actions Marketplace</strong></a>. The marketplace lets you filter by category, and each action listing includes a detailed description, usage examples, and sometimes even video guides.</p>
</li>
<li><p><strong>Using Actions</strong></p>
<p> Once you've found an action you want to use, you can add it to your workflow file by using the <code>uses</code> keyword, followed by the owner, repository, and tag of the action:</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">steps:</span>
   <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">My</span> <span class="hljs-string">Step</span>
     <span class="hljs-attr">uses:</span> <span class="hljs-string">owner/repo@tag</span>
</code></pre>
<p> For example, let's say you want to use the "Setup Python" action to set up a Python environment in your workflow. You can add it to your workflow file like this:</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">steps:</span>
   <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">up</span> <span class="hljs-string">Python</span>
     <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-python@v2</span>
     <span class="hljs-attr">with:</span>
     <span class="hljs-attr">python-version:</span> <span class="hljs-string">'3.x'</span>
</code></pre>
</li>
<li><p><strong>Using Inputs and Outputs</strong></p>
<p> Many actions have inputs that you can set to customize their behavior, and outputs that you can use in subsequent steps. Inputs are set using the <code>with</code> keyword, and outputs can be accessed using the <code>steps</code> context.</p>
<p> Here's an example of using an action with an input and an output:</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">steps:</span>
   <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">My</span> <span class="hljs-string">Step</span>
     <span class="hljs-attr">id:</span> <span class="hljs-string">my_step</span>
     <span class="hljs-attr">uses:</span> <span class="hljs-string">owner/repo@tag</span>
     <span class="hljs-attr">with:</span>
       <span class="hljs-attr">my_input:</span> <span class="hljs-string">'Hello, world!'</span>

   <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">My</span> <span class="hljs-string">Next</span> <span class="hljs-string">Step</span>
     <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"$<span class="hljs-template-variable">{{ steps.my_step.outputs.my_output }}</span>"</span>
</code></pre>
<p> In this example, <code>my_step</code> is using an action that has an input named <code>my_input</code> and an output named <code>my_output</code>.</p>
</li>
<li><p><strong>Creating Your Own Actions</strong></p>
<p> You're not limited to using actions created by others – you can also create your own actions and even share them on the marketplace. Creating an action involves writing a Dockerfile or JavaScript code and packaging it in a GitHub repository. For more information on creating actions, you can check out the <a target="_blank" href="https://docs.github.com/en/actions/creating-actions"><strong>Creating actions</strong></a> guide in the GitHub Docs.</p>
<p> Overall, community actions are a powerful tool that can save you a lot of time and effort when setting up your GitHub Actions workflows. They allow you to stand on the shoulders of giants and leverage the collective knowledge and experience of the GitHub community. Whether you're just getting started with GitHub Actions or you're an experienced user, I encourage you to explore the marketplace and see what actions others have created.</p>
</li>
</ol>
<h3 id="heading-optimizing-github-actions-workflows">Optimizing GitHub Actions Workflows</h3>
<p>When it comes to optimizing GitHub Actions workflows, you'll want to focus on reducing execution time, managing resources efficiently, and improving the maintainability of your workflow files. Here are some strategies you can use:</p>
<ol>
<li><p><strong>Parallelize Your Jobs</strong></p>
<p> GitHub Actions allows you to run jobs in parallel by default. By breaking your workflow into smaller jobs that can run at the same time, you can greatly reduce the total execution time. For example, you could have separate jobs for building, testing, and deploying your application.</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">jobs:</span>
   <span class="hljs-attr">build:</span>
     <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
     <span class="hljs-attr">steps:</span> <span class="hljs-comment"># ...</span>

   <span class="hljs-attr">test:</span>
     <span class="hljs-attr">needs:</span> <span class="hljs-string">build</span>
     <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
     <span class="hljs-attr">steps:</span> <span class="hljs-comment"># ...</span>

   <span class="hljs-attr">deploy:</span>
     <span class="hljs-attr">needs:</span> <span class="hljs-string">test</span>
     <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
     <span class="hljs-attr">steps:</span> <span class="hljs-comment"># ...</span>
</code></pre>
<p> In this example, the <code>test</code> job will only run if the <code>build</code> job succeeds, and the <code>deploy</code> job will only run if the <code>test</code> job succeeds.</p>
</li>
<li><p><strong>Use Caching</strong></p>
<p> Caching can drastically reduce the time it takes to install dependencies. GitHub Actions provides the <code>actions/cache</code> action to cache dependencies between jobs. This action allows you to specify a path (or paths) to cache and a key to uniquely identify the cache.</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">steps:</span>
   <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Cache</span> <span class="hljs-string">pip</span> <span class="hljs-string">packages</span>
     <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/cache@v2</span>
     <span class="hljs-attr">with:</span>
       <span class="hljs-attr">path:</span> <span class="hljs-string">~/.cache/pip</span>
       <span class="hljs-attr">key:</span> <span class="hljs-string">${{</span> <span class="hljs-string">runner.os</span> <span class="hljs-string">}}-pip-${{</span> <span class="hljs-string">hashFiles('**/requirements.txt')</span> <span class="hljs-string">}}</span>
       <span class="hljs-attr">restore-keys:</span> <span class="hljs-string">|</span>
         <span class="hljs-string">${{</span> <span class="hljs-string">runner.os</span> <span class="hljs-string">}}-pip-</span>
</code></pre>
<p> In this example, the cache key is based on the runner OS and a hash of the <code>requirements.txt</code> file. If the file hasn't changed since the last run, GitHub Actions can reuse the cached dependencies instead of installing them from scratch.</p>
</li>
<li><p><strong>Use Self-Hosted Runners</strong></p>
<p> While GitHub provides runners to execute your workflows, you can also host your own runners for more control over the environment and to reduce costs potentially. This is especially useful if you have a dedicated server or a cloud infrastructure already set up.</p>
<p> In part 3 we will set up a custom CICD for deploying full-stack application using a self-hosted runner.</p>
</li>
<li><p><strong>Conditionally Skip Jobs or Steps</strong></p>
<p> You can use the <code>if</code> keyword to conditionally skip jobs or steps based on the result of a previous step, the event that triggered the workflow, or other variables.</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">yamlCopy codejobs:</span>
   <span class="hljs-attr">test:</span>
     <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
     <span class="hljs-attr">steps:</span> <span class="hljs-comment"># ...</span>

   <span class="hljs-attr">deploy:</span>
     <span class="hljs-attr">if:</span> <span class="hljs-string">github.ref</span> <span class="hljs-string">==</span> <span class="hljs-string">'refs/heads/main'</span>
     <span class="hljs-attr">needs:</span> <span class="hljs-string">test</span>
     <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
     <span class="hljs-attr">steps:</span> <span class="hljs-comment"># ...</span>
</code></pre>
<p> In this example, the <code>deploy</code> job will only run if the workflow was triggered by a push to the <code>main</code> branch.</p>
</li>
<li><p><strong>Use Matrix Builds</strong></p>
<p> The matrix feature allows you to run a job on multiple versions of an operating system, with multiple versions of a language, or against multiple environments. This can help you ensure your application works across a variety of conditions.</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">yamlCopy codejobs:</span>
   <span class="hljs-attr">test:</span>
     <span class="hljs-attr">runs-on:</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.os</span> <span class="hljs-string">}}</span>
     <span class="hljs-attr">strategy:</span>
       <span class="hljs-attr">matrix:</span>
         <span class="hljs-attr">os:</span> [<span class="hljs-string">ubuntu-latest</span>, <span class="hljs-string">macos-latest</span>, <span class="hljs-string">windows-latest</span>]
         <span class="hljs-attr">python-version:</span> [<span class="hljs-string">'3.6'</span>, <span class="hljs-string">'3.7'</span>, <span class="hljs-string">'3.8'</span>, <span class="hljs-string">'3.9'</span>]
     <span class="hljs-attr">steps:</span> <span class="hljs-comment"># ...</span>
</code></pre>
<p> In this example, the <code>test</code> job will run on each combination of OS and Python version specified in the matrix.</p>
<p> Remember that each of these strategies has its own trade-offs and should be used judiciously. You should always test your workflows thoroughly to ensure they behave as expected.</p>
</li>
</ol>
<h3 id="heading-real-world-use-case"><strong>Real-World Use Case</strong></h3>
<p><strong>Use Case 1: Automating Code Reviews and PR Merges</strong></p>
<p>GitHub Actions can be used to automate tasks related to pull requests and code reviews. For instance, once a pull request is reviewed and approved by team members, an action can be triggered to automatically merge the PR if the build passes and all checks are successful. This reduces the manual work involved in managing pull requests.</p>
<p><strong>Use Case 2: Continuous Deployment to a Production Environment</strong></p>
<p>In a typical CI/CD pipeline, once changes are committed to the main branch, the code can be automatically built and deployed to a production environment using GitHub Actions. This involves building the code, running tests, possibly deploying to a staging environment for manual checks, and eventually deploying to production. This automated process ensures a faster, reliable, and repeatable deployment process.</p>
<p><strong>Use Case 3: Scheduled Jobs for Regular Maintenance Tasks</strong></p>
<p>GitHub Actions can be scheduled to run at specified times using cron syntax. This can be useful for regular maintenance tasks such as database cleanup, sending regular notifications or reports, or checking the status of external services or APIs.</p>
<p><strong>Use Case 4: Responding to Repository Events</strong></p>
<p>GitHub Actions can be used to respond to various events within a repository. For example, when a new issue is created, an action could be triggered to auto-assign the issue to a particular team member or to add specific labels. Similarly, an action could be triggered when a release is published to automatically create a ZIP file of the source code and attach it to the release.</p>
<p><strong>Use Case 5: Integration with External Services</strong></p>
<p>GitHub Actions can be used to integrate with external services or APIs. For example, once a PR is merged, an action can be triggered to send a message to a Slack channel notifying the team members about the new merge. Similarly, an action could be triggered to update a task in a project management tool like Jira whenever a GitHub issue is closed.</p>
<p>These are just a few examples of how GitHub Actions can be used in real-world scenarios to automate tasks and create efficient CI/CD pipelines. The possibilities are vast and limited only by the needs of your project and your creativity.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In Part 2 of our exploration into GitHub Actions, we've delved deeper into key concepts such as action versioning, job dependencies, matrix strategy, and the use of environment variables and secrets. We also examined how to optimize GitHub Actions workflows, including using community actions and self-hosted runners.</p>
<p>Our journey has shown us that GitHub Actions provide the flexibility and power needed to manage complex CI/CD pipelines efficiently. By taking advantage of the features we've discussed, we can create workflows that not only suit our current needs but are also scalable and maintainable for future developments.</p>
<p>As we wrap up this part, we are more equipped to handle the complexities and demands of real-world software delivery workflows using GitHub Actions.</p>
<p>Stay tuned for Part 3, where we will apply all that we've learned to a real-life scenario: setting up a CI/CD pipeline for a MERN stack application with a self-hosted runner. See you then!</p>
]]></content:encoded></item><item><title><![CDATA[Implementing CI/CD with GitHub Actions: A Comprehensive Beginner's Guide: Part 1]]></title><description><![CDATA[In the ever-evolving world of software development, the need for rapid, reliable, and efficient deployment methods has become more apparent than ever…]]></description><link>https://dhairya137.hashnode.dev/implementing-cicd-with-github-actions-a-comprehensive-beginners-guide-part-1</link><guid isPermaLink="true">https://dhairya137.hashnode.dev/implementing-cicd-with-github-actions-a-comprehensive-beginners-guide-part-1</guid><category><![CDATA[Docker]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[ci-cd]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Dhairya Patel]]></dc:creator><pubDate>Fri, 05 May 2023 06:25:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1683267501931/e924084e-9ae4-4679-a3fb-8c717e5a913e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>In the ever-evolving world of software development, the need for rapid, reliable, and efficient deployment methods has become more apparent than ever. Enter the concept of Continuous Integration and Continuous Deployment (CI/CD), a cornerstone of modern DevOps practices. CI/CD streamlines the process of integrating changes and deploying applications, facilitating faster and more frequent releases. This approach allows development teams to catch bugs earlier, improve software quality, and respond to market demands more swiftly.</p>
<p>While there are many tools available to implement CI/CD like Jenkins, GitLab CICD, Circle CI, Travis CI. in this guide, we'll focus on a powerful and accessible tool: GitHub Actions. Launched in 2019, GitHub Actions has quickly gained popularity due to its seamless integration with the GitHub platform and robust capabilities. It enables you to automate, customize, and execute software development workflows right in your repository. Whether you're looking to automate testing, build a package, or deploy your code, GitHub Actions provides a flexible platform to make it happen.</p>
<p>But why GitHub Actions? Besides the advantage of its tight integration with GitHub, it is incredibly flexible and supports a vast array of languages and platforms. It's perfect for both small projects and large enterprises, and its functionality can be extended with thousands of community-made actions. It also aligns with the "Infrastructure as Code" paradigm, which is a key aspect of DevOps practices.</p>
<h3 id="heading-lets-understand-about-continuous-integration-ci-continuous-deployment-cd-and-continuous-delivery-cd">Let's Understand about <strong>Continuous Integration (CI)</strong>, <strong>Continuous Deployment (CD)</strong>, and <strong>Continuous Delivery (CD).</strong></h3>
<ol>
<li><p><strong>Continuous Integration (CI)</strong>: This is a practice that encourages developers to integrate their code changes into a shared repository early and often, ideally multiple times per day. Once changes are submitted, they are automatically built and tested to catch bugs as quickly as possible. This approach reduces the risk of encountering serious integration issues down the line when preparing for a release.</p>
</li>
<li><p><strong>Continuous Delivery (CD)</strong>: This is an extension of continuous integration. It emphasizes making sure the software is always in a releasable state throughout its lifecycle. This means every change to the system (be it new features, bug fixes, configuration changes, etc.) is releasable and can be deployed to production at any time at the click of a button. This approach ensures that you can decide to release at any time because you always have a version of your software that is ready to go.</p>
</li>
<li><p><strong>Continuous Deployment (CD)</strong>: This is the next step after Continuous Delivery. While Continuous Delivery ensures our code is always in a deployable state, Continuous Deployment takes it a step further by automatically deploying every change that passes the automated testing phase. It means every change that is submitted (and passes the tests) gets deployed to production automatically, resulting in many production deployments every day. With this approach, developers receive immediate feedback on their work, and it's easier to fix bugs or roll back to a previous version if something goes wrong.</p>
</li>
</ol>
<h2 id="heading-understanding-github-actions"><strong>Understanding GitHub Actions</strong></h2>
<p>GitHub Actions is a powerful automation tool that seamlessly integrates with your GitHub repositories. It enables developers to create custom workflows that automate their software development practices. These workflows are event-driven, meaning they can be triggered by a wide variety of events on your GitHub repositories, such as a push, a pull request, or even the creation of a new release.</p>
<p>Workflows are composed of one or more jobs, and each job consists of a series of steps. These steps can be anything from commands or scripts to actions, which are reusable pieces of code. Actions can be created by you, the developer, or you can utilize actions shared by the GitHub community. This makes GitHub Actions incredibly flexible and powerful, as you can leverage the collective knowledge of millions of developers worldwide.</p>
<p>The real power of GitHub Actions lies in the 'Actions' themselves. Actions are small bits of code that perform singular tasks, like posting a message in your team's Slack channel when a new issue is created, or deploying your code to a cloud provider when a new release is made. You can string together these actions to create a workflow that suits your specific use case.</p>
<p>One of the unique selling points of GitHub Actions is its tight integration with GitHub. If you're already hosting your code on GitHub, you can set up a CI/CD pipeline without needing any external tool, which simplifies the setup process and reduces context switching. However, it's not just limited to CI/CD; you can use GitHub Actions to automate almost any task related to your project.</p>
<p>In essence, GitHub Actions takes the manual labor out of many of the repetitive tasks involved in managing a project and, in the process, helps to streamline your workflow, making your development process more efficient and effective.</p>
<h2 id="heading-getting-started-with-github-actions"><strong>Getting Started with GitHub Actions</strong></h2>
<p>To get started with GitHub Actions, you need to have a project hosted on GitHub. For the purpose of this guide, we will assume you already have a repository set up. If not, GitHub provides comprehensive documentation on creating your first repository.</p>
<p><strong>Setting up GitHub Actions</strong></p>
<ol>
<li><p><strong>Navigate to your GitHub repository</strong>: Once inside your repository, click on the 'Actions' tab. This tab is located between the 'Pull requests' and 'Projects' tabs.</p>
</li>
<li><p><strong>Select or create a workflow</strong>: Upon clicking the 'Actions' tab, you'll be taken to a page where GitHub suggests workflows based on the language and tools detected in your repository. You can use one of these pre-configured workflows or set up your own custom workflow. To set up a custom workflow, click on the "set up a workflow yourself" button.</p>
</li>
<li><p><strong>Configure your workflow</strong>: Once you click the "set up a workflow yourself" button, you'll be taken to a YAML file where you can define your workflow. By default, the file will include a basic workflow. This file is committed in the <code>.github/workflows</code> directory in your repository.</p>
</li>
</ol>
<p>The structure of the workflow file would look something like this:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">CI</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">main</span> ]
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">main</span> ]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">a</span> <span class="hljs-string">one-line</span> <span class="hljs-string">script</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">Hello,</span> <span class="hljs-string">world!</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">a</span> <span class="hljs-string">multi-line</span> <span class="hljs-string">script</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">|
        echo Add other actions to build,
        echo test, and deploy your project.</span>
</code></pre>
<p>This is a simple workflow that runs whenever someone pushes to the main branch or opens a pull request to the main branch. The job named 'build' will run on the latest version of Ubuntu. This job consists of three steps: checking out the repository and running two scripts.</p>
<p>This is just a simple example, and as you can see, GitHub Actions is quite versatile. You can add more actions to this workflow to build, test, and even deploy your project.</p>
<p>In the upcoming sections, we'll dive deeper into the structure of a workflow file and how to extend it to perform more complex tasks.</p>
<h2 id="heading-diving-into-the-workflow"><strong>Diving Into the Workflow</strong></h2>
<p>Now that we've created our workflow, let's break down its components to understand it better. A GitHub Actions workflow is a set of automated procedures that you add to your repository. Workflows are defined in YAML files and are composed of one or more jobs. Each job consists of a series of steps that perform individual tasks. Let's take a closer look at each element:</p>
<p><strong>Workflow</strong></p>
<p>A workflow is a configurable automated process that you set up in your repository to build, test, package, release, or deploy any project on GitHub. Workflows can be triggered by GitHub platform events (like a <code>push</code> or <code>pull_request</code> event) or scheduled to run at a specific time.</p>
<p><strong>Workflow File</strong></p>
<p>The workflow file is a YAML file where you define your workflows and their configurations. This file is stored in the <code>.github/workflows</code> directory in your repository.</p>
<p><strong>Jobs</strong></p>
<p>Jobs are a set of steps that execute on the same runner. By default, jobs run in parallel, but you can also configure them to depend on each other, running sequentially. Each job will run in a fresh instance of the virtual environment specified by <code>runs-on</code>.</p>
<p><strong>Steps</strong></p>
<p>Steps are individual tasks that can run commands or actions. Each step in a job executes on the same runner, allowing the steps to share data with each other.</p>
<p>Let's take a closer look at the example workflow we mentioned in the previous section:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">CI</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">main</span> ]
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">main</span> ]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">a</span> <span class="hljs-string">one-line</span> <span class="hljs-string">script</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">Hello,</span> <span class="hljs-string">world!</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">a</span> <span class="hljs-string">multi-line</span> <span class="hljs-string">script</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">|
        echo Add other actions to build,
        echo test, and deploy your project.</span>
</code></pre>
<ul>
<li><p><code>name</code>: This is the name of your workflow. It will show up in the 'Actions' tab of your GitHub repository.</p>
</li>
<li><p><code>on</code>: This is the event that will trigger your workflow. In this case, the workflow runs when there's a <code>push</code> or a <code>pull_request</code> event to the <code>main</code> branch.</p>
</li>
<li><p><code>jobs</code>: This is where you define the jobs that make up your workflow. In this example, we have one job called <code>build</code>.</p>
</li>
<li><p><code>runs-on</code>: This specifies the type of machine to run the job on. Here, we're using the latest version of Ubuntu.</p>
</li>
<li><p><code>steps</code>: This is where you define the steps for your job. Steps can run commands (<code>run</code>) or use an action (<code>uses</code>). In this example, we have three steps: checking out the code, running a one-line script, and running a multi-line script.</p>
</li>
</ul>
<p>This should give you a good starting point for understanding how GitHub Actions workflows are structured. In the next section, we'll look at how to extend this basic workflow to include building, testing, and deploying your application.</p>
<h2 id="heading-conclusion-whats-next"><strong>Conclusion: What's Next?</strong></h2>
<p>We've just scratched the surface of what's possible with GitHub Actions. From setting up our initial workflow to understanding the various components that make up a GitHub Actions configuration, we're now equipped with the foundational knowledge needed to start automating our software development processes.</p>
<p>But this is just the beginning. In the next part of this series, we'll explore how to extend our basic workflow to include building, testing, and deploying an application. We'll dive deeper into the potential of GitHub Actions, exploring topics like matrix builds, caching, and using community actions.</p>
<p>Remember, the journey to mastering any tool is a step-by-step process. As we continue to explore GitHub Actions in future posts, I encourage you to experiment on your own. Try setting up a basic workflow in one of your projects. As you get comfortable, gradually introduce more complex actions and observe how they improve your development process.</p>
<p>GitHub Actions offers a powerful and flexible platform for CI/CD and general automation. By mastering it, you'll be well on your way to creating more efficient, reliable, and robust development workflows.</p>
<p>Update - <a target="_blank" href="https://blog.dhairyapatel.in/implementing-cicd-with-github-actions-a-comprehensive-beginners-guide-part-2">Part 2</a></p>
]]></content:encoded></item><item><title><![CDATA[Why Docker is Essential for Modern DevOps Practices]]></title><description><![CDATA[Why Docker is Essential for Modern DevOps Practices. DevOps for beginners. Docker for beginners guide. Basic understanding of Docker.]]></description><link>https://dhairya137.hashnode.dev/why-docker-is-essential-for-modern-devops-practices</link><guid isPermaLink="true">https://dhairya137.hashnode.dev/why-docker-is-essential-for-modern-devops-practices</guid><category><![CDATA[Docker]]></category><category><![CDATA[Devops]]></category><category><![CDATA[docker images]]></category><category><![CDATA[Devops articles]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Dhairya Patel]]></dc:creator><pubDate>Mon, 20 Mar 2023 11:34:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679311414971/28cd9f94-bd82-432f-a2ff-61e618dcab34.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a DevOps engineer, you're constantly looking for ways to improve your software delivery pipeline and accelerate your practices. One tool that's become essential for modern DevOps practices is Docker. In this post, we'll explore why Docker is so important and how you can use it to improve your DevOps workflows.</p>
<h3 id="heading-what-is-docker">What is Docker?</h3>
<p>Docker is a containerization platform that allows developers to package their applications along with their dependencies into portable units called containers. Containers are lightweight and can run anywhere, from a developer's laptop to a production server.</p>
<h3 id="heading-why-docker-is-essential-for-modern-devops-practices">Why Docker is Essential for Modern DevOps Practices</h3>
<p>Docker has become an essential tool for modern DevOps practices. Here are some reasons why:</p>
<ul>
<li><strong>Portability</strong></li>
</ul>
<p>Docker containers are highly portable and can run on any platform, including Windows, Linux, and macOS. This makes it easy for developers to package their applications along with their dependencies and run them in any environment.</p>
<ul>
<li><strong>Consistency</strong></li>
</ul>
<p>Docker containers provide a consistent environment for running applications. Developers can package their applications and dependencies together, ensuring that the application will run the same way on any system.</p>
<ul>
<li><strong>Scalability</strong></li>
</ul>
<p>Docker containers can be easily scaled up or down to meet demand. This makes it easy to handle traffic spikes and reduces the need for overprovisioning resources.</p>
<ul>
<li><strong>Speed</strong></li>
</ul>
<p>Docker containers can be quickly created and deployed, making it easy to iterate and deploy applications quickly. This is especially useful for teams that practice continuous integration and continuous deployment.</p>
<ul>
<li><strong>Security</strong></li>
</ul>
<p>Docker containers provide a layer of isolation between the host system and the container. This reduces the risk of a security breach, as any vulnerabilities in the container are isolated from the host system.</p>
<h3 id="heading-best-practices-for-using-docker-in-devops">Best Practices for Using Docker in DevOps</h3>
<p>Here are some best practices for using Docker in DevOps:</p>
<ul>
<li><strong>Use Docker Compose</strong></li>
</ul>
<p>Docker Compose is a tool for defining and running multi-container Docker applications. It allows you to define your application's services, networks, and volumes in a single file, making it easy to manage complex applications.</p>
<ul>
<li><strong>Keep Your Images Small</strong></li>
</ul>
<p>When building your Docker image, it's important to use the smallest possible base image. This reduces the attack surface and makes your image more lightweight and portable.</p>
<ul>
<li><strong>Use Environment Variables</strong></li>
</ul>
<p>When running your Docker container, it's important to use environment variables to pass configuration information to your application. This makes it easy to switch between different environments, such as development, staging, and production.</p>
<h2 id="heading-heres-a-practical-guide-for-getting-started-with-docker-using-nginx">Here's a practical guide for getting started with Docker using nginx</h2>
<ol>
<li><p>Install Docker: Follow the installation instructions for your operating system to install Docker on your machine. Once installed, start the Docker service to begin working with Docker.</p>
</li>
<li><p>Create a Dockerfile: To create a Dockerfile for an nginx demo site, start by choosing a base image that includes nginx. For example, you can use the official nginx image from Docker Hub as your base image. Then, copy your demo site files into the container using the <code>COPY</code> command in your Dockerfile. Here's an example Dockerfile:</p>
<pre><code class="lang-markdown"> FROM nginx
 COPY . /usr/share/nginx/html
</code></pre>
<p> This Dockerfile uses the official nginx image as the base image and copies the contents of the current directory into the <code>/usr/share/nginx/html</code> directory inside the container.</p>
</li>
<li><p>Build a Docker image: Use the <code>docker build</code> command to build a Docker image from your Dockerfile. For example:</p>
<pre><code class="lang-makefile"> docker build -t my-nginx-site .
</code></pre>
<p> This command will build a Docker image with the tag <code>my-nginx-site</code> using the Dockerfile located in the current directory.</p>
</li>
<li><p>Run a Docker container: Use the <code>docker run</code> command to start a Docker container based on your Docker image. For example:</p>
<pre><code class="lang-markdown"> docker run -p 8080:80 my-nginx-site
</code></pre>
<p> This command will start a Docker container based on the <code>my-nginx-site</code> image, mapping port 8080 on the host machine to port 80 inside the container.</p>
</li>
<li><p>Test the nginx demo site: Open a web browser and navigate to <a target="_blank" href="http://localhost:8080"><code>http://localhost:8080</code></a> to view the nginx demo site running inside the Docker container.</p>
<p> By following these steps, you can quickly create and run a Docker container for an nginx demo site. This is just a basic example, but you can use Docker to containerize a wide variety of applications and services, and take advantage of the many benefits that Docker provides for modern DevOps practices.</p>
</li>
</ol>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In conclusion, Docker is a powerful tool for DevOps engineers, providing a standardized way to package and deploy applications. By automating deployment, streamlining development, simplifying testing, and scaling applications, DevOps engineers can use Docker to improve their software delivery pipeline and accelerate their DevOps practices. By following best practices like using Docker Compose, keeping your images small, and using a registry, you can unleash the power of Docker and take your DevOps practices to the next level.</p>
]]></content:encoded></item></channel></rss>