<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en_US"><generator uri="https://jekyllrb.com/" version="3.8.5">Jekyll</generator><link href="https://microcosem.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://microcosem.github.io/" rel="alternate" type="text/html" hreflang="en_US" /><updated>2020-01-26T20:41:49-05:00</updated><id>https://microcosem.github.io/feed.xml</id><title type="html">MicrocosEm</title><subtitle>Site description.</subtitle><author><name>MicrocosEm</name></author><entry xml:lang="en_US"><title type="html">No, but really—what is DevOps?</title><link href="https://microcosem.github.io/2019/12/29/no-but-really-what-is-devops.html" rel="alternate" type="text/html" title="No, but really—what is DevOps?" /><published>2019-12-29T00:00:00-05:00</published><updated>2019-12-29T00:00:00-05:00</updated><id>https://microcosem.github.io/2019/12/29/no-but-really-what-is-devops</id><content type="html" xml:base="https://microcosem.github.io/2019/12/29/no-but-really-what-is-devops.html">&lt;p&gt;In the Art World where I hail from, especially in the drastically underfunded Art World, collaboration is a necessary practice. In smaller art spaces such as non-profit galleries and residency programs, would-be teams often blend together into one mass, where the boundaries of a person’s roles or skills are blurred together. Don’t know how to spackle a wall? Time to learn, ‘cause we need extra hands. Never mounted a photograph on acid-free paper? Get that measuring tape and razor out. Oh, and don’t forget, we’re &lt;em&gt;all&lt;/em&gt; going to be planning events, managing artists, and taking care of the day-to-day paperwork together. Even the more office-based positions I’ve held in the Art World have been like this, to a large extent.&lt;/p&gt;

&lt;p&gt;This spirit of hypercollaboration is usually admirable, intrinsically comes packed with a series of learning experiences, and absolutely stokes a sense of community within the organization.&lt;/p&gt;

&lt;p&gt;In the more traditional Tech World, in my experience, the starting point is the other end of the (what I suppose we’re calling) spectrum. Roles are defined in high contrast: the technological limitations to working across team boundaries are matched only by their cultural counterparts.&lt;/p&gt;

&lt;p&gt;Coming into this profession more formally during the rise of the DevOps era has therefore been a very interesting experience. It’s clear to me that, over at least the past 15 years if not longer, developers and operations people (i.e., system administrators) have enjoyed a kind of self-satisfying identity knowing that they’re one, and not the other. On the better side, this has bred &lt;a href=&quot;https://www.youtube.com/watch?v=TnCY6Apxibk&quot;&gt;enjoyable jokes&lt;/a&gt;. On the worse side, it shatters the kind of collaborative flow that I had become so accustomed to in the Art World for so long.&lt;/p&gt;

&lt;p&gt;This isn’t to say that that Art World collaboration was always the best, either. Not knowing where your professional responsibilities and your commitments to a community begin and end can make it tough to know where to set boundaries. This can lead to an imbalance of work; where there is no formal structure, we must rely on our social structures to make up the difference in what’s acceptable in a micro-society like a job or other project involving others. Without explicit attentiveness to the way those social structures rush in, without giving form to what pushes down the dam, a practice that takes a lot of time and dedication, people end up drowning in their own workload. So, entering into a Tech World space where those roles were so definitively etched out provided some comfort from that as a possibility.&lt;/p&gt;

&lt;p&gt;What excites me most about the DevOps ideology is that it provides a bridge between the purely communal and the purely precise.&lt;/p&gt;

&lt;p&gt;But what has been strikingly apparent to me lately is how misunderstood DevOps, as a term, concept, or even as a role, seems to be understood.This is usually true of any new idea as it is encoded into practice.&lt;/p&gt;

&lt;p&gt;I’ve encountered a few places now where “DevOps” just seems to be a buzzwordy replacement for a developer. Maybe sometimes a developer who knows something about Docker, or networking, or Linux.&lt;/p&gt;

&lt;p&gt;In other places, the concept is driven by the idea of automating infrastructure, which is closer; but then, the infrastructure is only accounted for on the Ops side (which is really, technically, more like SysOps or Site Reliability Engineering). Don’t get me wrong: being that my origins are strongly system administrative, I love SysOps/SRE work. But, after having been exposed enough to the differences between that and DevOps, they’re truly not the same thing.&lt;/p&gt;

