<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Microservices on avni.sh</title>
    <link>http://www.avni.sh/categories/microservices/</link>
    <description>Recent content in Microservices on avni.sh</description>
    <image>
      <title>avni.sh</title>
      <url>http://www.avni.sh/cover.webp</url>
      <link>http://www.avni.sh/cover.webp</link>
    </image>
    <generator>Hugo -- 0.146.0</generator>
    <language>en</language>
    <lastBuildDate>Wed, 07 Jun 2023 00:00:00 +0000</lastBuildDate>
    <atom:link href="http://www.avni.sh/categories/microservices/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Network Functions</title>
      <link>http://www.avni.sh/posts/computer-science/technologies/cloud-native/network-functions/</link>
      <pubDate>Wed, 07 Jun 2023 00:00:00 +0000</pubDate>
      <guid>http://www.avni.sh/posts/computer-science/technologies/cloud-native/network-functions/</guid>
      <description>Network Functions (like routers, firewalls, and DHCP servers) are an integral part of the massive network deployments by telcos</description>
      <content:encoded><![CDATA[<p><strong>Telcos (Telecommunication companies)</strong> deploy networks that have high availability, are scalable, and resilient covering entire countries. Components like routers, firewalls, and DHCP servers (called <strong>Network Functions</strong>) are the building blocks of such large network deployments.</p>
<p>Traditionally, network functions were deployed on proprietary hardware with application-specific integrated circuits and installed on the telco&rsquo;s premise (baremetal deployment). Such network functions are called Platform Network Functions (PNFs).</p>
<p>PNF deployments present the following challenges:</p>
<ul>
<li>Hardware for network functions could not be moved easily and redeployed.</li>
<li>The deployment of PNFs required knowledge of proprietary hardware.</li>
<li>Services deployed on top of PNFs are tightly coupled with the hardware. Customization, scaling, and configuration of services could require change/upgrade of hardware components.</li>
<li>Access to PNFs is limited to the private cloud setup by the telco.</li>
</ul>
<h1 id="virtualized-network-functions-vnfs">Virtualized Network Functions (VNFs)</h1>
<p>The concept of <strong>Network Function Virtualization (NFV)</strong> was introduced in 2012 by a group of leading telcos. It proposed the transition of network functions from proprietary hardware to virtual machines. Individual network functions could be provisioned as a Virtual Machine on top of a hypervisor (<a href="/posts/computer-science/technologies/cloud-native/containers/#virtual-machines-vms" target="_blank">Virtualized Deployment</a>), such network functions are called <strong>Virtualized Network Functions (VNF)</strong>.</p>
<p>Benefits of VNFs over PNFs</p>
<ul>
<li>VNFs could be deployed on any generic hardware as long as they are supported by the hypervisor. This eliminates the need for proprietary hardware for deployment.</li>
<li>VNFs could be provisioned and moved easily.</li>
<li>The scalability of the network is improved.</li>
<li>Overall reduction in power and cooling requirements.</li>
</ul>
<p>Other than VNFs the NFV architecture comprises MANO (Management, Automation, and Network Orchestration) and NFVi (Network Function Virtualization infrastructure).</p>
<p>MANO is a framework for the management and orchestration of all the resources and their lifecycle in NFV architecture. This includes the compute, storage, and network resources. NFVi refers to the infrastructure that provides the resources to VNFs an example could be OpenStack.</p>
<p>Software Defined Network (SDN) could be created on top of VNFs to further ease the management and configurability of the network.</p>
<h2 id="example-of-a-vnf-infoblox">Example of a VNF: Infoblox</h2>
<p>Infoblox provides cloud networking solutions through products that provide a unified network view, global load balancing, reporting, analytics, etc.</p>
<p>The NFV provided by Infoblox has the following features:</p>
<ul>
<li>DNS, DHCP, and IP Address Management (IPAM) services.</li>
<li>DNS Cache Acceleration Services (vDCA) improves the performance of DNS resolution across the network.</li>
<li>Advanced DNS Protection (vADP) for networks deployed on OpenStack.</li>
<li>Integration with other Infoblox products like Infoblox IPAM and Infoblox vDiscovery.</li>
</ul>
<h1 id="cloud-native-network-functions-cnfs">Cloud-Native Network Functions (CNFs)</h1>
<p>Now, network functions are transitioning from virtual machines to containers (<a href="/posts/computer-science/technologies/cloud-native/containers/#containers" target="_blank">Containerized Deployment</a>). This will provide further benefits over PNFs like</p>
<ul>
<li>Improved scalability and efficient usage of resources.</li>
<li>Ability to use a mix of private, public, and hybrid cloud as containers could be deployed and migrated easily on any cloud environment.</li>
<li>Edge devices could also be included in the network to improve its reach and latency to the end users.</li>
</ul>
<p>Although <a href="/posts/computer-science/technologies/cloud-native/container-network-interfaces/" target="_blank">CNIs</a> could be used to create and configure network interfaces between containers, for telco&rsquo;s use case a network function container could require network interfaces that could interact with the specialized hardware directly. In such cases, an <a href="/posts/computer-science/technologies/cloud-native/kubernetes-operators/" target="_blank">operator</a> could be defined with custom resources that could procure the hardware directly to the workloads (containers) as a network interface. CNFs themselves could also be packaged as Kubernetes operators.</p>
<h2 id="example-of-a-cnf-nokia-cloud-mobility-manager">Example of a CNF: Nokia Cloud Mobility Manager</h2>
<p>Nokia&rsquo;s Cloud Mobility Manager is a control plane network function for packet networks following the 3GPP (3rd Generation Partnership Project) standard.</p>
<p>It is designed to be deployed in a cloud-native environment as VNF (in OpenStack KVM or VMware vCloud environments) or as CNF (in OpenShift). It provides the following benefits</p>
<ul>
<li>Performance and scalability depending on the workloads</li>
<li>Reliability and resiliency with fault monitoring, recovery, and overload control protection</li>
<li>Multi-generation access (2G/3G/4G/5G)</li>
</ul>
<h1 id="transition-from-vnfs-to-cnfs">Transition from VNFs to CNFs</h1>
<p>The journey from VNFs to CNFs for telcos is not easy due to the following challenges:</p>
<ul>
<li>Virtualization of a workload is relatively easier compared to its containerization.</li>
<li>A container isolates the process running inside it using <a href="/posts/computer-science/technologies/cloud-native/container-architecture/#namespaces" target="_blank">namespaces</a>. Some of the network function&rsquo;s workloads might not run as expected inside an isolated namespace.</li>
<li>Containers share the kernel with their host and traditionally the deployment of network functions requires kernel hacks.</li>
<li>The architecture of telco networks is complex. Migrating it to a container-based architecture would not be easy.</li>
</ul>
<p>In addition to this telcos work in a highly regulated environment where they are expected to have:</p>
<ul>
<li>Service Level Agreements (SLAs) for the availability of the network. Usually, this is 99.999% (~5.26 minutes in a year).</li>
<li>Maintain the backward compatibility of the network with up to 50 years of legacy requirements.</li>
<li>Tight regulation from governments and authorities.</li>
</ul>
<p>To ease this transition we have CNF Test Suite and CNF Testbed</p>
<h2 id="cnf-test-suite">CNF Test Suite</h2>
<p>The <strong>CNF Test Suite</strong> is defined by the Telecom User Group (TUG) and Cloud Native Network Function Working Group (CNF WG) for telcos to test their network functions deployed in a cloud-native environment for cloud-native principles:</p>
<ul>
<li>Configuration: The configuration for CNF should be defined in a declarative manner in ConfigMaps, Operators, or any other resource.</li>
<li>Compatibility, Installability &amp; Upgradability: CNFs should work with Certified Kubernetes Products. CNFs should be distributed &amp; deployed as Containers, Operators, or <a href="/posts/computer-science/technologies/cloud-native/helm-charts/" target="_blank">Helm Charts</a>.</li>
<li>Microservice: CNFs should be built as microservices.</li>
<li>State: The state of CNFs should be stored in a custom resource or a database like <code>etcd</code>.</li>
<li>Reliability, Resilience &amp; Availability: Failures are inevitable in a non-carrier grade cloud environment. So a CNF should be reliable, resilient, and available during such failures.</li>
<li>Observability &amp; Diagnostics: CNFs should provide endpoints and data to establish the observability stack (metrics, tracing, and logging).</li>
<li>Security: The containers associated with CNFs should be isolated from their host and other containers. They should also be verified against common CVE and other vulnerabilities.</li>
</ul>
<h2 id="cnf-testbed">CNF Testbed</h2>
<p><strong>CNF Testbed</strong> provides reference code and test suites for benchmarking the performance and resiliency between network functions deployed as VNFs and CNFs.</p>
<p>It provisions the infrastructure for a Kubernetes and OpenStack cluster. After that, it deploys similar workloads on both environments to benchmark their performance using testing tools such as NFVbench.</p>
<hr>
<p>Thank you for taking the time to read this blog post! Have questions, feedback or want to discuss this topic? Feel free to reach out at <a href="mailto:blog@avni.sh"><a href="mailto:blog@avni.sh">blog@avni.sh</a></a>.</p>
<p>If you found this content valuable and would like to stay updated with my latest posts, consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.</p>
<h1 id="resources">Resources</h1>
<p><a href="https://www.redhat.com/en/topics/cloud-computing/what-is-telco-cloud" target="_blank">What is telco cloud?</a><br>
<a href="https://www.redhat.com/en/topics/virtualization/what-is-nfv" target="_blank">What is NFV?</a><br>
<a href="https://www.sdxcentral.com/networking/nfv/definitions/whats-network-functions-virtualization-nfv/nfv-elements-overview/nfv-mano/" target="_blank">What is NFV MANO?</a><br>
<a href="https://www.techtarget.com/searchnetworking/definition/virtual-network-functions-VNF" target="_blank">What are Virtual Network Functions (VNFs)?</a><br>
<a href="https://www.vmware.com/topics/glossary/content/network-functions-virtualization-nfv.html" target="_blank">What is Network Functions Virtualization (NFV)? | VMware Glossary</a><br>
<a href="https://cloud.ibm.com/docs/vpc?topic=vpc-about-vnf" target="_blank">About virtual network functions over VPC</a><br>
<a href="https://blogs.infoblox.com/community/how-infoblox-supports-network-functions-virtualization/" target="_blank">Infoblox Network Function Virtualization</a><br>
<a href="https://www.redhat.com/en/topics/cloud-native-apps/vnf-and-cnf-whats-the-difference" target="_blank">VNF and CNF, what’s the difference?</a><br>
<a href="https://www.nokia.com/networks/core-networks/cloud-mobility-manager/" target="_blank">Nokia Cloud Mobility Manager</a><br>
<a href="https://www.youtube.com/watch?v=FvDJg2hZr0I&pp=ygUQbmV0d29yayBmdW5jdGlvbg%3D%3D" target="_blank">Lessons Learnt Developing and Deploying a Cloud Native Network Function - Paul Graham</a><br>
<a href="https://github.com/cncf/cnf-testsuite/" target="_blank">cncf/cnf-testsuite</a><br>
<a href="https://www.cncf.io/blog/2022/03/24/testing-cloud-native-best-practices-with-the-cnf-test-suite/" target="_blank">Testing cloud native best practices with the CNF Test Suite</a><br>
<a href="https://github.com/cncf/cnf-testbed" target="_blank">cncf/cnf-testbed</a><br>
<a href="https://www.youtube.com/watch?v=_fD_4FuU_jg&pp=ygUeY2xvdWQgbmF0aXZlIG5ldG93b3JrIGZ1bmN0aW9u" target="_blank">Cloud Native Network Function (CNF) Testbed - Taylor Carpenter &amp; Denver Williams</a><br>
<a href="https://www.youtube.com/watch?v=ZF5UJD7YJIw&pp=ygUIY25mIGNuY2Y%3D" target="_blank">K8s Best Practices for Telco Apps - Taylor Carpenter &amp; Bill Mulligan</a><br>
<a href="https://cloud.redhat.com/blog/building-cnf-applications-with-openshift-pipelines" target="_blank">Building CNF applications with OpenShift Pipelines</a><br>
<a href="https://www.youtube.com/watch?v=IL4nxbmUIX8&pp=ygUeY2xvdWQgbmF0aXZlIG5ldG93b3JrIGZ1bmN0aW9u" target="_blank">Keynote: E2E 5G Cloud Native Network - Heather Kirksey, Azhar Sayeed &amp; Fu Qiao</a><br>
<a href="https://www.redhat.com/en/topics/edge-computing/telecommunications" target="_blank">Understanding edge computing for telecommunications</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>Container Storage Interfaces (CSI)</title>
      <link>http://www.avni.sh/posts/computer-science/technologies/cloud-native/container-storage-interfaces/</link>
      <pubDate>Wed, 31 May 2023 00:00:00 +0000</pubDate>
      <guid>http://www.avni.sh/posts/computer-science/technologies/cloud-native/container-storage-interfaces/</guid>
      <description>Container Storage Interface (CSI) plugins are developed by storage providers to establish a consistent interface between container orchestrators and storage solutions</description>
      <content:encoded><![CDATA[<p>There is a multitude of choices for storage solutions (Amazon S3, Ceph, Google Cloud Storage, etc.) and combined with the choices of container orchestrators (Kubernetes, Apache Mesos, Docker Swarm, etc.) the permutations are endless.</p>
<p>A <strong>Container Storage Interface (CSI)</strong> plugin is implemented by the storage providers (Amazon, Google, IBM) as an interface to provision and mount volumes for workloads when requested by container orchestrators. The CSI plugin provisions the volume, procures it from the node hosting the container, and mounts it to the requesting container. It standardizes the process of allocating storage for containers between different orchestrators.</p>
<h1 id="volume-plugin">Volume Plugin</h1>
<p>Before CSIs, the <strong>Volume Plugins</strong> were used to link the storage solutions and container orchestrators.</p>
<p>Limitations of Volume Plugins</p>
<ul>
<li>Volume Plugins were a part of Kubernetes core. Thus, their development and release were tightly coupled with Kubernetes.</li>
<li>Instability and reliability issues with volume plugins could lead to the failure of core Kubernetes components.</li>
<li>Every container orchestrator required a different implementation of the plugin.</li>
<li>Orchestrators had to communicate with the storage solution throughout the lifecycle of the volume to avoid failures.</li>
<li>Volume Plugins had to be open source.</li>
</ul>
<h2 id="lifecycle-of-a-volume-with-volume-plugins">Lifecycle of a Volume with Volume Plugins</h2>
<p align="center"><img src="VolumePlugin.png" alt="Volume Plugins"></p>
<p align="center"><small><i>Volume Plugins</i></small></p>
<h1 id="csi-specification">CSI Specification</h1>
<p>The CSI specification defines interfaces for implementing a plugin</p>
<ul>
<li>The plugin should work seamlessly across all container orchestrators.</li>
<li>Ability to dynamically provision and unprovisioned volumes.</li>
<li>Mounting, unmounting, attaching, and detaching volumes on the node.</li>
<li>Ability to work with block and mountable volumes.</li>
<li>Performing snapshots of volumes, that could be used for provisioning a new volume.</li>
</ul>
<h1 id="architecture-of-a-csi">Architecture of a CSI</h1>
<p>The orchestrator interacts with the CSI plugin through gRPCs, a cross-platform Remote Procedure Call framework introduced by Google.</p>
<p><strong>Remote Procedure Calls (RPCs)</strong> are used in distributed systems to execute a procedure (subroutine) on a remote system. The CSI&rsquo;s RPCs are idempotent i.e. a call should not change the result beyond its initial application.</p>
<h2 id="rpc-endpoints">RPC endpoints</h2>
<p>There are three sets of gRPCs provided by the CSI Plugin: Identity, Controller, and Node service.</p>
<h3 id="identity-service-rpcs">Identity Service RPCs</h3>
<p>The <strong>Identity Service</strong> provides gPRC endpoints that allow an orchestrator to query the capabilities of the plugin, its health, and the metadata associated with the plugin.</p>
<p>gRPC endpoints provided by the identity service:</p>
<ul>
<li><code>GetPluginInfo</code>: Returns the plugin metadata</li>
<li><code>GetPluginCapabilities</code>: Returns the plugin capabilities (fetched from controller service)</li>
<li><code>Probe</code>: Returns the health of the plugin</li>
</ul>
<h3 id="controller-service-rpcs">Controller Service RPCs</h3>
<p><strong>Controller Service</strong> groups all the gRPC endpoints responsible for</p>
<ul>
<li>Creating and deleting the volume</li>
<li>Publishing and unpublishing the volume to the nodes</li>
<li>Creating snapshots of the volumes</li>
</ul>
<p>gRPC endpoints provided by the controller service:</p>
<ul>
<li><code>CreateVolume</code>: Creates the volumes on the storage solution. The volume could be created as an empty new volume or from an existing snapshot or from an existing volume.</li>
<li><code>DeleteVolume</code>: Deletes a provisioned volume on the storage solution. Although the volume is deleted its snapshots are functional and available as sources for new volumes.</li>
<li><code>ControllerPublishVolume</code>: Performs the necessary operations to make the volume available to the requesting node.</li>
<li><code>ControllerUnpublishVolume</code>: Reverts the operations performed in <code>ContainerPublishVolume</code>.</li>
<li><code>ValidateVolumeCapabilities</code>: Verifies the provisioned volume for capabilities requested by the orchestrator.</li>
<li><code>ListVolumes</code>: Returns information about all the volumes.</li>
<li><code>ControllerGetVolume</code>: Called by the orchestrator to fetch the current information associated with a specific volume.</li>
<li><code>GetCapacity</code>: Queries the storage pool for its current capacity.</li>
<li><code>ControllerGetCapabilities</code>: Returns the details of the capabilities of the controller service.</li>
<li><code>CreateSnapshot</code>: Creates a snapshot of an existing volume.</li>
<li><code>DeleteSnapshot</code>: Deletes the volume snapshot.</li>
<li><code>ListSnapshots</code>: Returns details of all snapshots.</li>
<li><code>ControllerExpandVolume</code>: Using this RPC the orchestrator can request for expansion in the size of a volume.</li>
</ul>
<h3 id="node-service-rpcs">Node Service RPCs</h3>
<p>These RPCs are called from the node where the volume is requested. They are responsible for the actions performed on the volume after it is mounted on the node.</p>
<p>gRPC endpoints provided by the controller service:</p>
<ul>
<li><code>NodeStageVolume</code>: After the workload (container) is scheduled on the orchestrator it calls <code>NodeStageVolume</code> on the volume provided by <code>ContainerPublishVolume</code>. The volume is mounted on the node on <code>stage_path</code> temporarily.</li>
<li><code>NodeUnstageVolume</code>: Reverts the actions performed by <code>NodeStageVolume</code>.</li>
<li><code>NodePublishVolume</code>: After <code>NodeStageVolume</code> is called the <code>NodePublishVolume</code> mounts the volume on <code>target_path</code> to be consumed by the workload.</li>
<li><code>NodeUnpublishVolume</code>: Reverts the actions performed by <code>NodeUnpublishVolume</code>.</li>
<li><code>NodeGetVolumeStats</code>: Returns the capacity statistics for a volume.</li>
<li><code>NodeGetCapabilities</code>: Called by the orchestrator to check the capabilities of the node services.</li>
<li><code>NodeGetInfo</code>: Returns the information for the node where the volume has to be mounted.</li>
<li><code>NodeExpandVolume</code>: Expands the volume mounted on the node. This call should be performed after <code>NodeStageVolume</code> and <code>NodePublishVolume</code>.</li>
</ul>
<h2 id="implementation-of-a-csi">Implementation of a CSI</h2>
<p>CSI specification provides examples of different implementations for plugins.</p>
<h3 id="centralized-controller-plugin">Centralized Controller Plugin</h3>
<p align="center"><img src="Centralized.png" alt="Centralized Controller CSI Plugin"></p>
<p align="center"><small><i>Centralized Controller CSI Plugin</i></small></p>
<p>The controller service RPCs could be packaged in a <strong>Controller Plugin</strong> located on the master node of the orchestrators. The node service RPCs are bundled as <strong>Node Plugin</strong> located on each worker node (or any node that could request volumes).</p>
<h3 id="headless-plugin">Headless Plugin</h3>
<p align="center"><img src="Headless.png" alt="Headless CSI Plugin"></p>
<p align="center"><small><i>Headless CSI Plugin</i></small></p>
<p>The Controller and Node Plugin could be placed together inside the worker node.</p>
<h3 id="controller-plugin">Controller Plugin</h3>
<p align="center"><img src="OnlyController.png" alt="Controller CSI Plugin"></p>
<p align="center"><small><i>Controller CSI Plugin</i></small></p>
<p>CSI could be deployed with just the Controller plugin on the worker node that provides both controller and node services.</p>
<h3 id="node-plugin">Node Plugin</h3>
<p align="center"><img src="OnlyNode.png" alt="Node CSI Plugin"></p>
<p align="center"><small><i>Node CSI Plugin</i></small></p>
<p>CSI could also be deployed with just a Node plugin on the worker node providing just the node services.</p>
<h2 id="volume-lifecycle-with-csi">Volume lifecycle with CSI</h2>
<p align="center"><img src="CSIVolumeLifecycle.png" alt="Lifecycle of a Volume managed by CSI Plugin"></p>
<p align="center"><small><i>Lifecycle of a Volume managed by CSI Plugin</i></small></p>
<h3 id="mounting-a-volume-on-a-container">Mounting a Volume on a Container</h3>
<ol>
<li>A Workload is scheduled by the container orchestrator.</li>
<li>The orchestrator calls <code>CreateVolume</code> RPC to provision a volume on the storage solution.</li>
<li>Once the volume is provisioned on the storage solution the orchestrator calls <code>ContainerPublishVolume</code> to make it available to the node.</li>
<li>After the volume is available to the node, the orchestrator calls <code>NodeStageVolume</code> to mount the volume on the node at <code>stage_path</code>.</li>
<li>Finally, the orchestrator calls <code>NodePublishVolume</code> to mount the volume on <code>target_path</code>. Thus making it available for the container.</li>
</ol>
<h3 id="deleting-a-volume">Deleting a Volume</h3>
<ol>
<li>The volume to be deleted is unpublished by the orchestrator by calling <code>NodeUnpublishVolume</code> RPC.</li>
<li>Once unpublished from the container the volume is unstaged by <code>NodeUnstageVolume</code>.</li>
<li>The unstaged volume is then unpublished from the node by <code>ContainerUnpublishVolume</code>.</li>
<li>Finally, the unpublished volume is deleted by <code>DeleteVolume</code> RPC.</li>
</ol>
<h1 id="example-of-a-csi-intel-optane-persistent-memory-pmem-csi-storage-driver">Example of a CSI: Intel Optane Persistent Memory PMEM-CSI Storage Driver</h1>
<p>Intel Optane is a tiered persistent memory technology. It is used as a high-performance storage for desktop (improving system boot time) and enterprise (CAD, Modeling, Media editing) workloads.</p>
<p>The PMEM-CSI storage driver is provided by Intel to configure the Optane Persistent Memory as a storage solution for Kubernetes. The persistent volumes could be provisioned dynamically on the Optane memory and mounted on the containers present on the cluster.</p>
<p>Using this driver the developers can take advantage of Optane memory in their cache data store like <a href="/posts/computer-science/technologies/cloud-native/operator-sdk/#creating-a-go-based-operator" target="_blank">Memcached Operator</a>.</p>
<hr>
<p>Thank you for taking the time to read this blog post! Have questions, feedback or want to discuss this topic? Feel free to reach out at <a href="mailto:blog@avni.sh"><a href="mailto:blog@avni.sh">blog@avni.sh</a></a>.</p>
<p>If you found this content valuable and would like to stay updated with my latest posts, consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.</p>
<h1 id="resources">Resources</h1>
<p><a href="https://www.computerweekly.com/feature/Container-storage-101-What-is-CSI-and-how-does-it-work" target="_blank">Container storage 101: What is CSI and how does it work? | Computer Weekly</a><br>
<a href="https://medium.com/google-cloud/understanding-the-container-storage-interface-csi-ddbeb966a3b" target="_blank">Understanding the Container Storage Interface (CSI) | by anoop vijayan maniankara</a><br>
<a href="https://www.youtube.com/watch?v=ktwY1anKN58" target="_blank">Container Storage Interface: Present and Future - Jie Yu, Mesosphere, Inc.</a><br>
<a href="https://www.xenonstack.com/blog/container-storage-interface" target="_blank">Container Storage Interface (CSI) for Kubernetes</a><br>
<a href="https://kubernetes-csi.github.io/docs/" target="_blank">Introduction - Kubernetes CSI Developer Documentation</a><br>
<a href="https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/" target="_blank">Container Storage Interface (CSI) for Kubernetes GA</a><br>
<a href="https://github.com/container-storage-interface/spec/blob/master/spec.md" target="_blank">container-storage-interface/spec</a><br>
<a href="https://pmem.io/" target="_blank">Persistent Memory</a><br>
<a href="https://github.com/intel/pmem-csi" target="_blank">intel/pmem-csi</a><br>
<a href="https://www.intel.com/content/www/us/en/developer/articles/technical/persistent-memory-pmem-csi.html" target="_blank">Persistent Memory (PMEM) CSI</a><br>
<a href="https://github.com/intel/pmem-csi/blob/devel/examples/redis-operator.md" target="_blank">Redis-pmem operator</a><br>
<a href="https://github.com/intel/pmem-csi/blob/devel/examples/memcached.md" target="_blank">memcached with PMEM</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>Container Network Interfaces (CNI)</title>
      <link>http://www.avni.sh/posts/computer-science/technologies/cloud-native/container-network-interfaces/</link>
      <pubDate>Wed, 24 May 2023 00:00:00 +0000</pubDate>
      <guid>http://www.avni.sh/posts/computer-science/technologies/cloud-native/container-network-interfaces/</guid>
      <description>Container Network Interfaces provide plugin-based solutions for creating and configuring network interfaces for containers</description>
      <content:encoded><![CDATA[<p><a href="/posts/computer-science/technologies/cloud-native/containers/#container-runtime" target="_blank">Container runtimes</a> allocate <a href="/posts/computer-science/technologies/cloud-native/container-architecture/#network" target="_blank">network namespaces</a> for containers deployed on the host. A network interface (like <code>docker0</code>, <code>bridge</code>,  or <code>host</code>) is configured inside the namespace to facilitate the communication with host, the internet, or other containers.</p>
<p>The <strong>Container Network Interface (CNI)</strong> project provides specifications and libraries for implementing a plugin-based solution for managing network interfaces for containers. The runtime executes the CNI plugins provided as binary executable files.</p>
<p>A <strong>network configuration</strong> is passed to the runtime as a JSON file. It contains the details of the CNI plugins and the network interfaces to be configured.</p>
<p>Example of a network configuration file from CNI specification</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;cniVersion&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;dbnet&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;plugins&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;bridge&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// plugin specific parameters
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="nt">&#34;bridge&#34;</span><span class="p">:</span> <span class="s2">&#34;cni0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;keyA&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;some more&#34;</span><span class="p">,</span> <span class="s2">&#34;plugin specific&#34;</span><span class="p">,</span> <span class="s2">&#34;configuration&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;ipam&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;host-local&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// ipam specific
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nt">&#34;subnet&#34;</span><span class="p">:</span> <span class="s2">&#34;10.1.0.0/16&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;gateway&#34;</span><span class="p">:</span> <span class="s2">&#34;10.1.0.1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;routes&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="nt">&#34;dst&#34;</span><span class="p">:</span> <span class="s2">&#34;0.0.0.0/0&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;dns&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;nameservers&#34;</span><span class="p">:</span> <span class="p">[</span> <span class="s2">&#34;10.1.0.1&#34;</span> <span class="p">]</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;tuning&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;capabilities&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;mac&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;sysctl&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;net.core.somaxconn&#34;</span><span class="p">:</span> <span class="s2">&#34;500&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;portmap&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;capabilities&#34;</span><span class="p">:</span> <span class="p">{</span><span class="nt">&#34;portMappings&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Runtime executes the CNI plugins with the following environment variables</p>
<ul>
<li><code>CNI_COMMAND</code>: Operation for CNI (<code>ADD</code>/<code>DELETE</code>/<code>CHECK</code>/<code>VERSION</code>)</li>
<li><code>CNI_ARGS</code>: Arguments for CNI</li>
<li><code>CNI_PATH</code>: Path to CNI binaries.</li>
<li><code>CNI_IFNAME</code>: Name of the network interface to be configured</li>
<li><code>CNI_NETNS</code>: Path to the container&rsquo;s network namespace</li>
<li><code>CNI_CONTAINERID</code>: Container ID assigned by the runtime</li>
</ul>
<h1 id="cni-operations">CNI Operations</h1>
<p>According to the CNI specification, a CNI plugin should have <code>ADD</code>, <code>DELETE</code>, <code>CHECK</code>, and <code>VERSION</code> operations.</p>
<h2 id="add"><code>ADD</code></h2>
<p>If the environment variable passed to the runtime by the CNI plugin is <code>CNI_COMMAND=ADD</code> then it will create or configure the network interfaces specified in the configuration.</p>
<p align="center"><img src="CNI_ADD.png" alt="Execution flow of ADD operation"></p>
<p align="center"><small><i>Execution flow of ADD operation</i></small></p>
<p>The interfaces will be created or configured in the order specified in the <code>plugins</code> key inside the network configuration. The result from the previous plugin execution will be passed to the next plugin and the final result will be stored with runtime.</p>
<p>So from the example above the <code>bridge</code> plugin will be executed first, followed by the <code>tuning</code> plugin, and finally the <code>portmap</code> plugin.</p>
<h2 id="delete"><code>DELETE</code></h2>
<p>The <code>DELETE</code> operation will undo operations performed by the <code>ADD</code>, deleting the created interfaces and reverting the configurations. It executes CNI plugins in the reverse order of their specification in network configuration. So the changes performed by the <code>portmap</code> plugin will be reverted first then the <code>tuning</code> plugin and lastly the <code>bridge</code> plugin.</p>
<p align="center"><img src="CNI_DELETE.png" alt="Execution flow of DELETE operation"></p>
<p align="center"><small><i>Execution flow of DELETE operation</i></small></p>
<p>It has to be preceded by an <code>ADD</code> operation because it takes the final result stored by the last <code>ADD</code> operation as input from container runtime.</p>
<h2 id="check"><code>CHECK</code></h2>
<p><code>CHECK</code> operation probes the container and network interfaces for their health and current status. The container runtime will go through the changes performed by each CNI plugin and validate its functionality. Similar to the <code>DELETE</code> operation it also uses the result from the previous <code>ADD</code> operation as input from container runtime.</p>
<h2 id="version"><code>VERSION</code></h2>
<p>The <code>VERSION</code> operation returns the version of the CNI plugin.</p>
<h1 id="reference-cni-plugins">Reference CNI Plugins</h1>
<h2 id="network-interface-plugins">Network Interface Plugins</h2>
<ul>
<li><code>bridge</code>: Creates a virtual switch that connects all the containers on a host. An IP address is assigned to each container by the bridge.</li>
<li><code>macvlan</code>: Creates a sub-interface from the host interface. Each virtual interface connected to <code>macvlan</code> has its own MAC address.</li>
</ul>
<ul>
<li><code>ipvlan</code>: Similar to <code>macvlan</code> but all connected devices share the same MAC address. Before transmitting a packet the kernel driver inspects the IP address of the destination virtual interface.</li>
<li><code>ptp</code>: Creates a point-to-point link between the container and its host by creating a Virtual Ethernet (veth) pair where one end resides inside the container and the other on the host.</li>
</ul>
<ul>
<li><code>host-device</code>: An existing device could be moved from the host&rsquo;s network namespace to the container&rsquo;s network namespace.</li>
<li><code>vlan</code>: Creates a Virtual Local Area Network (VLAN) interface between the host and the container.</li>
</ul>
<h2 id="windows-plugins">Windows Plugins</h2>
<p>These plugins are created specifically for Microsoft Windows containers.</p>
<ul>
<li><code>win-bridge</code>: All containers will be connected to a bridge network that has an endpoint on the host&rsquo;s network namespace.</li>
<li><code>win-overlay</code>: An <strong>Overlay network</strong> allows containers from different hosts to act as if they are on the same host. Using the <code>win-overlay</code> plugin all containers on a host could be connected to an Overlay network.</li>
</ul>
<h2 id="ip-address-allocation-plugin">IP Address Allocation Plugin</h2>
<ul>
<li><code>dhcp</code>: The IP leased by the DHCP server has to be renewed periodically. The <code>dhcp</code> plugin creates a daemon separate from the container to perform these requests.</li>
<li><code>host-local</code>: Allocates IPv4 and IPv6 addresses out of a provided range of addresses.</li>
<li><code>static</code>: Allocates static IPv4 and IPv6 addresses to the containers.</li>
</ul>
<h2 id="other-plugins">Other Plugins</h2>
<ul>
<li><code>tuning</code>: Used with other plugins to change system controls or interface attributes (like MTU and MAC address)</li>
<li><code>portmap</code>: Forwards traffic from the host&rsquo;s network ports to the container&rsquo;s network ports.</li>
<li><code>bandwidth</code>: Configures traffic bucket filter (tbf) to limit bandwidth on ingress and egress traffic.</li>
<li><code>sbr</code>: Configures source based routing (sbr) where the application (source) decides the correct network interface for outgoing traffic.</li>
<li><code>firewall</code>: Creates firewall rules to allow traffic to the container&rsquo;s IP address.</li>
</ul>
<h1 id="example-of-3rd-party-cni-plugin-weave">Example of 3rd Party CNI Plugin: Weave</h1>
<p>The <strong>Weave</strong> is a CNI plugin created by Weaveworks that creates an overlay mesh network that allows Dockers containers running on different hosts to connect.</p>
<p>Features of Weave:</p>
<ul>
<li>Containers from different hosts and data centers will act as if they are on the same network without any additional configuration.</li>
<li>Application containers (running on any host) could be exposed to the outside network.</li>
<li>It can create networks between legacy systems and containers.</li>
<li>Traffic could be encrypted.</li>
<li>Supports Amazon ECS, Apache Mesos, Kubernetes, etc.</li>
</ul>
<hr>
<p>Thank you for taking the time to read this blog post! Have questions, feedback or want to discuss this topic? Feel free to reach out at <a href="mailto:blog@avni.sh"><a href="mailto:blog@avni.sh">blog@avni.sh</a></a>.</p>
<p>If you found this content valuable and would like to stay updated with my latest posts, consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.</p>
<h1 id="resources">Resources</h1>
<p><a href="https://www.tigera.io/learn/guides/kubernetes-networking/kubernetes-cni/" target="_blank">Kubernetes CNI Explained</a><br>
<a href="https://www.youtube.com/watch?v=YjjrQiJOyME" target="_blank">Introduction to CNI, the Container Network Interface Project - Bryan Boreham &amp; Dan Williams</a><br>
<a href="https://www.youtube.com/watch?v=zChkx-AB5Xc" target="_blank">Deep Dive: CNI - Bryan Boreham, Weaveworks &amp; Dan Williams, Red Hat</a><br>
<a href="https://www.tigera.io/learn/guides/kubernetes-networking/" target="_blank">Kubernetes Networking: The Complete Guide | Tigera</a><br>
<a href="https://www.tigera.io/learn/guides/kubernetes-networking/container-networking/" target="_blank">Container Networking: What You Should Know</a><br>
<a href="https://github.com/containernetworking/cni/blob/main/SPEC.md" target="_blank">cni/SPEC.md at main · containernetworking/cni · GitHub</a><br>
<a href="https://www.youtube.com/watch?v=zmYxdtFzK6s" target="_blank">Kubernetes Networking: How to Write a CNI Plugin From Scratch - Eran Yanay, Twistlock</a><br>
<a href="https://www.redhat.com/sysadmin/cni-kubernetes" target="_blank">A brief overview of the Container Network Interface (CNI) in Kubernetes | Enable Sysadmin</a><br>
<a href="https://www.cni.dev/plugins/current/" target="_blank">CNI Plugins Overview</a><br>
<a href="https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/" target="_blank">Network Plugins | Kubernetes</a><br>
<a href="https://github.com/containernetworking/plugins" target="_blank">containernetworking/plugins</a><br>
<a href="https://www.weave.works/docs/net/latest/overview/" target="_blank">Introduction to Weave Net</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>Helm Charts</title>
      <link>http://www.avni.sh/posts/computer-science/technologies/cloud-native/helm-charts/</link>
      <pubDate>Tue, 16 May 2023 00:00:00 +0000</pubDate>
      <guid>http://www.avni.sh/posts/computer-science/technologies/cloud-native/helm-charts/</guid>
      <description>Helm is a package manager for Kubernetes</description>
      <content:encoded><![CDATA[<p>Package managers like <code>dnf</code> and <code>apt</code> increase the convenience of installing, updating, and maintaining applications on operating systems. For developers, a package manager provides a standardized way of packaging and distributing their applications.</p>
<p><strong>Helm</strong> is a package manager for Kubernetes. It is implemented in Go and installed as a binary <code>helm</code>. It interacts with the Kubernetes cluster using Kubernetes API.</p>
<h1 id="charts">Charts</h1>
<p>Helm distributes Kubernetes-based applications in a format called <strong>Chart</strong>.<br>
Charts can deploy all kinds of Kubernetes resources such as Deployments, Pods, Services, Persistent Volumes, etc.</p>
<h2 id="structure-of-a-helm-chart">Structure of a Helm Chart</h2>
<p>Chart&rsquo;s directory contains the following files and sub-directories:</p>
<ul>
<li><code>Chart.yaml</code> contains the chart&rsquo;s metadata</li>
<li><code>charts/</code> directory contains dependencies (other charts)</li>
<li><code>crds/</code> directory contains the custom resources required by the chart</li>
<li><code>templates/</code> stores the templates for Kubernetes resources</li>
<li><code>values.yaml</code> stores the configuration values for templates</li>
</ul>
<h3 id="chartyaml"><code>Chart.yaml</code></h3>
<p>The chart&rsquo;s metadata includes</p>
<ul>
<li>Chart&rsquo;s name</li>
<li>Application&rsquo;s version</li>
<li>Chart&rsquo;s type (<code>application</code> or <code>library</code>)
<ul>
<li><code>application</code> charts package Kubernetes applications that could be installed directly on the cluster.</li>
<li><code>library</code> charts are used by other charts to extend their functionality.</li>
</ul>
</li>
<li>Compatible Kubernetes versions</li>
<li>Keywords related to the chart, its homepage, and link to the chart&rsquo;s source code</li>
<li>Dependencies</li>
<li>Contact information of chart&rsquo;s maintainers</li>
</ul>
<h3 id="templates-and-values"><code>templates</code> and <code>values</code></h3>
<p>Resource templates are based on the <em>Go template</em> convention. Example of a Deployment resource template</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nginx-deployment</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">nginx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span>{{<span class="w"> </span><span class="l">.Values.replicaNum }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">nginx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">nginx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span>{{<span class="w"> </span><span class="l">.Values.containerName }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span>{{<span class="w"> </span><span class="l">.Values.containerImage }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span>{{<span class="w"> </span><span class="l">.Values.containerPort }}</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>The values to be substituted in this template will be provided from <code>values.yaml</code> file in the chart&rsquo;s root directory. Example of a <code>values.yaml</code> file for the template</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">replicaNum</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;3&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">containerName</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;nginx-test&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">containerImage</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;docker.io/library/nginx:1.14.2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;80&#34;</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>The structure of the <code>values.yaml</code> file is defined in <code>values.schema.json</code>. Chart developers can also define the datatype of each value. Example of a <code>values.schema.json</code> file</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;$schema&#34;</span><span class="p">:</span> <span class="s2">&#34;https://json-schema.org/draft-07/schema#&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;properties&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;replicaNum&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;string&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;containerName&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;string&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;containerImage&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;string&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;containerPort&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;string&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;required&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;replicaNum&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;containerName&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;containerImage&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;containerPort&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;title&#34;</span><span class="p">:</span> <span class="s2">&#34;Values&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;object&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="crds"><code>crds</code></h3>
<p>Custom Resources (CRs) are installed before any other resource is created by the helm chart. Some limitations imposed on the CRs created by the helm charts:</p>
<ul>
<li>YAML manifests of CRs could not be templated for values.</li>
<li>CRs would not be reinstalled with a chart.</li>
<li>Upgrades and rollbacks do not impact the CRs installed by the chart.</li>
<li>Helm doesn&rsquo;t delete CRs upon uninstallation.</li>
</ul>
<h1 id="chart-repository">Chart Repository</h1>
<p>Helm charts are distributed through HTTP servers called <strong>Repositories</strong>. The metadata of available charts in the repository is stored in <code>index.yaml</code>.</p>
<p>A repository could be added using</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm repo add jenkins-x https://storage.googleapis.com/chartmuseum.jenkins-x.io 
</span></span></code></pre></td></tr></table>
</div>
</div><p>Here <code>jenkins-x</code> is the name of the helm chart repository and <code>https://storage.googleapis.com/chartmuseum.jenkins-x.io</code> is the link to the repository.</p>
<p>Once a repository is added, it is recommended to update the information of available charts in the local cache.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm repo update
</span></span></code></pre></td></tr></table>
</div>
</div><p>The <code>repo</code> command also provides <code>list</code> and <code>remove</code> for listing and removing repositories respectively.</p>
<h1 id="searching-helm-charts-in-repositories-and-artifacthub">Searching Helm Charts in Repositories and ArtifactHub</h1>
<p>To search a helm chart within the added repositories the <code>search</code> command could be used</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm search repo tekton
</span></span></code></pre></td></tr></table>
</div>
</div><p><a href="https://artifacthub.io/" target="_blank">ArtifactHub</a> is a web platform that provides access to multiple Kubernetes-based applications including helm charts. <code>helm search</code> can also be used for searching packages in ArtifactHub</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm search hub tekton
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="installing-helm-charts">Installing Helm Charts</h1>
<p>Each instance of a helm chart installation is called a <strong>Release</strong>.</p>
<p>A release will be created after executing the following command.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm install tekton-pipelines jenkins-x/tekton
</span></span></code></pre></td></tr></table>
</div>
</div><p>Here, <code>tekton-pipelines</code> is the name of the release and <code>jenkins-x/tekton</code> is the name of the helm chart to be installed.</p>
<p>To install a local helm chart, the chart name will be substituted with the path to the chart&rsquo;s directory or archive</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm install test-helm-chart ./test-helm-chart
</span></span></code></pre></td></tr></table>
</div>
</div><p>Configuration values are passed from a file using the <code>--values</code> flag.</p>
<p>During development, if developers want to inspect the manifests created during release and avoid installing the chart on a cluster then they can run the <code>install</code> command with <code>--debug</code> and <code>--dry-run</code> flags.</p>
<h1 id="upgrading-and-rolling-back-releases">Upgrading and Rolling Back Releases</h1>
<p>Helm performs the least invasive upgrades on chart releases i.e. updates only the changed resources. The <code>upgrade</code> command is used to upgrade a chart release</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm upgrade -f newValues.yaml test-helm-chart ./test-helm-chart
</span></span></code></pre></td></tr></table>
</div>
</div><p>Release <code>test-helm-chart</code> will be updated with values in <code>newValues.yaml</code>.</p>
<p>On every upgrade, the chart release&rsquo;s <strong>revision number</strong> will be incremented by 1. To view the history of releases with their respective revision numbers</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm <span class="nb">history</span> test-helm-chart
</span></span></code></pre></td></tr></table>
</div>
</div><p>For some reason, if the chart upgrade doesn&rsquo;t go as planned it could be rolled back to a previous revision number.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm rollback test-helm-chart <span class="m">1</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>will rollback the <code>test-helm-chart</code> release to its first revision.</p>
<h1 id="uninstalling-helm-chart">Uninstalling Helm Chart</h1>
<p>The <code>uninstall</code> command will remove all the resources created by the helm chart release from the Kubernetes cluster (except CRs). It will also wipe the history of the release, to retain history the <code>--keep-history</code> flag could be used with the command.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm uninstall test-helm-chart
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="example-of-a-helm-chart-grafana-helm-chart">Example of a Helm Chart: Grafana Helm Chart</h1>
<p>Grafana is an analytics tool that provides utilities for creating dashboards with graphs and alerts. Using Grafana developers can create dashboards for monitoring services, presenting business metrics, etc.</p>
<p><a href="https://artifacthub.io/packages/helm/grafana/grafana" target="_blank">Grafana&rsquo;s Helm Chart</a> will perform the following changes to the Kubernetes cluster</p>
<ul>
<li>Create pods and other workload resources required by Grafana</li>
<li>Create services and security policies</li>
<li>Create ConfigMaps and Secrets for pods</li>
<li>Create PersistentVolumes</li>
<li>Create RBAC resources</li>
</ul>
<h1 id="creating-helm-charts">Creating Helm Charts</h1>
<p>The boilerplate code for helm charts is generated using <code>create</code> command.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm create test-helm-chart
</span></span></code></pre></td></tr></table>
</div>
</div><p>will create a directory <code>test-helm-chart</code> containing basic chart resources (<code>Chart.yaml</code>, <code>charts/</code>, <code>templates/</code>, <code>values.yaml</code>, etc.).</p>
<h1 id="testing-a-helm-chart">Testing a Helm Chart</h1>
<p>Developers can define tests for their helm charts in the <code>templates</code> directory (or <code>templates/tests</code> for better organization).</p>
<p>Tests are defined as Kubernetes Pods with the following annotation.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">annotations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">&#34;helm.sh/hook&#34;: </span><span class="l">test</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Test pods should finish with exit code <code>0</code> to be marked successful.</p>
<p>To execute tests defined with a release</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm <span class="nb">test</span> test-helm-chart
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="packaging-helm-charts">Packaging Helm Charts</h1>
<p>Charts are packaged as GZIP compressed archives with extension <code>tgz</code>. The <code>package</code> subcommand will create the archive from the chart&rsquo;s directory</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm package ./test-helm-chart
</span></span></code></pre></td></tr></table>
</div>
</div><p>The archive could be signed (<code>--sign</code>) by its developer using a key passed with a <code>--key</code> flag.</p>
<h1 id="distributing-helm-charts">Distributing Helm Charts</h1>
<h2 id="chart-repositories">Chart Repositories</h2>
<p>The chart repository server contains</p>
<ul>
<li>an <code>index.yaml</code> containing chart&rsquo;s metadata</li>
<li>charts packaged as archives (for example <code>test-helm-chart-0.0.1.tgz</code>)</li>
<li>chart&rsquo;s provenance files (<code>test-helm-chart-0.0.1.tgz.prov</code>) containing integrity and validation data</li>
</ul>
<p>A publicly available helm chart repository could be created using GitHub pages. Public helm chart repositories could also be added to ArtifactHub.</p>
<h2 id="oci-based-registries">OCI-based registries</h2>
<p>Some OCI-based registries like DockerHub support helm chart distribution. To distribute a chart through DockerHub the user has to login to the registry through <code>helm</code></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm registry login -u bovem docker.io
</span></span></code></pre></td></tr></table>
</div>
</div><p>Here <code>bovem</code> is the DockerHub username and <code>docker.io</code> is the link to the DockerHub registry. To push the chart to DockerHub it has to be packaged as an archive</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm push test-helm-chart-0.0.1.tgz oci://docker.io/bovem
</span></span></code></pre></td></tr></table>
</div>
</div><p>Similarly, a specific version of the chart could be pulled from DockerHub using the <code>pull</code> command.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">helm pull oci://docker.io/bovem/test-helm-chart --version 0.0.1
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<p>Thank you for taking the time to read this blog post! Have questions, feedback or want to discuss this topic? Feel free to reach out at <a href="mailto:blog@avni.sh"><a href="mailto:blog@avni.sh">blog@avni.sh</a></a>.</p>
<p>If you found this content valuable and would like to stay updated with my latest posts, consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.</p>
<h1 id="resources">Resources</h1>
<p><a href="https://docs.fedoraproject.org/en-US/quick-docs/dnf/" target="_blank">Using the DNF software package manager</a><br>
<a href="https://helm.sh/" target="_blank">Helm</a><br>
<a href="https://helm.sh/docs/topics/architecture/" target="_blank">Helm Architecture</a><br>
<a href="https://helm.sh/docs/intro/install/" target="_blank">Installing Helm</a><br>
<a href="https://helm.sh/docs/topics/charts/" target="_blank">Charts</a><br>
<a href="https://helm.sh/docs/topics/chart_repository/" target="_blank">The Chart Repository Guide</a><br>
<a href="https://artifacthub.io/packages/helm/jenkins-x/tekton" target="_blank">Tekton&rsquo;s Helm Chart</a><br>
<a href="https://artifacthub.io/" target="_blank">ArtifactHub</a><br>
<a href="https://artifacthub.io/packages/helm/grafana/grafana" target="_blank">Grafana&rsquo;s Helm Chart</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>Operator SDK and Bundle Images</title>
      <link>http://www.avni.sh/posts/computer-science/technologies/cloud-native/operator-sdk/</link>
      <pubDate>Wed, 10 May 2023 00:00:00 +0000</pubDate>
      <guid>http://www.avni.sh/posts/computer-science/technologies/cloud-native/operator-sdk/</guid>
      <description>Operators are packaged and distributed as bundle images</description>
      <content:encoded><![CDATA[<p>An Operator Bundle Image (OBI) is created to package custom resources and metadata associated with an <a href="/posts/computer-science/technologies/cloud-native/kubernetes-operators/" target="_blank">operator</a>. It&rsquo;s like any other <a href="/posts/computer-science/technologies/cloud-native/container-images/" target="_blank">container image</a> only difference is that it couldn&rsquo;t be executed but it could be distributed through an OCI-compliant image registry.</p>
<p>Contents of a bundle image are:</p>
<ul>
<li>Kubernetes Custom Resource Definitions (CRDs)</li>
<li><a href="/posts/computer-science/technologies/cloud-native/operators-on-openshift/#clusterserviceversion" target="_blank">ClusterServiceVersion (CSV)</a></li>
<li>Specification of operator&rsquo;s dependencies</li>
<li>Operator metadata like its name, version, channels, etc.</li>
</ul>
<p>The <a href="/posts/computer-science/technologies/cloud-native/kubernetes-operators/#control-loop" target="_blank">control loops</a> associated with the operator are defined in its <strong>Controller Manager</strong>. It is an executable that contains one or more custom controllers.</p>
<p>The <a href="/posts/computer-science/technologies/cloud-native/operators-on-openshift/#operator-lifecycle-manager-olm" target="_blank">Operator Lifecycle Manager (OLM)</a> pulls the bundle image from a registry and installs it on the cluster.</p>
<h1 id="operator-sdk">Operator SDK</h1>
<p><strong>Operator SDK</strong> is a project under Operator Framework that provides tools for building, testing, and packaging operators using the <code>operator-sdk</code> utility.</p>
<p>Using Operator SDK we can create operators based on Ansible Roles, Go Programming Language, or Helm Charts.</p>
<h1 id="creating-a-go-based-operator">Creating a Go-based Operator</h1>
<p>In this article, I will create a <strong>Memcached</strong> operator using <code>operator-sdk</code> CLI.</p>
<p>Memcached is a memory caching system, often used by developers to increase the performance of API calls or databases. It stores data as key-value pairs in RAM. The Memcached operator will make the following changes to the cluster</p>
<ul>
<li>Create a <code>Memcached</code> custom resource.</li>
<li>Add a controller manager for <code>Memcached</code> resources.</li>
<li>Implement APIs to interact with custom resources.</li>
</ul>
<p>Prerequisites:</p>
<ul>
<li>GNU Make (<code>make</code>)</li>
<li>Docker
<ul>
<li>DockerHub/Quay.io or any other public container image registry account</li>
</ul>
</li>
<li>Minikube or any Kubernetes cluster
<ul>
<li><code>kubectl</code> CLI utility</li>
</ul>
</li>
<li>Go</li>
<li>Operator SDK</li>
</ul>
<p>My environment details</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ cat /etc/os-release <span class="p">|</span> head -n <span class="m">5</span>
</span></span><span class="line"><span class="cl"><span class="nv">PRETTY_NAME</span><span class="o">=</span><span class="s2">&#34;Ubuntu 22.04.2 LTS&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">NAME</span><span class="o">=</span><span class="s2">&#34;Ubuntu&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">VERSION_ID</span><span class="o">=</span><span class="s2">&#34;22.04&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">VERSION</span><span class="o">=</span><span class="s2">&#34;22.04.2 LTS (Jammy Jellyfish)&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">VERSION_CODENAME</span><span class="o">=</span>jammy
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ docker version
</span></span><span class="line"><span class="cl">Client: Docker Engine - Community
</span></span><span class="line"><span class="cl"> Version:           23.0.3
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ minikube version
</span></span><span class="line"><span class="cl">minikube version: v1.30.1
</span></span><span class="line"><span class="cl">commit: 08896fd1dc362c097c925146c4a0d0dac715ace0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ go version
</span></span><span class="line"><span class="cl">go version go1.20.4 linux/amd64
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ operator-sdk version
</span></span><span class="line"><span class="cl">operator-sdk version: <span class="s2">&#34;v1.28.0&#34;</span>, commit: <span class="s2">&#34;484013d1865c35df2bc5dfea0ab6ea6b434adefa&#34;</span>, kubernetes version: <span class="s2">&#34;1.26.0&#34;</span>, go version: <span class="s2">&#34;go1.19.6&#34;</span>, GOOS: <span class="s2">&#34;linux&#34;</span>, GOARCH: <span class="s2">&#34;amd64&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="initializing-operator-project">Initializing Operator Project</h2>
<p><strong>Kubebuilder</strong> provides a standardized way of creating Kubernetes API using Go. It generates the CRDs associated with the API in an organized file structure.</p>
<p><code>init</code> subcommand from <code>operator-sdk</code> will generate custom resources, API, and controller manager for an operator based on the basic kubebuilder project layout.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir memcached-operator
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> memcached-operator
</span></span><span class="line"><span class="cl">operator-sdk init --domain example.com --repo github.com/example/memcached-operator
</span></span></code></pre></td></tr></table>
</div>
</div><p>Artifacts generated by the command</p>
<ul>
<li>YAML manifests for custom resources, controllers, Prometheus integration, and Role Based Access Control (RBAC) resources</li>
<li>Scorecard tests</li>
<li><code>Dockerfile</code> for an image containing the binary of the controller manager</li>
<li><code>go.mod</code> containing the definition of the Go module with basic dependencies</li>
<li><code>Makefile</code> for building, distributing, and deploying the operator</li>
<li><code>main.go</code> contains logic for the controller manager</li>
<li><code>PROJECT</code> file containing the operator&rsquo;s metadata (domain, project layout, name, repo, and version)</li>
<li><code>README.md</code> for documentation</li>
</ul>
<p><code>--domain</code> flag is used to specify a prefix of labels assigned to custom resources created by the operator.</p>
<p><code>--repo</code> refers to the Go module to be used for the operator, it needs to be specified if the project directory is outside <code>$GOPATH/src</code>.</p>
<p>Other flags for the <code>init</code> subcommand:</p>
<ul>
<li><code>--project-name</code>: To specify the name of the operator</li>
<li><code>--project-version</code>: To specify the operator version</li>
<li><code>--owner</code>: To specify the owner&rsquo;s name</li>
<li><code>--fetch-deps</code>: To toggle dependency installation by OLM during deployment</li>
</ul>
<h2 id="implementing-api">Implementing API</h2>
<p>Implementing an API for interacting with the custom resources created by the operator</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">operator-sdk create api --group cache --version v1alpha1 --kind Memcached --resource --controller
</span></span></code></pre></td></tr></table>
</div>
</div><p>After executing this command a new API resource will be added to the <code>PROJECT</code> file</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">resources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">api</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">crdVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">namespaced</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">controller</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">domain</span><span class="p">:</span><span class="w"> </span><span class="l">example.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">group</span><span class="p">:</span><span class="w"> </span><span class="l">cache</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Memcached</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">github.com/example/memcached-operator/api/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">v1alpha1</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>The API and controller&rsquo;s implementations will be stored in <code>api/v1alpha1/memcached_types.go</code> and <code>controllers/memcached_controller.go</code> respectively.</p>
<p>The creation of the controller could be toggled using the <code>--controller</code> flag. It is <code>true</code> by default.
<code>--group</code> flag is used to specify the group of the API resources created.
The value of the <code>--version</code> flag will specify the API version and <code>--kind</code> specifies the type of API to be implemented.
<code>--resource</code> toggles the creation of API resource&rsquo;s YAML manifests.</p>
<h2 id="building-an-operator-image">Building an Operator Image</h2>
<p>The difference between an <strong>Operator Image</strong> and an Operator Bundle Image is that an operator image could be used to deploy an operator directly on the cluster as a Deployment (it contains the binary of the controller manager) whereas the bundle image stores the necessary metadata, custom resources, and APIs associated with the operator (also used for deployment, but through OLM).</p>
<p><code>make</code> is an automation utility commonly used for processes like compiling/building applications. The <code>Makefile</code> in the operator project defines multiple <strong>targets</strong> like <code>docker-build</code> and <code>docker-push</code> for building and pushing the operator image to the registry respectively.</p>
<p>Keep in mind that you have to be logged in to the registry from your container engine before executing the following command</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">make docker-build docker-push <span class="nv">IMG</span><span class="o">=</span><span class="s2">&#34;docker.io/bovem/memcached-operator:v0.0.1&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>For development purposes, if you want to test the bundle image outside the cluster you can use the following command</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">make install run
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="building-the-operator-bundle-image">Building the Operator Bundle Image</h2>
<p><code>Makefile</code> target <code>bundle</code> will create a <code>bundle/</code> directory in the project&rsquo;s root containing manifests (CRDs) and metadata associated with the operator. A <a href="/posts/computer-science/technologies/cloud-native/building-container-images/" target="_blank">Containerfile</a> named <code>bundle.Dockerfile</code> will be created as well.</p>
<p>Targets <code>bundle-build</code> and <code>bundle-push</code> will build and push the bundle image respectively.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">make bundle bundle-build bundle-push <span class="nv">BUNDLE_IMG</span><span class="o">=</span><span class="s2">&#34;docker.io/bovem/memcached-operator-bundle:v0.0.1&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="files-inside-an-operator-bundle-image">Files inside an Operator Bundle Image</h3>
<h3 id="manifests-directory"><code>manifests</code> directory</h3>
<p>Contains CRDs including the <code>ClusterServiceVersion</code>.</p>
<h3 id="metadata-directory"><code>metadata</code> directory</h3>
<p><code>annotations.yaml</code> in the <code>metadata</code> directory stores the operator&rsquo;s metadata. This includes the path of the manifests and metadata directory, channels, etc.
<code>dependencies.yaml</code> specifies the dependencies to be satisfied before installation of the operator is initiated. These could be dependencies on other operators or specific API/Custom Resources.</p>
<h3 id="bundledockerfile"><code>bundle.Dockerfile</code></h3>
<p>The base image of the bundle is <code>scratch</code>. Inside the container image, the path to the manifest and metadata directory is <code>test/</code> and <code>test/metadata</code> respectively.</p>
<h1 id="deploying-an-operator">Deploying an Operator</h1>
<h2 id="direct-deployment-using-operator-image">Direct Deployment using Operator Image</h2>
<p><code>Makefile</code> provides a target <code>deploy</code> for deploying the operator</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">make deploy <span class="nv">IMG</span><span class="o">=</span><span class="s2">&#34;docker.io/bovem/memcached-operator:v0.0.1&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>After the deployment is completed successfully, we can create <code>Memcached</code> custom resource</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
</span></span></code></pre></td></tr></table>
</div>
</div><p>Executing the <code>make</code> command with the <code>undeploy</code> target will uninstall the operator from the cluster</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">make undeploy
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="olm-deployment-using-operator-bundle-image">OLM Deployment using Operator Bundle Image</h2>
<p>Before deploying the operator through the bundle image we have to make sure that OLM is installed on the cluster. To do that we can use the command</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">operator-sdk olm status
</span></span></code></pre></td></tr></table>
</div>
</div><p>If OLM is missing it could be installed easily from the Operator SDK itself</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">operator-sdk olm install
</span></span></code></pre></td></tr></table>
</div>
</div><p>Once OLM is installed, operator deployment is as easy as</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">operator-sdk run bundle docker.io/bovem/memcached-operator-bundle:v0.0.1
</span></span></code></pre></td></tr></table>
</div>
</div><p>and uninstalling it</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">operator-sdk cleanup docker.io/bovem/memcached-operator-bundle:v0.0.1
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="validating-the-operator-bundle-image">Validating the Operator Bundle Image</h1>
<p>Validating an OBI or the <code>bundle</code> directory ensures</p>
<ul>
<li>The <code>manifests</code> directory contains all the required CRDs including CSV.</li>
<li>Data present in the files inside the <code>manifests</code> directory matches the provided data.</li>
<li>The bundle format is valid.</li>
<li>Permissions and Configurations of the operator are valid for an OLM-enabled cluster.</li>
<li>Any additional validations defined with the operator are satisfied.</li>
</ul>
<p>To validate a bundle the image or <code>bundle</code> directory path could be passed as an argument</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">operator-sdk bundle validate docker.io/bovem/memcached-operator-bundle:v0.0.1
</span></span></code></pre></td></tr></table>
</div>
</div><p>or</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">operator-sdk bundle validate ./bundle
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="testing-operator-bundle-image-using-scorecard">Testing Operator Bundle Image using Scorecard</h1>
<p>Developers can define tests for their operator projects using the <strong>scorecard</strong>.</p>
<p>By default scorecard contains the following tests:</p>
<ul>
<li>Basic Test Suite (<code>basic-check-spec-test</code>): Tests for <code>spec</code> block in all CRDs.</li>
<li>OLM Test Suite:
<ul>
<li><code>olm-bundle-validation-test</code>: Validates bundle manifests</li>
<li><code>olm-crds-have-validation-test</code>: All CRDs contain a validation section containing validation for each spec and status field.</li>
<li><code>olm-crds-have-resources-test</code>: All CRDs have a <code>resources</code> section</li>
<li><code>olm-spec-descriptors-test</code>: Every field in the CRD&rsquo;s <code>spec</code> section has a descriptor listed in CSV</li>
<li><code>olm-status-descriptors-test</code>: Every field in the CRD&rsquo;s <code>status</code> section has a descriptor listed in CSV</li>
</ul>
</li>
</ul>
<p>Tests are defined in <code>config/scorecard/bases</code> as <strong>stages</strong> and executed on pods created on the cluster. At each stage, the tests are executed parallelly or sequentially. The result file is generated in JSON/XML/Text format.</p>
<p><code>scorecard</code> subcommand is used to trigger test execution</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">operator-sdk scorecard docker.io/bovem/memcached-operator-bundle:v0.0.1
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<p>Thank you for taking the time to read this blog post! Have questions, feedback or want to discuss this topic? Feel free to reach out at <a href="mailto:blog@avni.sh"><a href="mailto:blog@avni.sh">blog@avni.sh</a></a>.</p>
<p>If you found this content valuable and would like to stay updated with my latest posts, consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.</p>
<h1 id="resources">Resources</h1>
<p><a href="https://github.com/operator-framework/operator-registry/blob/v1.16.1/docs/design/operator-bundle.md" target="_blank">Operator Bundle</a><br>
<a href="https://book-v1.book.kubebuilder.io/basics/what_is_the_controller_manager.html" target="_blank">What is the Manager</a><br>
<a href="https://medium.com/swlh/what-is-memcached-d1498623db3b" target="_blank">What is Memcached</a><br>
<a href="https://book-v1.book.kubebuilder.io/getting_started/what_is_kubebuilder.html" target="_blank">Kubebuilder</a><br>
<a href="https://book.kubebuilder.io/cronjob-tutorial/basic-project.html" target="_blank">What’s in a basic project?</a><br>
<a href="https://sdk.operatorframework.io/docs/building-operators/golang/tutorial/" target="_blank">Go Operator Tutorial</a><br>
<a href="https://sdk.operatorframework.io/docs/cli/operator-sdk_init/" target="_blank">operator-sdk init</a><br>
<a href="https://sdk.operatorframework.io/docs/cli/operator-sdk_create_api/" target="_blank">operator-sdk create api</a><br>
<a href="https://sdk.operatorframework.io/docs/cli/operator-sdk_olm/" target="_blank">operator-sdk olm</a><br>
<a href="https://sdk.operatorframework.io/docs/cli/operator-sdk_run/" target="_blank">operator-sdk run</a><br>
<a href="https://sdk.operatorframework.io/docs/cli/operator-sdk_bundle_validate/" target="_blank">operator-sdk bundle validate</a><br>
<a href="https://sdk.operatorframework.io/docs/cli/operator-sdk_scorecard/" target="_blank">operator-sdk scorecard</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>Operators on OpenShift</title>
      <link>http://www.avni.sh/posts/computer-science/technologies/cloud-native/operators-on-openshift/</link>
      <pubDate>Wed, 03 May 2023 00:00:00 +0000</pubDate>
      <guid>http://www.avni.sh/posts/computer-science/technologies/cloud-native/operators-on-openshift/</guid>
      <description>OperatorHub and OLM have made it incredibly simple to work with operators on OpenShift</description>
      <content:encoded><![CDATA[<p>OpenShift provides an <em>Operators</em> section in its web console UI for the installation and management of <a href="/posts/computer-science/technologies/cloud-native/kubernetes-operators/" target="_blank">operators</a> on the cluster.</p>
<h1 id="operatorhub">OperatorHub</h1>
<p>The <strong>OperatorHub</strong> is an interface for searching and installing operators. It has the following categories of operators:</p>
<ul>
<li>Red Hat Operators: Operators developed and supported by Red Hat. Example: <a href="https://catalog.redhat.com/software/container-stacks/detail/5ec53f9d535cb70ab8c02991">Red Hat Quay Operator</a></li>
<li>Certified Operators: Operators listed by Red Hat&rsquo;s Independent Software Vendors (ISVs). Example: <a href="https://catalog.redhat.com/software/container-stacks/detail/5e9872712989e6a90307acd6">CockroachDB Operator</a></li>
<li>Red Hat Marketplace Operators: Applications purchased from Red Hat Marketplace available as Operators. Example: <a href="https://marketplace.redhat.com/en-us/products/dynatrace">Dynatrace Operator</a></li>
<li>Community Operators: Default catalog of Operators maintained by their communities. Example: <a href="https://github.com/infinispan/infinispan-operator">Infispan Operator</a></li>
</ul>
<p>OperatorHub fetches the catalog data from an operator installed by default on all clusters called <strong>Marketplace Operator</strong>.</p>
<p>The value of the field <code>disableAllDefaultSources</code> needs to be <code>false</code> in the CR called <code>cluster</code> to view the operator&rsquo;s catalog.</p>
<h2 id="installing-an-operator-from-operatorhub">Installing an Operator from OperatorHub</h2>
<p>OperatorHub is accessible from <strong>Operators -&gt; OperatorHub</strong> on the web UI of the OpenShift cluster.</p>
<p align="center"><img src="operator-hub.png" alt="OperatorHub UI"></p>
<p align="center"><small><i>OperatorHub UI</i></small></p>
<p>To install an operator you can just click the <strong>Install</strong> button and it will present options to select an update channel (version of the operator), installation mode (specific or all namespaces), and update approval (automatic or manual).</p>
<p align="center"><img src="install-operator.png" alt="Operator Installation"></p>
<p align="center"><small><i>Operator Installation</i></small></p>
<p>Once installed the operator should be visible in <strong>Operators -&gt; Installed Operators</strong>.</p>
<p align="center"><img src="installed-operators.png" alt="Installed Operators"></p>
<p align="center"><small><i>Installed Operators</i></small></p>
<h1 id="operator-framework">Operator Framework</h1>
<p><strong>Operator Framework</strong> is a set of developer tools for operators, it includes:</p>
<ul>
<li>Operator Lifecycle Manager (OLM): Handles installation, update, and management of operators on the cluster.</li>
<li>Operator SDK: SDK for building operators using Ansible, Helm Chart, or Go.</li>
<li>Operator Registry:  Registry of operators that could be hosted on an OpenShift cluster.</li>
</ul>
<p>To explore other projects under Operator Framework you can check their <a href="https://github.com/operator-framework">GitHub</a>.</p>
<h1 id="operator-lifecycle-manager-olm">Operator Lifecycle Manager (OLM)</h1>
<p>The <strong>Operator Lifecycle Manager (OLM)</strong> is installed by default on OpenShift clusters. It ensures:</p>
<ul>
<li>The dependencies of an operator (including other operators) are satisfied before its installation.</li>
<li>Installed operators are up-to-date.</li>
<li>Availability of the operators to the users of the cluster.</li>
<li>There are no conflicts between multiple operator installations.</li>
<li>Installed operators are accessible through UI, API, and CLI.</li>
</ul>
<p>The OLM itself consists of two operators: OLM Operator and Catalog Operator.</p>
<h2 id="olm-operator">OLM Operator</h2>
<p>The <strong>OLM Operator</strong> deploys the resources defined in <code>ClusterServiceVersion</code> present on the namespace.</p>
<h3 id="clusterserviceversion"><code>ClusterServiceVersion</code></h3>
<p>The <code>ClusterServiceVersion</code> or <code>CSV</code> represents a specific version of an operator. It contains operator metadata such as its name, description, version, details of the maintainer, installation strategy, and APIs provided by the operator.</p>
<h3 id="operatorgroup"><code>OperatorGroup</code></h3>
<p>OLM has cluster-admin privileges. Operators could request some of these cluster-admin permissions in their <code>CSV</code>. By creating an <code>OperatorGroup</code> the cluster administrator can take control of the permissions granted to the operators or limit them to one or more namespaces.</p>
<h3 id="operatorcondition"><code>OperatorCondition</code></h3>
<p>Operators can modify the OLM&rsquo;s management strategy by stating their conditions in <code>OperatorCondition</code>. For example, an operator could set the <code>status</code> for the Upgradable property to <code>False</code> if its installation has to be frozen on a specific version.</p>
<h2 id="catalog-operator">Catalog Operator</h2>
<p><strong>Catalog Operator</strong> installs the operator based on <code>InstallPlan</code>. It also updates the operator if a new version is available on its <code>Subscription</code>.</p>
<h3 id="catalogsource"><code>CatalogSource</code></h3>
<p><code>CatalogSource</code> is a metadata store for discovering and installing operators.</p>
<h3 id="subscription"><code>Subscription</code></h3>
<p><code>Subscription</code> specifies the <code>CatalogSource</code> to be referenced by OLM during installation and updates. The channel (alpha/beta/stable) of the operator could also be specified in the <code>Subscription</code>.</p>
<h3 id="installplan"><code>InstallPlan</code></h3>
<p>An <code>InstallPlan</code> defines the operator&rsquo;s custom resources to be installed.</p>
<h1 id="installing-a-specific-version-of-the-operator">Installing a Specific Version of the Operator</h1>
<p>By default, OpenShift installs the latest available version of an operator in the <code>CatalogSource</code>. If you want to change this behavior and install a specific version of the operator you have to edit its <code>Subscription</code> and specify the version to be installed in <code>startingCSV</code> and set <code>installPlanApproval</code> to <code>Manual</code>.
Note that if the required version is not available on the default <code>channel</code> then you have to change it as well.</p>
<p>Example of a <code>Subscription</code> for installing v1.9.0 of OpenShift Pipelines Operator</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">operators.coreos.com/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Subscription</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">openshift-pipelines-operator-rh</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">openshift-operators</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">channel</span><span class="p">:</span><span class="w"> </span><span class="l">pipelines-1.9</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">installPlanApproval</span><span class="p">:</span><span class="w"> </span><span class="l">Manual</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">openshift-pipelines-operator-rh</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">source</span><span class="p">:</span><span class="w"> </span><span class="l">redhat-operators</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">sourceNamespace</span><span class="p">:</span><span class="w"> </span><span class="l">openshift-marketplace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">startingCSV</span><span class="p">:</span><span class="w"> </span><span class="l">openshift-pipelines-operator-rh.v1.9.0</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>If an update is available in the future the operator&rsquo;s <code>InstallPlan</code> has to be approved manually, this will prevent automatic updates.</p>
<h1 id="uninstalling-an-operator">Uninstalling an Operator</h1>
<p>The option to uninstall an operator is present in its <strong>Actions</strong> on UI.</p>
<p>To uninstall an operator from CLI, you have to remove its <code>Subscription</code> as well as its <code>ClusterServiceVersion</code> using the <code>delete</code> subcommand on the specific resources.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Listing available subscriptions</span>
</span></span><span class="line"><span class="cl">$ oc get subscriptions -n openshift-operators
</span></span><span class="line"><span class="cl">NAME                              PACKAGE                           SOURCE
</span></span><span class="line"><span class="cl">openshift-pipelines-operator-rh   openshift-pipelines-operator-rh   redhat-operators
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Deleting subscription for OpenShift Pipelines Operator</span>
</span></span><span class="line"><span class="cl">$ oc delete subscription/openshift-pipelines-operator-rh -n openshift-operators
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Listing available clusterserviceversions</span>
</span></span><span class="line"><span class="cl">$ oc get clusterserviceversions -n openshift-operators
</span></span><span class="line"><span class="cl">NAME                                      DISPLAY                       VERSION
</span></span><span class="line"><span class="cl">openshift-pipelines-operator-rh.v1.10.0   Red Hat OpenShift Pipelines   1.10.0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Deleting clusterserviceversion for OpenShift Pipelines Operator</span>
</span></span><span class="line"><span class="cl">$ oc delete csv/openshift-pipelines-operator-rh.v1.10.0 -n openshift-operators
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<p>Thank you for taking the time to read this blog post! Have questions, feedback or want to discuss this topic? Feel free to reach out at <a href="mailto:blog@avni.sh"><a href="mailto:blog@avni.sh">blog@avni.sh</a></a>.</p>
<p>If you found this content valuable and would like to stay updated with my latest posts, consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.</p>
<h1 id="resources">Resources</h1>
<p><a href="https://docs.openshift.com/container-platform/4.12/operators/understanding/olm-understanding-operatorhub.html" target="_blank">OperatorHub</a><br>
<a href="https://olm.operatorframework.io/" target="_blank">Operator Lifecycle Manager (OLM)</a><br>
<a href="https://olm.operatorframework.io/docs/concepts/crds/clusterserviceversion/" target="_blank">ClusterServiceVersion</a><br>
<a href="https://olm.operatorframework.io/docs/concepts/crds/catalogsource/" target="_blank">CatalogSource</a><br>
<a href="https://olm.operatorframework.io/docs/concepts/crds/operatorcondition/" target="_blank">OperatorCondition</a><br>
<a href="https://olm.operatorframework.io/docs/concepts/crds/subscription/" target="_blank">Subscription</a><br>
<a href="https://olm.operatorframework.io/docs/concepts/crds/installplan/" target="_blank">InstallPlan</a><br>
<a href="https://olm.operatorframework.io/docs/concepts/crds/operatorgroup/" target="_blank">OperatorGroup</a><br>
<a href="https://olm.operatorframework.io/docs/concepts/olm-architecture/" target="_blank">OLM Architecture</a><br>
<a href="https://docs.openshift.com/container-platform/4.12/operators/understanding/olm/olm-workflow.html#olm-upgrades_olm-workflow" target="_blank">OLM Workflow</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>Kubernetes Operators</title>
      <link>http://www.avni.sh/posts/computer-science/technologies/cloud-native/kubernetes-operators/</link>
      <pubDate>Mon, 01 May 2023 00:00:00 +0000</pubDate>
      <guid>http://www.avni.sh/posts/computer-science/technologies/cloud-native/kubernetes-operators/</guid>
      <description>Operators provide developers an option to package their applications with the necessary business logic required for their deployment and management</description>
      <content:encoded><![CDATA[<p>Applications built to be deployed on Kubernetes could be packaged as Operators. Operators automate the process of installation, updates, and management of the application. These automations are defined by developers based on the application&rsquo;s business logic.</p>
<p>An Operator consists of:</p>
<ul>
<li>Custom Resources (CRs) required by the application</li>
<li>Custom controller for managing these CRs</li>
</ul>
<h1 id="control-loop">Control Loop</h1>
<p>A <strong>control loop</strong> is an infinite loop for monitoring the state of a system. If the desired state of the system is different from its current state then the control loop makes changes to the system until it reaches its desired state.</p>
<p>An example of a control loop is a thermostat that monitors the current room temperature on frequent time intervals and if the desired temperature is different from the current temperature then it increases or decreases the cooling.</p>
<h1 id="kubernetes-controller">Kubernetes Controller</h1>
<p>The <strong>Kubernetes controller</strong> functions as a control loop for the resources on the cluster. It performs necessary changes to the resources using the Kubernetes API such that they reach their desired state.</p>
<ul>
<li>If a deployment needs to be scaled up or down (due to a manual instruction from the cluster administrator or an increase/decrease in its resource utilization) then the controller will deploy/destroy pods in the deployment.</li>
<li>If an application has to be updated then the controller will make sure that eventually all the running containers are replaced with the latest version of the application (based on the rollout strategy defined in deployment).</li>
<li>If updated pods aren&rsquo;t healthy then the controller will roll back the changes until the deployment is stable (based on deployment and rollback strategies).</li>
</ul>
<p align="center"><img src="kubernetes-controller.png" alt="The Kubernetes Controller monitors and maintains the state of resources on the cluster"></p>
<p align="center"><small><i>The Kubernetes Controller monitors and maintains the state of resources on the cluster</i></small></p>
<h2 id="reconcile-loop-and-reconciliation">Reconcile Loop and Reconciliation</h2>
<p>The loop that constantly monitors the state of resources on the cluster is called <strong>reconcile loop</strong> and the process of making changes to the cluster to match the desired state is called <strong>reconciliation</strong>.</p>
<h1 id="human-operator">Human Operator</h1>
<p>Applications using CRs with developer-defined strategies for scaling, updates, rollback, etc. like a database application that maintains two instances: read-only and read-write could not be managed by the default Kubernetes Controller. Such applications will require a <strong>human operator</strong> to administer their deployment on the cluster. They will be responsible for:</p>
<ul>
<li>Installation and configuration of the application based on the cluster&rsquo;s environment.</li>
<li>Scaling strategies for the CRs based on current resource utilization, application&rsquo;s business logic, and other constraints.</li>
<li>Rollout and rollback of updates to the application without affecting the end users.</li>
<li>Fixing application issues during runtime.</li>
</ul>
<h1 id="kubernetes-operators">Kubernetes Operators</h1>
<p>A <strong>Kubernetes Operator</strong> aims to automate the functions of a Human Operator automating processes like</p>
<ul>
<li>installation</li>
<li>configuration and reconfiguration</li>
<li>update</li>
<li>backup</li>
<li>recovery</li>
</ul>
<p>By packaging an application as an Operator its developer could ensure that</p>
<ul>
<li>The deployment process is standardized in all environments.</li>
<li>The application could be scaled up or down based on the logic defined by the developer.</li>
<li>The application could fix itself upon encountering any known issues after deployment. Even rolling back to a previous state if required.</li>
<li>New changes are rolled out strategically without breaking existing functionality.</li>
</ul>
<h2 id="capability-levels">Capability Levels</h2>
<p>Based on the level of maturity or capability of automation provided by an Operator it could be classified into 5 levels:</p>
<ul>
<li>LEVEL 1: Automated installation and initial configuration of the application.</li>
<li>LEVEL 2: Patching and upgrading the application.</li>
<li>LEVEL 3: Manage the complete application lifecycle i.e. backup, failure, and recovery.</li>
<li>LEVEL 4: Provide application metrics, logs, and workload analysis</li>
<li>LEVEL 5: Scaling (Horizontal and Vertical), reconfiguration of application, tuning, and abnormality detection.</li>
</ul>
<p>A Level 3 operator could manage the whole application cycle while also performing Level 1 (installation &amp; configuration) and Level 2 (patches &amp; upgrades) automation.</p>
<p align="center"><img src="kubernetes-operator.png" alt="Just like the Kubernetes controller, a Kubernetes Operator monitors and maintains the state of Custom Resources of an applications"></p>
<p align="center"><small><i>Just like the Kubernetes controller, a Kubernetes Operator monitors and maintains the state of Custom Resources of an applications</i></small></p>
<h2 id="custom-resources-and-controller">Custom Resources and Controller</h2>
<p>A <strong>Custom Controller</strong> created by the Operator manages the state of the cluster&rsquo;s resources including Custom Resources (CRs) using Kubernetes API just like the default Kubernetes Controller.</p>
<p>It could be limited to a single namespace or deployed cluster-wide. It is recommended to have a single custom controller for a CR as multiple controllers can make changes simultaneously overwriting each other.</p>
<h1 id="example-of-a-kubernetes-operator-mariadb-operator">Example of a Kubernetes Operator: MariaDB Operator</h1>
<p><a href="https://operatorhub.io/operator/mariadb-operator-app" target="_blank">MariaDB Operator</a> performs the following changes on the Kubernetes Cluster automates:</p>
<ul>
<li>Deployment of MariaDB server on Kubernetes with default/configured version.</li>
<li>Creation of PersistentVolumeClaims (PVCs) and a custom database with a credential set.</li>
<li>Future upgrades for MariaDB without affecting current services.</li>
<li>Database backups</li>
<li>Metrics for database</li>
</ul>
<hr>
<p>Thank you for taking the time to read this blog post! Have questions, feedback or want to discuss this topic? Feel free to reach out at <a href="mailto:blog@avni.sh"><a href="mailto:blog@avni.sh">blog@avni.sh</a></a>.</p>
<p>If you found this content valuable and would like to stay updated with my latest posts, consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.</p>
<h1 id="resources">Resources</h1>
<p><a href="https://kubernetes.io/docs/concepts/extend-kubernetes/operator/" target="_blank">Operator Pattern</a><br>
<a href="https://www.cncf.io/blog/2022/06/15/kubernetes-operators-what-are-they-some-examples/" target="_blank">Kubernetes Operators</a><br>
<a href="https://www.redhat.com/en/topics/containers/what-is-a-kubernetes-operator" target="_blank">What is a Kubernetes Operator</a><br>
<a href="https://sdk.operatorframework.io/docs/overview/operator-capabilities/" target="_blank">Operator Capability Levels</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>Building Container Images</title>
      <link>http://www.avni.sh/posts/computer-science/technologies/cloud-native/building-container-images/</link>
      <pubDate>Mon, 20 Mar 2023 00:00:00 +0000</pubDate>
      <guid>http://www.avni.sh/posts/computer-science/technologies/cloud-native/building-container-images/</guid>
      <description>Container images are built from Containerfile, a plaintext file containing the steps to be executed during the build process.</description>
      <content:encoded><![CDATA[<p>Public registries provide container images for most use cases but they might not cover all of them. That&rsquo;s why container engines such as Podman &amp; Docker and CLI tools like <code>buildah</code> provide utilities for creating custom <a href="/posts/computer-science/technologies/cloud-native/container-images" target="_blank">container images</a>.</p>
<p>The build steps are written in a plaintext file called <strong>Containerfile</strong> and parsed by container engines (or <code>buildah</code>) during the build process.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="c"># Containerfile</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">FROM</span><span class="s"> node:18-alpine</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">LABEL</span> <span class="nv">version</span><span class="o">=</span><span class="s2">&#34;1.0&#34;</span> <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">WORKDIR</span><span class="s"> /app</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">COPY</span> . .<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> yarn install --production<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">CMD</span> <span class="p">[</span><span class="s2">&#34;node&#34;</span><span class="p">,</span> <span class="s2">&#34;src/index.js&#34;</span><span class="p">]</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">EXPOSE</span><span class="s"> 3000</span><span class="err">
</span></span></span></code></pre></td></tr></table>
</div>
</div><h2 id="containerfile-instructions">Containerfile Instructions</h2>
<p>Steps inside the containerfile are defined using instructions such as <code>FROM</code>, <code>RUN</code>, <code>ADD</code>, <code>COPY</code>, etc.
Container Engines go through the containerfile line-by-line and perform each step, stacking a new image layer on top of the previous one.</p>
<h3 id="from">FROM</h3>
<p><code>FROM</code> instruction specifies the base layer container image. The base layer image could be chosen based on OS preference (like Fedora, Kali, etc.) or programming language preference (like gcc, python, etc.), or any other preference.
It is mandatory to have a <code>FROM</code> instruction in the containerfile.</p>
<h3 id="arg">ARG</h3>
<p>Variables for <code>FROM</code> instruction are defined using <code>ARG</code>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">IMAGE_VERSION</span><span class="o">=</span><span class="m">3</span>.7<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">FROM</span><span class="s"> python:${IMAGE_VERSION}</span><span class="err">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>After <code>FROM</code> instruction is executed, the value for the variable declared in <code>ARG</code> becomes inaccessible.</p>
<h3 id="env">ENV</h3>
<p>The Environment variables (used by other instructions or container at runtime) are declared using <code>ENV</code>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">ENV</span> <span class="nv">DEPLOYMENT_FOLDER</span><span class="o">=</span>src
</span></span><span class="line"><span class="cl"><span class="k">WORKDIR</span><span class="s"> ${DEPLOYMENT_FOLDER}</span><span class="err">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>The <code>=</code> operator between the variable name and its value is optional.</p>
<h3 id="label">LABEL</h3>
<p><code>LABEL</code> is used to add metadata for container images in the form of key-value pairs. Metadata usually contains the author&rsquo;s name, author&rsquo;s email, version of the container image, etc.</p>
<h3 id="add">ADD</h3>
<p><code>ADD</code> instruction will copy files/directories from the source (host OS/URL/tarball) to the destination (container image). <code>ADD</code> allows permissions to be omitted on the copied files/directories with the <code>--chown</code> flag.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">ADD</span> --chown<span class="o">=</span>user:group src/ dest<span class="err">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Builds are performed inside a build context directory on the host. <code>ADD</code> will fail if the source file/directory is outside the build context.</p>
<h3 id="copy">COPY</h3>
<p><code>COPY</code> is similar to <code>ADD</code> but it is limited to sources on the host, it doesn&rsquo;t support URLs or tarball. <code>COPY</code> is sufficient for most use cases, so it is preferred over <code>ADD</code>.</p>
<h3 id="workdir">WORKDIR</h3>
<p><code>WORKDIR</code> changes the current working directory (inside the image) for the next instructions.</p>
<h3 id="run">RUN</h3>
<p><code>RUN</code> instruction executes the shell command passed as an argument.</p>
<p>A temporary container is created to execute the shell command and the changes on the file system (after command execution) are layered on top of existing image layers. By default, the instruction <code>RUN apt-get update -y</code> will be executed as <code>/bin/bash -c apt-get update -y</code>.</p>
<p><code>SHELL</code> instruction is used in containerfile to change the default shell for <code>RUN</code>, for example, <code>SHELL [&quot;/bin/zsh&quot;, &quot;-c&quot;]</code> will change the default shell from <code>bash</code> to <code>zsh</code>.</p>
<h3 id="expose">EXPOSE</h3>
<p><code>EXPOSE</code> specifies the network ports to be used by the container on runtime. The ports still have to be published when the <a href="/posts/computer-science/technologies/cloud-native/container-lifecycle/#port-forwarding" target="_blank">container is provisioned</a>.</p>
<h3 id="cmd">CMD</h3>
<p><code>CMD</code> adds a shell command that will be executed on container startup. <code>CMD</code> instructions could be written as <code>CMD param1 param2</code> or <code>CMD [&quot;/bin/executable&quot;, &quot;param1&quot;, &quot;param2&quot;]</code>.</p>
<p>Adding a <code>CMD</code> instruction can define the behavior of the container on startup, for example, a webserver container should start the webserver automatically on startup by running the invocation command defined in <code>CMD</code>.</p>
<h3 id="entrypoint">ENTRYPOINT</h3>
<p>If the executable on container startup is expected to be the same in most conditions then it is preferable to add <code>ENTRYPOINT</code> over <code>CMD</code>. Parameters for the executable could be passed from <code>CMD</code> instruction.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">ENTRYPOINT</span> /bin/executable<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">CMD</span> <span class="p">[</span><span class="s2">&#34;param1&#34;</span><span class="p">,</span> <span class="s2">&#34;param2&#34;</span><span class="p">]</span><span class="err">
</span></span></span></code></pre></td></tr></table>
</div>
</div><h3 id="volume">VOLUME</h3>
<p>To create a mount point inside the container, <code>VOLUME</code> instruction can be used. During runtime, the <code>--volume</code> flag is used to map a directory on the host to the mount point declared by <code>VOLUME</code> instruction. The data stored on the mount point will persist after container execution.</p>
<h3 id="onbuild">ONBUILD</h3>
<p>The command added in the <code>ONBUILD</code> instruction is executed when the built image is used as the base container image.</p>
<p>If container image <code>container-image-b</code> uses <code>container-image-a</code> as a base image, then the <code>ONBUILD</code> instruction defined in <code>container-image-a</code> will be triggered after <code>FROM</code> instruction is executed in <code>container-image-b</code>.</p>
<p align="center"><img src="on_build.png" alt="Execution of ONBUILD instruction"></p>
<p align="center"><small><i>Execution of ONBUILD instruction</i></small></p>
<p>Using <code>ONBUILD</code>, the developers can ensure that any new image built upon their container image has the latest source code or that packages are up-to-date.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">ONBUILD</span> <span class="k">RUN</span> git clone source-code-repo-link.git<span class="err">
</span></span></span></code></pre></td></tr></table>
</div>
</div><h3 id="user">USER</h3>
<p><code>USER</code> instruction sets <code>UID</code> or <code>GID</code> during the build process.</p>
<h2 id="building-container-images-using-docker">Building container images using Docker</h2>
<p>Docker and Podman provide <code>build</code> subcommand for building container images from containerfile.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build .
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>.</code> will be substituted with the current working directory on the host.</p>
<p>By default Docker expects <em>Dockerfile</em> (instead of <em>Containerfile</em>) in the build context, <code>--file</code> flag could be used to pass the path to containerfile.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build --tag node-application:latest . --file ./Containerfile
</span></span></code></pre></td></tr></table>
</div>
</div><p>Container Engines cache the built layers to save time and storage. Cached layers are reused in other image builds to disable this behavior <code>--no-cache</code> flag could be used with the <code>build</code> subcommand.</p>
<p><code>--rm</code> flag ensures that the intermediate container images are removed once the build process is successful.</p>
<p>Adding files with sensitive data to <code>.dockerignore</code> will make sure that they aren&rsquo;t included in the container image.</p>
<h2 id="building-container-images-using-buildah">Building container images using buildah</h2>
<p><code>buildah</code> provides a <code>bud</code> subcommand for building container images from containerfile.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl">buildah bud --tag <span class="s2">&#34;webserver:latest&#34;</span> .<span class="err">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Images built by <code>buildah</code> could then be executed using Podman and Docker or any other OCI image-compatible container engine. Podman also has a <code>build</code> subcommand for building container images and it uses the same code as <code>buildah</code>.</p>
<p><code>buildah</code> provides finer control over image building process that allows users to</p>
<ul>
<li>Create base images using the scratch image, an image with a minimal footprint.</li>
<li>Mount a container&rsquo;s root filesystem, useful for debugging container-related issues.</li>
<li>Create a container image from a working container, helpful if Containerfile isn&rsquo;t accessible.</li>
<li>Execution of containerfile instructions using subcommands. For example, <code>RUN</code> instruction could be executed on a container image using <code>buildah run</code>. Similarly for <code>COPY</code> and <code>ADD</code> instructions.</li>
<li>Debug image building process by executing instructions manually with buildah.</li>
<li>Making ad-hoc changes to existing container images.</li>
<li>Automating build process.</li>
</ul>
<hr>
<p>Thank you for taking the time to read this blog post! Have questions, feedback or want to discuss this topic? Feel free to reach out at <a href="mailto:blog@avni.sh"><a href="mailto:blog@avni.sh">blog@avni.sh</a></a>.</p>
<p>If you found this content valuable and would like to stay updated with my latest posts, consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.</p>
<h1 id="resources">Resources</h1>
<p><a href="https://opencontainers.org/about/overview/" target="_blank">About the Open Container Initiative</a><br>
<a href="https://github.com/opencontainers/image-spec/blob/main/manifest.md" target="_blank">OCI Image Manifest Specification</a><br>
<a href="https://github.com/opencontainers/image-spec/blob/main/layer.md" target="_blank">Image Layer Filesystem Changeset</a><br>
<a href="https://github.com/opencontainers/image-spec/blob/main/config.md" target="_blank">OCI Image Configuration</a><br>
<a href="https://docs.docker.com/storage/storagedriver/" target="_blank">About storage drivers</a><br>
<a href="https://docs.docker.com/engine/reference/builder/" target="_blank">Dockerfile reference</a><br>
<a href="https://docs.docker.com/engine/reference/commandline/build/" target="_blank">docker build</a><br>
<a href="https://github.com/containers/buildah/blob/main/README.md" target="_blank">buildah</a><br>
<a href="https://hub.docker.com/_/scratch" target="_blank">scratch container image</a><br>
<a href="https://github.com/containers/buildah/blob/main/docs/tutorials/01-intro.md" target="_blank">Buildah Tutorial 1</a><br>
<a href="https://github.com/containers/buildah#buildah-and-podman-relationship" target="_blank">Buildah and Podman relationship</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>Container Images</title>
      <link>http://www.avni.sh/posts/computer-science/technologies/cloud-native/container-images/</link>
      <pubDate>Fri, 17 Mar 2023 00:00:00 +0000</pubDate>
      <guid>http://www.avni.sh/posts/computer-science/technologies/cloud-native/container-images/</guid>
      <description>Container Images are static files containing the necessary resources for creating containers.</description>
      <content:encoded><![CDATA[<p>A container image is a static file that contains the necessary resources (packages, configuration, other dependencies) required to provision a container. It consists of multiple layered-filesystems and a <em>Manifest</em> file, containing its metadata.</p>
<h1 id="open-container-initiative-oci-specification">Open Container Initiative (OCI) Specification</h1>
<p>Open Container Initiative was established by The Linux Foundation in 2015 to provide</p>
<ul>
<li>Runtime specification</li>
<li>Image specification</li>
<li>Distribution specification
for container images.</li>
</ul>
<p>A container image created from OCI Image specification should have</p>
<ul>
<li>Layered Filesystem: Stores the packaged contents of the container.</li>
<li>Image Index and Manifest: Contains a manifest list that stores the container image metadata for multiple platforms.</li>
<li>Image Configuration: Arguments and environment variables for the applications inside the container.</li>
</ul>
<p>Images created from Docker, Podman, and buildah follow OCI Image Specification.</p>
<h2 id="layered-filesystems">Layered filesystems</h2>
<p>Each filesystem layer stores changes performed on top of the previous layer. They are differentiated using their SHA256 digest.</p>
<p align="center"><img src="container_image_layers.png" alt="Base and Image Layers"></p>
<p align="center"><small><i>Base and Image Layers</i></small></p>
<p>To build a container image the developer has to use another container image as a <strong>base layer</strong>. The base layer container image could be chosen based on OS preference (like Fedora, Kali, etc.) or programming language preference (like gcc, python, etc.), or any other preference. On top of the base layer developers can add changes (like installing packages and dependencies, performing configuration changes, etc.) by creating stacked <strong>image layers</strong>.</p>
<p>The layers are cached on the host to reduce the build time and storage space required by the image. Cached layers could also be reused in the build process of different images, for example, two images (on the same host) using base layer <code>ubuntu:22.0</code> will refer to the same cached layer.</p>
<p>When a container image is provisioned as a container, the base and image layers are locked as read-only.</p>
<p align="center"><img src="container_layer.png" alt="Read-Write layer added on top of Image Layers"></p>
<p align="center"><small><i>Read-Write layer added on top of Image Layers</i></small></p>
<p>A data state of image layers is generated and a writable <strong>container layer</strong> is added on top. It stores runtime changes (like file creation, configuration changes, etc.) and deleted with the container.</p>
<h2 id="image-index-and-manifest">Image Index and Manifest</h2>
<p>The <strong>Image Index</strong> of a container image contains the metadata related to the filesystem layers, the command to be executed once the container is provisioned and other information required by the container runtime.</p>
<p>To view the index of a container image, the <code>manifest</code> subcommand with <code>inspect</code> option could be used.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker manifest inspect docker.io/library/httpd:latest
</span></span></code></pre></td></tr></table>
</div>
</div><p>The output will be a JSON document with a <code>manifest</code> array containing references to multiple image variations based on the OS and CPU architecture of the host.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;schemaVersion&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;mediaType&#34;</span><span class="p">:</span> <span class="s2">&#34;application/vnd.docker.distribution.manifest.list.v2+json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;manifests&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mediaType&#34;</span><span class="p">:</span> <span class="s2">&#34;application/vnd.docker.distribution.manifest.v2+json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;size&#34;</span><span class="p">:</span> <span class="mi">1366</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;digest&#34;</span><span class="p">:</span> <span class="s2">&#34;sha256:d866e5c91f31fc6a122aaf37149cc67ba2ca0de68ae73ab206747a190937967e&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;platform&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;architecture&#34;</span><span class="p">:</span> <span class="s2">&#34;amd64&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;os&#34;</span><span class="p">:</span> <span class="s2">&#34;linux&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mediaType&#34;</span><span class="p">:</span> <span class="s2">&#34;application/vnd.docker.distribution.manifest.v2+json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;size&#34;</span><span class="p">:</span> <span class="mi">1366</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;digest&#34;</span><span class="p">:</span> <span class="s2">&#34;sha256:32588e5c7552750100ad3110a64d163b1881ce92216d67e828c42d3322c439d1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;platform&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;architecture&#34;</span><span class="p">:</span> <span class="s2">&#34;arm&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;os&#34;</span><span class="p">:</span> <span class="s2">&#34;linux&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;variant&#34;</span><span class="p">:</span> <span class="s2">&#34;v5&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mediaType&#34;</span><span class="p">:</span> <span class="s2">&#34;application/vnd.docker.distribution.manifest.v2+json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;size&#34;</span><span class="p">:</span> <span class="mi">1366</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;digest&#34;</span><span class="p">:</span> <span class="s2">&#34;sha256:67586a7e127abd9b362884172b575a43b3342725ae310c339f4f7d5e5bdba918&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;platform&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;architecture&#34;</span><span class="p">:</span> <span class="s2">&#34;arm&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;os&#34;</span><span class="p">:</span> <span class="s2">&#34;linux&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;variant&#34;</span><span class="p">:</span> <span class="s2">&#34;v7&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;mediaType&#34;</span><span class="p">:</span> <span class="s2">&#34;application/vnd.docker.distribution.manifest.v2+json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;size&#34;</span><span class="p">:</span> <span class="mi">1366</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;digest&#34;</span><span class="p">:</span> <span class="s2">&#34;sha256:bc5f484630b50cec12a50035d22ed717d980c52c9871105e91e276ebcbee69a2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;platform&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;architecture&#34;</span><span class="p">:</span> <span class="s2">&#34;arm64&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;os&#34;</span><span class="p">:</span> <span class="s2">&#34;linux&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&#34;variant&#34;</span><span class="p">:</span> <span class="s2">&#34;v8&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>An image could be built and distributed for different platforms with singular name and tag. The container engine verifies from the manifest if a compatible image is available for the host.</p>
<h2 id="image-configuration">Image Configuration</h2>
<p>The changes to be implemented on the filesystem during the creation of the container are stored in the <strong>image configuration</strong> . It also stores parameters and environment variables for applications inside the container.</p>
<p><code>docker image inspect image-name</code> could be used to view the configuration of a container image in JSON format.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span><span class="lnt">62
</span><span class="lnt">63
</span><span class="lnt">64
</span><span class="lnt">65
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>
</span></span><span class="line"><span class="cl">    <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Id&#34;</span>: <span class="s2">&#34;sha256:daab1fa13f8608841399e8552ab7833e90307543509ced13cd40b3f7411632a3&#34;</span>,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;RepoTags&#34;</span>: <span class="o">[</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;httpd:latest&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="o">]</span>,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;RepoDigests&#34;</span>: <span class="o">[</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;httpd@sha256:76618ddd53f315a1436a56dc84ad57032e1b2123f2f6489ce9c575c4b280c4f4&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="o">]</span>,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Parent&#34;</span>: <span class="s2">&#34;&#34;</span>,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Comment&#34;</span>: <span class="s2">&#34;&#34;</span>,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Created&#34;</span>: <span class="s2">&#34;2023-03-07T20:24:12.062824222Z&#34;</span>,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Container&#34;</span>: <span class="s2">&#34;30df81c7e4e9fcec6990ff7a5aee09f0b8d765a65d575599d92fa3d2c2da6048&#34;</span>,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;DockerVersion&#34;</span>: <span class="s2">&#34;20.10.23&#34;</span>,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Author&#34;</span>: <span class="s2">&#34;&#34;</span>,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Config&#34;</span>: <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Hostname&#34;</span>: <span class="s2">&#34;&#34;</span>,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Domainname&#34;</span>: <span class="s2">&#34;&#34;</span>,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;User&#34;</span>: <span class="s2">&#34;&#34;</span>,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;AttachStdin&#34;</span>: false,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;AttachStdout&#34;</span>: false,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;AttachStderr&#34;</span>: false,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;ExposedPorts&#34;</span>: <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;80/tcp&#34;</span>: <span class="o">{}</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Tty&#34;</span>: false,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;OpenStdin&#34;</span>: false,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;StdinOnce&#34;</span>: false,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Env&#34;</span>: <span class="o">[</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;PATH=/usr/local/apache2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&#34;</span>,
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;HTTPD_PREFIX=/usr/local/apache2&#34;</span>,
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;HTTPD_VERSION=2.4.56&#34;</span>,
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;HTTPD_SHA256=d8d45f1398ba84edd05bb33ca7593ac2989b17cb9c7a0cafe5442d41afdb2d7c&#34;</span>,
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;HTTPD_PATCHES=&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="o">]</span>,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Cmd&#34;</span>: <span class="o">[</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;httpd-foreground&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="o">]</span>,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Image&#34;</span>: <span class="s2">&#34;sha256:a5930d7e9b3c7fa530cff2806db329c228e08a75d9db73314b3b5724c0858b60&#34;</span>,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Volumes&#34;</span>: null,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;WorkingDir&#34;</span>: <span class="s2">&#34;/usr/local/apache2&#34;</span>,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Entrypoint&#34;</span>: null,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;OnBuild&#34;</span>: null,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Labels&#34;</span>: null,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;StopSignal&#34;</span>: <span class="s2">&#34;SIGWINCH&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Architecture&#34;</span>: <span class="s2">&#34;amd64&#34;</span>,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Os&#34;</span>: <span class="s2">&#34;linux&#34;</span>,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Size&#34;</span>: 145140075,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;VirtualSize&#34;</span>: 145140075,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;RootFS&#34;</span>: <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Type&#34;</span>: <span class="s2">&#34;layers&#34;</span>,
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Layers&#34;</span>: <span class="o">[</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;sha256:650abce4b096b06ac8bec2046d821d66d801af34f1f1d4c5e272ad030c7873db&#34;</span>,
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;sha256:2309cdaf4afb9ce407a51c6d3ae54fb915bfbcc480e596e72417206902b4c51f&#34;</span>,
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;sha256:849b101b0e3b3f03fad010462b6ab14f1372d449a1fb803750182547e7df3d28&#34;</span>,
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;sha256:a30707f342ec97e05e57d91804d8ae3d5f93d5e6c9ef2c4e8a3d805b70b0d53e&#34;</span>,
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;sha256:087e3023406cdd4c0765ee68d24c88da0c0335ac2ce82d991bd336a648c033c6&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="o">]</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>,
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Metadata&#34;</span>: <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;LastTagTime&#34;</span>: <span class="s2">&#34;0001-01-01T00:00:00Z&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">]</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="distributing-container-images">Distributing container images</h1>
<h2 id="tarfile">Tarfile</h2>
<h3 id="exporting-containers">Exporting containers</h3>
<p>Containers could be exported as tar archives using the <code>export</code> subcommand.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker <span class="nb">export</span> httpd-container &gt; httpd.tar
</span></span></code></pre></td></tr></table>
</div>
</div><p>To import this tarfile as a container image, the <code>import</code> subcommand is used.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker import httpd.tar
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>import</code> also supports URL for tarfile as an argument.</p>
<h3 id="exporting-container-images">Exporting container images</h3>
<p>To export container images as tarfile we can use the <code>save</code> subcommand. Output from the command needs to be redirected using <code>&gt;</code> operator into a <code>*.tar</code> file.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker save httpd:latest &gt; httpd.tar
</span></span></code></pre></td></tr></table>
</div>
</div><p>This tarfile could be loaded as a container image using the <code>load</code> subcommand and <code>&lt;</code> operator.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker load &lt; httpd.tar
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="container-registry">Container Registry</h2>
<p align="center"><img src="image_pull.png" alt="Distributing images using Container Registry"></p>
<p align="center"><small><i>Distributing images using Container Registry</i></small></p>
<p><strong>Container Registry</strong> is a service that allows the distribution of container images. It also provides features such as</p>
<ul>
<li>Access control to images</li>
<li>Vulnerability scanning</li>
<li>Control over distribution</li>
<li>High availability</li>
</ul>
<p>Some of the commonly used container registries are Quay.io, DockerHub, and Google Container Registry.</p>
<p>A registry account is necessary to distribute an image. The credentials from the registry will be used to log in from the container engine.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker login quay.io
</span></span></code></pre></td></tr></table>
</div>
</div><p>The image to be pushed should be tagged/renamed in format <code>&lt;registry-name&gt;/&lt;username&gt;/&lt;image-name&gt;:&lt;image-tag&gt;</code>, for example, <code>quay.io/username/webserver:latest</code>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker tag webserver:latest quay.io/username/webserver:latest
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>push</code> subcommand is used to upload the tagged image to the registry.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker push quay.io/username/webserver:latest
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="creating-images-from-existing-containers">Creating images from existing containers</h3>
<p align="center"><img src="containers_commit.png" alt="Commit running containers to container image"></p>
<p align="center"><small><i>Commit running containers to container image</i></small></p>
<p>To create a container image from a running container, the <code>commit</code> subcommand could be used.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker commit webserver-container quay.io/username/webserver:latest
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<p>Thank you for taking the time to read this blog post! Have questions, feedback or want to discuss this topic? Feel free to reach out at <a href="mailto:blog@avni.sh"><a href="mailto:blog@avni.sh">blog@avni.sh</a></a>.</p>
<p>If you found this content valuable and would like to stay updated with my latest posts, consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.</p>
<h1 id="resources">Resources</h1>
<p><a href="https://opencontainers.org/about/overview/" target="_blank">About the Open Container Initiative</a><br>
<a href="https://github.com/opencontainers/image-spec/blob/main/manifest.md" target="_blank">OCI Image Manifest Specification</a><br>
<a href="https://github.com/opencontainers/image-spec/blob/main/layer.md" target="_blank">Image Layer Filesystem Changeset</a><br>
<a href="https://github.com/opencontainers/image-spec/blob/main/config.md" target="_blank">OCI Image Configuration</a><br>
<a href="https://docs.docker.com/storage/storagedriver/" target="_blank">About storage drivers</a><br>
<a href="https://docs.docker.com/engine/reference/commandline/export/" target="_blank">docker export</a><br>
<a href="https://docs.docker.com/engine/reference/commandline/import/" target="_blank">docker import</a><br>
<a href="https://docs.docker.com/engine/reference/commandline/save/" target="_blank">docker save</a><br>
<a href="https://docs.docker.com/engine/reference/commandline/load/" target="_blank">docker load</a><br>
<a href="https://docs.docker.com/engine/reference/commandline/commit/" target="_blank">docker commit</a><br>
<a href="https://docs.docker.com/engine/reference/commandline/push/" target="_blank">docker push</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>Container Lifecycle</title>
      <link>http://www.avni.sh/posts/computer-science/technologies/cloud-native/container-lifecycle/</link>
      <pubDate>Fri, 10 Feb 2023 00:00:00 +0000</pubDate>
      <guid>http://www.avni.sh/posts/computer-science/technologies/cloud-native/container-lifecycle/</guid>
      <description>The state of a container is managed using the utilities provided by container engines.</description>
      <content:encoded><![CDATA[<p>Container Engines like Podman and Docker provide GUI and CLI utilities for managing the state of containers. They also provide features such as container image management, metrics, logging, and debugging tools. The examples in this article use Podman but CLI commands are mostly interoperable with Docker.</p>
<p>We can install Podman on your system by following the steps in <a href="https://podman.io/getting-started/installation" target="_blank">Podman Installation Instructions</a>. Podman also provides a graphical interface for managing containers, images, and other resources called <a href="https://podman-desktop.io/" target="_blank">Podman Desktop</a>.</p>
<h1 id="accessing-container-images">Accessing Container Images</h1>
<p>To create a container first, we have to download its image from a registry. A <strong>container image</strong> is a file that contains all the dependencies and executable code for the container.</p>
<p>Images could be fetched from container registries such as  <a href="https://hub.docker.com/" target="_blank">DockerHub</a> or <a href="https://quay.io/search" target="_blank">Quay.io</a>.</p>
<h2 id="login-to-container-image-registry">Login to Container Image Registry</h2>
<p>A <strong>container image registry</strong> is a service that handles the storage and distribution of container images. It is necessary to log in to the container registry to access private images.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman login docker.io
</span></span></code></pre></td></tr></table>
</div>
</div><p>After executing this command a prompt will ask for your username and password, for that we need to have a container registry account. Images used in this article are public so we don&rsquo;t need to create an account or log in.</p>
<p>Once login is successful, Podman stores the encrypted user information in file <code>${XDG_RUNTIME_DIR}/containers/auth.json</code>. <code>XDG_RUNTIME_DIR</code> is an environment variable set by <code>systemd</code> that stores the path of the directory containing user-specific runtime files.</p>
<h2 id="pulling-container-images">Pulling Container Images</h2>
<p align="center"><img src="image_pull.png" alt="Pulling container images from registry"></p>
<p align="center"><small><i>Pulling container images from registries</i></small></p>
<p>Public registries provide an interface for searching images or Podman&rsquo;s subcommand <code>search</code> could also be used.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman search httpd
</span></span></code></pre></td></tr></table>
</div>
</div><p>Once we have the image name we can pull it on our machine using the <code>pull</code> subcommand</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman pull docker.io/library/httpd 
</span></span></code></pre></td></tr></table>
</div>
</div><p>or using the Podman Desktop app you can go to <strong>Images</strong> -&gt; <strong>Pull an Image</strong>.</p>
<p align="center"><img src="podman_desktop_image_pull.png" alt="Pulling container image using Podman Desktop"></p>
<p align="center"><small><i>Pulling container image using Podman Desktop</i></small></p>
<p>Different versions/variations of container images are managed by <strong>Tags</strong>. The image tag <code>latest</code> is pulled by default. You can view the list of available tags on the registry&rsquo;s webpage or use <code>skopeo list-tags</code>. Skopeo requires the <em>transport</em> (<code>docker</code> in our case) to be mentioned explicitly with the image name.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">skopeo list-tags docker://docker.io/library/httpd
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="managing-container-lifecycle">Managing Container Lifecycle</h1>
<p>Once we have access to the container image we can easily create one or more containers.</p>
<h2 id="creating-a-container">Creating a Container</h2>
<p align="center"><img src="create_containers.png" alt="Creating a container from the container image"></p>
<p align="center"><small><i>Creating a container from the container image</i></small></p>
<p>We use the <code>run</code> subcommand for creating containers. If the image specified in the command is not present in the system then Podman will attempt to pull it from the registry.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman run --name httpd-test docker.io/library/httpd
</span></span></code></pre></td></tr></table>
</div>
</div><p>After executing this command a container named <code>httpd-test</code> will be created on your system and its output will be attached to your terminal. To exit you can use the shortcut <strong>Ctrl+C</strong>. To run the container in the background <code>--detach</code> flag could be used.</p>
<p><code>podman ps</code> command will list running containers on the system.</p>
<p>On Podman Desktop, the <strong>Containers</strong> section provides utilities for managing the state of containers.</p>
<p align="center"><img src="podman_desktop_create_containers.png" alt="Creating container using Podman Desktop"></p>
<p align="center"><small><i>Creating container using Podman Desktop</i></small></p>
<h2 id="executing-commands-inside-a-container">Executing Commands inside a Container</h2>
<p>To run a container and access its shell we have to use options <code>--interactive</code> &amp; <code>--tty</code>.</p>
<p><code>--interactive</code> will allow us to provide inputs to the process running inside the container and <code>--tty</code> will attach a pseudo-terminal to the container.</p>
<p><strong>Entrypoint</strong> specifies a command that is executed during container initialization. It could be defined by default in the image, but you can override it with the <code>--entrypoint</code> flag.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman run -it --name httpd-test <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>               --entrypoint<span class="o">=</span><span class="s2">&#34;/bin/bash&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>               docker.io/library/httpd
</span></span></code></pre></td></tr></table>
</div>
</div><p>To execute a command or script inside the container we use Podman&rsquo;s <code>exec</code> subcommand.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman <span class="nb">exec</span> httpd-test <span class="nb">echo</span> <span class="s2">&#34;Hello, world&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="attaching-volumes">Attaching Volumes</h2>
<p>Directories from the host could be mounted on a container. It is useful when scripts are edited outside the container (an IDE/Code Editor on the host) or the files have to be shared between multiple containers.</p>
<p>To mount a directory from host to container flag <code>--volume</code> could be used with <code>run</code> subcommand and values are passed as <code>&lt;HOSTDIR&gt;:&lt;CONTAINERDIR&gt;</code>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman run -it --name python-test --detach <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>               --entrypoint<span class="o">=</span><span class="s2">&#34;/bin/bash&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>               --volume <span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span>:/app:Z <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>               docker.io/library/python:3.10.6-slim-bullseye
</span></span></code></pre></td></tr></table>
</div>
</div><p>This command starts a python container with contents of the present working directory (fetched from the <code>pwd</code> command) mounted on the <code>/app</code> directory inside the container. Both directory paths have to be absolute.</p>
<p>Before mounting the host directory we have to change its <a href="/posts/computer-science/technologies/cloud-native/container-architecture/#security-enhanced-linux-selinux" target="_blank">SELinux</a> context to <code>container_file_t</code> otherwise we could encounter permission issues while accessing directory contents. To change the SELinux context you can append either <code>:z</code> (if multiple containers need read-write access) or <code>:Z</code> (if only the current container needs read-write access).</p>
<p align="center"><img src="podman_desktop_volumes.png" alt="Accessing volumes on Podman Desktop"></p>
<p align="center"><small>Accessing volumes on Podman Desktop</small></p>
<h2 id="port-forwarding">Port Forwarding</h2>
<p>To communicate with the services running inside the container, port forwarding has to be established from the host using the <code>--publish</code> flag in  the <code>run</code> subcommand. The port specified for the host and container could be different for example the following command forwards the output from the <code>httpd-test</code> container&rsquo;s port <code>80</code> to port <code>8080</code> on the host.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman run --name httpd-test --detach <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>           --publish 8080:80 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>           docker.io/library/httpd           
</span></span></code></pre></td></tr></table>
</div>
</div><p>To view the output we can visit <a href="http://localhost:8080" target="_blank">localhost:8080</a> in our browser.
It is advised that only the necessary network ports should be forwarded from container to host.</p>
<h2 id="pausing-an-executing-container">Pausing an Executing Container</h2>
<p align="center"><img src="pause.png" alt="Pausing running container"></p>
<p align="center"><small><i>Pausing running container</i></small></p>
<p>To demonstrate a paused container we will take the help of the following Python script</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># This script runs an infinite loop </span>
</span></span><span class="line"><span class="cl"><span class="c1"># with the time interval of 2 seconds to slow down the output</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl"><span class="n">i</span><span class="o">=</span><span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Value of i: </span><span class="si">{}</span><span class="s2">&#34;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">i</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">i</span><span class="o">+=</span><span class="mi">1</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Assuming that this script is saved in the current directory with name <code>counting.py</code>, we run a container created from image <code>docker.io/library/python:3.10.6-slim-bullseye</code> in detached mode.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman run -it --name python-test --detach <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>               --entrypoint<span class="o">=</span><span class="s2">&#34;/bin/bash&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>               -v <span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span>:/app:Z <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>               docker.io/library/python:3.10.6-slim-bullseye
</span></span></code></pre></td></tr></table>
</div>
</div><p>Then we execute the script inside the container in interactive mode</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman <span class="nb">exec</span> -it python-test python3 /app/counting.py
</span></span></code></pre></td></tr></table>
</div>
</div><p>An infinite loop will be initiated and the output should be visible on the terminal. Now we open a new terminal window and pause this container.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman pause python-test
</span></span></code></pre></td></tr></table>
</div>
</div><p>If we go back to the first terminal we can observe that the script execution is paused. When unpaused the container will restart right where it left.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman unpause python-test
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="stopping-an-executing-container">Stopping an Executing Container</h2>
<p align="center"><img src="stop.png" alt="Stop running containers"></p>
<p align="center"><small><i>Stop running containers</i></small></p>
<p>While pausing a container stops its execution in its current state, <em>stopping</em> a container kills the executing process.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman stop python-test
</span></span></code></pre></td></tr></table>
</div>
</div><p>Upon restarting container will run the entrypoint command, starting a new process.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman start python-test
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="deleting-a-container">Deleting a Container</h2>
<p align="center"><img src="delete.png" alt="Deleting containers"></p>
<p align="center"><small><i>Deleting containers</i></small></p>
<p>Before we delete a running container we have to stop it or we can use the <code>--force</code> flag along with the <code>rm</code> subcommand.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman rm python-test
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="metrics-and-logging">Metrics and Logging</h1>
<h2 id="listing-containers">Listing Containers</h2>
<p>By default, the <code>ps</code> subcommand fetches the list of running containers but to view all the containers regardless of their state we can use the <code>--all</code> flag.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman ps --all
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="resource-utilization-statistics">Resource Utilization Statistics</h2>
<p><code>stats</code> subcommand provides live statistics of resource utilization by containers.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman stats
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="accessing-logs-from-a-container">Accessing Logs from a Container</h2>
<p>To access the logs of a running/completed container we use the <code>logs</code> subcommand. To view the live output from a running container <code>--follow</code> flag is added.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">podman logs --follow httpd-test
</span></span></code></pre></td></tr></table>
</div>
</div><p>To access the container logs from Podman Desktop we can click on the container name in the <strong>Containers</strong> section.</p>
<p align="center"><img src="podman_desktop_container_logs.png" alt="Accessing container logs from Podman Desktop"></p>
<p align="center"><small><i>Accessing container logs from Podman Desktop</i></small></p>
<p>We can also attach a terminal to the container from the <strong>Terminal</strong> tab.</p>
<hr>
<p>Thank you for taking the time to read this blog post! Have questions, feedback or want to discuss this topic? Feel free to reach out at <a href="mailto:blog@avni.sh"><a href="mailto:blog@avni.sh">blog@avni.sh</a></a>.</p>
<p>If you found this content valuable and would like to stay updated with my latest posts, consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.</p>
<h1 id="resources">Resources</h1>
<p><a href="https://podman.io/getting-started/installation" target="_blank">Podman Installation Instructions</a><br>
<a href="https://podman-desktop.io/" target="_blank">Podman Desktop</a><br>
<a href="https://docs.podman.io/en/latest/markdown/podman-login.1.html" target="_blank">podman-login</a><br>
<a href="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables" target="_blank">XDG Base Directory Specification</a><br>
<a href="https://hub.docker.com/_/httpd" target="_blank">httpd container image</a><br>
<a href="https://hub.docker.com/_/python" target="_blank">python container image</a><br>
<a href="https://docs.podman.io/en/latest/markdown/podman-search.1.html" target="_blank">podman-search</a><br>
<a href="https://docs.podman.io/en/latest/markdown/podman-pull.1.html" target="_blank">podman-pull</a><br>
<a href="https://docs.podman.io/en/latest/markdown/podman-run.1.html" target="_blank">podman-run</a><br>
<a href="https://docs.podman.io/en/latest/markdown/podman-exec.1.html" target="_blank">podman-exec</a><br>
<a href="https://docs.podman.io/en/latest/markdown/podman-pause.1.html" target="_blank">podman-pause</a><br>
<a href="https://docs.podman.io/en/latest/markdown/podman-stop.1.html" target="_blank">podman-stop</a><br>
<a href="https://docs.podman.io/en/latest/markdown/podman-stats.1.html" target="_blank">podman-stats</a><br>
<a href="https://docs.podman.io/en/latest/markdown/podman-logs.1.html" target="_blank">podman-logs</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>Container Architecture</title>
      <link>http://www.avni.sh/posts/computer-science/technologies/cloud-native/container-architecture/</link>
      <pubDate>Fri, 27 Jan 2023 00:00:00 +0000</pubDate>
      <guid>http://www.avni.sh/posts/computer-science/technologies/cloud-native/container-architecture/</guid>
      <description>Creation and execution of container is enabled using features like namespaces, Cgroups, seccomp, and SELinux</description>
      <content:encoded><![CDATA[<p>To isolate the processes running inside a <a href="/posts/computer-science/technologies/cloud-native/containers/" target="_blank">container</a> from its host system, container engine uses the following four features:</p>
<ul>
<li>Namespaces</li>
<li>Control Groups</li>
<li>Secure Computing</li>
<li>Security-Enhanced Linux</li>
</ul>
<h2 id="namespaces">Namespaces</h2>
<p><strong>Namespaces</strong> are created to limit the reach of a container to its host&rsquo;s resources. It helps with security and well as limits resources available to the container.</p>
<p>Linux command <code>lsns</code> could be used for listing details of namespaces.</p>
<p>The namespaces essential for containers are User, Mount, Unix Timesharing System, Process ID, Network, and Inter-Process Communication.</p>
<h3 id="user">User</h3>
<p>The users and groups created inside a container are different from its host. Processes running inside the container as a <code>root</code> user could be mapped to a non-root user on the host.</p>
<p>Using the <code>id</code> command you can verify that the containers are present on a different user namespace than other processes on your host.</p>
<p>Running <code>id</code> on host:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ id
</span></span><span class="line"><span class="cl"><span class="nv">uid</span><span class="o">=</span>1000<span class="o">(</span>username<span class="o">)</span> <span class="nv">gid</span><span class="o">=</span>1000<span class="o">(</span>username<span class="o">)</span> <span class="nv">groups</span><span class="o">=</span>1000<span class="o">(</span>username<span class="o">)</span>,10<span class="o">(</span>wheel<span class="o">)</span> <span class="nv">context</span><span class="o">=</span>unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
</span></span></code></pre></td></tr></table>
</div>
</div><p>Running <code>id</code> inside a container created from <code>docker.io/library/httpd:latest</code> container image:</p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 328 41"
      >
      <g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>@</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>7</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>6</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='136' y='20' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='160' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='168' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='200' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='208' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='216' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='232' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='264' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='272' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='280' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='288' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='296' y='20' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>d</text>
</g>

    </svg>
  
</div>
<h3 id="process-id-pid">Process ID (<code>PID</code>)</h3>
<p>Each process running on the host has a unique Process ID (PID) assigned to it. The PIDs of processes running inside container are separate from PIDs assigned by the host. Due to process ID isolation, a container can&rsquo;t access the details of processes running on its host.</p>
<p>To fetch the list of PID namespaces you can use the command:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ lsns -t pid
</span></span><span class="line"><span class="cl">        NS TYPE NPROCS   PID USER  COMMAND
</span></span><span class="line"><span class="cl"><span class="m">4026531836</span> pid     <span class="m">128</span>  <span class="m">4996</span> user /usr/lib/systemd/systemd --user
</span></span><span class="line"><span class="cl"><span class="m">4026533251</span> pid      <span class="m">16</span>  <span class="m">4525</span> user nginx: worker process
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></td></tr></table>
</div>
</div><p>Just like other processes, containers also have PIDs assigned to them by the host. You can fetch the PID of a running container using the following command:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$  docker inspect -f <span class="s1">&#39;{{.State.Pid}}&#39;</span> deploy-hugo-server-1
</span></span><span class="line"><span class="cl"><span class="m">7172</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>ps aux</code> command could be used to list the running processes on the system along with their details.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ps aux <span class="p">|</span> grep <span class="m">7172</span>
</span></span><span class="line"><span class="cl">root        <span class="m">7172</span>  0.0  0.4 <span class="m">759596</span> <span class="m">68860</span> ?        Ssl  Jan22   0:32 /usr/lib/hugo/hugo server --buildFuture --bind<span class="o">=</span>0.0.0.0
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="mount-mnt">Mount (<code>mnt</code>)</h3>
<p>By using different mount namespaces for different processes we can ensure that they won&rsquo;t be able to access each other&rsquo;s files.</p>
<p>You can use <code>df</code> command to view the filesystems mounted on your system.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ df
</span></span><span class="line"><span class="cl">Filesystem     1K-blocks      Used Available Use% Mounted on
</span></span><span class="line"><span class="cl">devtmpfs            <span class="m">4096</span>         <span class="m">0</span>      <span class="m">4096</span>   0% /dev
</span></span><span class="line"><span class="cl">tmpfs            <span class="m">7849324</span>     <span class="m">42384</span>   <span class="m">7806940</span>   1% /dev/shm
</span></span><span class="line"><span class="cl">tmpfs            <span class="m">3139732</span>      <span class="m">2444</span>   <span class="m">3137288</span>   1% /run
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></td></tr></table>
</div>
</div><p>A container has its separate file system hierarchy which could be viewed by using <code>df</code> command on its shell.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">root@14ed72afd62e:/usr/local/apache2# df
</span></span><span class="line"><span class="cl">Filesystem     1K-blocks      Used Available Use% Mounted on
</span></span><span class="line"><span class="cl">overlay        <span class="m">498443264</span> <span class="m">308159584</span> <span class="m">185270656</span>  63% /
</span></span><span class="line"><span class="cl">tmpfs              <span class="m">65536</span>         <span class="m">0</span>     <span class="m">65536</span>   0% /dev
</span></span><span class="line"><span class="cl">tmpfs            <span class="m">1569860</span>       <span class="m">276</span>   <span class="m">1569584</span>   1% /etc/hosts
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></td></tr></table>
</div>
</div><p>It could also be viewed by the host in the file <code>/proc/&lt;CONTAINER_PID&gt;/mounts</code>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ docker inspect -f <span class="s1">&#39;{{.State.Pid}}&#39;</span> deploy-hugo-server-1
</span></span><span class="line"><span class="cl"><span class="m">7172</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ cat /proc/7172/mounts
</span></span><span class="line"><span class="cl">proc /proc proc rw,nosuid,nodev,noexec,relatime <span class="m">0</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">tmpfs /dev tmpfs rw,seclabel,nosuid,size<span class="o">=</span>65536k,mode<span class="o">=</span>755,inode64 <span class="m">0</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">devpts /dev/pts devpts rw,seclabel,nosuid,noexec,relatime,gid<span class="o">=</span>5,mode<span class="o">=</span>620,ptmxmode<span class="o">=</span><span class="m">666</span> <span class="m">0</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="unix-timesharing-system-uts">Unix Timesharing System (UTS)</h3>
<p>Unix Time System (UTS) namespace allows containers to have hostnames. We can verify this with the <code>hostname</code> command.</p>
<p>On the host:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>username@host-1 ~<span class="o">]</span>$ hostname
</span></span><span class="line"><span class="cl">host-1
</span></span></code></pre></td></tr></table>
</div>
</div><p>Inside a container:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">root@14ed72afd62e:/usr/local/apache2# hostname
</span></span><span class="line"><span class="cl">14ed72afd62e
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="network">Network</h3>
<p>Each container has a IP address and network ports assigned to it by its network namespace. It allows the developer to run multiple processes inside the container and expose them over different network ports.</p>
<p>To access or communicate with a process inside the container, port forwarding should be established from the host.</p>
<h3 id="inter-process-communication-ipc">Inter-Process Communication (IPC)</h3>
<p>Processes in the same IPC namespace can share the resources such as memory, semaphores, and message queues. Keeping separate IPC namespaces ensures that the processes inside a container cannot access the resources used by the host&rsquo;s processes.</p>
<h3 id="time">Time</h3>
<p>Time namespaces are available since the release of Linux Kernel 5.6.<br>
Maybe in the future containers can have a different time than their host.</p>
<h2 id="control-groups-cgroups">Control Groups (Cgroups)</h2>
<p>A <strong>control group</strong> is created to effectively allocate resources of host its processes. These Cgroups are hierarchical i.e. a child Cgroup could be spawned from the parent and it will inherit its certain attributes.</p>
<p>By creating a Cgroup a process in it could be prioritized, paused, removed, or resumed based on the resources allocated to it. It also helps in monitoring the resources used by particular processes.</p>
<p>If you are using an OS with <code>systemd</code> init system (to verify this you can use the command <code>ps -p 1 -o comm=</code>) then you can use the command <code>systemctl list-units</code> to list all the Cgroups. It will open a table containing the Cgroup name, state, and description. The names of the Cgroup will be in the form <code>&lt;parent-cgroup&gt;.&lt;child-cgroup&gt;</code> like <code>sys-devices-platform-serial8250-tty-ttyS0.device</code>.</p>
<p>To view the hierarchy of Cgroups you can use the command <code>systemd-cgls</code>. It presents cgroups as a tree structure.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Control group /:
</span></span><span class="line"><span class="cl">-.slice
</span></span><span class="line"><span class="cl">├─user.slice <span class="o">(</span><span class="c1">#1309)</span>
</span></span><span class="line"><span class="cl">│ └─user-1000.slice <span class="o">(</span><span class="c1">#10010)</span>
</span></span><span class="line"><span class="cl">│   ├─user@1000.service <span class="o">(</span><span class="c1">#10106)</span>
</span></span><span class="line"><span class="cl">│   │ ├─session.slice <span class="o">(</span><span class="c1">#10394)</span>
</span></span><span class="line"><span class="cl">│   │ │ ├─dbus-broker.service <span class="o">(</span><span class="c1">#10442)</span>
</span></span><span class="line"><span class="cl">│   │ │ │ ├─ <span class="m">5088</span> /usr/bin/dbus-broker-launch --scope user
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="secure-computing-seccomp">Secure Computing (Seccomp)</h2>
<p>Using <strong>Secure Computing</strong> (or seccomp) you can disable the system calls your process can make to the host&rsquo;s kernel.</p>
<p>A <em>seccomp profile</em> is a definition with a set of restricted and allowed system calls stored in a file. Default seccomp profile used by Docker: <a href="https://github.com/moby/moby/blob/master/profiles/seccomp/default.json" target="_blank">default.json</a>.</p>
<p>Docker allows you to define your seccomp profile for a container in JSON format.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run --rm -it --security-opt <span class="nv">seccomp</span><span class="o">=</span>/path/to/seccomp/profile.json hello-world
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="security-enhanced-linux-selinux">Security-Enhanced Linux (SELinux)</h2>
<p><strong>SELinux</strong> is a security architecture for GNU/Linux-based OS that defines access to files and processes. It is enforced on users or processes to restrict their access to the resources.</p>
<p>SELinux checks the <em>SELinux context</em> of the file or process to make decisions related to its access control. To view the SELinux context of a file use command <code>ls -Z &lt;FILENAME&gt;</code> and to view it for a process using the command <code>ps -eZ | grep &lt;PROCESS_NAME&gt;</code>.</p>
<hr>
<p>Thank you for taking the time to read this blog post! Have questions, feedback or want to discuss this topic? Feel free to reach out at <a href="mailto:blog@avni.sh"><a href="mailto:blog@avni.sh">blog@avni.sh</a></a>.</p>
<p>If you found this content valuable and would like to stay updated with my latest posts, consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.</p>
<h1 id="resources">Resources</h1>
<p><a href="https://www.youtube.com/watch?v=sK5i-N34im8" target="_blank">Cgroups, namespaces, and beyond: what are containers made from?</a><br>
<a href="https://opensource.com/article/21/8/container-linux-technology" target="_blank">4 Linux technologies fundamental to containers</a><br>
<a href="https://man7.org/linux/man-pages/man1/init.1.html" target="_blank">systemd(1) — Linux manual page</a><br>
<a href="https://www.redhat.com/sysadmin/7-linux-namespaces" target="_blank">The 7 most used Linux namespaces</a><br>
<a href="https://opensource.com/article/19/4/interprocess-communication-linux-storage" target="_blank">Inter-process communication in Linux: Shared storage</a><br>
<a href="https://www.tenable.com/audits/items/DISA_STIG_Docker_Enterprise_2.x_Linux_Unix_v1r1.audit:f0aaa2cd4bcb66461902f47c69bf323b" target="_blank">DKER-EE-001250 - The Docker Enterprise hosts IPC namespace must not be shared.</a><br>
<a href="https://www.phoronix.com/news/Time-Namespace-In-Linux-5.6" target="_blank">It&rsquo;s Finally Time: The Time Namespace Support Has Been Added To The Linux 5.6 Kernel</a><br>
<a href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/resource_management_guide/sec-obtaining_information_about_control_groups" target="_blank">Obtaining Information about Control Groups</a><br>
<a href="https://man7.org/linux/man-pages/man2/seccomp.2.html" target="_blank">seccomp(2) — Linux manual page</a><br>
<a href="https://www.redhat.com/en/topics/linux/what-is-selinux" target="_blank">What is SELinux?</a><br>
<a href="https://blog.christophersmart.com/2021/01/31/podman-volumes-and-selinux/" target="_blank">Podman volumes and SELinux</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>Containers</title>
      <link>http://www.avni.sh/posts/computer-science/technologies/cloud-native/containers/</link>
      <pubDate>Thu, 19 Jan 2023 00:00:00 +0000</pubDate>
      <guid>http://www.avni.sh/posts/computer-science/technologies/cloud-native/containers/</guid>
      <description>A container is a set of one or more isolated processes running on an Operating System</description>
      <content:encoded><![CDATA[<p>While creating an application in any programming language or framework you have to install its dependencies on your system.
This could include the compiler for the programming language, libraries, and frameworks.</p>
<p>If the environment for the deployment of this application is different then the whole process of dependency installation has to repeat. You might have to some other changes, like setting up environment variables and making changes to the configuration files.</p>
<p>This problem becomes more challenging once you start collaborating with other people. The complete installation process has to be repeated on every development machine. Even if complete documentation of the setup process is available the developers could have a different environment. It is also possible that the developers want to work on two different projects simultaneously and there could be a conflict between their configurations (like different versions of the framework).</p>
<p>When an application is deployed directly on the OS running on the physical hardware it is called <strong>baremetal deployment</strong>.</p>
<p align="center"><img src="baremetal_deployment.png" alt="Deployment of application directly on physical hardware"></p>
<p align="center"><small><i>Baremetal Deployment</i></small></p>
<h1 id="virtual-machines-vms">Virtual Machines (VMs)</h1>
<p>A <strong>virtual machine</strong> as the name suggests is a machine running upon the OS installed on the physical hardware. It has its virtual CPU, memory, networking interfaces, and other resources that are provided by a <em>hypervisor</em>.</p>
<p>A <strong>hypervisor</strong> is a program that is used to create and manage virtual machines. Some common hypervisors are:</p>
<ul>
<li>Oracle VM VirtualBox</li>
<li>Hyper-V</li>
</ul>
<h2 id="advantages-of-using-vms-for-deploying-applications">Advantages of using VMs for deploying applications</h2>
<p align="center"><img src="virtual_machine_deployment.png" alt="Deployment of application using Virtualization"></p>
<p align="center"><small><i>Virtualized Deployment</i></small></p>
<p>So, what advantage does development over VMs provide over development on the OS deployed on the physical hardware?</p>
<ol>
<li>The environment is persistent across the machines.<br>
If you create your application&rsquo;s development environment inside a VM, then you can share it with anyone else who is interested to run it themselves.<br>
The environment will be the same irrespective of the OS installed on the physical hardware.</li>
<li>Load balancing is easier in the production environment.<br>
You can run multiple instances of the same application such that the load could be distributed effectively. If one of the instances is down (due to updates or outage) the traffic to it could be re-routed to another instance.</li>
<li>Effective distribution of physical hardware resources available.<br>
The hypervisor creates a pool of resources available to it from the OS. Then these resources could be effectively distributed among the VMs created. A deployment VM could use fewer resources compared to a development VM.</li>
<li>Provides an option to run multiple OS on a single hardware.</li>
</ol>
<h1 id="containers">Containers</h1>
<p>A <strong>container</strong> is a set of one or more isolated processes running on an OS, it could be on a VM or directly on the physical hardware. Usually, it requires fewer resources than a virtual machine and provides similar performance and benefits depending on your use case.</p>
<p>The workloads running inside a container have very limited exposure to the resources of its host i.e its CPUs, memory, network, and storage. It uses the same kernel as its host, that&rsquo;s why you can&rsquo;t spin up a Linux container on a machine running Windows (without provisioning the VM through Windows Subsystem for Linux or any other hypervisor).</p>
<h2 id="advantages-of-using-containers-over-virtual-machines">Advantages of using Containers over Virtual Machines</h2>
<p align="center"><img src="containerized_deployment.png" alt="Deployment of application using containerization"></p>
<p align="center"><small><i>Containerized Deployment</i></small></p>
<ol>
<li>Relatively lighter compared to VMs<br>
Containers don&rsquo;t need virtual resources. The size of the container could be shrunk down to the point where only the dependencies required for the application are present on it.</li>
<li>Faster deployment<br>
Due to their smaller size compared to VMs they could be deployed faster.</li>
<li>More scalable<br>
It is possible to execute multiple containers in the same amount of resources required by a VM for an application.</li>
</ol>
<p>Depending on your use case the deployment could be a mix of both virtualization and containerization. Like a service comprising of multiple containers could be deployed on a VM.</p>
<h1 id="managing-containers">Managing Containers</h1>
<h2 id="container-runtime">Container Runtime</h2>
<p>The container is executed through a <strong>container runtime</strong>. It sets up the namespaces for the container.</p>
<p>Examples of container runtimes:</p>
<ul>
<li>containerd: Developed by Docker and donated to Cloud Native Computing Foundation (CNCF). Used in Docker Engine.</li>
<li>cri-o: Container Runtime commonly used in Kubernetes.</li>
</ul>
<h2 id="container-images">Container Images</h2>
<p>A <strong>container image</strong> is a file that contains all the dependencies and executable code for the container. It is a way of storing and sharing containers.</p>
<h2 id="container-image-registries">Container Image Registries</h2>
<p>A <strong>container image registry</strong> is a service that handles the storage and distribution of container images.</p>
<p>Some of the common container registries are: DockerHub, Quay.io, etc.</p>
<h2 id="container-engine">Container Engine</h2>
<p>When a container runtime is expanded with CLI and/or GUI utility for</p>
<ul>
<li>Creation of containers</li>
<li>Changing the state of container (starting, stopping, resuming, deleting)</li>
<li>Managing container images</li>
<li>APIs for developers to create layered products on top of it</li>
</ul>
<p>it becomes a <strong>container engine</strong>. Some examples of container engines are:</p>
<ul>
<li>Docker</li>
<li>Podman</li>
</ul>
<h2 id="open-container-initiative-oci">Open Container Initiative (OCI)</h2>
<p>Open Container Initiative was established in 2015 for providing open specifications for:</p>
<ul>
<li>Container Runtime</li>
<li>Container Image</li>
<li>Container Image Distribution</li>
</ul>
<p>The objective is to establish a standardized format for the containers such that an OCI container image could be used between different container engines without any issues.</p>
<h2 id="docker">Docker</h2>
<p>One of the most commonly used container engines. It is developed by Docker, Inc.
It uses a daemon (<code>dockerd</code>) that provides all the management services for the container. This daemon runs with root privileges.</p>
<h2 id="podman">Podman</h2>
<p>The acronym for <em>Pod Manager tool</em> is a daemonless container engine originally developed by Red Hat.</p>
<p>Unlike docker, it doesn&rsquo;t use any daemon for interactions with containers.
It also has other security-focused features like the containers don&rsquo;t run as root by default. This reduces the attack surface as the host system cannot be accessed from the inside of the container.</p>
<h3 id="skopeo">Skopeo</h3>
<p><code>skopeo</code> is a CLI utility used alongside <code>podman</code> for the management, inspection, and transfer of container images.</p>
<h3 id="buildah">Buildah</h3>
<p><code>buildah</code> is another CLI utility used alongside <code>podman</code> and <code>skopeo</code> for building OCI container images.</p>
<hr>
<p>Thank you for taking the time to read this blog post! Have questions, feedback or want to discuss this topic? Feel free to reach out at <a href="mailto:blog@avni.sh"><a href="mailto:blog@avni.sh">blog@avni.sh</a></a>.</p>
<p>If you found this content valuable and would like to stay updated with my latest posts, consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.</p>
<h1 id="resources">Resources</h1>
<p><a href="https://www.virtualbox.org/" target="_blank">Oracle VM VirtualBox</a><br>
<a href="https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/about/" target="_blank">Hyper-V</a><br>
<a href="https://containerd.io/" target="_blank">containerd</a><br>
<a href="https://cri-o.io/" target="_blank">cri-o</a><br>
<a href="https://hub.docker.com/" target="_blank">DockerHub</a><br>
<a href="https://quay.io/" target="_blank">Quay.io</a><br>
<a href="https://opencontainers.org/" target="_blank">Open Container Initiative</a><br>
<a href="https://www.docker.com/" target="_blank">Docker</a><br>
<a href="https://podman.io/" target="_blank">Podman</a><br>
<a href="https://github.com/containers/skopeo" target="_blank">Skopeo</a><br>
<a href="https://buildah.io/" target="_blank">Buildah</a></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
