Frakkingsweet Frakkingsweet 2023 Thu, 12 Oct 2023 01:52:41 GMT Some nifty development and operations tips and tricks https://www.frakkingsweet.com/ https://www.frakkingsweet.com/favicon.png Frakkingsweet https://www.frakkingsweet.com/ Vecc.GhostTemplating 3D-Printer ASP ASP.Net Azure C-Sharp DevOps DFS Docker ESX Exchange 2007 Exchange 2010 Games IIS6 Java JavaScript Kubernetes Linux Mac MVC Operations PowerShell Programming Rants Raspberry Pi Server 2003R2 Server 2008R2 Server 2012R2 Server 2016 Server 2019 Tfs TMG TypeScript WebForms Windows 10 Windows 7 Windows 8 Windows XP Edward Cooke https://www.frakkingsweet.com/author/edward/ 60 Edward Cooke https://www.frakkingsweet.com/author/edward GoBGP with health checks While rebuilding my network, I'm going through the exercise of making everything highly available with GoBGP. https://www.frakkingsweet.com/gobgp-with-health-checks/ 1be43f71-a9a6-4b5a-a954-1609ba40aff6 Docker DevOps Linux Edward Cooke Thu, 12 Oct 2023 01:10:23 GMT <p>While rebuilding my network, I'm going through the exercise of making everything highly available with GoBGP.</p><h1 id="purpose">Purpose</h1><p>I am currently in the process of rebuilding my entire network, moving away from my dying Raspberry Pi 4's and back to a dedicated PC. My new setup is using Proxmox for the hypervisor (or orchestrator of the hypervisor?) and micro-segmenting each component of my network, DNS/DHCP/package repository/image registry/Kubernetes clusters/etc. I am also making everything redundant.</p><p>The way I am handling the redundancy is by using BGP to distribute the traffic between each node. If the BGP neighbor stops announcing (machine is dead), then it stops sending traffic there. If the health check fails, then GoBGP will denounce itself and be removed from the peering.</p><p>My standard setup is each virtual machine has a single virtual network card. The IP announced through BGP is assigned to the loopback interface to avoid polluting the subnet with bogus IP addresses. It also allows the services to listen on it whether the network interface is up or down while the machine is booting. I then have a Docker image running GoBGP and a set of scripts. These scripts call the GoBGP CLI to manage the daemon and GoBGP neighbors.</p><p>You can see my Docker image repository here:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/EdwardCooke/gobgp-healthcheck"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - EdwardCooke/gobgp-healthcheck</div><div class="kg-bookmark-description">Contribute to EdwardCooke/gobgp-healthcheck development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/pinned-octocat.svg" alt=""><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">EdwardCooke</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/7f74265d7549ed72280b7099b8054973f6b9df0f11452db892a3d0b01acfa88c/EdwardCooke/gobgp-healthcheck" alt=""></div></a></figure><p>Currently I have 2 built-in health checks. One for doing a simple <code>curl</code> command and another for doing a <code>dig</code> dns lookup. They are all configurable and usable through command line arguments.</p><p>I added additional flexibility by making each piece of that puzzle in the image configurable and replaceable as needed via command line arguments.</p><p>As happy as I am with this setup, there is one downside to the GoBGP CLI approach, the CLI does not support setting passwords for the neighbor. You can see in this issue that I opened up, no traction yet from upstream on whether they would accept a PR or not.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/osrg/gobgp/issues/2711"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Set neighbor password through CLI · Issue #2711 · osrg/gobgp</div><div class="kg-bookmark-description">I’m wondering if I can set the password to talk to the neighbor through the cli when adding it. It looks like its in the API, but I can’t seem to set it, and looking at the code for the CLI it does…</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/pinned-octocat.svg" alt=""><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">osrg</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/55a95883d87598ff966b3476b79f1175443122fcb403158c396ca495cbe3c47d/osrg/gobgp/issues/2711" alt=""></div></a></figure><p>If you have any questions about how I got GoBGP to do what I needed let me know in the comments, I'll be happy to help in any way I can.</p><h1 id="conclusion">Conclusion</h1><p>I originally started out using ExaBGP, but that proved to be a fruitless endeavor. No matter what I did, after a few hours the daemon would stop responding and would not do anything. The process was not terminated, it just stopped doing anything. No logs, no health checks, nothing. I spent a couple of weeks troubleshooting it before finally giving up and went a different direction. I settled on GoBGP, it took a little more work to get setup, but not by much. I still had to build custom scripts to use it for what I needed. Just more of them to handle the lifecycle of the health checks.</p><p>On a side note, everything in my network is being accomplished using Terraform and cloud init using images provided by Debian with a couple of customizations. Currently all services are inside of a Docker container which is configured using Docker compose. Those docker-compose.yml files are checked into a Git repository. If there is any configuration or data storage necessary for that container it is mounted from my NAS which is encrypted and backed up to Azure.</p><p>Being able to delete a VM after I do something silly, and type <code>terraform apply</code> and have it rebuilt from scratch is really nice.</p><p>I am also taking this time to re-architect and re-engineer all aspects of my network. I rebuilt DNS using a multi-tier approach, another post on that is coming and moving from ISC-DHCP-Server to Kea. I'm also changing from the Docker self-hosted registry to using Harbor.</p><p>This is a big undertaking, and the underpinnings of all of it has to be stable and reliable. And GoBGP seems to fit this need nicely.</p><h1 id="links">Links</h1><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/EdwardCooke/gobgp-healthcheck/tree/main"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - EdwardCooke/gobgp-healthcheck</div><div class="kg-bookmark-description">Contribute to EdwardCooke/gobgp-healthcheck development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/pinned-octocat.svg" alt=""><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">EdwardCooke</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/7f74265d7549ed72280b7099b8054973f6b9df0f11452db892a3d0b01acfa88c/EdwardCooke/gobgp-healthcheck" alt=""></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/osrg/gobgp"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - osrg/gobgp: BGP implemented in the Go Programming Language</div><div class="kg-bookmark-description">BGP implemented in the Go Programming Language. Contribute to osrg/gobgp development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/pinned-octocat.svg" alt=""><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">osrg</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/db2a60698019b370a1af2d0da45147dcb014a23c69d5612b630c571af1e2b075/osrg/gobgp" alt=""></div></a></figure> Edward Cooke https://www.frakkingsweet.com/author/edward Pre-pull images in the docker-in-docker image We needed to pre-pull images into the upstream docker:dind image. Here's a way of doing this. https://www.frakkingsweet.com/pre-pull-images-in-the-docker-in-docker-image/ f11cb76a-a049-472a-b6ae-60e0e11d6457 DevOps Docker Linux Edward Cooke Wed, 02 Aug 2023 02:52:27 GMT <p>We needed to pre-pull images into the upstream <code>docker:dind</code> image. Here's a way of doing this.</p><h2 id="overview">Overview</h2><p>Our builds always use the same upstream images, we're tired of re-pulling them every time it builds. It adds a lot of unnecessary time when they can be pre-cached in our build image.</p><p>As with most things with shell scripts and Docker, you will want to do this in a Linux or WSL environment.</p><h2 id="solution">Solution</h2><p>We already use the <code>docker:dind</code> image in our on-prem build environment so it made sense to continue using that image and extend it. Everywhere we looked, people say this can't be done, I didn't believe them, so I did it.</p><p>I broke down the steps, start the docker daemon, pull the images, call it a day. It wasn't as simple as that for some reasons explained below.</p><h2 id="problems">Problems</h2><ul><li>The Docker daemon requires <code>--privileged</code> on the run command. Meaning it has full access to the system. This is not supported (out of the box) by Docker.</li><li>When I finally got it running it was using the <code>vfs</code> volume driver, when running as a regular container it was using <code>overlay2</code>.</li><li>The <code>overlay2</code> storage driver is not compatible with storage provided by the build context.</li></ul><h2 id="how-i-did-it">How I did it</h2><h3 id="create-the-buildx-build-context">Create the <code>buildx</code> build context</h3><p>First step, start the docker daemon during the build. This required running the build with elevated privileges. To do this we had to create a special <code>buildx</code> build context with a couple of flags enabled. To create that I used the following command:</p><!--kg-card-begin: markdown--><pre><code class="language-bash">docker buildx create --name builder \ --driver docker-container \ --buildkitd-flags '--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host' </code></pre> <!--kg-card-end: markdown--><p>This command creates a new <code>buildx</code> build context named <code>builder</code> using the <code>docker-container</code> driver with the ability to run a privileged <code>RUN</code> command in the <code>Dockerfile</code>.</p><h3 id="create-the-dockerfile">Create the <code>Dockerfile</code></h3><p>The <code>Dockerfile</code> is a simple one, we set the syntax to a special one that allows us to use <code>--security=insecure</code> in the <code>RUN</code> command.</p><!--kg-card-begin: markdown--><pre><code class="language-dockerfile"># syntax=docker/dockerfile:1-labs FROM docker:dind COPY install.sh /opt/install.sh RUN --security=insecure /opt/install.sh </code></pre> <!--kg-card-end: markdown--><p>This will run the <code>/opt/install.sh</code> command with elevated privileges.</p><h3 id="create-the-installsh-script">Create the <code>install.sh</code> script</h3><p>Here's the link to <code>install.sh</code> in my git repository. It's large enough to not want to paste it into my page.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/EdwardCooke/prepull-dind/blob/main/install.sh"><div class="kg-bookmark-content"><div class="kg-bookmark-title">prepull-dind/install.sh at main · EdwardCooke/prepull-dind</div><div class="kg-bookmark-description">Contribute to EdwardCooke/prepull-dind development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt=""><span class="kg-bookmark-publisher">GitHub</span><span class="kg-bookmark-author">EdwardCooke</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/7055ce68fcddf1bdfcbeefbbbe87917afb01e13563ff3476159ea34a1ffef23b/EdwardCooke/prepull-dind" alt=""></div></a></figure><p>This script does the following</p><ol><li>Creates a temporary ext4 volume and mounts it on <code>/var/lib/docker</code></li><li>Starts the Docker daemon in the background</li><li>Pulls the image(s)</li><li>Stops the daemon</li><li>Moves the files from <code>/var/lib/docker</code> to <code>/opt/temp</code></li><li>Unmounts and removes the temporary volume and file</li><li>Moves the files back</li></ol><p>To increase the size of the temporary volume, in case you're copying in a bunch of base images, modify the <code>dd</code> line in <code>install.sh</code> to be an appropriate size. The size of this file doesn't affect your overall image since it's removed at the end.</p><p>To pull other images you will modify this file, and change the line that currently pulls <code>alpine:latest</code> to pull whatever images you would like.</p><p>Don't forget to mark this file as executable.</p><!--kg-card-begin: markdown--><pre><code class="language-bash">chmod +x install.sh </code></pre> <!--kg-card-end: markdown--><p>Build the image</p><p>Now we have all of the files in place. To build the image we will use <code>buildx</code>, our build context <code>builder</code>, allowing <code>security.insecure</code> and pushing into our main Docker context.</p><!--kg-card-begin: markdown--><pre><code class="language-bash">docker buildx build \ --allow security.insecure \ --output type=docker \ -t mydind \ --builder builder \ --progress=plain \ . </code></pre> <!--kg-card-end: markdown--><h3 id="test">Test</h3><p>To test this, we will start the docker image we just built and in another terminal, run <code>docker image ls</code> in that container and see the <code>alpine:latest</code> image is there.</p><h4 id="start-it">Start it</h4><!--kg-card-begin: markdown--><pre><code class="language-bash">docker run -it --rm --privileged --name dind mydind </code></pre> <!--kg-card-end: markdown--><h4 id="validate">Validate</h4><!--kg-card-begin: markdown--><pre><code class="language-bash">docker exec -i dind docker image ls </code></pre> <!--kg-card-end: markdown--><p>You should see this output</p><!--kg-card-begin: markdown--><pre><code class="language-text">REPOSITORY TAG IMAGE ID CREATED SIZE alpine latest c1aabb73d233 6 weeks ago 7.33MB </code></pre> <!--kg-card-end: markdown--><h2 id="caveat">Caveat</h2><p>If you want to create a volume for your <code>/var/lib/docker</code> directory you'll need to do a little more work. In the <code>install.sh</code> you won't want to move the files back to <code>/var/lib/docker</code>. Instead, you will need a new <code>entrypoint</code> script that checks to see if <code>/var/lib/docker</code> has anything in it and if not, recursively copy the <code>/opt/temp</code> over to that directory. After that, have the script execute <code>exec dockerd-entrypoint.sh $@</code>. </p><h2 id="conclusion">Conclusion</h2><p>I had a lot of fun with this. I like little projects where everyone says you can't do something. It makes it a fun challenge.</p><h2 id="links">Links</h2><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/EdwardCooke/prepull-dind"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - EdwardCooke/prepull-dind</div><div class="kg-bookmark-description">Contribute to EdwardCooke/prepull-dind development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt=""><span class="kg-bookmark-publisher">GitHub</span><span class="kg-bookmark-author">EdwardCooke</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/7055ce68fcddf1bdfcbeefbbbe87917afb01e13563ff3476159ea34a1ffef23b/EdwardCooke/prepull-dind" alt=""></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.docker.com/engine/reference/builder/#run---security"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Dockerfile reference</div><div class="kg-bookmark-description">Find all the available commands you can use in a Dockerfile and learn how to use them, including COPY, ARG, ENTRYPOINT, and more.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://docs.docker.com/assets/favicons/docs@2x.ico" alt=""><span class="kg-bookmark-publisher">Docker Documentation</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://docs.docker.com/assets/favicons/docs@2x.ico" alt=""></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://hub.docker.com/_/docker"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Docker</div><div class="kg-bookmark-description"></div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://hub.docker.com/favicon.ico" alt=""></div></div></a></figure> Edward Cooke https://www.frakkingsweet.com/author/edward Cloud-init, Hyper-V and a custom image Today I'm going to go over using Cloud-init with Hyper-V and customizing the base image. https://www.frakkingsweet.com/cloud-init-hyper-v-and-a-custom-image/ b3e387b3-219b-42e3-b743-fc1e561fd3ac Linux DevOps Edward Cooke Sun, 09 Jul 2023 17:36:17 GMT <p>Today I'm going to go over using <code>Cloud-init</code> with <code>Hyper-V</code> and customizing the base image.</p><h2 id="overview">Overview</h2><p>I build a lot of VM's and tear them down throughout a general work week, I am tired of spending a bunch of time baby-sitting an installer to enter the same information all the time. I wanted to make this a single script that I can run to create an entire environment.</p><p>To accomplish this I needed an image that has Cloud-init setup in it that works with Hyper-V. I chose the Ubuntu cloud image for Azure. Azure runs on Hyper-V so it fit well right out of the box, mostly. I do need to pull out some of the Azure specific customizations. I'll cover that as well.</p><p>To customize the image and to remove the Azure components you'll need to be an Administrator on your computer. You will need Windows Subsystem for Linux installed from the Microsoft Store. The reason you need WSL from the store is that we mount the Ubuntu cloud image inside of it so we can customize it. That feature is only in the Store instance of WSL.</p><p>To create the virtual machine you will need the Windows Assessment and Deployment Kit with the <code>Deployment Tools</code> feature. This is used to create the <code>iso</code> image that contains the Cloud-init configuration.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://learn.microsoft.com/en-us/windows-hardware/get-started/adk-install"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Download and install the Windows ADK</div><div class="kg-bookmark-description">Instructions on how to download and install the Windows ADK</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://learn.microsoft.com/favicon.ico" alt=""><span class="kg-bookmark-publisher">Microsoft Learn</span><span class="kg-bookmark-author">windows-driver-content</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://learn.microsoft.com/en-us/media/logos/logo-ms-social.png" alt=""></div></a></figure><h3 id="assumptions-with-this-post">Assumptions with this post</h3><ol><li>You'll be using the <code>Ubuntu Mantic</code> distribution.</li><li>You have admin rights on your computer</li><li>You have Windows Subsystem for Linux already setup and running with default settings and a distribution with <code>chroot</code>.</li><li>You're using a directory named  <code>c:\Cloud-init</code> to store everything.</li><li>You have the <code>Windows Assessment and Deployment Kit</code> with a minimum, the <code>Deployment Tools</code> feature installed at the default location of <code>C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit</code>.</li><li>You have this repository checked out</li></ol><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/EdwardCooke/hyperv-cloud-init/tree/main"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - EdwardCooke/hyperv-cloud-init: Easy scripts for creating a cloud-init based VM in Hyper-V</div><div class="kg-bookmark-description">Easy scripts for creating a cloud-init based VM in Hyper-V - GitHub - EdwardCooke/hyperv-cloud-init: Easy scripts for creating a cloud-init based VM in Hyper-V</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt=""><span class="kg-bookmark-publisher">GitHub</span><span class="kg-bookmark-author">EdwardCooke</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/16f28d7e63d382739b7972d8acba8259517813cecaff69a79ec2039101282ea6/EdwardCooke/hyperv-cloud-init" alt=""></div></a></figure><h2 id="create-the-base-image">Create the base image</h2><h3 id="download">Download</h3><p>First step in creating the base image, download the Azure Cloud-init image from Ubuntu's cloud image site linked here</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://cloud-images.ubuntu.com/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Ubuntu Cloud Images - the official Ubuntu images for public clouds, Openstack, KVM and LXD</div><div class="kg-bookmark-description">Ubuntu Cloud Images are the official Ubuntu images that have been customized by Canonical to run on public clouds that provide Ubuntu Certified Images, Openstack, KVM, LXD and more.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://assets.ubuntu.com/v1/49a1a858-favicon-32x32.png" alt=""><span class="kg-bookmark-publisher">the official Ubuntu images for public clouds, Openstack, KVM and LXD</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cloud-images.ubuntu.com/icons/blank.gif" alt=""></div></a></figure><p>I'm going to be picking on the latest one, <code>mantic</code>. The image name for the <code>mantic</code> version of Ubuntu is <code>mantic-server-cloudimg-amd64-azure.vhd.tar.gz</code>.</p><h3 id="extract-the-vhd">Extract the <code>vhd</code></h3><p>Now that you have that downloaded, extract the <code>vhd</code> file from that archive. You can use WSL to do this, or an application like 7-zip.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.7-zip.org/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">7-Zip</div><div class="kg-bookmark-description"></div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.7-zip.org/favicon.ico" alt=""></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.7-zip.org/7ziplogo.png" alt=""></div></a></figure><h3 id="customize-the-base-image">Customize the base image</h3><p>Now that you have the <code>vhd</code> extracted, we need to customize it.</p><p>Here is a link to the <code>create-vm.ps1</code> script that will convert the <code>vhd</code> to a dynamically sizing <code>vhdx</code>, mount the <code>vhdx</code> in WSL, remove the Azure specific components, and finally <code>chroot</code> into the mounted <code>vhdx</code> so you can customize it.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/EdwardCooke/hyperv-cloud-init/blob/main/set-cloudinit.ps1"><div class="kg-bookmark-content"><div class="kg-bookmark-title">hyperv-cloud-init/set-cloudinit.ps1 at main · EdwardCooke/hyperv-cloud-init</div><div class="kg-bookmark-description">Easy scripts for creating a cloud-init based VM in Hyper-V - hyperv-cloud-init/set-cloudinit.ps1 at main · EdwardCooke/hyperv-cloud-init</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt=""><span class="kg-bookmark-publisher">GitHub</span><span class="kg-bookmark-author">EdwardCooke</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/16f28d7e63d382739b7972d8acba8259517813cecaff69a79ec2039101282ea6/EdwardCooke/hyperv-cloud-init" alt=""></div></a></figure><p>You can execute this from where you checked out the repo in an administrator PowerShell window</p><!--kg-card-begin: markdown--><pre><code class="language-powershell">./set-cloudinit-ubuntu.ps1 -Source C:\cloud-init\livecd.ubuntu-cpc.azure.vhd -Destination C:\cloud-init\ubuntu-mantric.vhdx </code></pre> <!--kg-card-end: markdown--><p>You should be greeted with output that looks very similar to this.</p><!--kg-card-begin: markdown--><pre><code class="language-text">Converting image to a dynamic disk Mounting in WSL The disk was successfully mounted as '/mnt/wsl/61447da8-c86a-4526-b900-5048ec59d556'. Note: The location will be different if you have modified the automount.root setting in /etc/wsl.conf. To unmount and detach the disk, run 'wsl.exe --unmount \\?\C:\cloud-init\ubuntu-mantric.vhdx'. Modifying the cloud-init data source Removing the azure specific configuration Entering your image environment Press ctrl+d or type exit when you are done customizing your image root@edslaptop:/# </code></pre> <!--kg-card-end: markdown--><p>You are now in the <code>chroot</code> environment of your golden image. Customize as you see fit. Once you are happy with your image, press <code>ctrl+d</code> or type <code>exit</code> and press enter to exit the <code>chroot</code> environment.</p><p>After you exit the <code>chroot</code> environment, you should now be greeted with the following</p><!--kg-card-begin: markdown--><pre><code class="language-text">Unmounting from WSL The operation completed successfully. </code></pre> <!--kg-card-end: markdown--><p>Your golden image is now setup and ready to be cloned.</p><h2 id="building-your-virtual-machine">Building your virtual machine</h2><h3 id="setup-cloud-init-files">Setup <code>Cloud-init</code> files</h3><p>We're going to now setup some basic example Cloud-init files. You can find the example Cloud-init configuration in the <code>example</code> directory here</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/EdwardCooke/hyperv-cloud-init/tree/main/example"><div class="kg-bookmark-content"><div class="kg-bookmark-title">hyperv-cloud-init/example at main · EdwardCooke/hyperv-cloud-init</div><div class="kg-bookmark-description">Easy scripts for creating a cloud-init based VM in Hyper-V - hyperv-cloud-init/example at main · EdwardCooke/hyperv-cloud-init</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt=""><span class="kg-bookmark-publisher">GitHub</span><span class="kg-bookmark-author">EdwardCooke</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/16f28d7e63d382739b7972d8acba8259517813cecaff69a79ec2039101282ea6/EdwardCooke/hyperv-cloud-init" alt=""></div></a></figure><p>Those example files will show how to build a VM with the hostname of <code>example</code>, a single user with username/password of <code>example</code> and install <code>curl</code> and <code>ca-certificates</code>. It will also enable password based <code>ssh</code> login, which is disabled by default and upgrade all of the currently installed packages.</p><h3 id="use-the-cloud-init-configurations-and-build-the-vm">Use the Cloud-init configurations and build the VM</h3><p>Now that you have your Cloud-init configuration build, it's time to build the actual virtual machine. We'll use the <code>create-vm.ps1</code> script for that. It is very simple to use, here is the minimal usage of the script. It creates a virtual machine named <code>Test</code>, with a root disk of 30 gigs, using the <code>example</code> directory, the base image we just created and 4 gigs of memory.</p><!--kg-card-begin: markdown--><pre><code class="language-powershell">./create-vm.ps1 -VirtualMachineName Test -VirtualDiskSize 30 -CloudInitDirectory ./example -RootDiskPath C:\cloud-init\ubuntu-mantric.vhdx -MemorySize 4 </code></pre> <!--kg-card-end: markdown--><p>You should now be greeted with an output looking like this.</p><!--kg-card-begin: markdown--><pre><code class="language-text">Copying root disk from C:\cloud-init\ubuntu-mantric.vhdx to C:\ProgramData\Microsoft\Windows\Virtual Hard Disks\Test.vhdx Resizing virtual disk to 30 gigabytes Creating cloud init iso at C:\ProgramData\Microsoft\Windows\Virtual Hard Disks\Test-cidata.iso OSCDIMG 2.56 CD-ROM and DVD-ROM Premastering Utility Copyright (C) Microsoft, 1993-2012. All rights reserved. Licensed only for producing Microsoft authorized content. Scanning source tree Scanning source tree complete (2 files in 1 directories) Computing directory information complete Image file is 57344 bytes Writing 2 files in 1 directories to C:\ProgramData\Microsoft\Windows\Virtual Hard Disks\Test-cidata.iso 100% complete Final image file is 57344 bytes Done. Creating virtual machine Disabling dynamic memory Setting secure boot settings Adding cidata iso to the virtual machine Starting virtual machine Virtual machine creation complete </code></pre> <!--kg-card-end: markdown--><h3 id="check-your-virtual-machine">Check your virtual machine</h3><p>Open the <code>Hyper-V Manager</code> application and check your <code>Test</code> virtual machine, if it's not the basic <code>login</code> screen then wait a couple of minutes.</p><p>Once the <code>login</code> screen is up, try logging in with the <code>example</code> user, the password is also <code>example</code>.</p><h2 id="conclusion">Conclusion</h2><p>This is the under pinning of how to use Hyper-V, Cloud-init, PowerShell and WSL to customize a base image and actually use it in Hyper-V. Next post will be using PowerShell to easily create a set of Cloud-init files to quickly build a basic Ubuntu virtual machine.</p><p>This was a fun project to do. I'm glad I did it, even thought it was a little difficult to figure it all out.</p><p>Hopefully you find it useful as well.</p><h2 id="links">Links</h2><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://cloudinit.readthedocs.io/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">cloud-init 23.2.1 documentation</div><div class="kg-bookmark-description"></div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://cloudinit.readthedocs.io/favicon.ico" alt=""><span class="kg-bookmark-publisher">cloud-init 23.2.1 documentation</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cloudinit.readthedocs.io/_static/logo.png" alt=""></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://learn.microsoft.com/en-us/azure/virtual-machines/linux/create-upload-ubuntu"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Create and upload an Ubuntu Linux VHD in Azure - Azure Virtual Machines</div><div class="kg-bookmark-description">Learn to create and upload an Azure virtual hard disk (VHD) that contains an Ubuntu Linux operating system.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://learn.microsoft.com/favicon.ico" alt=""><span class="kg-bookmark-publisher">Microsoft Learn</span><span class="kg-bookmark-author">srijang</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://learn.microsoft.com/en-us/media/logos/logo-ms-social.png" alt=""></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://learn.microsoft.com/en-us/azure/virtual-machines/linux/create-upload-generic"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Prepare Linux for imaging - Azure Virtual Machines</div><div class="kg-bookmark-description">Learn to prepare a Linux system to be used for an image in Azure.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://learn.microsoft.com/favicon.ico" alt=""><span class="kg-bookmark-publisher">Microsoft Learn</span><span class="kg-bookmark-author">srijang</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://learn.microsoft.com/en-us/media/logos/logo-ms-social.png" alt=""></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/EdwardCooke/hyperv-cloud-init/tree/main"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - EdwardCooke/hyperv-cloud-init: Easy scripts for creating a cloud-init based VM in Hyper-V</div><div class="kg-bookmark-description">Easy scripts for creating a cloud-init based VM in Hyper-V - GitHub - EdwardCooke/hyperv-cloud-init: Easy scripts for creating a cloud-init based VM in Hyper-V</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt=""><span class="kg-bookmark-publisher">GitHub</span><span class="kg-bookmark-author">EdwardCooke</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/16f28d7e63d382739b7972d8acba8259517813cecaff69a79ec2039101282ea6/EdwardCooke/hyperv-cloud-init" alt=""></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://cloud-images.ubuntu.com/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Ubuntu Cloud Images - the official Ubuntu images for public clouds, Openstack, KVM and LXD</div><div class="kg-bookmark-description">Ubuntu Cloud Images are the official Ubuntu images that have been customized by Canonical to run on public clouds that provide Ubuntu Certified Images, Openstack, KVM, LXD and more.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://assets.ubuntu.com/v1/49a1a858-favicon-32x32.png" alt=""><span class="kg-bookmark-publisher">the official Ubuntu images for public clouds, Openstack, KVM and LXD</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cloud-images.ubuntu.com/icons/blank.gif" alt=""></div></a></figure> Edward Cooke https://www.frakkingsweet.com/author/edward MetalLB, Calico, BGP, and LoadBalancer IPs This post will cover exposing only your LoadBalancer service IP's to your BGP Peer. https://www.frakkingsweet.com/metallb-calico-bgp-loadbalancer-and-loadbalancer-ips/ 8a06285e-0542-4d85-85cc-28347c67e473 DevOps Kubernetes Edward Cooke Wed, 28 Jun 2023 17:37:23 GMT <p>This post will cover exposing only your <code>LoadBalancer</code> service IP's to your BGP Peer.</p><h2 id="problem-statement">Problem statement</h2><p>Configure MetalLB and Calico to expose your LoadBalancer services to your upstream BGP peered router</p><h2 id="summary">Summary</h2><p>Installing MetalLB is easy, so I'm not going to cover that, I will however, cover the configuration of it and Calico so that it will only expose only the IP's associated with your LoadBalancer services to your BGP peers.</p><p>According to the docs, in theory, setting <code>disableBGPExport</code> to <code>true</code> should do what you want, however, that has an unintended side effect. Traffic destined between your pods in your cluster will go out through your default gateway which makes it so pods can't communicate with one another nor the public internet. If you use Linux as your gateway, you may see kernel entries stating <code>IPv4 martian source</code>.</p><p>I'm going to show manifests using the <code>192.168.3.0/24</code> subnet for your <code>LoadBalancer</code> services, <code>64512</code> as your autonomous system ID, and <code>192.168.0.1</code> as your peer address. You'll want to update those values to fit your network accordingly. My examples also work for <code>local</code> as your <code>externalTrafficPolicy</code>.</p><p>I am not going to cover configuring your upstream router, that is very dependent on what you use and how your network is configured, you'll need to research that if you don't know how to do it.</p><p>Another thing you may notice, my manifests use <code>crd.projectcalico.org/v1</code> and not what the docs from Tigera show, <code>projectcalico.org/v3</code>. The reason being, that API doesn't exist in the installed manifests, I don't know why they show that API in their docs, but, they do.</p><h2 id="solution">Solution</h2><h3 id="install-metallb">Install MetalLB</h3><p>First step is installing MetalLB, we'll cover the configuration, so go ahead and install it.</p><p>You can get that information from the MetalLB site:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://metallb.universe.tf/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">MetalLB, bare metal load-balancer for Kubernetes</div><div class="kg-bookmark-description"></div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://metallb.universe.tf/images/favicon.png" alt=""></div></div><div class="kg-bookmark-thumbnail"><img src="https://metallb.universe.tf/images/logo/metallb-white.png" alt=""></div></a></figure><h3 id="configure-metallbs-ip-address-pool">Configure MetalLB's IP address pool</h3><p>Next step is creating the <code>IPAddressPool</code>. Here's the manifest for that, it's simple and only contains the <code>addresses</code> option. We don't need, nor want, any BGP configuration for MetalLB. That is all handled by Calico.</p><!--kg-card-begin: markdown--><pre><code class="language-yaml">apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: default namespace: metallb-system spec: addresses: - 192.168.3.0/24 </code></pre> <!--kg-card-end: markdown--><h3 id="setup-a-calico-bgp-filter">Setup a Calico BGP filter</h3><p>Next up is we need to create a filter for what Calico will send to the upstream BGP peer. Here we will allow everything in the <code>192.168.3.0/24</code> subnet.</p><!--kg-card-begin: markdown--><pre><code class="language-yaml">apiVersion: crd.projectcalico.org/v1 kind: BGPFilter metadata: name: default spec: exportV4: - action: Reject matchOperator: NotIn cidr: 192.168.3.0/24 </code></pre> <!--kg-card-end: markdown--><h3 id="setup-a-calico-bgp-peer">Setup a Calico BGP peer</h3><p>Now that we have our filter blocking everything except <code>192.168.3.0/24</code> we'll create the <code>BGPPeer</code>, referencing the filter we just created.</p><!--kg-card-begin: markdown--><pre><code class="language-yaml">apiVersion: crd.projectcalico.org/v1 kind: BGPPeer metadata: name: default spec: peerIP: 192.168.0.1 asNumber: 64512 keepOriginalNextHop: false filters: - default </code></pre> <!--kg-card-end: markdown--><h3 id="setup-the-calico-bgp-configuration">Setup the Calico BGP Configuration</h3><p>Now that we have a peer, create the <code>BGPConfiguration</code>. We add each individual IP in the subnet for a reason. If you do the entire subnet, <code>192.168.3.0/24</code> instead, it will push that entire subnet to all your nodes. If you want to use the <code>local</code> type in your service, that won't work since that requires the pod to be on the node that the traffic is coming in on. The <code>externalTrafficPolicy</code> set to <code>local</code> on the service is required to preserve the client IP address. Without that it will show the <code>kubeproxy</code> address running on the node.</p><p>For a <code>/24</code> subnet you can use this handy little bash command to generate those entries, replace the <code>192.168.3</code> with your subnet.</p><!--kg-card-begin: markdown--><pre><code class="language-bash">for i in $(seq 1 255); do echo &quot; - cidr: 192.168.3.$i/32&quot;; done </code></pre> <!--kg-card-end: markdown--><!--kg-card-begin: markdown--><pre><code class="language-yaml">apiVersion: crd.projectcalico.org/v1 kind: BGPConfiguration metadata: name: default spec: logSeverityScreen: Info asNumber: 64512 listenPort: 178 serviceLoadBalancerIPs: - cidr: 192.168.3.0/32 - cidr: 192.168.3.1/32 - cidr: 192.168.3.2/32 - cidr: 192.168.3.3/32 - cidr: 192.168.3.4/32 - cidr: 192.168.3.5/32 - cidr: 192.168.3.6/32 - cidr: 192.168.3.7/32 - cidr: 192.168.3.8/32 - cidr: 192.168.3.9/32 - cidr: 192.168.3.10/32 - cidr: 192.168.3.11/32 - cidr: 192.168.3.12/32 - cidr: 192.168.3.13/32 - cidr: 192.168.3.14/32 - cidr: 192.168.3.15/32 - cidr: 192.168.3.16/32 - cidr: 192.168.3.17/32 - cidr: 192.168.3.18/32 - cidr: 192.168.3.19/32 - cidr: 192.168.3.20/32 - cidr: 192.168.3.21/32 - cidr: 192.168.3.22/32 - cidr: 192.168.3.23/32 - cidr: 192.168.3.24/32 - cidr: 192.168.3.25/32 - cidr: 192.168.3.26/32 - cidr: 192.168.3.27/32 - cidr: 192.168.3.28/32 - cidr: 192.168.3.29/32 - cidr: 192.168.3.30/32 - cidr: 192.168.3.31/32 - cidr: 192.168.3.32/32 - cidr: 192.168.3.33/32 - cidr: 192.168.3.34/32 - cidr: 192.168.3.35/32 - cidr: 192.168.3.36/32 - cidr: 192.168.3.37/32 - cidr: 192.168.3.38/32 - cidr: 192.168.3.39/32 - cidr: 192.168.3.40/32 - cidr: 192.168.3.41/32 - cidr: 192.168.3.42/32 - cidr: 192.168.3.43/32 - cidr: 192.168.3.44/32 - cidr: 192.168.3.45/32 - cidr: 192.168.3.46/32 - cidr: 192.168.3.47/32 - cidr: 192.168.3.48/32 - cidr: 192.168.3.49/32 - cidr: 192.168.3.50/32 - cidr: 192.168.3.51/32 - cidr: 192.168.3.52/32 - cidr: 192.168.3.53/32 - cidr: 192.168.3.54/32 - cidr: 192.168.3.55/32 - cidr: 192.168.3.56/32 - cidr: 192.168.3.57/32 - cidr: 192.168.3.58/32 - cidr: 192.168.3.59/32 - cidr: 192.168.3.60/32 - cidr: 192.168.3.61/32 - cidr: 192.168.3.62/32 - cidr: 192.168.3.63/32 - cidr: 192.168.3.64/32 - cidr: 192.168.3.65/32 - cidr: 192.168.3.66/32 - cidr: 192.168.3.67/32 - cidr: 192.168.3.68/32 - cidr: 192.168.3.69/32 - cidr: 192.168.3.70/32 - cidr: 192.168.3.71/32 - cidr: 192.168.3.72/32 - cidr: 192.168.3.73/32 - cidr: 192.168.3.74/32 - cidr: 192.168.3.75/32 - cidr: 192.168.3.76/32 - cidr: 192.168.3.77/32 - cidr: 192.168.3.78/32 - cidr: 192.168.3.79/32 - cidr: 192.168.3.80/32 - cidr: 192.168.3.81/32 - cidr: 192.168.3.82/32 - cidr: 192.168.3.83/32 - cidr: 192.168.3.84/32 - cidr: 192.168.3.85/32 - cidr: 192.168.3.86/32 - cidr: 192.168.3.87/32 - cidr: 192.168.3.88/32 - cidr: 192.168.3.89/32 - cidr: 192.168.3.90/32 - cidr: 192.168.3.91/32 - cidr: 192.168.3.92/32 - cidr: 192.168.3.93/32 - cidr: 192.168.3.94/32 - cidr: 192.168.3.95/32 - cidr: 192.168.3.96/32 - cidr: 192.168.3.97/32 - cidr: 192.168.3.98/32 - cidr: 192.168.3.99/32 - cidr: 192.168.3.100/32 - cidr: 192.168.3.101/32 - cidr: 192.168.3.102/32 - cidr: 192.168.3.103/32 - cidr: 192.168.3.104/32 - cidr: 192.168.3.105/32 - cidr: 192.168.3.106/32 - cidr: 192.168.3.107/32 - cidr: 192.168.3.108/32 - cidr: 192.168.3.109/32 - cidr: 192.168.3.110/32 - cidr: 192.168.3.111/32 - cidr: 192.168.3.112/32 - cidr: 192.168.3.113/32 - cidr: 192.168.3.114/32 - cidr: 192.168.3.115/32 - cidr: 192.168.3.116/32 - cidr: 192.168.3.117/32 - cidr: 192.168.3.118/32 - cidr: 192.168.3.119/32 - cidr: 192.168.3.120/32 - cidr: 192.168.3.121/32 - cidr: 192.168.3.122/32 - cidr: 192.168.3.123/32 - cidr: 192.168.3.124/32 - cidr: 192.168.3.125/32 - cidr: 192.168.3.126/32 - cidr: 192.168.3.127/32 - cidr: 192.168.3.128/32 - cidr: 192.168.3.129/32 - cidr: 192.168.3.130/32 - cidr: 192.168.3.131/32 - cidr: 192.168.3.132/32 - cidr: 192.168.3.133/32 - cidr: 192.168.3.134/32 - cidr: 192.168.3.135/32 - cidr: 192.168.3.136/32 - cidr: 192.168.3.137/32 - cidr: 192.168.3.138/32 - cidr: 192.168.3.139/32 - cidr: 192.168.3.140/32 - cidr: 192.168.3.141/32 - cidr: 192.168.3.142/32 - cidr: 192.168.3.143/32 - cidr: 192.168.3.144/32 - cidr: 192.168.3.145/32 - cidr: 192.168.3.146/32 - cidr: 192.168.3.147/32 - cidr: 192.168.3.148/32 - cidr: 192.168.3.149/32 - cidr: 192.168.3.150/32 - cidr: 192.168.3.151/32 - cidr: 192.168.3.152/32 - cidr: 192.168.3.153/32 - cidr: 192.168.3.154/32 - cidr: 192.168.3.155/32 - cidr: 192.168.3.156/32 - cidr: 192.168.3.157/32 - cidr: 192.168.3.158/32 - cidr: 192.168.3.159/32 - cidr: 192.168.3.160/32 - cidr: 192.168.3.161/32 - cidr: 192.168.3.162/32 - cidr: 192.168.3.163/32 - cidr: 192.168.3.164/32 - cidr: 192.168.3.165/32 - cidr: 192.168.3.166/32 - cidr: 192.168.3.167/32 - cidr: 192.168.3.168/32 - cidr: 192.168.3.169/32 - cidr: 192.168.3.170/32 - cidr: 192.168.3.171/32 - cidr: 192.168.3.172/32 - cidr: 192.168.3.173/32 - cidr: 192.168.3.174/32 - cidr: 192.168.3.175/32 - cidr: 192.168.3.176/32 - cidr: 192.168.3.177/32 - cidr: 192.168.3.178/32 - cidr: 192.168.3.179/32 - cidr: 192.168.3.180/32 - cidr: 192.168.3.181/32 - cidr: 192.168.3.182/32 - cidr: 192.168.3.183/32 - cidr: 192.168.3.184/32 - cidr: 192.168.3.185/32 - cidr: 192.168.3.186/32 - cidr: 192.168.3.187/32 - cidr: 192.168.3.188/32 - cidr: 192.168.3.189/32 - cidr: 192.168.3.190/32 - cidr: 192.168.3.191/32 - cidr: 192.168.3.192/32 - cidr: 192.168.3.193/32 - cidr: 192.168.3.194/32 - cidr: 192.168.3.195/32 - cidr: 192.168.3.196/32 - cidr: 192.168.3.197/32 - cidr: 192.168.3.198/32 - cidr: 192.168.3.199/32 - cidr: 192.168.3.200/32 - cidr: 192.168.3.201/32 - cidr: 192.168.3.202/32 - cidr: 192.168.3.203/32 - cidr: 192.168.3.204/32 - cidr: 192.168.3.205/32 - cidr: 192.168.3.206/32 - cidr: 192.168.3.207/32 - cidr: 192.168.3.208/32 - cidr: 192.168.3.209/32 - cidr: 192.168.3.210/32 - cidr: 192.168.3.211/32 - cidr: 192.168.3.212/32 - cidr: 192.168.3.213/32 - cidr: 192.168.3.214/32 - cidr: 192.168.3.215/32 - cidr: 192.168.3.216/32 - cidr: 192.168.3.217/32 - cidr: 192.168.3.218/32 - cidr: 192.168.3.219/32 - cidr: 192.168.3.220/32 - cidr: 192.168.3.221/32 - cidr: 192.168.3.222/32 - cidr: 192.168.3.223/32 - cidr: 192.168.3.224/32 - cidr: 192.168.3.225/32 - cidr: 192.168.3.226/32 - cidr: 192.168.3.227/32 - cidr: 192.168.3.228/32 - cidr: 192.168.3.229/32 - cidr: 192.168.3.230/32 - cidr: 192.168.3.231/32 - cidr: 192.168.3.232/32 - cidr: 192.168.3.233/32 - cidr: 192.168.3.234/32 - cidr: 192.168.3.235/32 - cidr: 192.168.3.236/32 - cidr: 192.168.3.237/32 - cidr: 192.168.3.238/32 - cidr: 192.168.3.239/32 - cidr: 192.168.3.240/32 - cidr: 192.168.3.241/32 - cidr: 192.168.3.242/32 - cidr: 192.168.3.243/32 - cidr: 192.168.3.244/32 - cidr: 192.168.3.245/32 - cidr: 192.168.3.246/32 - cidr: 192.168.3.247/32 - cidr: 192.168.3.248/32 - cidr: 192.168.3.249/32 - cidr: 192.168.3.250/32 - cidr: 192.168.3.251/32 - cidr: 192.168.3.252/32 - cidr: 192.168.3.253/32 - cidr: 192.168.3.254/32 </code></pre> <!--kg-card-end: markdown--><h3 id="configure-a-service-with-a-local-external-traffic-policy">Configure a service with a local external traffic policy</h3><p>Here is an example of a <code>local</code> external traffic policy service, this will get assigned an IP address from the allowed pool and that IP will then go up to your BGP peer.</p><!--kg-card-begin: markdown--><pre><code class="language-yaml">apiVersion: v1 kind: Service metadata: name: lb namespace: default spec: ports: - port: 80 protocol: TCP targetPort: 2368 selector: my: selector type: LoadBalancer externalTrafficPolicy: Local </code></pre> <!--kg-card-end: markdown--><h2 id="conclusion">Conclusion</h2><p>That should be all there is to exposing your <code>local</code> <code>LoadBalancer</code> services using MetalLB and Calico with BGP configuring your upstream router.</p><p>Setting this up was fun and interesting. I learned a lot about BGP and FRR, that's what I used on my Linux router for this project. Figuring out some of the nuances and inconsistencies in the documentation was a bit challenging, but, that's what these posts are for right?</p><h2 id="links">Links</h2><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://metallb.universe.tf/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">MetalLB, bare metal load-balancer for Kubernetes</div><div class="kg-bookmark-description"></div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://metallb.universe.tf/images/favicon.png" alt=""></div></div><div class="kg-bookmark-thumbnail"><img src="https://metallb.universe.tf/images/logo/metallb-white.png" alt=""></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.tigera.io/calico/latest/networking/configuring/bgp"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Configure BGP peering | Calico Documentation</div><div class="kg-bookmark-description">Configure BGP peering with full mesh, node-specific peering, ToR, and/or Calico route reflectors.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://docs.tigera.io/img/favicon.png" alt=""><span class="kg-bookmark-publisher">Calico Documentation</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://d33wubrfki0l68.cloudfront.net/3df9045b2c359113fa20f08d15f0599ae6abbc18/eb989/img/brands/ebpf_logo.svg" alt=""></div></a></figure>