<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Projects on Home</title><link>/categories/projects/</link><description>Recent content in Projects on Home</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sat, 22 Nov 2025 00:00:00 +0000</lastBuildDate><atom:link href="/categories/projects/" rel="self" type="application/rss+xml"/><item><title>Compound Simulation – Exploring Portfolio Uncertainty</title><link>/2025/compound-simulation-exploring-portfolio-uncertainty/</link><pubDate>Sat, 22 Nov 2025 00:00:00 +0000</pubDate><guid>/2025/compound-simulation-exploring-portfolio-uncertainty/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Financial planning is often built on a deterministic story: &lt;em&gt;“If I invest X € each month at 5 % per year, I’ll have Y € in 20 years.”&lt;/em&gt; But real markets are anything but deterministic. Price fluctuations, volatility, and unexpected shocks can significantly change outcomes.&lt;/p&gt;
&lt;p&gt;This new tool builds on the foundation of the &lt;a href="https://michard.io/2025/compound-simulation-exploring-portfolio-uncertainty/"&gt;Compound Interest Calculator&lt;/a&gt;, which takes a deterministic view of capital growth. This new tool introduces a probabilistic perspective by using Monte Carlo simulation to explore a spectrum of possible portfolio trajectories based on the users assumptions. Instead of a single projected curve, it generates a fan chart that visualizes uncertainty bands, the likelihood of reaching specific targets, and how sensitive outcomes are to your savings rate.&lt;/p&gt;
&lt;p&gt;This is not a crystal ball. It’s a scenario explorer — a way to understand how uncertain markets shape financial trajectories.&lt;/p&gt;
&lt;p&gt;You can try the web tool here:&lt;br&gt;
&lt;a href="https://compound-simulation.michard.io/" target="_blank" rel="noreferrer" class="download"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentcolor" class="clip" width="13" height="13" style="vertical-align: middle; margin-right: .3rem;"&gt;
&lt;path d="M352 0c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9L370.7 96 201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L416 141.3l41.4 41.4c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6V32c0-17.7-14.3-32-32-32H352zM80 32C35.8 32 0 67.8 0 112V432c0 44.2 35.8 80 80 80H400c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32V432c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z"&gt;&lt;/path&gt;
&lt;/svg&gt;Open the Compound Simulation Tool&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="what-the-tool-does"&gt;What the Tool Does&lt;/h2&gt;
&lt;p&gt;The simulation is based on a small set of input parameters — initial capital, monthly contributions, expected return (μ), volatility (σ), investment horizon, and optionally a target value.&lt;/p&gt;
&lt;p&gt;Using these assumptions, the app runs multiple simulation paths and provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fan chart of portfolio trajectories – median, expected path, and uncertainty bands (percentiles).&lt;/li&gt;
&lt;li&gt;Distribution of end values – showing the spread of possible outcomes at the horizon.&lt;/li&gt;
&lt;li&gt;Target probability – the likelihood of reaching (or exceeding) your goal.&lt;/li&gt;
&lt;li&gt;Stress test – a downside scenario with halved returns and doubled volatility.&lt;/li&gt;
&lt;li&gt;Savings elasticity – the effect on median outcomes from marginally increasing monthly contributions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This shifts the focus from a single deterministic projection to a probabilistic view of potential futures.&lt;/p&gt;
&lt;h2 id="how-to-use-it-online"&gt;How to Use It Online&lt;/h2&gt;
&lt;p&gt;Running the hosted app is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open the simulation tool.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enter your core parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Initial Capital [€]&lt;/li&gt;
&lt;li&gt;Monthly Savings [€]&lt;/li&gt;
&lt;li&gt;Annual Return μ&lt;/li&gt;
&lt;li&gt;Volatility σ&lt;/li&gt;
&lt;li&gt;Time Horizon (years)&lt;/li&gt;
&lt;li&gt;(Optional) Define a target and target date.&lt;/li&gt;
&lt;li&gt;(Optional) Enable the Stress Test to explore adverse scenarios.&lt;/li&gt;
&lt;li&gt;(Optional) Add a Savings Elasticity Increment (e.g. +€50/month) to assess sensitivity.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The output includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A fan chart showing uncertainty over time.&lt;/li&gt;
&lt;li&gt;A distribution histogram of end values.&lt;/li&gt;
&lt;li&gt;A target probability indicator.&lt;/li&gt;
&lt;li&gt;A sensitivity summary for additional contributions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="run-locally"&gt;Run Locally&lt;/h2&gt;
&lt;p&gt;If you want to host or modify the simulation app yourself:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clone the repository&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/smichard/compound_simulation
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;Navigate to the project directory&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; compound_simulation
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="3"&gt;
&lt;li&gt;Build the container image
Run the following command to build an image named compound_simulation_app (or choose any name you prefer):&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman build -t compound_simulation_app -f Containerfile .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This command uses the provided Containerfile to set up the environment, including all required R packages for running the Shiny app.&lt;/p&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Start the app locally&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman run --rm -p 3838:3838 compound_simulation_app
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This launches a container and maps port 3838 inside the container to the same port on your host system.&lt;/p&gt;
&lt;ol start="5"&gt;
&lt;li&gt;Access the app in your browser&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;http://localhost:3838/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should now see the Compound Simulation app running locally.&lt;/p&gt;
&lt;h2 id="why-this-matters"&gt;Why This Matters&lt;/h2&gt;
&lt;p&gt;Uncertainty is real — any deterministic projection hides the range of plausible outcomes. Markets fluctuate, assumptions shift, and unexpected events can have a lasting impact. Probabilistic thinking helps make better decisions by accounting for both upside and downside scenarios instead of focusing on a single expected path.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Goal probability provides a tangible measure: “What are the chances I’ll reach €X by year Y?”&lt;/li&gt;
&lt;li&gt;Savings elasticity reveals whether increasing contributions might be more effective than simply chasing higher returns.&lt;/li&gt;
&lt;li&gt;For investors, educators, or anyone exploring financial planning under uncertainty, this tool complements the &lt;a href="https://michard.io/2025/compound-simulation-exploring-portfolio-uncertainty/"&gt;Compound Interest Calculator&lt;/a&gt; by adding a probabilistic layer to previously deterministic projections.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;Compound Simulation brings uncertainty to the forefront. By combining Monte Carlo simulation, sensitivity analysis, and clear visualizations, it highlights that financial projections aren’t fixed—they’re distributions. The tool helps explore not only expected growth but also the range of potential outcomes and their probabilities.&lt;/p&gt;
&lt;p&gt;It can be used as a teaching aid, a scenario testing environment, or a personal planning companion. And since it’s open source, you can easily adapt it to your own assumptions, risk parameters, or visualization preferences.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Related Post - Compound Interest Calculator - &lt;a href="https://michard.io/2025/compound-simulation-exploring-portfolio-uncertainty/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Web App - &lt;a href="https://compound-simulation.michard.io/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GitHub Repository - &lt;a href="https://github.com/smichard/compound_simulation"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Compound Interest Calculator – Visualizing Capital Growth</title><link>/2025/compound-interest-calculator-visualizing-capital-growth/</link><pubDate>Sat, 25 Oct 2025 00:00:00 +0000</pubDate><guid>/2025/compound-interest-calculator-visualizing-capital-growth/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Understanding how capital develops over time is a cornerstone of financial planning. While compound interest formulas are straightforward on paper, the interplay between savings rate, interest, and time is often less intuitive. To address this, I built the Compound Interest Calculator – a Shiny app that visualizes how capital grows based on different input parameters.&lt;/p&gt;
&lt;p&gt;The tool illustrates not only the raw numbers but also the dynamics of savings and interest accumulation. It allows you to model different scenarios, compare strategies, and identify milestones such as when your savings generate more returns than your yearly contributions.&lt;/p&gt;
&lt;p&gt;The idea for this tool was sparked after watching a &lt;a href="https://www.youtube.com/watch?v=F3Q-1W4QEVI"&gt;YouTube video&lt;/a&gt; that explains why the first €100,000 is such a critical milestone in building wealth. There are many excellent videos and articles that explore this concept in depth. But to make it truly tangible — and to experiment interactively with savings rates, interest assumptions, and time horizons — I decided to build a tool of my own. The result: a simple, hands-on way to see compound interest in action and explore how various strategies may impact the growth of your capital over time.&lt;/p&gt;
&lt;p&gt;You can try the web tool here:&lt;br&gt;
&lt;a href="https://compound-calculator.michard.io" target="_blank" rel="noreferrer" class="download"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentcolor" class="clip" width="13" height="13" style="vertical-align: middle; margin-right: .3rem;"&gt;
&lt;path d="M352 0c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9L370.7 96 201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L416 141.3l41.4 41.4c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6V32c0-17.7-14.3-32-32-32H352zM80 32C35.8 32 0 67.8 0 112V432c0 44.2 35.8 80 80 80H400c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32V432c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z"&gt;&lt;/path&gt;
&lt;/svg&gt; Open the Compound Interest Calculator &lt;/a&gt;&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;The easiest way to use the calculator is online (see above).&lt;br&gt;
If you want to run it locally, you’ll need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An environment capable of running containers, e.g. Podman, or&lt;/li&gt;
&lt;li&gt;R with Shiny installed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="getting-started"&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;The online version is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the Compound Interest Calculator.&lt;/li&gt;
&lt;li&gt;Enter your investment parameters, e.g. start year, savings rate, interest rate, investment period.&lt;/li&gt;
&lt;li&gt;Click Calculate and explore the generated charts and tables.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="input-parameters"&gt;Input Parameters&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Start Year:&lt;/strong&gt; The year when the investment begins.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Initial Capital:&lt;/strong&gt; The amount of money you start with.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Savings Rate:&lt;/strong&gt; The amount of money you plan to save regularly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Savings Interval:&lt;/strong&gt; The frequency at which you save the specified savings rate (either monthly or yearly).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Investment Period:&lt;/strong&gt; The total number of years you plan to invest.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interest Rate:&lt;/strong&gt; The annual interest rate (as a percentage) that your capital will earn.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adjustment Rate:&lt;/strong&gt; The annual rate (as a percentage) at which your savings rate will increase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Savings Suspension:&lt;/strong&gt; The number of years after which you plan to stop saving money.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Target Value:&lt;/strong&gt; A specific capital value you aim to achieve. The app will indicate when (or if) this value is reached.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="generated-diagrams"&gt;Generated Diagrams&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Overview:&lt;/strong&gt; Shows the growth of accumulated savings and total capital over time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Distribution:&lt;/strong&gt; Displays a pie chart showing the distribution between total savings and total interest earned.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Savings Rate:&lt;/strong&gt; Represents the annual savings rate in relation to the value of the generated interest each year. This visualization illustrates the development of both the savings rate and the generated interest over time. Additionally, it highlights the year when the generated interest surpasses the annual savings rate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Normalized Values:&lt;/strong&gt; Displays the values of the savings rate and generated interests, both normalized based on the annual growth comprised of the savings rate and yearly interests. This provides a clearer perspective on how each component contributes to the overall growth each year.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Goals:&lt;/strong&gt; Displays the development of total capital and highlights specific milestones, such as when the capital doubles from the initial investment. It also indicates when the user-defined target value is achieved.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Values:&lt;/strong&gt; A table that provides a detailed breakdown of the capital at the beginning of the year, savings amount per year, generated interest per year, and capital at the end of the year.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="run-locally"&gt;Run Locally&lt;/h2&gt;
&lt;p&gt;If you want to host the calculator yourself:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clone this repository&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/smichard/compound_interest_calculator.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;Navigate to the project directory:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; compound_interest_calculator
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="3"&gt;
&lt;li&gt;Build the container image:
Run the following command to build a Docker image. Replace &lt;code&gt;my_app&lt;/code&gt; with a name of your choice for the image.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman build -t my_app -f Containerfile
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This command will use the provided Containerfile to build an image named &lt;code&gt;my_app&lt;/code&gt;. The process will install the necessary R packages and set up the environment for the Shiny app.&lt;/p&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Run the Shiny app locally:&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After building the image, you can run the Shiny app locally using the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman run --rm -p 3838:3838 my_app
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This command will start a container from the &lt;code&gt;my_app&lt;/code&gt; image and map port 3838 of the container to port 3838 of your local machine.&lt;/p&gt;
&lt;ol start="5"&gt;
&lt;li&gt;Access the Shiny app in a browser
Open a web browser and navigate to:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;http://localhost:3838/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should now see your Shiny app running!&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;The Compound Interest Calculator helps bridge the gap between abstract formulas and practical insights. It turns the often-theoretical concept of compound growth into something tangible and interactive. By visualizing how capital evolves over time, it allows users to experiment with different savings rates, investment horizons, and interest assumptions — and to see immediately how these variables influence the trajectory of their capital.&lt;/p&gt;
&lt;p&gt;Whether used for personal financial planning, educational purposes, or illustrating investment concepts, the tool provides a clear and structured way to explore “what-if” scenarios. It highlights key inflection points — such as when generated interest surpasses annual savings — making the dynamics of compounding easier to grasp and communicate.&lt;/p&gt;
&lt;p&gt;Ultimately, the calculator is designed to make complex relationships between time, capital, and interest transparent, empowering users to make more informed, data-driven decisions about their long-term financial strategies.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;YouTube Video - Nischa: Why Net Worth Skyrockets After $100K - &lt;a href="https://www.youtube.com/watch?v=F3Q-1W4QEVI"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Web App - &lt;a href="https://compound-calculator.michard.io/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GitHub Repository - &lt;a href="https://github.com/smichard/compound_interest_calculator"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Enhancing Code Project Documentation through Automated Changelogs</title><link>/2024/enhancing-code-project-documentation-through-automated-changelogs/</link><pubDate>Tue, 26 Mar 2024 00:00:00 +0000</pubDate><guid>/2024/enhancing-code-project-documentation-through-automated-changelogs/</guid><description>&lt;p&gt;This article was published on March 25, 2024, on &lt;a href="https://www.opensourcerers.org/2024/03/25/enhancing-code-project-documentation-through-automated-changelogs/"&gt;opensourcerers.org&lt;/a&gt;:&lt;/p&gt;
&lt;h2 id="abstract"&gt;Abstract&lt;/h2&gt;
&lt;p&gt;In the rapidly evolving landscape of software development, documentation of modifications and updates is crucial for maintaining project continuity and ensuring team alignment. This blog article introduces &lt;strong&gt;Conventional Changelog&lt;/strong&gt;, a tool developed to address this very challenge. The tool transforms a project’s commit history into a detailed, readable changelog. Its adherence to the Conventional Commits and Semantic Versioning practices fosters a well-structured documentation that enhances transparency for users and contributors alike. Versatile by design, it integrates seamlessly into various deployment environments, from local IDEs to continuous integration pipelines like GitHub Actions and Tekton Tasks.&lt;/p&gt;
&lt;h2 id="motivation"&gt;Motivation&lt;/h2&gt;
&lt;p&gt;As projects evolve, maintaining a clear history of changes becomes a challenge. Traditional methods often fall short, leading to overlooked updates or a cluttered changelog. The need for a solution that not only automates this process but also aligns with best practices in software development — such as Semantic Versioning and Conventional Commits — sparked the idea to develop the proposed tool. &lt;strong&gt;Conventional Changelog&lt;/strong&gt; addresses this gap, offering a solution that is both comprehensive and easy to adopt, ensuring no code commit goes unrecorded.&lt;/p&gt;
&lt;p&gt;The proposed approach integrates three foundational best practices to enhance a project’s documentation:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://semver.org/"&gt;&lt;strong&gt;Semantic Versioning:&lt;/strong&gt;&lt;/a&gt; This practice involves structuring version numbers as MAJOR.MINOR.PATCH. Each segment signifies the nature of changes: MAJOR versions indicate incompatible API changes, MINOR versions add features in a backward-compatible manner, and PATCH versions address backward-compatible bug fixes. This method provides a clear, incremental structure for versioning that reflects the scope and impact of changes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.conventionalcommits.org/en/v1.0.0/#specification"&gt;&lt;strong&gt;Conventional Commits:&lt;/strong&gt;&lt;/a&gt; Building on the idea of structured commit messages, this practice categorizes code changes to clearly communicate their intent. Based on the &lt;a href="https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines"&gt;Angular Convention&lt;/a&gt; for code commits, valid categories are: feat:, fix:, build:, chore:, ci:, docs:, style:, refactor:, perf:, and test:. The proposed tool introduces additional categories such as deploy:, gitops:, and demo:. The motivation is to cover code changes of deployment files (e.g. Kubernetes manifests), code changes which trigger automated GitOps-driven deployments, code changes which are motivated by demonstration purposes. This ensures a well-organized commit history.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://keepachangelog.com/en/1.1.0/"&gt;&lt;strong&gt;Keep a Changelog:&lt;/strong&gt;&lt;/a&gt; Advocates for maintaining a changelog as a curated list of notable changes for each project version. It emphasizes structuring the changelog in a way that is accessible and informative for users, grouping changes by type and listing them chronologically. Including an “Unreleased” section helps to offer visibility into the latest code commit which might be part of upcoming software releases.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Together, these practices offer a comprehensive framework for managing software versioning, commit documentation, and changelog maintenance, making it easier for teams to navigate the complexities of project development and for users to stay informed about significant updates.&lt;/p&gt;
&lt;h2 id="executing-the-script-a-multifaceted-approach"&gt;Executing the Script: A Multifaceted Approach&lt;/h2&gt;
&lt;p&gt;The tool can be operated in various ways. These methods are explained in more detail below. To avoid exceeding the scope, minimal examples for the individual options will be used. This flexibility allows developers to choose the best approach for their individual workflow, enhancing productivity and ensuring accurate documentation of project evolution.&lt;/p&gt;
&lt;h3 id="local-execution"&gt;Local execution&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Conventional Changelog&lt;/strong&gt; stands out for its adaptability, easily incorporating into the local development environment. Developers can execute the script directly, ensuring their changelog remains up-to-date with every commit.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./generate_changelog_local.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alternatively, utilizing a container engine like Podman or Docker offers an isolated setup, guaranteeing consistent execution across different environments Independent of the underlying operating system.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First, build the container image using the provided Dockerfile. This step creates an image with the necessary environment to run the script:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman build -t &amp;lt;image-name&amp;gt; -f Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;After building the image, run the container. This step mounts the current working directory into the container, allowing the script to access and update the changelog file within the project directory:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman run -it --rm -v &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;:/repo&amp;#34;&lt;/span&gt; &amp;lt;image-name&amp;gt; sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="3"&gt;
&lt;li&gt;Inside the container, navigate to the mounted repository directory and execute the script. This process generates the changelog within the containerized environment, reflecting the changes back to the local repository:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; repo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./generate_changelog_local.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="github-action"&gt;GitHub Action&lt;/h3&gt;
&lt;p&gt;Integrating &lt;strong&gt;Conventional Changelog&lt;/strong&gt; into a CI/CD pipeline as a GitHub Action streamlines the process of keeping your changelog current and comprehensive. The configuration of the GitHub Actions workflow allows for the changelog generation to be initiated based on certain git operations, targeted branches, or through workflow dispatch, providing flexibility in how and when updates are documented.&lt;br&gt;
The following GitHub Actions workflow example is designed to trigger the automatic generation of an updated changelog with every code push to the main branch. For this functionality to operate correctly, it’s necessary to adjust the GitHub workflow permissions to have both read and write access in the repository settings (Settings -&amp;gt; Actions -&amp;gt; General -&amp;gt; Workflow permissions).&lt;/p&gt;
&lt;div class="collapsable-code"&gt;
&lt;input id="627951348" class="toggle" type="checkbox"checked /&gt;
&lt;label for="627951348" class="lbl-toggle"&gt;
&lt;span class="collapsable-code__language"&gt;YAML&lt;/span&gt;
&lt;span class="collapsable-code__title"&gt;GitHub Action Workflow&lt;/span&gt;
&lt;span class="collapsable-code__toggle" data-label-expand="△" data-label-collapse="▽"&gt;&lt;/span&gt;
&lt;/label&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-YAML" data-lang="YAML"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Generate Changelog&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;push&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;main ]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;changelog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Generate and Commit Changelog&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Checkout Repository&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/checkout@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Generate Changelog&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;smichard/conventional_changelog@2.0.0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Set Git User Info&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; git config user.name &amp;#39;GitHub Actions Bot&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; git config user.email &amp;#39;actions@github.com&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Commit Changelog&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; git add CHANGELOG.md
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; git commit -m &amp;#34;docs: :robot: changelog file generated&amp;#34; || echo &amp;#34;No changes to commit&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; git push&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This automation streamlines the maintenance of the project’s documentation, ensuring a real-time, accurate account of changes, fixes, and new features. It’s a seamless process that saves time and improves accuracy, crucial for projects with frequent updates.&lt;/p&gt;
&lt;h3 id="tekton-task"&gt;Tekton Task&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Conventional Changelog&lt;/strong&gt; extends its versatility by offering seamless integration as a task within &lt;a href="https://tekton.dev/docs/"&gt;Tekton pipelines&lt;/a&gt;. This feature is particularly beneficial for users operating in Kubernetes and OpenShift environments, allowing for the automation of changelog generation as part of a deployment workflow.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Begin by applying the provided &lt;code&gt;tekton/task_generate_changelog.yml&lt;/code&gt; configuration. This step enables using the provided Task as part of a Tekton Pipeline. Make sure to have the git-clone Task installed in your &lt;a href="https://hub.tekton.dev/tekton/task/git-clone"&gt;cluster&lt;/a&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;oc apply -f tekton/task_generate_changelog.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;Integrate the provided task into a Tekton pipeline. Find below a minimal pipeline configuration. This pipeline illustrates a minimal configuration which retrieves a Git repository and generates the changelog. However, the provided pipeline can serve as a blueprint to be adopted in a larger context. If the generated changelog file needs to be committed back to the repository, additional steps are required to handle the commit process:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="collapsable-code"&gt;
&lt;input id="489675231" class="toggle" type="checkbox"checked /&gt;
&lt;label for="489675231" class="lbl-toggle"&gt;
&lt;span class="collapsable-code__language"&gt;yaml&lt;/span&gt;
&lt;span class="collapsable-code__title"&gt;Minimal Tekton Task&lt;/span&gt;
&lt;span class="collapsable-code__toggle" data-label-expand="△" data-label-collapse="▽"&gt;&lt;/span&gt;
&lt;/label&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;tekton.dev/v1beta1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Pipeline&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;minimal-pipeline&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;workspaces&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;source&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;git-url&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;URL of the git repository&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;fetch-repository&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;taskRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;git-clone&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Task&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;workspaces&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;output&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;source&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;url&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;$(params.git-url)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;revision&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;main&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;generate-changelog&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;taskRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;generate-changelog&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;workspaces&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;source&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;source&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runAfter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;fetch-repository&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Apply the pipeline:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;oc apply -f tekton/pipeline
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Integrating the solution as part of a Tekton pipeline, just as with a GitHub Action workflow, demonstrates the solution’s flexibility and ensures a timely and accurate record of changes, bug fixes, and new features.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;In a dynamic software development world, maintaining an accurate and comprehensive project history is pivotal for team alignment and project continuity. The introduction of &lt;strong&gt;Conventional Changelog&lt;/strong&gt; offers a robust solution to this challenge, transforming commit histories into detailed, structured changelogs. This tool marries the principles of Conventional Commits and Semantic Versioning with the best practices of changelog maintenance, ensuring a transparent and accessible documentation process. Versatile enough to integrate with local IDEs, containerized environments, GitHub Actions, and Tekton Tasks, &lt;strong&gt;Conventional Changelog&lt;/strong&gt; streamlines documentation workflows, making it an essential tool for developers seeking to automate and enhance their project documentation practices. This post presented the motivation behind &lt;strong&gt;Conventional Changelog&lt;/strong&gt;, outlined its background, and provided practical guidance on its multifaceted execution strategies, demonstrating its utility in modern software development environments.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;GitHub Repository of Conventional Changelog - &lt;a href="https://github.com/smichard/conventional_changelog"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GitHub Action Marketplace - &lt;a href="https://github.com/marketplace/actions/generate-changelog-based-on-conventional-commits"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Semantic Versioning Specification - &lt;a href="https://semver.org/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Conventional Commits Specification - &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/#specification"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Angular Commit Message Guidelines - &lt;a href="https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Keep A Changelog Specification - &lt;a href="https://keepachangelog.com/en/1.1.0/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Tekton Documentation - &lt;a href="https://tekton.dev/docs/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Documentation for the git clone Tekton Task - &lt;a href="https://hub.tekton.dev/tekton/task/git-clone"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Cloud Native Tutorial</title><link>/2023/cloud-native-tutorial/</link><pubDate>Tue, 07 Mar 2023 00:00:00 +0000</pubDate><guid>/2023/cloud-native-tutorial/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Last year I created a tutorial which explains the basics of application deployment in a cloud-native environment. I have also recorded videos for the individual chapters for a Dell internal learning platform, these are not publicly available.&lt;br&gt;
With this post I describe the individual chapters of the tutorial, the individual parts of the tutorial follow on from each other. The corresponding code can be found on GitHub. A simple website serves as a sample application and can be run as a container in various cloud environments. The sample application’s source code is part of the tutorial.&lt;br&gt;
For simplicity, do not attempt this while using a corporate firewall. Comments, additions, and collaboration are welcome.&lt;/p&gt;
&lt;a href="https://github.com/smichard/cloud_bites_tutorial" target="_blank" rel="noreferrer" class="download"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentcolor" class="clip" width="13" height="13" style="vertical-align: middle; margin-right: .3rem;"&gt;
&lt;path d="M352 0c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9L370.7 96 201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L416 141.3l41.4 41.4c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6V32c0-17.7-14.3-32-32-32H352zM80 32C35.8 32 0 67.8 0 112V432c0 44.2 35.8 80 80 80H400c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32V432c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z"&gt;&lt;/path&gt;
&lt;/svg&gt; Find the code on GitHub &lt;/a&gt;
&lt;hr&gt;
&lt;h2 id="1-setup-of-local-environment"&gt;1. Setup of local environment&lt;/h2&gt;
&lt;p&gt;The tutorial provides a virtual machine (VM) to ensure a consistent development environment. This VM is deployed using &lt;a href="https://www.vagrantup.com/"&gt;Vagrant&lt;/a&gt;. Vagrant allows it to leverage a declarative configuration file that describes the required software, packages, and operating system configuration. The VM used in this tutorial is based on Ubuntu 20.04 LTS. The VM is allocated 1 vCPU and 4 GB of RAM.&lt;br&gt;
We recommend &lt;a href="https://www.virtualbox.org/"&gt;Virtualbox&lt;/a&gt; as the virtualization software for this tutorial.&lt;br&gt;
This tutorial uses &lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt; as a code editor.&lt;/p&gt;
&lt;p&gt;After installing the three components (Vagrant, VirtualBox, and Visual Studio Code), the GitHub repository can be cloned, and the VM can be started. The first line ensures that that the software tracking tool git is still installed if it is not already present on the PC:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;winget install --id Git.Git -e --source winget
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git config --global core.autocrlf &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/smichard/cloud_bites_tutorial
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; cloud_bites_tutorial
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vagrant up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After the VM has started and all software components have been fully installed, you can log into the VM via ssh:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vagrant ssh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The VM has been configured to mount the host PC&amp;rsquo;s file system, it is available in the &lt;em&gt;vagrant&lt;/em&gt; folder:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /vagrant
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="2-build-and-run-docker-containers-locally"&gt;2. Build and run Docker containers locally&lt;/h2&gt;
&lt;p&gt;In this section, a container with the demo application is created first. This container is started locally can be reached via the host PC’s web browser.&lt;/p&gt;
&lt;p&gt;Switch to the directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; cloud_bites_tutorial/1_2_app_sources
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;View the container images available in the VM:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker images
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Build the container image based on the folder’s Dockerfile:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker build -t &amp;lt;image_name&amp;gt; .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker images
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running the Docker container. With the following command the container runs in the background and the VM ports &lt;em&gt;8082&lt;/em&gt; are mapped to the container port &lt;em&gt;80&lt;/em&gt;. The website is available via the Host PC&amp;rsquo;s web browser at &lt;em&gt;localhost:8082&lt;/em&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d -p 8082:80 &amp;lt;image_name&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The sample application’s source code can be modified easily with the help of a small helper script. This helper script is run twice, and the container image is rebuilt twice. The commands shown start new containers with the newly generated container images. The website is then available via the host PC&amp;rsquo;s web browser under &lt;em&gt;localhost:8083&lt;/em&gt; and &lt;em&gt;localhost:8084&lt;/em&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./update_script.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker build -t &amp;lt;image_name_2&amp;gt; .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d -p 8083:80 &amp;lt;image_name_2&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./update_script.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker build -t &amp;lt;image_name_3&amp;gt; .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d -p 8084:80 &amp;lt;image_name_3&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running containers can be listed and stopped with the following commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker ps
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;kill&lt;/span&gt; &amp;lt;container_id&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The created container images can be pushed to the container registry if you have an account on Docker Hub. The container image must contain the Dockerhub username. So, the container image may need to be rebuilt or named appropriately via a tag:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker login
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker push &amp;lt;dockerhub_username&amp;gt;/&amp;lt;image_name&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="3-deploy-a-local-kubernetes-cluster-using-k3d"&gt;3. Deploy a local Kubernetes cluster using K3D&lt;/h2&gt;
&lt;p&gt;In the following section you are going to use the &lt;a href="https://k3d.io"&gt;K3D&lt;/a&gt; project to locally deploy a minimal Kubernetes cluster. K3D is a lightweight wrapper to run &lt;a href="https://k3s.io"&gt;K3S&lt;/a&gt;, Rancher Lab’s minimal Kubernetes distribution, in Docker. K3D makes it very easy to create single- and multi-node K3S clusters in Docker, e.g. for local development on Kubernetes. A good introduction to K3D with some use cases can be found in the &lt;a href="https://www.youtube.com/watch?v=mCesuGk-Fks"&gt;DevOps Toolkit YouTube video&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Deploy a local K3D cluster with three control planes and three worker nodes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;k3d cluster create local-cluster --servers &lt;span class="m"&gt;3&lt;/span&gt; --agents &lt;span class="m"&gt;3&lt;/span&gt; -p &lt;span class="s2"&gt;&amp;#34;8080:80@loadbalancer&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The cluster and the individual nodes can be displayed with the following commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;k3d cluster list
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get nodes
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The following command deploys the demo application to the local Kubernetes cluster that has just been created. This step creates a deployment with multiple pods, a service, and an ingress controller. You can find the declarative configuration in the &lt;code&gt;1_3_local_deployment/deployment.yml&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f 1_3_local_deployment/deployment.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The following command displays the deployment, the replica set, the pods, and the service:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get deployments,replicasets,pods,services
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The website is then available via the host PC&amp;rsquo;s web browser under &lt;em&gt;localhost:8080&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="4-basic-operations-to-handle-pods-and-deployments"&gt;4. Basic operations to handle Pods and Deployments&lt;/h2&gt;
&lt;p&gt;The following section details a few basic commands for handling pods and deployments. The following commands create two pods named &lt;code&gt;my-pod-1&lt;/code&gt; and &lt;code&gt;my-pod-2&lt;/code&gt; each with the container image &lt;code&gt;nginx:alpine&lt;/code&gt;. The last command shows all pods running in the default namespace:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl run my-pod-1 --image&lt;span class="o"&gt;=&lt;/span&gt;nginx:alpine
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl run my-pod-2 --image&lt;span class="o"&gt;=&lt;/span&gt;nginx:alpine
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get pods
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The following command will stop and delete the pod named &lt;code&gt;my-pod-1&lt;/code&gt;. Since this pod is not part of a ReplicaSet, it will not be restarted:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl delete pod my-pod-1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get pods
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The following command scales the deployment created in the previous section to six replicas. The second command displays the deployment, the replica sets, and the pods in the default namespace. Now, six pods should be shown for &lt;code&gt;dpl-demo-1&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl scale deployment dpl-demo-1 --replicas&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get deployments,replicasets,pods
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now it&amp;rsquo;s time to change the deployment. The &lt;code&gt;1_3_local_deployment/deployment.yml&lt;/code&gt; file must be modified to do this. For example, in line 24, the container image can be changed from &lt;code&gt;demo-app:sydney&lt;/code&gt; to &lt;code&gt;demo-app:london&lt;/code&gt;. The adjusted deployment is rolled out again with the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f 1_3_local_deployment/deployment.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get deployments,replicasets,pods
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The second command displayed the deployment, the replica sets, and the pods for the default namespace. By launching the changed deployment, a new replica set was created in which the number of pods is scaled up to the required number of pods. The number of pods for the existing replica set is scaled to zero.&lt;br&gt;
The deployment is changed a second time and rolled out again. How does this occur? The container image is modified from &lt;code&gt;demo-app:london&lt;/code&gt; to &lt;code&gt;demo-app:newyork&lt;/code&gt; in line 24 of the &lt;code&gt;1_3_local_deployment/deployment.yml&lt;/code&gt; file. The adjusted deployment is rolled out again with the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f 1_3_local_deployment/deployment.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get deployments,replicasets,pods
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The demo application’s website is always available via the host PC&amp;rsquo;s web browser via &lt;em&gt;localhost:8080&lt;/em&gt;.&lt;br&gt;
The following command shows a list of revisions for the &lt;code&gt;dpl-demo-1&lt;/code&gt; deployment. The second command displays details for a specific revision:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl rollout &lt;span class="nb"&gt;history&lt;/span&gt; deployment dpl-demo-1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl rollout &lt;span class="nb"&gt;history&lt;/span&gt; deployment dpl-demo-1 --revision&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Use the following command to roll back the deployment to a specific revision:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl rollout undo deployment dpl-demo-1 --to-revision&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If the deployment is rolled back, the number of pods for the corresponding ReplicaSet is scaled, meaning no new ReplicaSet is created, but an existing one is used.&lt;/p&gt;
&lt;h2 id="5-deploy-a-remote-kubernetes-cluster-using-google-cloud--part-1"&gt;5. Deploy a remote Kubernetes cluster using Google Cloud – part 1&lt;/h2&gt;
&lt;p&gt;In the following section, a Kubernetes cluster is deployed in a public cloud. For this purpose, we use &lt;a href="https://cloud.google.com/"&gt;Google Cloud&lt;/a&gt; in this tutorial. For simplicity, we assume that a Google Cloud account already exists.
&lt;style type="text/css"&gt;.notice{--root-color:#444;--root-background:#eff;--title-color:#fff;--title-background:#7bd;--warning-title:#c33;--warning-content:#fee;--info-title:#fb7;--info-content:#fec;--note-title:#6be;--note-content:#e7f2fa;--tip-title:#5a5;--tip-content:#efe}@media (prefers-color-scheme:dark){.notice{--root-color:#ddd;--root-background:#eff;--title-color:#fff;--title-background:#7bd;--warning-title:#800;--warning-content:#400;--info-title:#a50;--info-content:#420;--note-title:#069;--note-content:#023;--tip-title:#363;--tip-content:#121}}body.dark .notice{--root-color:#ddd;--root-background:#eff;--title-color:#fff;--title-background:#7bd;--warning-title:#800;--warning-content:#400;--info-title:#a50;--info-content:#420;--note-title:#069;--note-content:#023;--tip-title:#363;--tip-content:#121}.notice{line-height:24px;margin-bottom:24px;border-radius:4px;color:var(--root-color);background:var(--root-background)}.notice p:last-child{margin-bottom:0; padding: .5rem 1.2rem 1rem;}.notice-title{margin:-18px -18px 12px;padding:4px 18px;border-radius:4px 4px 0 0;font-weight:700;color:var(--title-color);background:var(--title-background)}.notice.warning .notice-title{background:var(--warning-title)}.notice.warning{background:var(--warning-content)}.notice.info .notice-title{background:var(--info-title)}.notice.info{background:var(--info-content)}.notice.note .notice-title{background:var(--note-title)}.notice.note{background:var(--note-content)}.notice.tip .notice-title{background:var(--tip-title)}.notice.tip{background:var(--tip-content)}.icon-notice{display:inline-flex;align-self:center;margin-right:8px}.icon-notice img,.icon-notice svg{height:1em;width:1em;fill:currentColor}.icon-notice img,.icon-notice.baseline svg{top:.125em;position:relative}&lt;/style&gt;
&lt;div&gt;&lt;svg width="0" height="0" display="none" xmlns="http://www.w3.org/2000/svg"&gt;&lt;symbol id="tip-notice" viewBox="0 0 512 512" preserveAspectRatio="xMidYMid meet"&gt;&lt;path d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"/&gt;&lt;/symbol&gt;&lt;symbol id="note-notice" viewBox="0 0 512 512" preserveAspectRatio="xMidYMid meet"&gt;&lt;path d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"/&gt;&lt;/symbol&gt;&lt;symbol id="warning-notice" viewBox="0 0 576 512" preserveAspectRatio="xMidYMid meet"&gt;&lt;path d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"/&gt;&lt;/symbol&gt;&lt;symbol id="info-notice" viewBox="0 0 512 512" preserveAspectRatio="xMidYMid meet"&gt;&lt;path d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"/&gt;&lt;/symbol&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class="notice warning" &gt;
&lt;p class="first notice-title"&gt;&lt;span class="icon-notice baseline"&gt;&lt;svg&gt;&lt;use href="#warning-notice"&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;Warning&lt;/p&gt;&lt;p&gt;Costs are generated at the public cloud provider used in the following tutorial sections. It is, therefore, vital to limit the runtime of the clusters and prevent possible idle time to minimize costs.&lt;/p&gt;&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;First, obtain access to your Google Cloud account:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gcloud auth login
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The following command creates a new Google Cloud project, generating a random project name. Last, the newly created project is specified in the active configuration.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gcp-&lt;/span&gt;&lt;span class="k"&gt;$(($(&lt;/span&gt;date +%s%d&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1000000&lt;/span&gt;&lt;span class="k"&gt;))$((&lt;/span&gt;&lt;span class="nv"&gt;$RANDOM&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gcloud projects create &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; --name &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gcloud config &lt;span class="nb"&gt;set&lt;/span&gt; project &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Before proceeding with the tutorial, make sure to &lt;a href="https://cloud.google.com/billing/docs/how-to/modify-project#enable_billing_for_a_new_project"&gt;enable billing&lt;/a&gt; on the new project.&lt;/p&gt;
&lt;p&gt;Then some required Google Cloud APIs have to be activated:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gcloud services &lt;span class="nb"&gt;enable&lt;/span&gt; compute.googleapis.com container.googleapis.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For simplicity let&amp;rsquo;s define a default compute region and a default compute zone:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gcloud config &lt;span class="nb"&gt;set&lt;/span&gt; compute/region europe-west3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gcloud config &lt;span class="nb"&gt;set&lt;/span&gt; compute/zone europe-west3a
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, the Kubernetes cluster can be deployed:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gcloud beta container clusters create my-gke-cluster --zone europe-west3-a
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To interact with the newly created Kubernetes Cluster, fetch its credentials. This commands updates the local &lt;em&gt;kubeconfig&lt;/em&gt; file with the appropriate credentials and endpoint information:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gcloud container clusters get-credentials my-gke-cluster
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the demo application can be deployed on the newly created cluster:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f 1_5_cloud_deployment/deployment.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A load balancer was created as a networking service in the previous step. After a certain wait time, a public IP address is displayed. The website of the demo application can then be reached via a web browser:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get services
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Kubernetes cluster can be deleted with the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gcloud container clusters delete my-gke-cluster
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="6-deploy-a-remote-kubernetes-cluster-using-google-cloud--part-2"&gt;6. Deploy a remote Kubernetes cluster using Google Cloud – part 2&lt;/h2&gt;
&lt;p&gt;In this section, another Kubernetes cluster is created on Google Cloud. This time, the Cloud Console is used:
&lt;figure&gt;&lt;img src="/images/posts/post_06/google_cloud_gke.png"data-src="/images/posts/post_06/google_cloud_gke.png"
/&gt;&lt;figcaption&gt;
&lt;h4&gt;Google Cloud Screenshot&lt;/h4&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;Once the Kubernetes Cluster has been created successfully, fetch its credentials:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gcloud container clusters get-credentials my-gke-cluster-2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the demo application can be deployed on the newly created cluster:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f 1_5_cloud_deployment/deployment.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A load balancer was created as a networking service in the previous step. After a certain wait time, a public IP address is displayed. The website of the demo application can then be reached via a web browser:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get services
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="notice tip" &gt;
&lt;p class="first notice-title"&gt;&lt;span class="icon-notice baseline"&gt;&lt;svg&gt;&lt;use href="#tip-notice"&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;Tip&lt;/p&gt;&lt;p&gt;It is crucial to delete the Kubernetes cluster if unused to keep costs low.&lt;/p&gt;&lt;/div&gt;
&lt;h2 id="7-deploy-a-remote-kubernetes-cluster-leveraging-terraform"&gt;7. Deploy a remote Kubernetes cluster leveraging Terraform&lt;/h2&gt;
&lt;p&gt;In this section a Kubernetes cluster is created on Google Cloud leveraging &lt;a href="https://www.terraform.io"&gt;Terraform&lt;/a&gt;. Terraform is an infrastructure as code tool allowing one to build infrastructure safely and efficiently in a declarative way on various platforms. &lt;br&gt;
First the Google Cloud environment has to be prepared by enabling a couple of API&amp;rsquo;s:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gcloud services &lt;span class="nb"&gt;enable&lt;/span&gt; compute.googleapis.com container.googleapis.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The following command authorizes the Google Cloud SDK to access Google Cloud using your user account credentials. This step adds your account to the Application Default Credentials, allowing Terraform to access these credentials to provision resources on Google Cloud:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gcloud auth application-default login
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The following command initializes a working directory containing Terraform configuration files:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; 1_7_terraform_deployment
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;terraform init
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;strong&gt;terraform plan&lt;/strong&gt; command creates an execution plan, which lets you preview the changes that Terraform plans to make to your infrastructure. By default, when Terraform creates a plan it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reads the current state of any already-existing remote objects to make sure that the Terraform state is up-to-date.&lt;/li&gt;
&lt;li&gt;Compares the current configuration to the prior state and noting any differences.&lt;/li&gt;
&lt;li&gt;Proposes a set of change actions that should, if applied, make the remote objects match the configuration.
With the following command, you will create a Terraform plan. You must pass the project ID generated earlier as a variable:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;terraform plan -var &lt;span class="nv"&gt;project_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;strong&gt;terraform apply&lt;/strong&gt; command executes the actions proposed in a Terraform plan.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;terraform apply -var &lt;span class="nv"&gt;project_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After creating the Kubernetes Cluster successfully, you can fetch its credentials:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gcloud container clusters get-credentials my-terraform-cluster --region europe-west3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the demo application can be deployed on the newly created cluster:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f 1_5_cloud_deployment/deployment.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A load balancer was created as a networking service in the previous step. After a certain wait time, a public IP address is displayed. The website of the demo application can then be reached via a web browser:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get services
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;strong&gt;terraform destroy&lt;/strong&gt; command conveniently eliminates all remote objects managed by a particular Terraform configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;terraform destroy -var &lt;span class="nv"&gt;project_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you don’t need your Google Cloud project anymore, you can delete the project:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gcloud projects delete &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="8-visualize-kubernetes-workloads-with-vmware-octant"&gt;8. Visualize Kubernetes workloads with VMware Octant&lt;/h2&gt;
&lt;p&gt;You will explore, in this section, the various Kubernetes clusters created earlier with &lt;a href="https://octant.dev/"&gt;VMware Octant&lt;/a&gt;. Octant is a tool for developers to understand how applications run on a Kubernetes cluster. It aims to be part of the developer’s toolkit for gaining insight and approaching complexity found in Kubernetes. Octant offers a combination of introspective tooling, cluster navigation, and object management. It also provides a plugin system to extend its capabilities further.&lt;/p&gt;
&lt;p&gt;Start Octant:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;octant
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Octant application can be reached via a web browser of the host PC using &lt;em&gt;localhost:8001&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id="summary"&gt;Summary&lt;/h3&gt;
&lt;p&gt;The tutorial gives a quick overview of the basics of application deployment in a cloud-native environment. This tutorial is intended to provide an easy introduction with a quick learning curve. There is by far no claim to completeness. There are certainly ways to improve or expand the tutorial.&lt;br&gt;
You can download the slides I used to present the tutorial via the following link:
&lt;div class="wrapper_download_left"&gt;
&lt;a class="download" href="/documents/Cloud_Bites_Slides.pdf" download&gt;
&lt;svg t="1581822650945" fill="currentcolor" class="clip" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3250"
width="20" height="20" style="vertical-align: middle;"&gt;
&lt;path d="M645.51621918 141.21142578c21.36236596 0 41.79528808 4.04901123 61.4025879 12.06298852a159.71594214 159.71594214 0 0 1 54.26367236 35.87255836c15.84503198 16.07739258 27.76959252 34.13726783 35.78356909 54.13513184 7.86071778 19.30572486 11.76635766 39.80291724 11.76635767 61.53607177 0 21.68371583-3.90563989 42.22045875-11.76635767 61.54101586-8.01397729 19.99291992-19.95831275 38.02807617-35.78356909 54.08569313l-301.39672877 302.0839231c-9.21038818 9.22027564-20.15112281 16.48278832-32.74310277 21.77270508-12.29040503 4.81036401-24.54125953 7.19329834-36.82177783 7.19329834-12.29040503 0-24.56103516-2.38293433-36.85638427-7.19329834-12.63647461-5.28991675-23.53271461-12.55737281-32.7381587-21.77270508-9.55151367-9.58117675-16.69042992-20.44775367-21.50573731-32.57995583-4.7856443-11.61804223-7.15869117-23.91339135-7.15869188-36.9255979 0-13.14074708 2.37304688-25.55474854 7.16363524-37.19256639 4.81036401-11.94927954 11.94927954-22.78619408 21.50079395-32.55029274l278.11614966-278.46221923c6.45172119-6.51104737 14.22344971-9.75421118 23.27563501-9.75421119 8.8692627 0 16.54705787 3.24316383 23.03338622 9.75421119 6.47644019 6.49127173 9.73937964 14.18389916 9.73937964 23.08282495 0 9.0521853-3.26293945 16.81896972-9.73937964 23.32012891L366.97489888 629.73773218c-6.32812477 6.2935183-9.48724342 14.08007836-9.48724415 23.30529736 0 9.06701684 3.15417457 16.75964356 9.48724414 23.08776904 6.80273414 6.50610328 14.55963111 9.75915528 23.26574683 9.75915527 8.67150855 0 16.43334961-3.253052 23.27563501-9.76409935l301.37695313-302.04931665c18.93988037-18.96459937 28.40734887-42.04742432 28.40734814-69.25836158 0-27.16149926-9.4674685-50.26409912-28.40734815-69.22869849-19.44415283-19.13269043-42.55664086-28.72375464-69.31274438-28.72375536-26.97363258 0-49.99218727 9.59106422-69.1001587 28.72375536L274.3370815 536.89227319a159.99774146 159.99774146 0 0 0-35.80828883 54.33288526c-8.0337522 19.65179443-12.04321289 40.2824707-12.04321289 61.79809618 0 21.20910645 4.00451661 41.81011963 12.04321289 61.79809547 8.17218018 20.34393287 20.10168481 38.36920166 35.80828883 54.08569312 16.225708 16.06256104 34.30535888 28.13049292 54.23400854 36.15930176 19.91381813 8.0337522 40.47033667 12.06793189 61.64978002 12.0679326 21.13989281 0 41.70135474-4.03417969 61.63000513-12.0679326 19.91876221-8.02386474 38.01818872-20.09674073 54.2241211-36.15435768l300.86773656-301.53515601c6.47644019-6.50115991 14.23828125-9.76904273 23.28057912-9.76904344 8.88903833 0 16.56188941 3.26293945 23.04821776 9.76904344 6.48632836 6.48632836 9.7245481 14.17895508 9.7245481 23.06799269 0 9.09667992-3.23822046 16.8535769-9.7245481 23.37451172L552.40379244 815.35449242c-22.00012231 22.01989722-47.32745362 38.88336158-75.986938 50.49151564C449.10209565 877.14270043 420.37834101 882.78857422 390.21592671 882.78857422c-30.01904297 0-58.74279761-5.64587378-86.20587183-16.94256616-28.6842041-11.60815406-54.00659203-28.47161842-76.00671362-50.49151564a226.19586182 226.19586182 0 0 1-50.13061524-75.90289354A226.86328125 226.86328125 0 0 1 160.9697104 653.04797364c0-30.08331323 5.62115479-58.88122559 16.90795899-86.38385035 11.40545654-28.37768578 28.11566138-53.75939917 50.13061523-76.15997313h0.24719287L530.14164643 189.20135474c15.69177247-15.731323 33.68737817-27.70037818 53.98681641-35.89727735C604.09666377 145.26043701 624.55430562 141.23120141 645.51127583 141.23120141V141.21142578z" p-id="3251"&gt;&lt;/path&gt;
&lt;/svg&gt;
Download slide deck
&lt;/a&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;GitHub - &lt;a href="https://github.com/smichard/cloud_bites_tutorial"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Cloud-Native Computing Foundation - &lt;a href="https://www.cncf.io/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Virtualbox - &lt;a href="https://www.virtualbox.org/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Vagrant - &lt;a href="https://www.vagrantup.com/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;K3D - &lt;a href="https://k3d.io/v5.4.6/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Terraform - &lt;a href="https://www.terraform.io/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Visual Studio Code - &lt;a href="https://code.visualstudio.com/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;VMware Octant - &lt;a href="https://octant.dev/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Introduction to K3D on Youtube - &lt;a href="https://youtu.be/mCesuGk-Fks"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Introduction Terraform on Youtube - &lt;a href="https://youtu.be/l5k1ai_GBDE"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>graphR. - Visualizing RV Tool exports</title><link>/2023/graphr.-visualizing-rv-tool-exports/</link><pubDate>Sun, 29 Jan 2023 00:00:00 +0000</pubDate><guid>/2023/graphr.-visualizing-rv-tool-exports/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;With the advent of hyper-converged infrastructure (HCI), the initial question was how to design such a solution. Unfortunately, sizing tools were missing at the beginning. I found the evaluation of RV Tools very helpful to design HCI solutions, unfortunately, a detailed analysis could take a lot of time and be very error-prone. Therefore I developed a small tool called graphR. which automates the evaluation of RV Tools and compiles a visual presentation of the information contained within one Excel export.&lt;br&gt;
&lt;a href="http://www.robware.net/rvtools/"&gt;RV Tools&lt;/a&gt; is a VMware utility that connects to a vCenter and gathers information with an impressive level of detail on the VMware environment (e. g. on virtual machines, on ESX hosts, on the network configuration). The data collection is fast and easy. The end result can be stored in a Microsoft Excel file. RV Tools exports are a great way to collect data on VMware environments. However, analyzing RV Tool exports, especially of complex environments can be time-consuming, error-prone, and cumbersome.&lt;br&gt;
That&amp;rsquo;s where &lt;em&gt;graphR.&lt;/em&gt; steps in. &lt;em&gt;GraphR.&lt;/em&gt; processes RV Tool exports which are saved as Microsoft Excel or as comma-separated files. It performs statistical analysis on the data contained within the Microsoft Excel file. The dataset is visualized through some beautiful-looking diagrams. Finally, all tables and charts are assembled in one downloadable PDF report. Hence &lt;em&gt;graphR.&lt;/em&gt; enables the generation of a concise report with some great graphics in order to derive meaningful insights on the analyzed VMware environment.&lt;/p&gt;
&lt;p&gt;The tool can be adjusted to your specific needs (see below) or used through the web tool:&lt;br&gt;
&lt;a href="https://www.graphr.de" target="_blank" rel="noreferrer" class="download"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentcolor" class="clip" width="13" height="13" style="vertical-align: middle; margin-right: .3rem;"&gt;
&lt;path d="M352 0c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9L370.7 96 201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L416 141.3l41.4 41.4c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6V32c0-17.7-14.3-32-32-32H352zM80 32C35.8 32 0 67.8 0 112V432c0 44.2 35.8 80 80 80H400c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32V432c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z"&gt;&lt;/path&gt;
&lt;/svg&gt; visit the graphR. website &lt;/a&gt;&lt;/p&gt;
&lt;style type="text/css"&gt;.notice{--root-color:#444;--root-background:#eff;--title-color:#fff;--title-background:#7bd;--warning-title:#c33;--warning-content:#fee;--info-title:#fb7;--info-content:#fec;--note-title:#6be;--note-content:#e7f2fa;--tip-title:#5a5;--tip-content:#efe}@media (prefers-color-scheme:dark){.notice{--root-color:#ddd;--root-background:#eff;--title-color:#fff;--title-background:#7bd;--warning-title:#800;--warning-content:#400;--info-title:#a50;--info-content:#420;--note-title:#069;--note-content:#023;--tip-title:#363;--tip-content:#121}}body.dark .notice{--root-color:#ddd;--root-background:#eff;--title-color:#fff;--title-background:#7bd;--warning-title:#800;--warning-content:#400;--info-title:#a50;--info-content:#420;--note-title:#069;--note-content:#023;--tip-title:#363;--tip-content:#121}.notice{line-height:24px;margin-bottom:24px;border-radius:4px;color:var(--root-color);background:var(--root-background)}.notice p:last-child{margin-bottom:0; padding: .5rem 1.2rem 1rem;}.notice-title{margin:-18px -18px 12px;padding:4px 18px;border-radius:4px 4px 0 0;font-weight:700;color:var(--title-color);background:var(--title-background)}.notice.warning .notice-title{background:var(--warning-title)}.notice.warning{background:var(--warning-content)}.notice.info .notice-title{background:var(--info-title)}.notice.info{background:var(--info-content)}.notice.note .notice-title{background:var(--note-title)}.notice.note{background:var(--note-content)}.notice.tip .notice-title{background:var(--tip-title)}.notice.tip{background:var(--tip-content)}.icon-notice{display:inline-flex;align-self:center;margin-right:8px}.icon-notice img,.icon-notice svg{height:1em;width:1em;fill:currentColor}.icon-notice img,.icon-notice.baseline svg{top:.125em;position:relative}&lt;/style&gt;
&lt;div&gt;&lt;svg width="0" height="0" display="none" xmlns="http://www.w3.org/2000/svg"&gt;&lt;symbol id="tip-notice" viewBox="0 0 512 512" preserveAspectRatio="xMidYMid meet"&gt;&lt;path d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"/&gt;&lt;/symbol&gt;&lt;symbol id="note-notice" viewBox="0 0 512 512" preserveAspectRatio="xMidYMid meet"&gt;&lt;path d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"/&gt;&lt;/symbol&gt;&lt;symbol id="warning-notice" viewBox="0 0 576 512" preserveAspectRatio="xMidYMid meet"&gt;&lt;path d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"/&gt;&lt;/symbol&gt;&lt;symbol id="info-notice" viewBox="0 0 512 512" preserveAspectRatio="xMidYMid meet"&gt;&lt;path d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"/&gt;&lt;/symbol&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div class="notice info" &gt;
&lt;p class="first notice-title"&gt;&lt;span class="icon-notice baseline"&gt;&lt;svg&gt;&lt;use href="#info-notice"&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;Info&lt;/p&gt;&lt;p&gt;The web tool is deployed using a non-persistent container. The uploaded file is cached for evaluation. A cron job ensures that the generated PDF files are deleted at 15-minute intervals. Therefore, no data is stored permanently.&lt;/p&gt;&lt;/div&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To run &lt;em&gt;graphR.&lt;/em&gt; you just need an environment that supports Docker containers. To customize &lt;em&gt;graphR.&lt;/em&gt; according to your needs the installation of the open-source programming language &lt;a href="https://www.r-project.org/"&gt;R&lt;/a&gt; is recommended.&lt;/p&gt;
&lt;h2 id="getting-started"&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;The easiest way to use &lt;em&gt;graphR.&lt;/em&gt; is to pull the latest pre-build Docker container from Dockerhub and run it within your environment. The following commands will download &lt;em&gt;graphR.&lt;/em&gt; from Dockerhub and make it available in your environment on port &lt;em&gt;80&lt;/em&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker pull smichard/graphr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d -p 80:3838 smichard/graphr
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="customize"&gt;Customize&lt;/h2&gt;
&lt;p&gt;To customize &lt;em&gt;graphR.&lt;/em&gt; according to your needs, e.g. by adding new ways to plot the data, altering threshold values, or adding a custom design just clone this repository:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/smichard/graphR.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since the core of &lt;em&gt;graphR.&lt;/em&gt; is written in R the installation of R is recommended to see the changes taking effect. If you are using R-Studio as a code editor the &lt;em&gt;graphr_dashboard.Rproj&lt;/em&gt; file contains all necessary files to adjust &lt;em&gt;graphR.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Following is a short description of the most important files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;app.R&lt;/em&gt; - the main file, which is needed by the Shiny web framework to display the web app. Here the GUI of the web app is described, also the &lt;em&gt;libraries.R&lt;/em&gt; and the &lt;em&gt;server_rv.R&lt;/em&gt; files are sourced&lt;/li&gt;
&lt;li&gt;&lt;em&gt;server_rv.R&lt;/em&gt; - contains all necessary functions to ingest the raw data, perform some basic analysis, generate diagrams, and finally generate the pdf report&lt;/li&gt;
&lt;li&gt;&lt;em&gt;plottingFunctions.R&lt;/em&gt; - a set of functions to display text, data frames, and diagrams on slides&lt;/li&gt;
&lt;li&gt;&lt;em&gt;libraries.R&lt;/em&gt; - contains a list of all required R packages, and also sources the &lt;em&gt;plottingFunctions.R&lt;/em&gt; file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In case you want to use custom backgrounds according to your corporate identity just replace the image files within the &lt;em&gt;/graphr/backgrounds&lt;/em&gt; folder and make sure to use the &lt;em&gt;.png&lt;/em&gt; file format. The recommended image dimensions are 960 px times 540 px.&lt;/p&gt;
&lt;p&gt;Once all changes are done you can build your own custom &lt;em&gt;graphR.&lt;/em&gt; container using the following commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker build -t &amp;lt;project name&amp;gt; .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d -p 80:3838 &amp;lt;project name&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="how-to-use-graphr"&gt;How to use graphR.&lt;/h2&gt;
&lt;p&gt;The use of graphR. is designed to be simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Collect the data with the &lt;a href="http://www.robware.net/rvtools/"&gt;RV Tools&lt;/a&gt; and save the export as &lt;em&gt;.xls&lt;/em&gt;, &lt;em&gt;.xlsx&lt;/em&gt; or as &lt;em&gt;.csv&lt;/em&gt; file&lt;/li&gt;
&lt;li&gt;Upload the &lt;em&gt;.xls&lt;/em&gt; / &lt;em&gt;.xlsx&lt;/em&gt; file (recommended) or the &lt;em&gt;tabvInfo.csv&lt;/em&gt; to graphR. and hit &lt;em&gt;Generate Report&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Enjoy your report&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="demo"&gt;Demo&lt;/h2&gt;
&lt;p&gt;Get a glimpse through this YouTube video:
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/dotbSX79FJg?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;The tool provides an easy way to systematically analyze RV tools. The evaluation is fast and straightforward. This evaluation helps to get a quick overview of existing VMware environments.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;RVTools - &lt;a href="http://www.robware.net/rvtools/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;graphR. website - &lt;a href="https://graphr.de/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GitHub repository - &lt;a href="https://github.com/smichard/graphR.git"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;R - The open source programming language for statistical computing - &lt;a href="https://www.r-project.org/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;R-Studio - Used as code editor for R and for debugging and visualization - &lt;a href="https://www.rstudio.com/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Shiny - Used as web application framework for R - &lt;a href="https://shiny.rstudio.com/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Docker - Used to package all dependencies into one container - &lt;a href="https://www.docker.com/"&gt;link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>