<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: jdm</title>
    <description>The latest articles on DEV Community by jdm (@jmarhee).</description>
    <link>https://dev.to/jmarhee</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F31737%2Faddb09de-dc83-4a90-9e62-d4db7fcff50d.JPG</url>
      <title>DEV Community: jdm</title>
      <link>https://dev.to/jmarhee</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jmarhee"/>
    <language>en</language>
    <item>
      <title>RKE2/K3s on macOS + Apple Containers</title>
      <dc:creator>jdm</dc:creator>
      <pubDate>Thu, 08 Oct 2026 21:11:01 +0000</pubDate>
      <link>https://dev.to/jmarhee/rke2k3s-on-macos-apple-containers-pm6</link>
      <guid>https://dev.to/jmarhee/rke2k3s-on-macos-apple-containers-pm6</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw45me44vag71gbnbc9x8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw45me44vag71gbnbc9x8.png" alt=" " width="800" height="80"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This project covers both &lt;a href="https://docs.rke2.io/" rel="noopener noreferrer"&gt;RKE2&lt;/a&gt; and &lt;a href="https://k3s.io/" rel="noopener noreferrer"&gt;K3s&lt;/a&gt; on macOS with &lt;a href="https://github.com/apple/container" rel="noopener noreferrer"&gt;Apple's &lt;code&gt;container&lt;/code&gt; project&lt;/a&gt; as the runtime for each of the processes. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jmarhee/rke2-silicon" rel="noopener noreferrer"&gt;rke2-silicon&lt;/a&gt; runs a single Kubernetes node on Apple silicon. &lt;code&gt;rke2-silicon up&lt;/code&gt; starts the control plane as Apple containers and returns. A per-user LaunchAgent keeps the node Ready and starts each pod as its own container. Every container is a Linux micro-VM. The catalog is Kubernetes 1.36.4: RKE2 &lt;code&gt;v1.36.4+rke2r1&lt;/code&gt; and K3s &lt;code&gt;v1.36.4+k3s1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The node is for work that reads something on this Mac, processes it here, and writes to a store that already has an address. A camera gateway, an NFS export, a directory a bench just filled.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is actually running
&lt;/h2&gt;

&lt;p&gt;There is no &lt;code&gt;rke2&lt;/code&gt; binary, no containerd, and no kubelet. &lt;code&gt;up&lt;/code&gt; writes certificates on the Mac, mounts &lt;code&gt;~/.rke2-silicon&lt;/code&gt; at &lt;code&gt;/var/lib/rke2-silicon&lt;/code&gt;, and starts Rancher's hardened images with the Apple &lt;code&gt;container&lt;/code&gt; CLI. For an RKE2 release those processes are &lt;code&gt;etcd&lt;/code&gt;, &lt;code&gt;kube-apiserver&lt;/code&gt;, &lt;code&gt;kube-controller-manager&lt;/code&gt;, and &lt;code&gt;kube-scheduler&lt;/code&gt;. &lt;code&gt;config.yaml&lt;/code&gt; uses RKE2's keys. Those keys become process arguments. The processes do not read the file.&lt;/p&gt;

&lt;p&gt;The LaunchAgent is the kubelet. It heartbeats the node and starts pods. CNI, kube-proxy, and CoreDNS are not installed. Traefik is a Deployment this installer applies.&lt;/p&gt;

&lt;p&gt;The control plane and the workloads sit on &lt;code&gt;192.168.128.0/24&lt;/code&gt;. The Mac is &lt;code&gt;192.168.128.1&lt;/code&gt; on that network. &lt;code&gt;kubectl&lt;/code&gt; uses &lt;code&gt;https://127.0.0.1:6443&lt;/code&gt;. Pods reach each other on that network, reach a host directory through virtiofs, and reach the GPU broker at &lt;code&gt;192.168.128.1:10443&lt;/code&gt;. The broker is a host process. &lt;code&gt;apple.com/gpu&lt;/code&gt; is a concurrency limit of four, not a slice of the GPU.&lt;/p&gt;

&lt;p&gt;LAN clients use a different address. &lt;code&gt;ingress-address&lt;/code&gt; is a &lt;code&gt;/32&lt;/code&gt; alias on the LAN interface. A root LaunchDaemon accepts 80 and 443 there and connects to Traefik on 8080 and 8443, because the container CLI will not bind host ports below 1024. ClusterIP, NodePort, and LoadBalancer stay as records. Nothing routes them.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;up&lt;/code&gt; never downloads or runs the RKE2 installer. &lt;code&gt;kubernetes-version&lt;/code&gt; in &lt;code&gt;config.yaml&lt;/code&gt; selects a row in the catalog, and that row names the container images Rancher already publishes for that release.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;v1.36.4+rke2r1&lt;/code&gt; the row is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker.io/rancher/hardened-kubernetes:v1.36.4-rke2r1-build20260821&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker.io/rancher/hardened-etcd:v3.6.14-k3s1-build20260819&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;up&lt;/code&gt; argument writes certificates and the kubeconfig on the Mac under &lt;code&gt;~/.rke2-silicon&lt;/code&gt;, then tasks the Apple &lt;code&gt;container&lt;/code&gt; CLI to run four containers. The CLI pulls an image the first time that name is started. Each container mounts &lt;code&gt;~/.rke2-silicon&lt;/code&gt; at &lt;code&gt;/var/lib/rke2-silicon&lt;/code&gt; and runs one binary from the image: &lt;code&gt;etcd&lt;/code&gt;, &lt;code&gt;kube-apiserver&lt;/code&gt;, &lt;code&gt;kube-controller-manager&lt;/code&gt;, and &lt;code&gt;kube-scheduler&lt;/code&gt;. The keys in &lt;code&gt;config.yaml&lt;/code&gt; are turned into flags on those commands. The processes do not read the file.&lt;/p&gt;

&lt;p&gt;Once the apiserver answers, &lt;code&gt;up&lt;/code&gt; creates the node object and the ConfigMap &lt;code&gt;kube-system/rke2-silicon-version&lt;/code&gt; through the API. The LaunchAgent keeps that node Ready and starts each pod as another container. Traefik is a Deployment this program applies. The RKE2 charts, CNI, kube-proxy, and CoreDNS are not installed.&lt;/p&gt;

&lt;p&gt;A version that is not in the catalog fails. A later &lt;code&gt;up&lt;/code&gt; with the same version leaves the running containers in place. A new RKE2 release is added by putting its version and image tags in the catalog, then running &lt;code&gt;upgrade&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repo &amp;amp; Examples
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/jmarhee/rke2-silicon" rel="noopener noreferrer"&gt;&lt;code&gt;rke2-silicon&lt;/code&gt;&lt;/a&gt; repository contains the examples and sample configurations for rke2 and k3s installations. &lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements &amp;amp; Installation
&lt;/h2&gt;

&lt;p&gt;You need macOS 26 on Apple silicon, the &lt;a href="https://github.com/apple/container" rel="noopener noreferrer"&gt;Apple &lt;code&gt;container&lt;/code&gt; CLI&lt;/a&gt; 1.4.1 or newer, &lt;code&gt;kubectl&lt;/code&gt;, Go 1.26, and passwordless &lt;code&gt;sudo&lt;/code&gt; for &lt;code&gt;/sbin/ifconfig&lt;/code&gt; and for installing the forwarder.&lt;/p&gt;

&lt;p&gt;Copy &lt;code&gt;examples/config.yaml&lt;/code&gt; and set three addresses. &lt;code&gt;node-ip&lt;/code&gt; is this Mac. &lt;code&gt;ingress-address&lt;/code&gt; is a free address on the same LAN, kept out of DHCP. &lt;code&gt;tls-san&lt;/code&gt; repeats &lt;code&gt;node-ip&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;kubernetes-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1.36.4+rke2r1&lt;/span&gt;
&lt;span class="na"&gt;node-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dreamland&lt;/span&gt;
&lt;span class="na"&gt;node-ip&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;192.168.1.20&lt;/span&gt;
&lt;span class="na"&gt;ingress-address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;192.168.0.200&lt;/span&gt;
&lt;span class="na"&gt;tls-san&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;192.168.1.20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; rke2-silicon ./cmd/rke2-silicon
./rke2-silicon up &lt;span class="nt"&gt;--config&lt;/span&gt; config.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;up&lt;/code&gt; creates the &lt;code&gt;rke2-silicon&lt;/code&gt; network on first start, installs the alias, and loads the LaunchAgent. The binary the agent runs has to be the one you just built. &lt;code&gt;go run&lt;/code&gt; throws that binary away when the process exits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate Cluster Status
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.rke2-silicon/rke2-silicon.yaml
kubectl get &lt;span class="nt"&gt;--raw&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/readyz
kubectl get nodes
container list &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;/readyz&lt;/code&gt; returns &lt;code&gt;ok&lt;/code&gt;. The node is &lt;code&gt;Ready&lt;/code&gt;. &lt;code&gt;container list&lt;/code&gt; shows &lt;code&gt;rke2-silicon-etcd&lt;/code&gt;, &lt;code&gt;rke2-silicon-kube-apiserver&lt;/code&gt;, &lt;code&gt;rke2-silicon-kube-controller-manager&lt;/code&gt;, and &lt;code&gt;rke2-silicon-kube-scheduler&lt;/code&gt;, each in state &lt;code&gt;running&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample workload: a directory on the Mac
&lt;/h2&gt;