&lt;p&gt;So, just what is DevOps, anyway?&lt;/p&gt;

&lt;p&gt;DevOps is the definition of a role that goes between roles. Specifically, it is the work of &lt;em&gt;automating the development process.&lt;/em&gt; This means that DevOps engineers must work closely with developers in order to automate their development environments, ensuring consistency across stages. DevOps as a method is intended to make the entire development process faster, easier, more consistent, and, via those principles, also more secure. In engineering, I think one of the greatest things to have is &lt;em&gt;mobility&lt;/em&gt;. In this way, the DevOps mascot maybe ought to be a hummingbird: no directionality is unavailable to them.&lt;/p&gt;

&lt;p&gt;It means not having to rely on local environments, all of which are as different as the human beings working on them, because &lt;a href=&quot;https://youtu.be/HoLs0V8T5AA?t=42&quot;&gt;we have the technology&lt;/a&gt; to work in abstractions of abstractions, ones where we have all the control. And if that fails, it means the ability to roll back easily after a faulty deploy. It can mean &lt;em&gt;portability&lt;/em&gt;, the ability to hand an identical Vagrantfile to multiple developers and be confident that there will be far fewer inscrutable “works on my machine” issues.&lt;/p&gt;

&lt;p&gt;It is perhaps easier to understand culturally, rather than technically, where the offered definitions of DevOps are often far too tool-dependent: rather than isolating themselves away and focusing purely on infrastructure design and maintenance, DevOps pulls the system administrator into, if not literal code, the &lt;em&gt;idea&lt;/em&gt; of code: that is literally what is meant by “Infrastructure as Code”.&lt;/p&gt;

&lt;p&gt;I think the single most important distinction, though, and the entire driving force behind this blog post here, is the distinction between an SRE and a DevOps Engineer.&lt;/p&gt;

&lt;p&gt;SREs are the shepards of deployments onto and automation of &lt;em&gt;infrastructure&lt;/em&gt; they know backwards and forwards. DevOps is the sheparding of automated &lt;em&gt;environments&lt;/em&gt; for the ease of development.&lt;/p&gt;

&lt;p&gt;Unlike &lt;em&gt;developers&lt;/em&gt; who are writing code all the time and absolutely need to know their language(s), DevOps Engineers don’t &lt;em&gt;need&lt;/em&gt; to be experts in any programming languages (except in specific circumstances), but they should have static familarity with more than one. They &lt;em&gt;need&lt;/em&gt; to be comfortable with Linux, with the idea of automation, and with the concepts of abstraction. They &lt;em&gt;need&lt;/em&gt; to be comfortable with the concept of code, or at least the idea of declarative languages and stateful/lessness.&lt;/p&gt;

&lt;p&gt;DevOps Engineers are also required, far more I think than traditional system administrators, to be collaborative with the development team. (It’d be mighty hard to write infrastructure automation for the development team when you don’t even know what the development team is doing.)&lt;/p&gt;

&lt;p&gt;So, really, what is DevOps?&lt;/p&gt;

&lt;p&gt;It’s not just a role, it’s not just a title: it’s a method. It’s the enacted belief that infrastructural operations and development are reliant upon each other. Given the truth of that, DevOps is an attempt to (re)bridge two sides of the same coin that have been artificially distinct from one another for too long.&lt;/p&gt;

&lt;p&gt;Now, is it even useful to argue the semantic finer points? In some other contexts, I might say no. But in a work context (and DevOps is a job-based term), it affects the exact thing I mentioned earlier: knowing your role. I guess that’s what got me to writing this today: a process of disambiguation is needed to understand what role I’m in at the moment, or what hat I’ve got on that day. I might be a SysOps engineer one day, a DevOps engineer the next. The fluidity of these identities is okay so long as the distinction between roles is clear, paving the way for clarity of communication (big for me), as well as self-advocacy on the job.&lt;/p&gt;

&lt;p&gt;Moreover, this is an exciting field, and an exciting time to be in this field, but it can get confusing fast without enough exposure to what other people think the title is all about.&lt;/p&gt;

&lt;p&gt;Happy automating!&lt;/p&gt;</content><author><name>MicrocosEm</name></author><summary type="html">In the Art World where I hail from, especially in the drastically underfunded Art World, collaboration is a necessary practice. In smaller art spaces such as non-profit galleries and residency programs, would-be teams often blend together into one mass, where the boundaries of a person’s roles or skills are blurred together. Don’t know how to spackle a wall? Time to learn, ‘cause we need extra hands. Never mounted a photograph on acid-free paper? Get that measuring tape and razor out. Oh, and don’t forget, we’re all going to be planning events, managing artists, and taking care of the day-to-day paperwork together. Even the more office-based positions I’ve held in the Art World have been like this, to a large extent.</summary></entry><entry xml:lang="en_US"><title type="html">A quick and easy guide to using Caddy as a load balancer on Google Cloud Platform</title><link href="https://microcosem.github.io/2019/12/19/a-quick-and-easy-guide-to-using-caddy-as-a-load-balancer-on-google-cloud-platform.html" rel="alternate" type="text/html" title="A quick and easy guide to using Caddy as a load balancer on Google Cloud Platform" /><published>2019-12-19T00:00:00-05:00</published><updated>2019-12-19T00:00:00-05:00</updated><id>https://microcosem.github.io/2019/12/19/a-quick-and-easy-guide-to-using-caddy-as-a-load-balancer-on-google-cloud-platform</id><content type="html" xml:base="https://microcosem.github.io/2019/12/19/a-quick-and-easy-guide-to-using-caddy-as-a-load-balancer-on-google-cloud-platform.html">&lt;p&gt;When I was tinkering with deploying a Caddy container as a Load Balancer for an application container on GCP, I tripped over a few basic things that I think are probably pretty common confusions when you’re just getting started. Most of the guides I found online didn’t really consider some of these things, so, after successfully deploying this for myself, I figured I would write a quick-‘n-easy guide about this in the hopes to help anyone else who’s having trouble with this.&lt;/p&gt;

&lt;h2 id=&quot;notes&quot;&gt;Notes&lt;/h2&gt;

&lt;p&gt;In thinking about starting a blog that includes technical posts, one of the things I frequently think about is what pieces of context might be useful for my potential readers (including future versions of myself!) to determine whether or not the content will actually be useful to them and their case. So, as this will be my first published post containing such content, I’m going to start by outlining the assumptions the rest of this post makes.&lt;/p&gt;

&lt;p&gt;This post is written with the assumption that you have some basic or working knowledge of both Kubernetes and of GCP &lt;a href=&quot;https://cloud.google.com/getting-started&quot;&gt;Google Cloud Platform&lt;/a&gt;. It also assumes, for the sake of issue scoping, that you’re trying to do the following in a running cluster on GCP. The same principles likely hold for EKS (AWS’s managed Kubernetes platform), and for a self-hosted solution, you’ll have to consider how this could map onto whatever you’re using for your networking (this may be something like a network policy manager such as &lt;a href=&quot;https://www.projectcalico.org/calico-networking-for-kubernetes/&quot;&gt;Calico&lt;/a&gt; or &lt;a href=&quot;https://github.com/coreos/flannel&quot;&gt;Flannel&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Our &lt;strong&gt;assumed environment&lt;/strong&gt; is therefore:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A working application container…&lt;/li&gt;
  &lt;li&gt;…running in a working Kubernetes cluster…&lt;/li&gt;
  &lt;li&gt;…hosted on GCP.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will also assume that the &lt;strong&gt;end goal&lt;/strong&gt; is, given the above stated context:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;To create a working Caddy container image…&lt;/li&gt;
  &lt;li&gt;…built using Docker…&lt;/li&gt;
  &lt;li&gt;…functioning as a Load Balancer…&lt;/li&gt;
  &lt;li&gt;…with HTTPS enabled.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;caddy&quot;&gt;Caddy&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This guide deals with Caddy V.1.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://caddyserver.com/v1/docs&quot;&gt;Caddy&lt;/a&gt; is a lightweight Web server whose primary claim to fame is that it uses the &lt;a href=&quot;https://github.com/mholt/certmagic&quot;&gt;CertMagic&lt;/a&gt; library to handle &lt;a href=&quot;https://caddyserver.com/v1/docs/automatic-https&quot;&gt;&lt;em&gt;automatic&lt;/em&gt; HTTPS&lt;/a&gt; for your Web server. It’s a really elegant solution to helping encrypt more of the Web, and is a favorite among DevOps engineers and system administrators for testing purposes when you want to make sure HTTPS will work. It’s particularly nice that you can set up Caddy to get certificates from Let’s Encrypt’s &lt;a href=&quot;https://letsencrypt.org/docs/staging-environment/&quot;&gt;staging environment&lt;/a&gt;, so that you can test a ton and not be limited by LE by accidentally throttling their servers.&lt;/p&gt;