&lt;p&gt;The instrument-drop example is a PersistentVolume whose path is a directory that already exists. The pod appends a line there through virtiofs. Create the directory first, mode &lt;code&gt;0777&lt;/code&gt;, so the guest can write it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /Users/Shared/rke2-silicon/bench
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;0777 /Users/Shared/rke2-silicon/bench
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; examples/pv/workload.yaml
kubectl rollout status deployment/bench
&lt;span class="nb"&gt;cat&lt;/span&gt; /Users/Shared/rke2-silicon/bench/drop.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;kubectl get pvc bench&lt;/code&gt; shows &lt;code&gt;Bound&lt;/code&gt;. The file contains a line like &lt;code&gt;parsed 2026-10-08T19:30:00Z&lt;/code&gt;. That line was written by the pod and read on the Mac. The volume's reclaim policy is &lt;code&gt;Retain&lt;/code&gt;, so deleting the claim leaves the directory in place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete &lt;span class="nt"&gt;-f&lt;/span&gt; examples/pv/workload.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Stop or Uninstall Cluster
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;stop&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt; stop the &lt;code&gt;rke2-silicon&lt;/code&gt; containers and leave them on disk, along with &lt;code&gt;~/.rke2-silicon&lt;/code&gt; and the ingress alias. Start them again with &lt;code&gt;up&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;uninstall&lt;/code&gt; deletes those containers, including ones that are already stopped. The data directory and the ingress alias stay, so a later &lt;code&gt;up&lt;/code&gt; can reuse the certificates and etcd data.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;uninstall --purge&lt;/code&gt; removes the containers, the &lt;code&gt;rke2-silicon&lt;/code&gt; network, &lt;code&gt;~/.rke2-silicon&lt;/code&gt;, the ingress alias, and the launchd jobs. &lt;code&gt;stop --purge&lt;/code&gt; and &lt;code&gt;down --purge&lt;/code&gt; do the same thing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./rke2-silicon stop
./rke2-silicon uninstall
./rke2-silicon uninstall &lt;span class="nt"&gt;--purge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>kubernetes</category>
      <category>macos</category>
      <category>rke2</category>
      <category>k3s</category>
    </item>
    <item>
      <title>Sampling Methods in Python</title>
      <dc:creator>jdm</dc:creator>
      <pubDate>Tue, 04 Jul 2023 04:06:09 +0000</pubDate>
      <link>https://dev.to/jmarhee/sampling-methods-in-python-3li8</link>
      <guid>https://dev.to/jmarhee/sampling-methods-in-python-3li8</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AafKLe604qSAitSpb" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AafKLe604qSAitSpb" width="1024" height="819"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Photo by Clay Banks on Unsplash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sampling is a fundamental concept in statistics and data analysis. It involves selecting a subset of individuals or data points from a larger population for analysis. In Python, there are various sampling methods available that allow us to extract representative samples from a population.&lt;/p&gt;

&lt;p&gt;In this document, we will explore different sampling methods in Python and provide code examples for each method. We will cover the following sampling methods:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Simple Random Sampling&lt;/li&gt;
&lt;li&gt;Stratified Sampling&lt;/li&gt;
&lt;li&gt;Cluster Sampling&lt;/li&gt;
&lt;li&gt;Systematic Sampling&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s dive into each method and see how it can be implemented in Python.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Simple Random Sampling
&lt;/h3&gt;

&lt;p&gt;Simple random sampling involves randomly selecting individuals from a population without any specific criteria. Each individual has an equal chance of being selected. This method is commonly used when the population is homogenous.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import random

population = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
sample_size = 5