&lt;p&gt;So, how do we use this in Kubernetes, specifically, GCP, which has its own networking “gotchya!”s that need to be considered?&lt;/p&gt;

&lt;h2 id=&quot;containers-and-kubernetes&quot;&gt;Containers and Kubernetes&lt;/h2&gt;

&lt;p&gt;Let’s start with the Caddy side of things. If you’re at all familiar with the basics of Kubernetes, it’ll probably be obvious what the first steps are in getting your Caddy container to work is to have the container to begin with. This means that first, we’ll have to make ourselves a container from an image, ideally one that is customized for our purposes.&lt;/p&gt;

&lt;p&gt;Caddy calls its configuration files &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Caddyfiles&lt;/code&gt;. In my case, I wanted to write a simple Caddyfile that would trigger the automatic HTTPS capability as well as act as a proxy for my actual Web container.&lt;/p&gt;

&lt;p&gt;Caddy’s &lt;a href=&quot;https://caddyserver.com/v1/docs/automatic-https&quot;&gt;requirements for automatic HTTPS&lt;/a&gt; are pretty simple:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The hostname:
    &lt;ul&gt;
      &lt;li&gt;is not empty&lt;/li&gt;
      &lt;li&gt;is not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;is not an IP address&lt;/li&gt;
      &lt;li&gt;has no more than 1 wildcard (*)&lt;/li&gt;
      &lt;li&gt;wildcard must be left-most label&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The port is not explicitly 80&lt;/li&gt;
  &lt;li&gt;The scheme is not explicitly http&lt;/li&gt;
  &lt;li&gt;TLS is not turned off in site’s definition&lt;/li&gt;
  &lt;li&gt;Certificates and keys are not provided by you&lt;/li&gt;
  &lt;li&gt;Caddy is able to bind to ports 80 and 443 (unless you use the DNS challenge)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So long as all of the above criteria are met, Caddy &lt;em&gt;should&lt;/em&gt; be able to automatically issue HTTPS certificates. These are all basically what you might expect to be true for the issuing of a TLS certificate.&lt;/p&gt;