sample = random.sample(population, sample_size)
print(sample)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[3, 5, 9, 7, 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Stratified Sampling
&lt;/h3&gt;

&lt;p&gt;Stratified sampling involves dividing the population into distinct subgroups or strata based on certain characteristics. Then, a random sample is selected from each stratum proportionate to its size. This method ensures that each subgroup is adequately represented in the final sample.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from sklearn.model_selection import train_test_split

data = [...] # Your dataset
labels = [...] # Labels for each data point

# Stratified sampling using train_test_split from scikit-learn
train_data, test_data, train_labels, test_labels = train_test_split(data, labels, test_size=0.2, stratify=labels)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Cluster Sampling
&lt;/h3&gt;

&lt;p&gt;Cluster sampling involves dividing the population into clusters or groups. Instead of selecting individuals, entire clusters are chosen randomly. This method is useful when it is difficult or expensive to access individual elements of the population.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import random

clusters = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
cluster_sample_size = 2

selected_clusters = random.sample(clusters, cluster_sample_size)
sample = []

for cluster in selected_clusters:
    sample.extend(cluster)

print(sample)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Systematic Sampling
&lt;/h3&gt;

&lt;p&gt;Systematic sampling involves selecting individuals from a population at regular intervals after an initial random start. This method is useful when the population is large and ordered in some way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;population = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
sample_size = 3

start_index = random.randint(0, sample_size - 1)
sample = [population[i] for i in range(start_index, len(population), sample_size)]

print(sample)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are just a few examples of sampling methods available in Python. Depending on your specific requirements and the characteristics of your data, you may need to adapt or combine these methods to achieve the desired sampling outcome.&lt;/p&gt;

&lt;p&gt;Remember that sampling is a powerful technique for analyzing large datasets and making inferences about a population. It allows us to draw meaningful conclusions while reducing computational costs and time.&lt;/p&gt;

</description>
      <category>datasceince</category>
      <category>statistics</category>
      <category>python</category>
      <category>biostatistics</category>
    </item>
    <item>
      <title>How to delete a Kubernetes namespace stuck at Terminating Status</title>
      <dc:creator>jdm</dc:creator>
      <pubDate>Wed, 17 Aug 2022 21:27:37 +0000</pubDate>
      <link>https://dev.to/jmarhee/how-to-delete-a-kubernetes-namespace-stuck-at-terminating-status-53o5</link>
      <guid>https://dev.to/jmarhee/how-to-delete-a-kubernetes-namespace-stuck-at-terminating-status-53o5</guid>
      <description>&lt;p&gt;After running a command like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete my-namespace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the command hangs indefinitely after accepting the deletion request, you might find it sitting at &lt;code&gt;Terminating&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get ns/my-namespace
NAME      STATUS         AGE
testing   Terminating    113m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might be due to a &lt;a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/" rel="noopener noreferrer"&gt;Finalizer&lt;/a&gt; in the spec, specific conditions to be met during deletion. You can view the finalizers for your namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"spec"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"finalizers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="s2"&gt;"kubernetes"&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what a standard namespace with no other conditions might otherwise look like, so if it's hanging, this is likely way.&lt;/p&gt;

&lt;p&gt;You can modify the resource using &lt;code&gt;kubectl edit ns/my-namespace&lt;/code&gt;, and replace the finalizer with an empty array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"spec"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"finalizers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or use the Kube API to update the specification to allow it to finalize manually if this is unsuccessful by dumping the resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get ns/my-namespace &lt;span class="nt"&gt;-o&lt;/span&gt; json &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; my-namespace.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and editing that &lt;code&gt;my-namespace.json&lt;/code&gt; to remove the finalizer, and then making a &lt;code&gt;PUT&lt;/code&gt; request to the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl replace &lt;span class="nt"&gt;--raw&lt;/span&gt; &lt;span class="s2"&gt;"/api/v1/namespaces/my-namespace/finalize"&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ./my-namespace.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or using an HTTP client to issue the request (usually will require authentication if not exposing via &lt;a href="https://kubernetes.io/docs/tasks/extend-kubernetes/http-proxy-access-api/" rel="noopener noreferrer"&gt;&lt;code&gt;kubectl proxy&lt;/code&gt;&lt;/a&gt;) using that same json dump:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; PUT &lt;span class="nt"&gt;--data-binary&lt;/span&gt; @my-namespace.json &lt;span class="s2"&gt;"https://&amp;lt;Kube API Endpoint&amp;gt;/api/v1/namespaces/my-namespace/finalize"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then confirm, once you receive a successful response, that the namespace is deleted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get ns/my-namespace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which should return no resource. &lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>containers</category>
      <category>docker</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>Exporting images with Containerd CLI (ctr)</title>
      <dc:creator>jdm</dc:creator>
      <pubDate>Sun, 07 Aug 2022 03:29:55 +0000</pubDate>
      <link>https://dev.to/jmarhee/exporting-images-with-containerd-cli-ctr-5321</link>
      <guid>https://dev.to/jmarhee/exporting-images-with-containerd-cli-ctr-5321</guid>
      <description>&lt;p&gt;I recently found that a Docker image I use as a part of one of my Helm charts was no longer available on DockerHub, and I hadn't mirrored it in another location. It was running in a &lt;a href="//k3s.io"&gt;K3s&lt;/a&gt; cluster, meaning I couldn't &lt;code&gt;docker tag original-maintainer/image:tag me/image:tag&lt;/code&gt; it and push to the Hub myself back on my local machine, which was running the Docker CLI. &lt;/p&gt;

&lt;p&gt;First, logging into a node with the image on it, I ran the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ctr &lt;span class="nt"&gt;-n&lt;/span&gt; k8s.io images &lt;span class="nb"&gt;export &lt;/span&gt;bind9:9.11.tar docker.io/internetsystemsconsortium/bind9:9.11 &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which exported the image to a tarball. This is similar to &lt;a href="https://docs.docker.com/engine/reference/commandline/image_save/" rel="noopener noreferrer"&gt;&lt;code&gt;docker image save&lt;/code&gt;&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Then, pulling it down to my Docker CLI machines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scp jdmarhee@192.168.0.60:bind9:9.11.tar &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then &lt;a href="https://docs.docker.com/engine/reference/commandline/load/" rel="noopener noreferrer"&gt;&lt;code&gt;load&lt;/code&gt;&lt;/a&gt; the image from the tarball:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; docker image load &lt;span class="nt"&gt;--input&lt;/span&gt; bind9&lt;span class="se"&gt;\:&lt;/span&gt;9.11.tar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which returns the original tag for the image (&lt;code&gt;internetsystemsconsortium/bind9:9.11&lt;/code&gt;) so I can re-tag it and push:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image tag internetsystemsconsortium/bind9:9.11 jmarhee/bind9:9.11

docker push jmarhee/bind9:9.11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now this image is available on Docker Hub for my (and your) new clusters to access. &lt;/p&gt;

</description>
      <category>docker</category>
      <category>containerd</category>
      <category>dns</category>
      <category>devops</category>
    </item>
    <item>
      <title>Recurring Background Jobs in Flask</title>
      <dc:creator>jdm</dc:creator>
      <pubDate>Tue, 24 May 2022 05:07:48 +0000</pubDate>
      <link>https://dev.to/jmarhee/recurring-background-jobs-in-flask-3407</link>
      <guid>https://dev.to/jmarhee/recurring-background-jobs-in-flask-3407</guid>
      <description>&lt;p&gt;Feeling some frustration with most RSS reader apps being way too feature-heavy for my liking, I recently tried building a &lt;a href="https://github.com/jmarhee/rss-reader-simple" rel="noopener noreferrer"&gt;very simple RSS feed reader&lt;/a&gt; that pulls from a &lt;code&gt;feeds.yaml&lt;/code&gt; file that lists URLs and does little else beyond aggregating those links and, where available, providing the article body.  &lt;/p&gt;

&lt;p&gt;In the app, a function like this builds the data body that is populated when Flask renders the template on the page in &lt;code&gt;parser.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;feedparser&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;buildConfig&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;FEED_YAML_PATH&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Loader&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullLoader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;feeds&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;buildFeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;feed_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;feed_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;feedparser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;new_feed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;feed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;feed_data&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
        &lt;span class="n"&gt;feed_body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_feed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;feed_body&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;basically just dumping all of the feed data into a larger JSON object for Flask to render in the template. &lt;/p&gt;

&lt;p&gt;I wanted to be able to see new feeds I added to this file without restarting Flask, and one way of doing this was to regenerate this data object on every page load, but that would make it take forever to load unless I added a bunch of additional logic or other dependencies or components (storing feeds in memory, etc.) &lt;/p&gt;

&lt;p&gt;In order to avoid adding new components to the app (it's a very small Docker image, and restoring app data only amounts to this Yaml file being available), I decided to add a recurring background task using the &lt;code&gt;apscheduler&lt;/code&gt; package which has a &lt;code&gt;BackgroundScheduler&lt;/code&gt; function in &lt;code&gt;app.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apscheduler.schedulers.background&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BackgroundScheduler&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then I have it define on app startup the variable that will store the feed_data object to be updated by the recurring job we'll define in a moment, and initialize it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;feed_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="nd"&gt;@app.before_first_request&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize_feeds&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;feeds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;feed_data&lt;/span&gt;
    &lt;span class="n"&gt;feed_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildFeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;feed_data&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;and then adding an &lt;code&gt;updateFeeds&lt;/code&gt; function that behaves similarly to the above function (in a production app, you could avoid repeating yourself and have the above function reference this one, but for the sake of demonstration):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_feeds&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;feeds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;feed_data&lt;/span&gt;
    &lt;span class="n"&gt;feed_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildFeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;feed_data&lt;/span&gt;

&lt;span class="n"&gt;scheduler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BackgroundScheduler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_job&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update_feeds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;interval&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then having that function run as an argument to the &lt;code&gt;scheduler.add_job()&lt;/code&gt; function above, with an interval of 1 minute. &lt;/p&gt;

&lt;p&gt;So, now, on changes to your Yaml file containing a new URL, the feed_data object will be updated in the background, and load time will be unaffected. &lt;/p&gt;

</description>
      <category>flask</category>
      <category>python</category>
      <category>rss</category>
      <category>blogs</category>
    </item>
    <item>
      <title>Rancher Logging Operator with in-cluster Syslog</title>
      <dc:creator>jdm</dc:creator>
      <pubDate>Thu, 19 May 2022 23:44:35 +0000</pubDate>
      <link>https://dev.to/jmarhee/rancher-logging-operator-with-in-cluster-syslog-16i9</link>
      <guid>https://dev.to/jmarhee/rancher-logging-operator-with-in-cluster-syslog-16i9</guid>
      <description>&lt;p&gt;The &lt;a href="https://rancher.com/docs/rancher/v2.6/en/logging/" rel="noopener noreferrer"&gt;Rancher Logging Operator&lt;/a&gt; supports a number of backends, but a very common one is logging to a syslog endpoint. However, rather than maintaining separate logging infrastructure if this is a new piece of your environment, you can deploy it to your Kubernetes clusters directly, either using the method in the UI above, or via the Helm chart. &lt;/p&gt;

&lt;p&gt;If you plan to deploy charts via Fleet (which I've written about &lt;a href="https://dev.to/jmarhee/manage-an-authoritative-dns-server-on-kubernetes-with-helm-and-fleet-3o6n"&gt;before&lt;/a&gt;, or some other GitOps flow, you would pull the following charts from the Rancher chart repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add rancher-charts https://charts.rancher.io
helm repo update
helm fetch rancher-charts/rancher-logging-crd
helm fetch rancher-charts/rancher-logging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but before we apply them, we want to setup the syslog endpoint in the cluster for the logging operator to log to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rsyslog-deployment&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rsyslog&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rsyslog&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rsyslog&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rsyslog&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sudheerc1190/rsyslog:latest&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;514&lt;/span&gt;
        &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;250m&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;524Mi&lt;/span&gt;
      &lt;span class="na"&gt;restartPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Always&lt;/span&gt;
      &lt;span class="na"&gt;terminationGracePeriodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;here we're just creating a syslog &lt;code&gt;Deployment&lt;/code&gt;; for production, I would recommend creating a &lt;code&gt;PersistentVolume&lt;/code&gt; and mount it to &lt;code&gt;/var/log/remote&lt;/code&gt; so in subsequent recreations of this &lt;code&gt;Pod&lt;/code&gt;, you can restore past logging data for this or other clusters.&lt;/p&gt;

&lt;p&gt;Because we want this just available internal to the cluster in this example, we'll just create the following &lt;code&gt;Service&lt;/code&gt; objects to create cluster DNS entires for this endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;syslog-service-tcp"&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;514&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;514&lt;/span&gt;
      &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rsyslog"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;syslog-service-udp"&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;514&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;514&lt;/span&gt;
      &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;UDP&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rsyslog"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;so depending upon which protocol you'd like to use for logging, you can use a hostname like &lt;code&gt;syslog-service-udp.default.svc.cluster.local&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, you can create the CRDs and Logging Operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm &lt;span class="nb"&gt;install &lt;/span&gt;rancher-logging-crd ./rancher-logging-crd-100.1.2+up3.17.4.tgz &lt;span class="nt"&gt;-n&lt;/span&gt; cattle-logging-system &lt;span class="nt"&gt;--create-namespace&lt;/span&gt;

helm &lt;span class="nb"&gt;install &lt;/span&gt;rancher-logging ./rancher-logging-100.1.2+up3.17.4.tgz &lt;span class="nt"&gt;-n&lt;/span&gt; cattle-logging-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the version on these charts should match, and that they should be deployed to the &lt;code&gt;cattle-logging-system&lt;/code&gt; namespace.&lt;/p&gt;

&lt;p&gt;In the logging operator, there are two pieces &lt;code&gt;Flow&lt;/code&gt; (and cluster-wide &lt;code&gt;ClusterFlow&lt;/code&gt;) resources and &lt;code&gt;Output&lt;/code&gt; (and &lt;code&gt;ClusterOutput&lt;/code&gt;) resources. This example will just use cluster-scoped examples that span namespaces, but &lt;a href="https://banzaicloud.com/docs/one-eye/logging-operator/configuration/log-routing/" rel="noopener noreferrer"&gt;the matching rules&lt;/a&gt; and &lt;a href="https://banzaicloud.com/docs/one-eye/logging-operator/configuration/output/" rel="noopener noreferrer"&gt;output formatting&lt;/a&gt; will work similarly in both cases.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;ClusterOutput&lt;/code&gt; will be what connects to our Syslog service, using &lt;code&gt;host: syslog-service-udp.default.svc.cluster.local&lt;/code&gt; on port 514, in this case using &lt;code&gt;transport: udp&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logging.banzaicloud.io/v1beta1&lt;/span&gt;
  &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterOutput&lt;/span&gt;
  &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;testing&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cattle-logging-system&lt;/span&gt;
  &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;syslog&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;buffer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;total_limit_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2GB&lt;/span&gt;
        &lt;span class="na"&gt;flush_thread_count&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;
        &lt;span class="na"&gt;timekey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10m&lt;/span&gt;
        &lt;span class="na"&gt;timekey_use_utc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;timekey_wait&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1m&lt;/span&gt;
      &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app_name_field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubernetes.pod_name&lt;/span&gt;
        &lt;span class="na"&gt;hostname_field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;custom-cluster-name&lt;/span&gt;
        &lt;span class="na"&gt;log_field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;message&lt;/span&gt;
        &lt;span class="na"&gt;rfc6587_message_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;syslog-service-udp.default.svc.cluster.local&lt;/span&gt;
      &lt;span class="na"&gt;insecure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;514&lt;/span&gt;
      &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;udp&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;List&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and you can verify the status of the output (deployment status, problems, etc.) with &lt;code&gt;kubectl get clusteroutput/testing -n cattle-logging-system&lt;/code&gt;. The &lt;code&gt;ClusterOutput&lt;/code&gt; should be namespaces with the operator deployment. In the above spec, under &lt;code&gt;format&lt;/code&gt;, you can refer to &lt;a href="https://banzaicloud.com/docs/one-eye/logging-operator/configuration/plugins/outputs/format_rfc5424/" rel="noopener noreferrer"&gt;the RFC format&lt;/a&gt; for other options for logging fields. &lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;ClusterFlow&lt;/code&gt;, we're going to use just a basic rule, ask to exclude logs from the &lt;code&gt;kube-system&lt;/code&gt; namespace, and then &lt;code&gt;select&lt;/code&gt; everything from the &lt;code&gt;applications&lt;/code&gt; namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logging.banzaicloud.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterFlow&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;global-ns-clusterflow&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;namespaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;kube-system&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;namespaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;applications&lt;/span&gt;
  &lt;span class="na"&gt;globalOutputRefs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;testing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and you'll see we use the &lt;code&gt;testing&lt;/code&gt; output as the global endpoint for sending logs. You can check the status of the Flow using &lt;code&gt;kubectl get clusterflow/global-ns-clusterflow&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;I usually test after this point using a deployment like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-deployment&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:1.14.2&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-service&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then deploy it once to an excluded namespace and then an included one, then make a few requests (&lt;code&gt;curl -s http://{LB_IP}&lt;/code&gt;, if the output and flow report no issues, you'll see the logs start to populate after a few minutes). You can then check the logs manually (if nothing is scraping syslog externally) with something formatted like &lt;code&gt;kubectl exec -it {rsyslog_pod} -- tail -f /var/log/remote/{date hierarchy}/{hour}.log&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;More about the BanzaiCloud Operator can be found &lt;a href="https://banzaicloud.com/docs/one-eye/logging-operator/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and more on the Logging features in Rancher from this operator can be found &lt;a href="https://rancher.com/docs/rancher/v2.6/en/logging/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>logging</category>
      <category>kubernetes</category>
      <category>rancher</category>
      <category>syslog</category>
    </item>
    <item>
      <title>Changing Netplan Renderer Backend</title>
      <dc:creator>jdm</dc:creator>
      <pubDate>Fri, 06 May 2022 21:04:05 +0000</pubDate>
      <link>https://dev.to/jmarhee/changing-netplan-renderer-backend-a93</link>
      <guid>https://dev.to/jmarhee/changing-netplan-renderer-backend-a93</guid>
      <description>&lt;p&gt;I recently changed desktop environments on Linux, and found that my choice didn't render really well through &lt;code&gt;xrdp&lt;/code&gt; and rather than figure it out, I wanted to try the screensharing option in the Ubuntu 20.04 system settings. &lt;/p&gt;

&lt;p&gt;However, when I went to enable it, I found the following error in syslog:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;May  6 15:49:08 boris gnome-control-c[64701]: message repeated 3 times: [ Failed to enable service vino-server: GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Sharing cannot be enabled on this network, status is '0']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a &lt;a href="https://bugs.launchpad.net/ubuntu/+source/gnome-control-center/+bug/1877121" rel="noopener noreferrer"&gt;known bug in this GNOME package&lt;/a&gt; when your network interface Vino would be binding to is managed by systemd-networkd. You can see which interfaces are currently managed using the &lt;code&gt;networkctl&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jdmarhee@boris /etc/netplan $ networkctl
IDX LINK       TYPE     OPERATIONAL SETUP    
  1 lo         loopback carrier     unmanaged
  2 eno1       ether    routable    managed
  3 tailscale0 none     routable    unmanaged
  4 docker0    bridge   no-carrier  unmanaged

4 links listed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;code&gt;eno1&lt;/code&gt; was being managed. In Ubuntu 20.04, the network interfaces are configured using netplan. The systemd-networkd backend is the default for doing this, so I needed to update netplan to use NetworkManager instead. Your config will be in a file like this one in &lt;code&gt;/etc/netplan&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jdmarhee@boris /etc/netplan $ cat 00-installer-config.yaml 
# This is the network config written by 'subiquity'
network:
  ethernets:
    eno1:
      dhcp4: true
  version: 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and add the following under &lt;code&gt;network&lt;/code&gt; key to specify the backend (I add it right under the &lt;code&gt;version&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  renderer: NetworkManager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then apply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;netplan apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then verify that that interface is no longer managed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jdmarhee@boris /etc/netplan $ networkctl
IDX LINK       TYPE     OPERATIONAL SETUP    
  1 lo         loopback carrier     unmanaged
  2 eno1       ether    routable    unmanaged
  3 tailscale0 none     routable    unmanaged
  4 docker0    bridge   no-carrier  unmanaged

4 links listed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and should be able to enable Vino without issue (amongst other preferences that rely on the interface not being managed by systemd). &lt;/p&gt;

</description>
      <category>linux</category>
      <category>desktop</category>
      <category>gnome</category>
      <category>networking</category>
    </item>
    <item>
      <title>Network Policies with Canal and Flannel on K3s</title>
      <dc:creator>jdm</dc:creator>
      <pubDate>Wed, 05 Jan 2022 06:10:55 +0000</pubDate>
      <link>https://dev.to/jmarhee/network-policies-with-canal-and-flannel-on-k3s-11oe</link>
      <guid>https://dev.to/jmarhee/network-policies-with-canal-and-flannel-on-k3s-11oe</guid>
      <description>&lt;p&gt;Flannel is a popular Container Network Interface (CNI) addon for Kubernetes, however, it does not provide (because it is Layer 3 network focused on transport between hosts, rather than container networking with the host) robust support for &lt;code&gt;NetworkPolicy&lt;/code&gt; resources. Now, the policy features from another popular CNI, Calico, can be imported to Flannel using &lt;a href="https://projectcalico.docs.tigera.io/getting-started/kubernetes/flannel/flannel" rel="noopener noreferrer"&gt;Canal&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I won't talk a lot about specific &lt;a href="https://kubernetes.io/docs/concepts/services-networking/network-policies/" rel="noopener noreferrer"&gt;NetworkPolicy&lt;/a&gt; in this post, but a little bit about why you'd want a NetworkPolicy controller is for things like creating policies around access to Ingresses, to or from port or IP ranges, things like that-- the sort of concerns you might have creating ACLs and security rules on a traditional network, but scriptable and templateable for Kubernetes like any other resource type. &lt;/p&gt;

&lt;p&gt;Installing Canal requires applying a single manifest (containing the Calico controller, policy agent, and service accounts), however, because the Pod CIDR may differ (and in the case of &lt;a href="//k3s.io"&gt;K3s&lt;/a&gt;, it will be 10.42.0.0/24) from the Calico-expected default of 10.244.0.0/16, an environmental variable (&lt;code&gt;CALICO_IPV4POOL_CIDR&lt;/code&gt;) and its accompanying value in the manifest.&lt;/p&gt;

&lt;p&gt;You can retrieve your Pod CIDR using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get nodes &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.items[*].spec.podCIDR}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then modify that variable (commented out) after you download &lt;code&gt;https://docs.projectcalico.org/manifests/canal.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Or use a CLI tool like &lt;code&gt;sed&lt;/code&gt; to modify it while writing the file locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://docs.projectcalico.org/manifests/canal.yaml | &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s|            # - name: CALICO_IPV4POOL_CIDR|            - name: CALICO_IPV4POOL_CIDR|g'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"s|            #   value: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;192.168.0.0/16&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;|              value: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get nodes &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.items[*].spec.podCIDR}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;|g"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then (or alternatively) manage this manifest however you typically might do so (in Helm chart, or have an automation tool handle this templating for you, etc.)&lt;/p&gt;

&lt;p&gt;If you are a K3s user, conveniently, any manifests written to &lt;code&gt;/var/lib/rancher/k3s/server/manifests&lt;/code&gt; will be applied automatically, so you can have the above simply write the file for you when provisioning your cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;## Applies the modified manifest to K3s, which automatically applies the contents of /var/lib/rancher/k3s/server/manifests&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://docs.projectcalico.org/manifests/canal.yaml | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s|            # - name: CALICO_IPV4POOL_CIDR|            - name: CALICO_IPV4POOL_CIDR|g'&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"s|            #   value: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;192.168.0.0/16&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;|              value: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get nodes &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.items[*].spec.podCIDR}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;|g"&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /var/lib/rancher/k3s/server/manifests/canal.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;after your K3s install command, if you'd like, on cluster spin-up. &lt;/p&gt;

&lt;p&gt;Checking the status of the &lt;code&gt;calico-controllers&lt;/code&gt; Deployment will let you know when you are ready to proceed to introduce policy objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get deployments &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Examples of common &lt;code&gt;NetworkPolicy&lt;/code&gt; usage can be &lt;a href="https://kubernetes.io/docs/concepts/services-networking/network-policies/" rel="noopener noreferrer"&gt;found here.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>k3s</category>
      <category>kubernetes</category>
      <category>network</category>
      <category>automation</category>
    </item>
    <item>
      <title>Manage an Authoritative DNS Server on Kubernetes with Helm and Fleet</title>
      <dc:creator>jdm</dc:creator>
      <pubDate>Fri, 24 Dec 2021 05:23:19 +0000</pubDate>
      <link>https://dev.to/jmarhee/manage-an-authoritative-dns-server-on-kubernetes-with-helm-and-fleet-3o6n</link>
      <guid>https://dev.to/jmarhee/manage-an-authoritative-dns-server-on-kubernetes-with-helm-and-fleet-3o6n</guid>
      <description>&lt;p&gt;Among my other clusters, I run infrastructure-specific services out of a small Kubernetes cluster. One such service (aside from things like a VPN gateway, Docker registry, etc.) is a DNS nameserver pair that I use inside and out of the cluster. I install and configure these namservers using &lt;a href="https://helm.sh/docs/intro/using_helm/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; and deploy/update the &lt;code&gt;Deployment&lt;/code&gt; (and &lt;code&gt;ConfigMap&lt;/code&gt;s that it serves zone files from) using &lt;a href="//fleet.rancher.io/"&gt;Fleet&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;In my &lt;code&gt;authoritative-dns&lt;/code&gt; folder &lt;a href="https://github.com/rancher/fleet-examples/tree/master/simple" rel="noopener noreferrer"&gt;in my repo that Fleet watches&lt;/a&gt;, after running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm create authoritative-dns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which creates the directory and the template chart (i.e. a templates directory, Chart.yaml, etc.), I am mostly going to be concerned with &lt;code&gt;templates/deployment.yaml&lt;/code&gt; and &lt;code&gt;templates/configmap.yaml&lt;/code&gt; (and optionally &lt;code&gt;templates/service.yaml&lt;/code&gt;) being set-up to receive the BIND configuration and zone files I'd like it to serve.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;templates/configmap.yaml&lt;/code&gt;, I'm going to set up two &lt;code&gt;ConfigMap&lt;/code&gt; resources, one to handle the &lt;code&gt;named.conf&lt;/code&gt; configuration, and another to hold the zone file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bind-config&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- toYaml .Values.bindconfig | nindent 2&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bind-zone-config&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- toYaml .Values.bindzones | nindent 2&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.Values&lt;/code&gt; in template brackets (&lt;code&gt;{{ }}&lt;/code&gt;) will refer to data in your &lt;code&gt;Values.yaml&lt;/code&gt; file, which will be the part of the Chart you want most of your modifications to go into, hold variable value configuration items. I'll return to that in a moment.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;templates/deployment.yaml&lt;/code&gt;, we just need a &lt;code&gt;Deployment&lt;/code&gt; that will mount these &lt;code&gt;ConfigMap&lt;/code&gt; resources to the BIND container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- include "authoritative-dns.selectorLabels" . | nindent 4&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.replicaCount&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- include "authoritative-dns.selectorLabels" . | nindent 6&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;roll&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;randAlphaNum 5 | quote&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- with .Values.podAnnotations&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- toYaml . | nindent 8&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- end&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- include "authoritative-dns.selectorLabels" . | nindent 8&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;hostNetwork&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;affinity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- toYaml .Values.affinity | nindent 8&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;-bind&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.image.image&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;:{{ .Values.image.tag }}&lt;/span&gt;
          &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dns-tcp&lt;/span&gt;
              &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.service.targetPort&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
              &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dns-udp&lt;/span&gt;
              &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.service.targetPort&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
              &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;UDP&lt;/span&gt;
          &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- if .Values.readinessProbe.enabled&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
          &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;tcpSocket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dns-tcp&lt;/span&gt;
            &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
            &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
          &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- end&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
          &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- if .Values.readinessProbe.enabled&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
          &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;tcpSocket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dns-udp&lt;/span&gt;
            &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
            &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
          &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- end&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
          &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- toYaml .Values.resources | nindent 12&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
          &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bind-config&lt;/span&gt;
            &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/bind&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bind-zone-config&lt;/span&gt;
            &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/bind&lt;/span&gt;
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bind-config&lt;/span&gt;
          &lt;span class="na"&gt;configMap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bind-config&lt;/span&gt;
            &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;named.conf&lt;/span&gt;
                &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;named.conf&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bind-zone-config&lt;/span&gt;
          &lt;span class="na"&gt;configMap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bind-zone-config&lt;/span&gt;
&lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- toYaml .Values.zoneconfigs | nindent 12&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where in this case, we want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bind-zone-config&lt;/span&gt;
          &lt;span class="na"&gt;configMap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bind-zone-config&lt;/span&gt;
&lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- toYaml .Values.zoneconfigs | nindent 12&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to refer to each zone file &lt;code&gt;ConfigMap&lt;/code&gt; file, which you'll also populate in your &lt;code&gt;Values.yaml&lt;/code&gt; file as well, and then appear in the container under &lt;code&gt;/var/lib/bind/{{your zone}}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: the Deployment contains an annotation, &lt;code&gt;roll: {{ randAlphaNum 5 | quote }}&lt;/code&gt;-- this is because BIND requires a restart (and thereforce the Pods need to be restarted) to pick up changes in the zone configs. This will generate an updated value for the Pod to be annotated with triggering the restart-- the above restarts the Pod on every upgrade, but there's other &lt;a href="https://v3.helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments" rel="noopener noreferrer"&gt;options to roll your deployment pods&lt;/a&gt; (i.e. based on changes to the configmap specifically, etc.)&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;Deployment&lt;/code&gt;, because of how I use it, just uses the host network, so a &lt;code&gt;Service&lt;/code&gt; is not required, but you can use a template like this &lt;code&gt;template/service.yaml&lt;/code&gt; to expose on ports 53/TCP and 53/UDP with a &lt;code&gt;LoadBalancer&lt;/code&gt;, in this case with MetalLB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;include "authoritative-dns.name" .&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;-tcp&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- include "authoritative-dns.labels" . | nindent 4&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- if .Values.service.annotations&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- toYaml .Values.service.annotations | nindent 4&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- end&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.service.type&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;externalTrafficPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Local&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.service.targetPort&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.service.targetPort&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dns-tcp&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- include "authoritative-dns.selectorLabels" . | nindent 4&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;include "authoritative-dns.name" .&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;-udp&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- include "authoritative-dns.labels" . | nindent 4&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- if .Values.service.annotations&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- toYaml .Values.service.annotations | nindent 4&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- end&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.service.type&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.service.targetPort&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.service.targetPort&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;UDP&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dns-udp&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- include "authoritative-dns.selectorLabels" . | nindent 4&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, with these templates in place, now we need a &lt;code&gt;Values.yaml&lt;/code&gt; to supply the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;replicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ORD1&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;authoritative-dns&lt;/span&gt;
&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;internetsystemsconsortium/bind9&lt;/span&gt;
  &lt;span class="na"&gt;pullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
  &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9.11&lt;/span&gt;

&lt;span class="na"&gt;imageConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;

&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;53&lt;/span&gt;
  &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;53&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="c1"&gt;# resources:&lt;/span&gt;
&lt;span class="c1"&gt;#   requests:&lt;/span&gt;
&lt;span class="c1"&gt;#     memory: 1Gi&lt;/span&gt;
&lt;span class="c1"&gt;#     cpu: 300m&lt;/span&gt;

&lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;bindzones&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;c00lz0ne.internal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;$TTL    604800&lt;/span&gt;
      &lt;span class="s"&gt;@       IN      SOA     ns1.c00lz0ne.internal. admin.c00lz0ne.internal. (&lt;/span&gt;
                                    &lt;span class="s"&gt;5         ; Serial&lt;/span&gt;
                               &lt;span class="s"&gt;604800         ; Refresh&lt;/span&gt;
                                &lt;span class="s"&gt;86400         ; Retry&lt;/span&gt;
                              &lt;span class="s"&gt;2419200         ; Expire&lt;/span&gt;
                               &lt;span class="s"&gt;604800 )       ; Negative Cache TTL&lt;/span&gt;
      &lt;span class="s"&gt;;&lt;/span&gt;
      &lt;span class="s"&gt;c00lz0ne.internal.       IN      NS      ns1.c00lz0ne.internal.&lt;/span&gt;
      &lt;span class="s"&gt;c00lz0ne.internal.       IN      NS      ns2.c00lz0ne.internal.&lt;/span&gt;
      &lt;span class="s"&gt;ns1                      IN      A       10.24.0.11&lt;/span&gt;
      &lt;span class="s"&gt;ns2                      IN      A       10.24.0.12&lt;/span&gt;
      &lt;span class="s"&gt;rke00.mgr                IN      A       10.24.0.99&lt;/span&gt;
      &lt;span class="s"&gt;rke01.mgr                IN      A       10.24.0.100&lt;/span&gt;
      &lt;span class="s"&gt;rke-lb.mgr               IN      A       100.69.29.9&lt;/span&gt;

&lt;span class="na"&gt;bindconfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;named.conf&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;options {&lt;/span&gt;
            &lt;span class="s"&gt;directory "/var/cache/bind";&lt;/span&gt;
            &lt;span class="s"&gt;listen-on port 53 { any; };&lt;/span&gt;
            &lt;span class="s"&gt;auth-nxdomain yes;&lt;/span&gt;
            &lt;span class="s"&gt;forwarders { &lt;/span&gt;
                    &lt;span class="s"&gt;1.1.1.1; &lt;/span&gt;
                    &lt;span class="s"&gt;1.0.0.1; &lt;/span&gt;
            &lt;span class="s"&gt;};&lt;/span&gt;
            &lt;span class="s"&gt;listen-on-v6 { ::1; };&lt;/span&gt;
            &lt;span class="s"&gt;allow-recursion {&lt;/span&gt;
                    &lt;span class="s"&gt;none;&lt;/span&gt;
            &lt;span class="s"&gt;};&lt;/span&gt;
            &lt;span class="s"&gt;allow-transfer {&lt;/span&gt;
                    &lt;span class="s"&gt;none;&lt;/span&gt;
            &lt;span class="s"&gt;};&lt;/span&gt;
            &lt;span class="s"&gt;allow-update {&lt;/span&gt;
                    &lt;span class="s"&gt;none;&lt;/span&gt;
            &lt;span class="s"&gt;};&lt;/span&gt;
    &lt;span class="s"&gt;};&lt;/span&gt;

    &lt;span class="s"&gt;zone "c00lz0ne.internal" {&lt;/span&gt;
      &lt;span class="s"&gt;type master;&lt;/span&gt;
      &lt;span class="s"&gt;file "/var/lib/bind/c00lz0ne.internal";&lt;/span&gt;
    &lt;span class="s"&gt;};&lt;/span&gt;

&lt;span class="na"&gt;zoneconfigs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;                 
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;c00lz0ne.internal&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;c00lz0ne.internal&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, you'll see for my cool domain &lt;code&gt;c00lz0ne.internal&lt;/code&gt;, I need to populate &lt;code&gt;bindzones.c00lz0ne.internal&lt;/code&gt; with the BIND zonefile itself, then &lt;code&gt;bindconfig.named.conf&lt;/code&gt; needs to be updated to add that file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    zone "c00lz0ne.internal" {
      type master;
      file "/var/lib/bind/c00lz0ne.internal";
    };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which you'll see in the above uses the mount path we defined in the &lt;code&gt;Deployment&lt;/code&gt; for the &lt;code&gt;ConfigMap&lt;/code&gt; containing these files, and then finally, &lt;code&gt;zoneconfigs.items&lt;/code&gt; which should contain a map of keys and paths for each of the domain zone files you created, and then referenced in &lt;code&gt;named.conf&lt;/code&gt; so it mounts to the &lt;code&gt;Deployment&lt;/code&gt; template when it renders.&lt;/p&gt;

&lt;p&gt;Typically, at this point, you could apply your chart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm &lt;span class="nb"&gt;install &lt;/span&gt;authoritative-dns ./
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then when the &lt;code&gt;Deployment&lt;/code&gt; is online:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jdmarhee@boris ~/repos/terraform-digitalocean-k3s-highavailability &lt;span class="o"&gt;(&lt;/span&gt;master&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods
NAME                                 READY   STATUS    RESTARTS   AGE
authoritative-dns-6576df5d48-7glqk   1/1     Running   0          15s
authoritative-dns-6576df5d48-nhjsq   1/1     Running   0          15s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can test resolution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig +short &lt;span class="nt"&gt;-t&lt;/span&gt; ns ns1.c00lz0ne.internal @&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SVC_IP&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, because we want the &lt;code&gt;Deployment&lt;/code&gt; to update with the changes to the &lt;code&gt;ConfigMap&lt;/code&gt; and other values, we can use a &lt;a href="https://fleet.rancher.io/quickstart/" rel="noopener noreferrer"&gt;GitOps tool like Fleet to update on changes to the repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's say I keep all of my charts in a repo (Fleet also supports private repositories, but for the sake of demonstration, a basic public repo) called &lt;code&gt;helm-charts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fleet.cattle.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GitRepo&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sample&lt;/span&gt;
  &lt;span class="c1"&gt;# This namespace is special and auto-wired to deploy to the local cluster&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fleet-local&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Everything from this repo will be ran in this cluster. You trust me right?&lt;/span&gt;
  &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://github.com/c00lz0ne-infra/helm-charts"&lt;/span&gt;
  &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;authoritative-dns&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;code&gt;paths&lt;/code&gt; represent the charts that Fleet will track changes to, once this is applied and you can &lt;a href="https://fleet.rancher.io/quickstart/#get-status" rel="noopener noreferrer"&gt;see Fleet is running&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In my case, I only want this chart deployed to specific clusters (in this case, with &lt;code&gt;env&lt;/code&gt; matching &lt;code&gt;svcs&lt;/code&gt;), so to the above GitRepo, I add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;clusterSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;svcs&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;svcs-cluster&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;so when this, or a new cluster with that label match (or using any of the other methods of &lt;a href="https://fleet.rancher.io/gitrepo-targets/" rel="noopener noreferrer"&gt;mapping to downstream clusters&lt;/a&gt;), the chart will be applied on each change to a specified branch or revision in the git repository. &lt;/p&gt;

&lt;p&gt;In my case, I will only have a small and fixed number of clusters that will ever be managed that way, but if you want to great ClusterGroups to target (using these matching rules, rather than mapping the GitRepo to the rule, map the GitRepo to the clustergroup, and manage it as its own resource), it can be defined like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterGroup&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fleet.cattle.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;services-group&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;clusters&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;svcs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then update the GitRepo resource to use this matching rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;clusterGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;group1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;clusterGroupSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or optionally use a &lt;a href="https://fleet.rancher.io/gitrepo-targets/#defining-targets" rel="noopener noreferrer"&gt;clusterGroupSelector&lt;/a&gt;, or a combination of these to subset this group further (for example, in region &lt;code&gt;us-east&lt;/code&gt; within that group), name a specific group, or in the above example, find ClusterGroups that are in a specific region.&lt;/p&gt;

&lt;p&gt;The result of all of this is that rather than writing complex rules for when and how to run Helm to target multiple clusters, Fleet will use the above resources to manage this application of chart changes on a specific branch, across the defined clusters.&lt;/p&gt;

</description>
      <category>rancher</category>
      <category>gitops</category>
      <category>dns</category>
      <category>devops</category>
    </item>
    <item>
      <title>Using OpenPolicyAgent to Inject Proxy Configuration into Kubernetes Workloads</title>
      <dc:creator>jdm</dc:creator>
      <pubDate>Sat, 18 Dec 2021 06:49:46 +0000</pubDate>
      <link>https://dev.to/jmarhee/using-openpolicyagent-to-inject-proxy-configuration-into-kubernetes-workloads-2dde</link>
      <guid>https://dev.to/jmarhee/using-openpolicyagent-to-inject-proxy-configuration-into-kubernetes-workloads-2dde</guid>
      <description>&lt;p&gt;It's not unusual to find enterprise services behind a corporate proxy, and in order to get out, must have this proxy access configured to access out through it. The same applies to services running o Kubernetes. &lt;/p&gt;

&lt;p&gt;One way to do this is manually, by creating a &lt;code&gt;Secret&lt;/code&gt; resource containing a proxy string like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create secret generic corporate-proxy &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;authentication-string&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"http://{USERNAME}:{PASSWORD}@{PROXY_ADDRESS}:{PROXY_PORT}"&lt;/span&gt; &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no-proxy-string&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"localhost,127.0.0.1,0.0.0.0,10.0.0.0/8,cattle-system.svc,10.42.0.0/24,.svc,.cluster.local,example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and, mount it to the environment of a Kubernetes application resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appWithProxy&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appWithProxy&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yourApplication&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTP_PROXY&lt;/span&gt;
      &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;corporate-proxy&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;authentication-string&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NO_PROXY&lt;/span&gt;
      &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;corporate-proxy&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no-proxy-string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and when you &lt;code&gt;exec&lt;/code&gt; into that container, you can use that proxy to access public resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--proxy&lt;/span&gt; &lt;span class="nv"&gt;$HTTP_PROXY&lt;/span&gt; http://ipinfo.io/json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, this is a tedious, manual process that you would, then, need to repeat for every resource you'd like to have proxy access.&lt;/p&gt;

&lt;p&gt;Among the many things OpenPolicyAgent Gatekeeper does, is allow you to mutate Kubernetes API requests based on a variety of scoping rules, and inject things like environment variables into resources that meet said scope. &lt;/p&gt;

&lt;p&gt;This is done using a Mutating Admission Webhook. Using &lt;a href="https://slack.engineering/simple-kubernetes-webhook/" rel="noopener noreferrer"&gt;Mutating Admission Webhook to inject environment variables like these proxy settings&lt;/a&gt;, for example, to modify requests to create a resource like a &lt;code&gt;Pod&lt;/code&gt; or &lt;code&gt;Deployment&lt;/code&gt;, etc. to inject the proxy configuration data. There are other types of Webhooks, such as (in the linked example) Validating Webhooks, which do things like confirm a workload has a label, or an appropriate name, etc. to dictate some other action by the Admission Webhook.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rancher.com/docs/rancher/v2.0-v2.4/en/cluster-admin/tools/opa-gatekeeper/" rel="noopener noreferrer"&gt;OPA Gatekeeper&lt;/a&gt;, in this scenario, will do much of this for you, and instead, mutation and scoping is dictated by policies. In our case, we want to create a namespace that will contain resources that we wish to mutate, upon request, to include mounting the above &lt;code&gt;corporate-proxy&lt;/code&gt; resource &lt;code&gt;Secret&lt;/code&gt; as an environment variable.&lt;/p&gt;

&lt;p&gt;OPA also provides &lt;a href="https://www.openpolicyagent.org/docs/latest/kubernetes-primer/#testing-policies" rel="noopener noreferrer"&gt;facilities for testing these policies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After &lt;a href="https://open-policy-agent.github.io/gatekeeper/website/docs/install/#using-prebuilt-image" rel="noopener noreferrer"&gt;installing OPA Gatekeeper&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.7/deploy/gatekeeper.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or if you're a &lt;a href="https://rancher.com/docs/rancher/v2.0-v2.4/en/cluster-admin/tools/opa-gatekeeper/" rel="noopener noreferrer"&gt;Rancher user, Gatekeeper can be installed and enabled from the UI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We can then define a &lt;code&gt;ModifySet&lt;/code&gt; resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mutations.gatekeeper.sh/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ModifySet&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo-annotation-owner&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;proxy-test&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;applyTo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;versions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v1"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;kinds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pod"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deployment"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Namespaced&lt;/span&gt;
      &lt;span class="na"&gt;kinds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
          &lt;span class="na"&gt;kinds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pod"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;spec.containers[name:*].env"&lt;/span&gt;
    &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;fromList&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTP_PROXY&lt;/span&gt;
            &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;corporate-proxy&lt;/span&gt;
                &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;authentication-string&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NO_PROXY&lt;/span&gt;
            &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;corporate-proxy&lt;/span&gt;
                &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;authentication-string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which is deployed to the &lt;code&gt;proxy-test&lt;/code&gt; namespace, and a couple of lines down, the &lt;code&gt;scope&lt;/code&gt; is set to &lt;code&gt;Namespaced&lt;/code&gt;, meaning that it will only apply the following to matching resources in that namespace. &lt;/p&gt;

&lt;p&gt;If the API server passes a request that is that namespace, and matches any apiGroup ("*"), and is of &lt;code&gt;kind: Pod&lt;/code&gt;, it will read a spec like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shell-demo&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;proxy-test&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shared-data&lt;/span&gt;
    &lt;span class="na"&gt;emptyDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SOME_EXISTING_VAR&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Present"&lt;/span&gt;
    &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shared-data&lt;/span&gt;
      &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/usr/share/nginx/html&lt;/span&gt;
  &lt;span class="na"&gt;dnsPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Default&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where it will look in &lt;code&gt;spec.containers&lt;/code&gt;, and you'll see since this request does not contain an &lt;code&gt;env&lt;/code&gt; block, it will inject our &lt;code&gt;Secret&lt;/code&gt; from above (&lt;code&gt;secret/corporate-proxy&lt;/code&gt;) into this spec appending &lt;code&gt;env&lt;/code&gt; represented above in Yaml, but can also be inserted as JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HTTP_PROXY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"valueFrom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"secretKeyRef"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"corporate-proxy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"authentication-string"&lt;/span&gt;&lt;span class="p"&gt;}}}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and create the &lt;code&gt;Pod&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;You can test this out by creating two &lt;code&gt;Pods&lt;/code&gt; using the above spec, one in the &lt;code&gt;proxy-test&lt;/code&gt; namespace and one in another:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt;&amp;gt; proxy.yaml
apiVersion: v1
kind: Pod
metadata:
  name: shell-demo
  namespace: proxy-test
spec:
  volumes:
  - name: shared-data
    emptyDir: {}
  containers:
  - name: nginx
    image: nginx
    env:
    - name: SOME_EXISTING_VAR
      value: "Present"
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html
  dnsPolicy: Default
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; proxy.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and one in another:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt;&amp;gt; no-proxy.yaml
apiVersion: v1
kind: Pod
metadata:
  name: shell-demo
  namespace: default
spec:
  volumes:
  - name: shared-data
    emptyDir: {}
  containers:
  - name: nginx
    image: nginx
    env:
    - name: SOME_EXISTING_VAR
      value: "Present"
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html
  dnsPolicy: Default
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; no-proxy.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and when you run &lt;code&gt;kubectl describe&lt;/code&gt; on the namespaced &lt;code&gt;Pod&lt;/code&gt;, you'll see the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@ubuntu-proxy-k3s:~# kubectl describe pod/shell-demo &lt;span class="nt"&gt;-n&lt;/span&gt; proxy-test
Name:         shell-demo
Namespace:    proxy-test
...
Containers:
  nginx:
    Image:      nginx
...
    Environment:
      SOME_EXISTING_VAR:     &lt;span class="s2"&gt;"Present"&lt;/span&gt;
      HTTP_PROXY:  &amp;lt;&lt;span class="nb"&gt;set &lt;/span&gt;to the key &lt;span class="s1"&gt;'authentication-string'&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;secret &lt;span class="s1"&gt;'corporate-proxy'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  Optional: &lt;span class="nb"&gt;false
      &lt;/span&gt;NO_PROXY:    &amp;lt;&lt;span class="nb"&gt;set &lt;/span&gt;to the key &lt;span class="s1"&gt;'authentication-string'&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;secret &lt;span class="s1"&gt;'corporate-proxy'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  Optional: &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where the one in the &lt;code&gt;default&lt;/code&gt; namespace will not have an Envrionment set. &lt;/p&gt;

&lt;p&gt;These &lt;code&gt;ModifySet&lt;/code&gt; resources can be written to mutate based on a number of other scopes as well, and any part of a manifest that you specify in the &lt;code&gt;location&lt;/code&gt; key, and then assign with a &lt;code&gt;value&lt;/code&gt; under parameters as we did above. Other examples of using OPA Gatekeeper resources to mutate resources can be found &lt;a href="https://open-policy-agent.github.io/gatekeeper/website/docs/mutation/#!" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>networking</category>
      <category>openpolicyagent</category>
      <category>k3s</category>
    </item>
    <item>
      <title>Capturing and Posting Video Screenshots at Random Intervals</title>
      <dc:creator>jdm</dc:creator>
      <pubDate>Tue, 31 Aug 2021 05:08:35 +0000</pubDate>
      <link>https://dev.to/jmarhee/capturing-and-posting-video-screenshots-at-random-intervals-2739</link>
      <guid>https://dev.to/jmarhee/capturing-and-posting-video-screenshots-at-random-intervals-2739</guid>
      <description>&lt;p&gt;I recently created a couple of bot Twitter accounts, &lt;a href="//twitter.com/screenshotsVice"&gt;@ScreenshotsVice&lt;/a&gt; which shares screencaps from episodes of "Miami Vice" and &lt;a href="//twitter.com/pasolinibot"&gt;@Pasolinibot&lt;/a&gt; which shares caps from the filmography of Pier Paolo Pasolini, both at regular intervals. &lt;/p&gt;

&lt;p&gt;The way these accounts are managed are that, when a cronjob is triggered, from a video library, a series of screencaptures are dumped (if they were set to refresh on the previous run), and then the listing in the directory is shuffled, and then the script selects one at random and posts it to Twitter. &lt;/p&gt;

&lt;p&gt;Because the intervals are managed by cronjobs, I'll focus on what happens when a job is triggered. &lt;/p&gt;

&lt;p&gt;The Python script that selects and posts the capture relies on the &lt;code&gt;tweepy&lt;/code&gt; Twitter API client for Python, so you will need your &lt;a href="//developers.twitter.com"&gt;Twitter Developer API credentials&lt;/a&gt;. The script that generates the captures, before running, requires that &lt;a href="https://github.com/jmarhee/screenshot-capture-twitter" rel="noopener noreferrer"&gt;&lt;code&gt;ffmpeg&lt;/code&gt; be installed&lt;/a&gt; and the directory where the video library is stored and the directory where you'll store screencaptures.&lt;/p&gt;

&lt;p&gt;To generate the capture library, the following script is used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;LNVC_DATA_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SOURCE_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;LNVC_PREV_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SOURCE_DIR_OUT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-Screens&lt;/span&gt;
&lt;span class="nv"&gt;REFRESH_SCREENS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nv"&gt;DUMP_INTERVAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nv"&gt;DUMP_RANDOM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nv"&gt;RANDOMIZE_NAMES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LNVC_DATA_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"No source path found. Set LNVC_DATA_PATH."&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LNVC_PREV_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"No target path found. Set LNVC_PREV_PATH."&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REFRESH_SCREENS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nv"&gt;$LNVC_PREV_PATH&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sets up options for things like randomizing filenames, whether or not to refresh the library, defining the video source and the capture outputs, the latter two are the only ones that are required.&lt;/p&gt;

&lt;p&gt;After this, the video library is iterated, and the captures created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;v &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nv"&gt;$LNVC_DATA_PATH&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DUMP_RANDOM&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;LENGTH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nv"&gt;$LNVC_DATA_PATH&lt;/span&gt;/&lt;span class="nv"&gt;$v&lt;/span&gt; 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep &lt;/span&gt;Duration | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 4 | &lt;span class="nb"&gt;sed &lt;/span&gt;s/,//&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nv"&gt;HOUR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; RANDOM &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$LENGTH&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;":"&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 2&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="nv"&gt;$HOUR&lt;/span&gt; &amp;lt; 10 &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nv"&gt;HOUR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%02d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;$HOUR&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nv"&gt;MINUTE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; RANDOM &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$LENGTH&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;":"&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 2&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="nv"&gt;$MINUTE&lt;/span&gt; &amp;lt; 10 &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nv"&gt;MINUTE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%02d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;$MINUTE&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nv"&gt;SECOND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; RANDOM &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="m"&gt;59&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="nv"&gt;$SECOND&lt;/span&gt; &amp;lt; 10 &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nv"&gt;SECOND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%02d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;$SECOND&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nv"&gt;$LNVC_DATA_PATH&lt;/span&gt;/&lt;span class="nv"&gt;$v&lt;/span&gt; &lt;span class="nt"&gt;-ss&lt;/span&gt; &lt;span class="nv"&gt;$HOUR&lt;/span&gt;:&lt;span class="nv"&gt;$MINUTE&lt;/span&gt;:&lt;span class="nv"&gt;$SECOND&lt;/span&gt;.000 &lt;span class="nt"&gt;-vframes&lt;/span&gt; 1 &lt;span class="nv"&gt;$LNVC_PREV_PATH&lt;/span&gt;/&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; /dev/urandom | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-dc&lt;/span&gt; A-Za-z0-9 | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; 13 &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;.jpg
    &lt;span class="k"&gt;fi

    if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DUMP_INTERVAL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nv"&gt;$LNVC_DATA_PATH&lt;/span&gt;/&lt;span class="nv"&gt;$v&lt;/span&gt; &lt;span class="nt"&gt;-frames&lt;/span&gt;:v 1 &lt;span class="nt"&gt;-vf&lt;/span&gt; &lt;span class="nv"&gt;fps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1/&lt;span class="nv"&gt;$DUMP_INTERVAL&lt;/span&gt; &lt;span class="nv"&gt;$LNVC_PREV_PATH&lt;/span&gt;/&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; /dev/urandom | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-dc&lt;/span&gt; A-Za-z0-9 | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; 13 &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;.jpg
        &lt;span class="c"&gt;# The filenames will be prefixed by a random string, but per-video; if you'd like them randomized in totality, set `RANDOMIZE_NAMES`&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RANDOMIZE_NAMES&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
            &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$LNVC_PREV_PATH&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;&lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="nv"&gt;$f&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; /dev/urandom | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-dc&lt;/span&gt; A-Za-z0-9 | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; 13 &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;.jpg&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done
        fi
    fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;DUMP_RANDOM&lt;/code&gt; is enabled, random timecodes will be dumped from a video file, &lt;code&gt;DUMP_INTERVAL&lt;/code&gt; will dump video files into captures at each interval (useful for films, rather than slices from an episode of a TV series). Filenames (which helps when selecting images randomly to post) can be selectively re-randomized. &lt;/p&gt;

&lt;p&gt;This script has dumped videos from &lt;code&gt;SOURCE_DIR&lt;/code&gt; into &lt;code&gt;SOURCE_DIR_OUT&lt;/code&gt;, which our poster script will read from.&lt;/p&gt;

&lt;p&gt;First, we have to setup the Twitter authentication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tweepy&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="n"&gt;twitter_auth_keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;consumer_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;twitter_consumer_key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;consumer_secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;     &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;twitter_consumer_secret&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;access_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;twitter_access_token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;access_token_secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;twitter_access_token_secret&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;



    &lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tweepy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OAuthHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;

            &lt;span class="n"&gt;twitter_auth_keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;consumer_key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

            &lt;span class="n"&gt;twitter_auth_keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;consumer_secret&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_access_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;

            &lt;span class="n"&gt;twitter_auth_keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;access_token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

            &lt;span class="n"&gt;twitter_auth_keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;access_token_secret&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tweepy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;API&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then lock in our selected image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SOURCE_DIR_OUT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SOURCE_DIR_OUT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;media&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;media_upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you'll see above that it reads the captures into a list of filenames, then shuffles the list, before selecting one at random, then we post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="n"&gt;tweet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#pasolini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;post_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;media_ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;media_id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;



&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The package can be found on &lt;a href="https://github.com/jmarhee/screenshot-capture-twitter" rel="noopener noreferrer"&gt;Github&lt;/a&gt; along with some additional examples (one using a Kubernetes CronJob resource, for example) for use. &lt;/p&gt;

</description>
      <category>python</category>
      <category>ffmpeg</category>
      <category>twitter</category>
    </item>
    <item>
      <title>Using pre-commit and post-update git hooks</title>
      <dc:creator>jdm</dc:creator>
      <pubDate>Wed, 09 Jun 2021 05:42:08 +0000</pubDate>
      <link>https://dev.to/jmarhee/using-pre-commit-and-post-update-git-hooks-1e54</link>
      <guid>https://dev.to/jmarhee/using-pre-commit-and-post-update-git-hooks-1e54</guid>
      <description>&lt;p&gt;Adding some additional processing to your git workflow is sometimes useful/cool/interesting, and the functionality provided by &lt;a href="https://githooks.com/" rel="noopener noreferrer"&gt;githooks&lt;/a&gt; make this fairly accessible to make use of. I typically make use of very few of these (in place of having things happen on the server-side — tests, etc.-that can be enabled externally or using server-side hooks) on the client-side, and adapt two included in &lt;code&gt;.git/hooks/&lt;/code&gt; by default, &lt;code&gt;pre-commit&lt;/code&gt; and &lt;code&gt;post-update&lt;/code&gt;, for, you guessed it, before commits are made, and after a set of commits are pushed.&lt;/p&gt;

&lt;p&gt;In my capacity as an operations engineer, I make use of tools like Terraform often, which has the benefit of including a formatting tool and a validation tool — this is a good example of where a pre-commit hook can be useful — before I create a commit, I can validate the manifest and check formatting/style of the manifests being updated. I can do this by modifying, in my project root, &lt;code&gt;.git/hooks/pre-commit.sample&lt;/code&gt;, and add something like this to check &lt;code&gt;.tf&lt;/code&gt; files against these standards:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;modified_files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git ls-files &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in &lt;/span&gt;modified_files
&lt;span class="k"&gt;do
    if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$f&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.tf &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;terraform validate &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
        terraform &lt;span class="nb"&gt;fmt&lt;/span&gt; &lt;span class="nv"&gt;$f&lt;/span&gt; &lt;span class="nt"&gt;-check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
        &lt;/span&gt;git add &lt;span class="nv"&gt;$f&lt;/span&gt;
    &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’ve adapted my approach from &lt;a href="https://gist.github.com/jamtur01/a567078b7ba545c3492f7cd32a65450d" rel="noopener noreferrer"&gt;James Turnbull’s pre-commit hook script&lt;/a&gt; to process this slightly differently, but also highlights that these are basically just a scriptable interface to manage git’s behavior.&lt;/p&gt;

&lt;p&gt;To enable this hook, copy the file to &lt;code&gt;.git/hooks/pre-commit&lt;/code&gt; (no &lt;code&gt;.sample&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Let’s take a look at a more involved example in my &lt;code&gt;post-update&lt;/code&gt; script. In my case, I am working on a Mac, so I’d like to make the most of this — maybe introduce some more visual cues for me to follow, so I want to use some Applescript as well.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;.git/hooks/post-update&lt;/code&gt;, I want to grab the hash and message of the last commit pushed, and the branch I pushed to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;COMMIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git log &lt;span class="nt"&gt;-1&lt;/span&gt; HEAD | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1 | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;MESSAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git log &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;%B &lt;span class="nt"&gt;-n&lt;/span&gt; 1 &lt;span class="nv"&gt;$COMMIT&lt;/span&gt; | xargs &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git branch | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="se"&gt;\*&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="nt"&gt;-f2&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and to make this useful, I can see a summary of my latest push by adding this call to &lt;code&gt;osascript&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/usr/bin/osascript &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
display dialog "To &lt;/span&gt;&lt;span class="nv"&gt;$BRANCH&lt;/span&gt;&lt;span class="sh"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="nv"&gt;$MESSAGE&lt;/span&gt;&lt;span class="se"&gt;\n\n\t&lt;/span&gt;&lt;span class="sh"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$COMMIT&lt;/span&gt;&lt;span class="sh"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;" with title "Git Push" buttons {"I meant to do that"} default button 1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To have a pop-up dialog and just a reminder of what I did, so I stop to check my work one last time before moving on to my next task.&lt;/p&gt;

</description>
      <category>git</category>
      <category>terraform</category>
      <category>automation</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