&lt;p&gt;In my case, my Caddyfile ended up looking like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mydomain.site {
    proxy / myapp-web:80 {
        transparent
        websocket
    }
    log stdout
    errors stdout
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A couple of things are somewhat interesting about this Caddyfile. For starters, you ought to notice that the domain name for the website, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mydomain.site&lt;/code&gt;, is &lt;em&gt;not&lt;/em&gt; prepended with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http&lt;/code&gt; scheme. As we were told by Caddy, the scheme for our domain name must &lt;em&gt;not&lt;/em&gt; be explicitly &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http&lt;/code&gt; if we want to get automatic TLS. So, check.&lt;/p&gt;

&lt;p&gt;Perhaps the more interesting thing, though, and the detail possibly more relevant to our specific situation, is the domain to which we are proxying. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;myapp-web&lt;/code&gt; is actually going to be the hostname from within the VPC, which the container is automatically assigned based on the labels of your Kubernetes manifest. Here, we explicitly define that the port for the service &lt;em&gt;to which&lt;/em&gt; we will be &lt;em&gt;proxying&lt;/em&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;80&lt;/code&gt;. Meanwhile, we’ve of course left off the port for our main host at the top.&lt;/p&gt;

&lt;p&gt;Following our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proxy&lt;/code&gt; line, we’ve got a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transparent&lt;/code&gt; preset, which is actually just shorthand for &lt;a href=&quot;https://caddyserver.com/v1/docs/proxy&quot;&gt;the more familiar proxy pass headers&lt;/a&gt;, as well as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;websocket&lt;/code&gt; preset, which means we’re telling Caddy to proxy forward WebSocket connections from our proxy.&lt;/p&gt;

&lt;p&gt;Then we have some pretty clear lines telling us what’s going to happen to our error and access logs (they’ll be sent to standard out).&lt;/p&gt;

&lt;p&gt;And that’s really all there is to it! I think that’s the magic of Caddy: its simplicity. The double-edged sword factor comes in only with the fact that Caddy is magicking away a lot of things, and you should probably know what those things are actually collapsing to (eg. the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transparent&lt;/code&gt; preset). But if you’re already comfortable with Web servers, then this is really a nice way of creating a simple, straightforward, and lightweight server.&lt;/p&gt;

&lt;p&gt;In just a moment, we’ll look at passing in our options for using the Let’s Encrypt staging environment and running our process. (If you’re wondering about Caddy’s ability to use DNS challenges rather than HTTP challenges, you can read more about that &lt;a href=&quot;https://github.com/caddyserver/dnsproviders&quot;&gt;here&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;Okay, so now that we’ve got what we presume to be a working Caddyfile, the next step in our process is to containerize that bad boy in order to make sure that our configuration works so that we have a base image to use for K8s.&lt;/p&gt;

&lt;p&gt;My Dockerfile for the Caddy container (and image) looked like this:&lt;/p&gt;

&lt;div class=&quot;language-docker highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; abiosoft/caddy&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; Caddyfile /etc/Caddyfile&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;/usr/bin/caddy&quot;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;--conf&quot;, &quot;/etc/Caddyfile&quot;, &quot;-ca&quot;, &quot;https://acme-staging-v02.api.letsencrypt.org/directory&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Again, lovely in its simplicity. Here, we base our container image on the original Caddy container, grabbing that Caddy source code for our ultimate process. Next, we copy the Caddyfile from our current working directory and put it into a reasonable location within the container’s filesystem, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/Caddyfile&lt;/code&gt;. Then, we want to make sure that we run the Caddy process by placing its executable as our entrypoint, which is the thing that will be run as our primary process.&lt;/p&gt;

&lt;p&gt;At last, we pass in the options to the process (Caddy) to configure it. The options we pass in here are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--conf&lt;/code&gt; - Letting the Caddy process know which configuration file we want it to use.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/Caddyfile&lt;/code&gt; - The path to the Caddyfile we want used.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-ca&lt;/code&gt; - This is our option to Caddy where we’ll define what CA (Certificate Authority) we want our server to use. This is absolutely necessary if we want to explicitly set the location for a testing CA, which, we do.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://acme-staging-v02.api.letsencrypt.org/directory&lt;/code&gt; - Finally, the URL to the Let’s Encrypt staging envrionment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As Caddy’s docs say:&lt;/p&gt;

&lt;blockquote cite=&quot;https://caddyserver.com/v1/docs/automatic-https&quot;&gt;
  &lt;p&gt;To test or experiment with your Caddy configuration, make sure you use the -ca flag to change the ACME endpoint to a staging or development URL, otherwise you are likely to hit rate limits which can block your access to HTTPS for up to a week. This is especially common when using process managers or containers. Caddy’s default CA is Let’s Encrypt, which has a staging endpoint that is not subject to the same rate limits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So we’ll take them up on that advice.&lt;/p&gt;

&lt;p&gt;Finally, we’ve got to build our Docker image at the very least, and maybe run it locally if we want to test it out.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker build -t myapp-caddy:testing .&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I like to give my images clear names and tags for versioning, a basic best practice.&lt;/p&gt;

&lt;p&gt;Once we’ve got all these pieces in play, it’s time to move on to our clusters hosted on GCP; the really fun (and tricky) stuff.&lt;/p&gt;

&lt;h2 id=&quot;gcp&quot;&gt;GCP&lt;/h2&gt;

&lt;p&gt;If you’ve never pushed a Docker image to GCR (that’s Google Container Registry), it’s extremely easy to do so. You will have to first make sure you’re logged in via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcloud&lt;/code&gt;; you can read more about that &lt;a href=&quot;https://cloud.google.com/sdk/gcloud/reference/auth/login&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once that’s done, you’ll want to grab the location of your image either from the Google Cloud Web console or from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcloud&lt;/code&gt; on the command line using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcloud container images list&lt;/code&gt;. If you don’t yet have a repository, you’ll want to choose one from the &lt;a href=&quot;https://cloud.google.com/container-registry/docs/overview&quot;&gt;list of available locations&lt;/a&gt;, as well as the ID of your project, which are also visible by going to the Google Web console at the top of the page. Alternatively, you can always choose to host your images elsewhere; an AWS or other type of repository will work just as well, so long as you can get a public reference to its location.&lt;/p&gt;

&lt;p&gt;In this case, let’s say our repository hostname is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcr.io&lt;/code&gt;, and our project ID is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testing-123456&lt;/code&gt;. What we need to do now is tag our image accordingly:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker tag myapp-caddy:testing gcr.io/testing-123456/myapp-caddy:testing&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You &lt;em&gt;can&lt;/em&gt; change the name of your container as well as its tags from their original settings, but why make it more complicated? Consistency is key, especially as we traverse contexts, platforms, and digital locations.&lt;/p&gt;

&lt;p&gt;All right, next, push the tagged container to GCR:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker push gcr.io/testing-123456/myapp-caddy:testing&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We should be able to watch this happen, and it should not take long.&lt;/p&gt;

&lt;p&gt;Once we’ve got our Caddy image pushed into our GCR repository, our next step is to configure the container to act as our load balancer. This is the trickiest step, and the step I stumbled on. I’ll explain why in just a moment.&lt;/p&gt;

&lt;p&gt;First and foremost, we have to do what should be relatively obvious: we have to create a manifest that will deploy our Caddy container.&lt;/p&gt;

&lt;p&gt;My manifest looks like so:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;extensions/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Deployment&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;myapp-caddy-deployment'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;myapp&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;myapp-caddy-deployment&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;testing&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;testing&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;load-balancer&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;myapp-caddy-deployment'&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;myapp-caddy'&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gcr.io/testing-123456/myapp-caddy:testing&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;containerPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;containerPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;443&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;imagePullPolicy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Always&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;limits&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;64Mi'&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;cpu&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;100m'&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;64Mi'&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;cpu&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;100m'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, I’m setting my deployment container to be configured in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testing&lt;/code&gt; environment, and am basing my container on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testing&lt;/code&gt; Caddy image we just pushed.&lt;/p&gt;

&lt;p&gt;But, and this is key: in order for our Caddy service to be reachable, we need to also deploy a service for it. Here’s what my Caddy service looked like:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;myapp-caddy-service&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;myapp&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;myapp-caddy-deployment&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;testing&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;testing&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;myapp-caddy-deployment&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;LoadBalancer&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;externalTrafficPolicy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Local&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;loadBalancerIP&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;76.12.243.14&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;TCP&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;TCP&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;443&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Okay, so, a couple of things should stand out to you right away. Firstly, that we’ve assigned a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type&lt;/code&gt; of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadBalancer&lt;/code&gt; to our service, which is what we want, because we want external traffic to be able to reach our in-VPC containers. But, wait, where did that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loadBalancerIP&lt;/code&gt; come from? Ahh, now this is where we need to interact specifically with GCP’s console.&lt;/p&gt;

&lt;p&gt;I believe by far the easiest way to do this is from the Web console. On the GCP console, navigating to “VPC network” and then to “External IP addresses,” you can reserve a static IP address on your VPC. This is necessary, over something like an Ingress, if you want to assign a DNS name to a service, which, we do, considering the fact we’re going to be using TLS as well. (And besides, we want a domain name anyway!)&lt;/p&gt;

&lt;p&gt;Clicking the “RESERVE STATIC ADDRESS” button leads you to a wizard, in which you can configure exactly what kind of address you’d like to create. It’s extremely important to select the correct region in which your cluster exists, as well as your Network Service Tier. In the wrong region, you’ll get an incorrect IP, and external load balancing was, at the time of this writing, only offered through the Premium Network Service Tier, so selecting anything else would/will not work.&lt;/p&gt;

&lt;p&gt;Once you have a static IP reserved, you can take it and plug it into your manifest, like the one above. And that’s where the above IP came from! (Well, not really, I made that one up. I’m not handing out in-production IPs over here willy-nilly; but you get the idea.)&lt;/p&gt;

&lt;p&gt;Once all of that is complete, you can go ahead and configure DNS for that service’s IP and… voilà!&lt;/p&gt;

&lt;p&gt;A couple really important concepts to remember: you’re trying to make the CADDY container accessible via the outside world, so you need that container to have a SERVICE of its own. Why do I stress something that, to many of you, should be strikingly obvious? Well, let me tell you a story.&lt;/p&gt;

&lt;h2 id=&quot;we-learn-from-our-mistakes&quot;&gt;We learn from our mistakes&lt;/h2&gt;

&lt;p&gt;Recently, on this exact task, I led myself astray without even realizing that I was doing so. And this issue isn’t particularly specific to Caddy, per se, as much as it is relevant for system administrators who find themselves falling back on different/previous models of building infrastructure, particularly Web and network infrastructure.&lt;/p&gt;

&lt;p&gt;Somehow momentarily forgetting the fact that I was on Kubernetes, I made the mistake of thinking, “okay, so I’ve got a load balancer, and I want it to proxy to my Web application container.” What this thinking produced was the following mistake in my manifest for the Caddy container:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;myapp-web'&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;latest&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;myapp-web'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The particularly astute among you can probably see where I went wrong, already. To make matters even worse, when deploying the Service for my Caddy container, I made the following mistake, too:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;myapp-caddy-service&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;myapp&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;myapp-web&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;myapp-web&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, what’s the main mistake here?&lt;/p&gt;

&lt;p&gt;In thinking, “Oh, I have a load balancer that needs to proxy traffic to my Web application container, then the Caddy service should be pointing at the Web container.”&lt;/p&gt;

&lt;p&gt;I highlight this mistake because I think it’s a really good example of more traditional sysadmin thinking muddying the waters for this particular modern DevOps context and concept. I was thinking too much about the relationship between reverse proxy server (and TLS terminator/generator) and Web server, and not enough about the nature of the platform, or, moreover, &lt;em&gt;how&lt;/em&gt; this relationship was being enacted. And, further, I forgot where one process ended and another began. Even another experienced sysadmin on my team took a look at my broken config, and we worked on this for hours (tunnel vision is a killer) without seeing the problem. It was finally a third engineer who took a look at the work, and immediately recognized what was wrong. It was kind of embarrassing to make such an obvious mistake, but as one of my mentors frequently says, “obvious things are hard to see.” They’re even harder to see when you’ve gotten so entrenched in one method of thinking, that you forget that your method has changed.&lt;/p&gt;

&lt;p&gt;Not to digress too far here, but I really think that this is why a background in philosophy really helps: you have to be able to recognize when you’ve left one universe of thinking and entered into another. You have to come to an understanding of the shape, process, logic of the universe in which you’re working—that is to say, its &lt;em&gt;grammar&lt;/em&gt;. And you also have to take breaks from time to time, clear your mind, and try to prevent tunnel-vision from reifying what you think is right, but which is actually not working.&lt;/p&gt;

&lt;p&gt;At the end of the day, my Caddy container &lt;em&gt;had no Services&lt;/em&gt; to expose the Caddy container to the outside world, so when I was trying to hit the service, of course I received nothing more than an “Unable to connect” error in my browser.&lt;/p&gt;

&lt;p&gt;So, of course, once I appropriately configured a service (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadBalancer&lt;/code&gt;) for the Caddy container itself, then the Caddy container did its thing within the VPC, reverse proxying traffic to our Web service endpoint (with the hostname of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;myapp-web&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The moral of the story is to try keeping some visualizations nearby that can help you think through what your network is actually doing, and how the services on it are being reached. It’s also about not feeling too bad about it when you make a mistake. Changing our working mental models of how all this is supposed to go is no small feat! Mistakes are valuable learning opportunities, and I certainly wanted to share mine here.&lt;/p&gt;</content><author><name>MicrocosEm</name></author><summary type="html">When I was tinkering with deploying a Caddy container as a Load Balancer for an application container on GCP, I tripped over a few basic things that I think are probably pretty common confusions when you’re just getting started. Most of the guides I found online didn’t really consider some of these things, so, after successfully deploying this for myself, I figured I would write a quick-‘n-easy guide about this in the hopes to help anyone else who’s having trouble with this.</summary></entry></feed>