{"id":12796,"date":"2021-03-12T09:46:45","date_gmt":"2021-03-12T08:46:45","guid":{"rendered":"https:\/\/blog.mi.hdm-stuttgart.de\/?p=12796"},"modified":"2023-08-06T21:41:37","modified_gmt":"2023-08-06T19:41:37","slug":"kiss-dry-n-solid-yet-another-kubernetes-system-built-with-ansible-and-observed-with-metrics-server-on-arm64","status":"publish","type":"post","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/03\/12\/kiss-dry-n-solid-yet-another-kubernetes-system-built-with-ansible-and-observed-with-metrics-server-on-arm64\/","title":{"rendered":"KISS, DRY \u2018n SOLID \u2014 Yet another Kubernetes System built with Ansible and observed with Metrics Server on arm64"},"content":{"rendered":"\n<p>This blog post shows how a plain Kubernetes cluster is automatically created and configured on three arm64 devices using an orchestration tool called Ansible. The main focus relies on Ansible; other components that set up and configure the cluster are Docker, Kubernetes, Helm, NGINX, Metrics Server and Kubernetes Dashboard. Individual steps are covered more or less; the whole procedure follows three principles:<\/p>\n\n\n\n<!--more-->\n\n\n\n<ul class=\"wp-block-list\" id=\"block-9398c86b-119d-4fa1-9d92-4aa39c23dd8d\">\n<li>Keep it simple and stupid<\/li>\n\n\n\n<li>Don\u2019t repeat yourself<\/li>\n\n\n\n<li>S.O.L.I.D.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Hardware Components<\/h2>\n\n\n\n<p>For this tutorial-oriented blog post, a Raspberry Pi 3 is used as a configuration, aka. bastion host and three NVIDIA Jetson Nano for the Kubernetes cluster. They all run around 5 Watt, which is very nice.<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Jetson_Nano.jpg\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"12994\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/03\/12\/kiss-dry-n-solid-yet-another-kubernetes-system-built-with-ansible-and-observed-with-metrics-server-on-arm64\/jetson_nano\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Jetson_Nano.jpg\" data-orig-size=\"1200,979\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Jetson_Nano\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Jetson_Nano-1024x835.jpg\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Jetson_Nano-1024x835.jpg\" alt=\"\" class=\"wp-image-12994\" width=\"268\" height=\"217\"><\/a><figcaption style=\"font-size: 0.7em\">Source: <a href=\"https:\/\/developer.nvidia.com\/sites\/default\/files\/akamai\/embedded\/images\/jetsonNano\/JetsonNano-DevKit_Front-Top_Right_trimmed.jpg\">https:\/\/developer.nvidia.com\/sites\/default\/files\/akamai\/embedded\/images\/jetsonNano\/JetsonNano-DevKit_Front-Top_Right_trimmed.jpg<\/a><\/figcaption><\/figure>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>The Raspberry Pi 3 has the eponymous Raspberry Pi OS Lite (2021-01-11) installed.[1] Since the official NVIDIA JetPack, an Ubuntu 18.04 image for the Jetson Nano, has an extracted size of over 14 GB, which contains various unnecessary packages like the window manager or office applications that consume important resources, a custom minified Ubuntu 20.04 LTS was built from scratch.[2] Attention: make sure that the kernel modules <code class=\"\" data-line=\"\">ip_set<\/code> and netfilters <code class=\"\" data-line=\"\">xt_set<\/code> are loaded! If this is not possible directly via <code class=\"\" data-line=\"\">modprobe<\/code>, the kernel and the corresponding modules must be rebuilt from source.[3] These modules will be needed later for the Kubernetes Pod network.<\/p>\n<\/div>\n\n\n\n<figure class=\"wp-block-table\"><table style=\"padding: 7px;font-size: 0.7em\"><thead><tr><th>Hardware<\/th><th>Arch<\/th><th>Memory<\/th><th>Name<\/th><th>IP<\/th><\/tr><\/thead><tbody><tr><td>Raspberry Pi (bastion)<\/td><td>arm32<\/td><td>1 GB<\/td><td>uls42b<\/td><td>10.0.100.42<\/td><\/tr><tr><td>Jetson Nano (master)<\/td><td>aarch64<\/td><td>4 GB<\/td><td>uls10m<\/td><td>10.0.100.10<\/td><\/tr><tr><td>Jetson Nano (worker1)<\/td><td>aarch64<\/td><td>4 GB<\/td><td>uls11w1<\/td><td>10.0.100.11<\/td><\/tr><tr><td>Jetson Nano (worker2)<\/td><td>aarch64<\/td><td>4 GB<\/td><td>uls12w2<\/td><td>10.0.100.12<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Installation<\/h2>\n\n\n\n<p>After the micro SD (secure digital) cards are written with the OS (operating system) images, special hardening settings are made, at least one SSH key is created to get access to the bastion device via SSH (secure shell). An Ansible script creates and deploy the other SSH keys for the Jetsons over CLI (command line interface).<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/sys_arch.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"472\" data-attachment-id=\"13002\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/03\/12\/kiss-dry-n-solid-yet-another-kubernetes-system-built-with-ansible-and-observed-with-metrics-server-on-arm64\/sys_arch\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/sys_arch.png\" data-orig-size=\"1485,684\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"sys_arch\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/sys_arch-1024x472.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/sys_arch-1024x472.png\" alt=\"\" class=\"wp-image-13002\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/sys_arch-1024x472.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/sys_arch-300x138.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/sys_arch-768x354.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/sys_arch.png 1485w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption style=\"font-size: 0.7em\">Hardware set up<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Requirements<\/h3>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>First, the bastion host has to be configured. Therefore, the Ubuntu package <code class=\"\" data-line=\"\">python3-pip<\/code> should already be included in the system. Python\u2019s <code class=\"\" data-line=\"\">pip<\/code> is needed to easily install the latest stable Ansible version from the PyPi package repository, as the official Raspberry Pi OS repositories are very outdated. The corresponding requirements file contains the following two lines to specify a specific version of Ansible and a linter, which are then executed and installed with the <code class=\"\" data-line=\"\">pip3 install --user --requirement requirements.txt<\/code> command:[4]<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"\" data-line=\"\">ansible==2.10.6\nansible-lint==5.0.2\n<\/code><\/pre>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Ansible<\/h3>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Ansible_logo.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"12992\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/03\/12\/kiss-dry-n-solid-yet-another-kubernetes-system-built-with-ansible-and-observed-with-metrics-server-on-arm64\/ansible_logo\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Ansible_logo.png\" data-orig-size=\"256,315\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Ansible_logo\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Ansible_logo.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Ansible_logo.png\" alt=\"\" class=\"wp-image-12992\" width=\"85\" height=\"105\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Ansible_logo.png 256w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Ansible_logo-244x300.png 244w\" sizes=\"auto, (max-width: 85px) 100vw, 85px\" \/><\/a><figcaption style=\"font-size: 0.7em\">Source: <a href=\"https:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/2\/24\/Ansible_logo.svg\/256px-Ansible_logo.svg.png\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/2\/24\/Ansible_logo.svg\/256px-Ansible_logo.svg.png<\/a><\/figcaption><\/figure>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-bash\" data-line=\"\">ansible            \tansible-doc         \tansible-playbook\nansible-config      \tansible-galaxy      \tansible-pull\nansible-connection  \tansible-inventory   \tansible-test\nansible-console     \tansible-lint        \tansible-vault\n<\/code><\/pre>\n<\/div>\n\n\n\n<p>Yeah, the first step is done; Ansible is up and running on the Raspberry Pi. But what is Ansible about, and what can I do with it? Ansible at the top is a project from RedHat. With Ansible, it is possible to provide repeatable playbooks to automate infrastructure, applications, networks, or even containers on multiple machines. The main benefit of Ansible is the <em>push<\/em> delivery mechanism, besides tools like Puppet, Chef or SaltStack. It is no longer needed to install the provisioning software and the agent, on the remote hosts. Only simple CLI based administration applications around Ansible are installed on Bastion or on your local machine, which connects to the cluster via SSH. The decentralized approach used by Ansible reduces maintenance efforts and costs. Ansible sets those remote hosts in an inventory list; a project specific configuration file can be created, and the specific roles perform through playbooks. A Role is subdivided into tasks, handlers, environments, templates, or also tests. Readymade roles could be found in a Hub called Ansible Galaxy[5].&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Orchestration_overview.png\"><img loading=\"lazy\" decoding=\"async\" width=\"651\" height=\"333\" data-attachment-id=\"12996\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/03\/12\/kiss-dry-n-solid-yet-another-kubernetes-system-built-with-ansible-and-observed-with-metrics-server-on-arm64\/orchestration_overview\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Orchestration_overview.png\" data-orig-size=\"651,333\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Orchestration_overview\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Orchestration_overview.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Orchestration_overview.png\" alt=\"\" class=\"wp-image-12996\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Orchestration_overview.png 651w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Orchestration_overview-300x153.png 300w\" sizes=\"auto, (max-width: 651px) 100vw, 651px\" \/><\/a><figcaption style=\"font-size: 0.7em\">Source: <a href=\"https:\/\/medium.com\/formcept\/configuration-management-and-continuous-deployment-cd0892dce998\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/medium.com\/formcept\/configuration-management-and-continuous-deployment-cd0892dce998<\/a><\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Environment<\/h4>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>It&#8217;s exhausting to do authentication in a remote login shell every time manually, so automate this by adjusting and configuring some settings. Of course, it is possible to set up the local SSH agent to avoid retyping the SSH keys secret: <code class=\"\" data-line=\"\">ssh-agent bash &amp;&amp; ssh-add ~\/.ssh\/id_rsa<\/code>. But for specific use cases, this could lead to problems; that&#8217;s why we use here Ansible for key management. For this, the global configuration file <code class=\"\" data-line=\"\">\/etc\/ansible\/ansible.cfg<\/code>, or a project-specific file <code class=\"\" data-line=\"\">ansible.cfg<\/code> is created in the project&#8217;s working directory (for most of us, a Git repository). This file contains default parameters to clarify the pipelining with the connection to the inventory cluster. In this example, the content of the file looks like this: [6]<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"\" data-line=\"\">[defaults]\n...\ninventory = inventory_uls\nroles_path = roles\nvault_identity_list = default@~\/.ssh\/.vault_pass_uls\nremote_port = 42424\ndisplay_skipped_hosts = False\nstdout_callback = skippy\nlog_path = logs\/ansible.log\n...\n[privilege_escalation]\nbecome = True\nbecome_method = sudo\nbecome_user = root\n...\n[inventory]\nunparsed_is_failed = True\n...\n[ssh_connection]\ntimeout = 23\npipelining = True\nssh_args = -o ControlMaster=auto -o ControlPersist=230s\n...\n<\/code><\/pre>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>Besides the default settings for the Ansible CLI, infrastructure Details need to be configured in inventory files. The variable <code class=\"\" data-line=\"\">inventory<\/code> defines the path to the hosts and groups and related variables containing encrypted files such as SSH access. Sensitive data should not be stored in clear text; for this, Ansible provides the tool <code class=\"\" data-line=\"\">ansible-vault<\/code>. With this tool, you can crypt, view, or edit the credentials or other files.[7] Following file should be kept private! The variable <code class=\"\" data-line=\"\">vault_identity_list = default@~\/.ssh\/.vault_pass_uls<\/code> contains the global password to decrypt secrets via <code class=\"\" data-line=\"\">ansible-vault<\/code>. Ansible can set other interesting global variables in the <code class=\"\" data-line=\"\">privilege_escalation<\/code> section, where we can see that the commands are executed as superuser.<\/p>\n<\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Hosts<\/h4>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>For now, the common login concept is settled up, but where are the machines we want to connect to. Basically, they are stored in the inventory list, called <code class=\"\" data-line=\"\">hosts<\/code>. The group and hostnames should be different; otherwise, Ansible would not differentiate it. The inventory list of the servers looks like this: [8]<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-bash\" data-line=\"\">[master]\nuls10m\n\n[worker]\nuls11w1\nuls12w2\n\n[cluster:children]\nmaster\nworker\n<\/code><\/pre>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>As you can see, the upcoming Kubernetes cluster consists of three nodes which are divided into a master node and two worker nodes. Below that, you can see yet another grouping of all or various possibilities. Mapping to the specific IP <code class=\"\" data-line=\"\">ansible_host: 10.0.100.10<\/code> is defined in a separate file <code class=\"\" data-line=\"\">host_vars\/uls10m<\/code>. To check these hosts, Ansible offers another tool to list the available machines <code class=\"\" data-line=\"\">ansible-inventory --list -y<\/code>:<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-bash\" data-line=\"\">all:\n  children:\n    cluster:\n      children:\n        master:\n          hosts:\n            uls10m:\n              ansible_host: 10.0.100.10\n              ansible_python_interpreter: \/usr\/bin\/python3\n              ansible_ssh_private_key_file: \/home\/config\/.ssh\/id_ed25519\n              ansible_ssh_user: jetson\n              ansible_sudo_pass: nano\n        worker:\n          hosts:\n            uls11w1:\n              ansible_host: 10.0.100.11\n              ansible_python_interpreter: \/usr\/bin\/python3\n              ansible_ssh_private_key_file: \/home\/config\/.ssh\/id_ed25519\n              ansible_ssh_user: jetson\n              ansible_sudo_pass: nano\n            uls12w2:\n              ansible_host: 10.0.100.12\n              ansible_python_interpreter: \/usr\/bin\/python3\n              ansible_ssh_private_key_file: \/home\/config\/.ssh\/id_ed25519\n              ansible_ssh_user: jetson\n              ansible_sudo_pass: nano\n    ungrouped: {} \n<\/code><\/pre>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>OMG, did you see my SSH user and the password \ud83d\ude09 At this point, I would like to mark that the use of Ansible Vault is quite useful. But be careful, \u201cEncryption with Ansible Vault ONLY protects &#8216;data at rest&#8217;. Once the content is decrypted (&#8216;data in use&#8217;), play and plugin authors are responsible for avoiding any secret disclosure\u201d.[9] However, security concepts are not discussed in this post. To check the communication via Ansible just for the worker, the command <code class=\"\" data-line=\"\">ansible worker -m ping -u jetson<\/code> can be used:<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-bash\" data-line=\"\">uls11w1 | SUCCESS =&gt; {\n    &quot;changed&quot;: false,\n    &quot;ping&quot;: &quot;pong&quot;\n}\nuls12w2 | SUCCESS =&gt; {\n    &quot;changed&quot;: false,\n    &quot;ping&quot;: &quot;pong&quot;\n}\n<\/code><\/pre>\n<\/div>\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>What is common for Ansible, are return values like the <code class=\"\" data-line=\"\">changed<\/code>, <code class=\"\" data-line=\"\">failed<\/code> or <code class=\"\" data-line=\"\">stdout<\/code> attribute in the output console; this shows if a command has already been executed.[10] Now that the communication between Ansible and the server is working, we can start with specific playbook roles.<\/p>\n<\/div>\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:100%\">\n<h4 class=\"wp-block-heading\">Roles &amp; Playbook<\/h4>\n\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>\u201cRoles are units of organization in Ansible. Assigning a role to a group of hosts (or a set of groups, or host patterns, and so on) implies that they should implement a specific behavior. A role may include applying certain variable values, certain tasks, and certain handlers \u2013 or just one or more of these things. Because of the file structure associated with a role, roles become redistributable units that allow you to share behavior among playbooks \u2013 or even with other users.\u201d[11] What has already been mentioned is that roles have a specific structure. Ansible can easily initialize this structure with the following command: <code class=\"\" data-line=\"\">ansibile-galaxy init kubernetes<\/code>.<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-bash\" data-line=\"\">..\/roles\/kubernetes\/\n\u251c\u2500\u2500 defaults\n\u2502   \u2514\u2500\u2500 main.yml\n\u251c\u2500\u2500 handlers\n\u2502   \u2514\u2500\u2500 main.yml\n\u251c\u2500\u2500 meta\n\u2502   \u2514\u2500\u2500 main.yml\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 tasks&lt;span style=&quot;color: silver&quot;&gt;\n\u2502   \u251c\u2500\u2500 config-helm.yml\n\u2502   \u251c\u2500\u2500 config-kubernetes-dashboard.yml\n\u2502   \u251c\u2500\u2500 config-master-create-token.yml\n\u2502   \u251c\u2500\u2500 config-master.yml\n\u2502   \u251c\u2500\u2500 config-metrics-server.yml\n\u2502   \u251c\u2500\u2500 config-nginx.yml\n\u2502   \u251c\u2500\u2500 config-worker.yml\n\u2502   \u251c\u2500\u2500 install.yml&lt;\/span&gt;\n\u2502   \u251c\u2500\u2500 main.yml&lt;span style=&quot;color: silver&quot;&gt;\n\u2502   \u2514\u2500\u2500 uninstall.yml&lt;\/span&gt;\n\u251c\u2500\u2500 templates&lt;span style=&quot;color: silver&quot;&gt;\n\u2502   \u251c\u2500\u2500 kubernetes-dashboard-service-nodeport.yml.j2\n\u2502   \u251c\u2500\u2500 kubernetes-dashboard.yml.j2\n\u2502   \u251c\u2500\u2500 metrics-server.yml.j2\n\u2502   \u251c\u2500\u2500 nginx_values.yml.j2&lt;\/span&gt;\n\u251c\u2500\u2500 tests\n\u2502   \u251c\u2500\u2500 inventory\n\u2502   \u2514\u2500\u2500 test.yml\n\u2514\u2500\u2500 vars\n    \u2514\u2500\u2500 main.yml\n<\/code><\/pre>\n<\/div>\n\n\n\n<ul class=\"wp-block-list\">\n<li>default \u2013 contain variables for the role with the lowest priority; higher variables can overwrite them without problems<\/li>\n\n\n\n<li>handlers \u2013 handle inside or outside the role when, for example, a service needs to be restarted<\/li>\n\n\n\n<li>meta \u2013 just basic meta data<\/li>\n\n\n\n<li>tasks \u2013 the main tasks of the role are defined here. As we can see in the tree structure above, there are already a few additional tasks added besides <code class=\"\" data-line=\"\">`main.yml`<\/code><\/li>\n\n\n\n<li>templates \u2013 are also available to distribute predefined files from local to remotes<\/li>\n\n\n\n<li>tests \u2013 are necessary to check with Ansible Molecule if this role works properly<\/li>\n\n\n\n<li>vars \u2013 are also variables but used for storing information like connection strings, which can be encrypted also [12]<\/li>\n<\/ul>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>There\u2019s no magic; with the init command, all files are empty \ud83d\ude09 Let\u2019s fill them with some tasks. For this purpose, Ansible uses Yet Another Modeling Language (YAML) Syntax to describe; the <code class=\"\" data-line=\"\">task\/main.yml<\/code> is mainly used to install or uninstall Kubernetes:<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-yaml\" data-line=\"\">---\n# role handling for special inventory\n- fail:\n    msg: &quot;Please provide role_action=[install|uninstall|config-master|config-worker|config-nginx|config-metrics-server|config-kubernetes-dashboard]&quot;\n  when: role_action is not defined\n\n# tasks file to install kubernetes\n- include: install.yml\n  when: role_action == &#039;install&#039;\n\n# tasks to configure master\n- include: config-master.yml\n  when: role_action in [&#039;install&#039;, &#039;config-master&#039;] and ansible_hostname in groups[&#039;master&#039;]\n\n# tasks to add\/configure worker\n- include: config-worker.yml\n  when: role_action in [&#039;install&#039;, &#039;config-worker&#039;] and ansible_hostname in groups[&#039;worker&#039;]\n\n- include: config-nginx.yml\n  when: role_action in [&#039;install&#039;, &#039;config-master&#039;, &#039;config-nginx&#039;] and ansible_hostname in groups[&#039;master&#039;]\n\n# tasks to add\/configure metrics incl dashboard\n- include: config-metrics-server.yml\n  when: role_action in [&#039;install&#039;, &#039;config-master&#039;, &#039;config-metrics-server&#039;] and ansible_hostname in groups[&#039;master&#039;]\n\n- include: config-kubernetes-dashboard.yml\n  when: role_action in [&#039;install&#039;, &#039;config-master&#039;, &#039;config-kubernetes-dashboard&#039;] and ansible_hostname in groups[&#039;master&#039;]\n\n# uninstall\n- include: uninstall.yml\n  when: role_action == &#039;uninstall&#039;\n<\/code><\/pre>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>The configuration goes along with the installation process, but could be also triggered by using the <code class=\"\" data-line=\"\">role_action<\/code> config option. The conditional is used to reduce the number of roles, to differ between master and worker, to keep the file structure simple and clean, and to keep the option to execute individual configuration files anyway. [13]<\/p>\n<\/div>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Kubernetes_logo.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"12995\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/03\/12\/kiss-dry-n-solid-yet-another-kubernetes-system-built-with-ansible-and-observed-with-metrics-server-on-arm64\/kubernetes_logo\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Kubernetes_logo.png\" data-orig-size=\"512,512\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Kubernetes_logo\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Kubernetes_logo.png\" class=\"wp-image-12995\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Kubernetes_logo.png\" alt=\"\" width=\"76\" height=\"76\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Kubernetes_logo.png 512w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Kubernetes_logo-300x300.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Kubernetes_logo-150x150.png 150w\" sizes=\"auto, (max-width: 76px) 100vw, 76px\" \/><\/a><figcaption style=\"font-size: 0.7em\">Source: <a href=\"https:\/\/kubernetes.io\/images\/favicon.png\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/kubernetes.io\/images\/favicon.png<\/a><\/figcaption><\/figure>\n\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>What Kubernetes is and what it does is not covered here. <a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/02\/27\/migrating-from-heroku-to-hetzner-achieving-scalability-with-docker-kubernetes-and-rancher\/\" target=\"_blank\" rel=\"noopener\">Click here<\/a>, to get more information about Kubernetes. Furthermore, to focus more on Ansible, individual scripts such as Docker installation are shortened or partially omitted. To install Kubernetes via Ansible on the cluster, we first create a new file called <code class=\"\" data-line=\"\">task\/install.yml<\/code> with the following procedure:<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-yaml\" data-line=\"\">---\n- name: get system architecture\n  shell: dpkg --print-architecture\n  register: sys_architecture\n\n- name: check if Kubernetes already exist\n  stat:\n    path: \/usr\/bin\/kubeadm\n  register: package_exist\n\n- name: remove swapfile from \/etc\/fstab\n  mount:\n    name: &quot;{{ item }}&quot;\n    fstype: swap\n    state: absent\n  with_items:\n    - swap\n    - none\n\n- name: disable swap\n  command: swapoff -a\n  when: ansible_swaptotal_mb &gt; 0\n\n- name: set kernelmodule for bridge interface\n  command: &quot;{{ item }}&quot;\n  with_items:\n    - modprobe bridge\n    - modprobe br_netfilter\n  when: not package_exist.stat.exists\n\n- name: add kernelparameter for bridge interface\n  sysctl:\n    name: &quot;{{ item.name }}&quot;\n    value: &quot;{{ item.value }}&quot;\n    state: present\n  with_items:\n    - { name: &#039;vm.swappiness&#039;, value: &#039;0&#039; }\n    - { name: &#039;net.bridge.bridge-nf-call-iptables&#039;, value: &#039;1&#039; }\n  when: not package_exist.stat.exists\n\n- name: add apt Kubernetes signing key for Ubuntu\n  apt_key:\n    url: https:\/\/packages.cloud.google.com\/apt\/doc\/apt-key.gpg\n    state: present\n  when: not package_exist.stat.exists\n\n- name: add Kubernetes repository\n  apt_repository:\n    repo: deb [arch={{ sys_architecture.stdout }}] https:\/\/packages.cloud.google.com\/apt kubernetes-xenial main\n    state: present\n    filename: kubernetes\n  when: not package_exist.stat.exists\n\n- name: install Kubernetes binaries\n  apt: \n    name: &quot;{{ packages }}&quot;\n    state: present\n    update_cache: yes\n  vars:\n    packages:\n      - kubelet \n      - kubeadm \n      - kubectl\n  when: not package_exist.stat.exists\n...\n<\/code><\/pre>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>Ansible has its own modules[14] ranging from packaging over network configurations to third party hooks. First of all, it&#8217;s important to know what kind of system architecture is used to install Kubernetes. To avoid a reinstallation, a query will check if Kubernetes is already installed. After that, some tasks are defined that are necessary for Kubernetes, e.g., disable the paging memory (swap). After the pre-required tasks, more tasks like adding the repository, install Kubernetes and restart the service daemon is defined. Ansible uses YAML, and YAML uses the mustache syntax <code class=\"\" data-line=\"\">{{ var }}<\/code> for the data binding, so the variables in the curly braces are placeholders. [15]<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>Alrighty, just a few lines to install Kubernetes on multiple Ubuntu instances. It\u2019s also possible to refactor this to support different Linux derivatives. The other file, <code class=\"\" data-line=\"\">task\/uninstall.yml<\/code>, works vice versa. An excerpt to uninstall Kubernetes could look like this:<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-yaml\" data-line=\"\">...\n- name: force reset kubernetes cluster\n  command: &quot;kubeadm reset -f&quot;\n...\n- name: remove Kubernetes binaries\n  apt: \n    name: &quot;{{ packages }}&quot;\n    state: absent\n    update_cache: yes\n  vars:\n    packages:\n      - kubelet \n      - kubeadm \n      - kubectl\n  when: not package_exist.stat.exists\n...\n<\/code><\/pre>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>In the first block, we see that the <code class=\"\" data-line=\"\">kubeadm<\/code> command is executed. This causes the services to be stopped gracefully and further that symlinks, directories and config files are removed. The second block finally removes with <code class=\"\" data-line=\"\">state: absent<\/code> all packages related to the APT (Advanced Package Tool) repository.<\/p>\n<\/div>\n\n\n\n<p>Now the time has come to create the playbook. What has already been mentioned is that playbooks are given different roles. Playbooks, therefore, describe the course of a process for a specific area of the inventory. \u201cA playbook is a list of plays. A play is minimally a mapping between a set of hosts selected by a host specifier (usually chosen by groups but sometimes by hostname globs) and the tasks which run on those hosts to define the role that those systems will perform. There can be one or many plays in a playbook.\u201d[16] In this guide there\u2019s a playbook with two roles:&nbsp;<\/p>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-yaml\" data-line=\"\">- hosts: cluster\n  roles:\n  - docker\n  tags:\n  - docker\n\n- hosts: cluster\n  roles:\n  - kubernetes\n  tags:\n  - kubernetes\n<\/code><\/pre>\n<\/div>\n\n\n\n<p>After installing the roles for Docker and Kubernetes, the following tools are available on the inventory devices.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>docker \u2013 the <em>runC<\/em> and <em>containerd<\/em> runtime[17]&nbsp;<\/li>\n\n\n\n<li>kubeadm \u2013 <em>initialize<\/em> and <em>join<\/em> components[18]&nbsp;<\/li>\n\n\n\n<li>kubelet \u2013 a <em>node agent<\/em> to manage pods[19]&nbsp;<\/li>\n\n\n\n<li>kubectl \u2013 <em>configure<\/em> and <em>control<\/em> cluster[20]<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Configuration<\/h4>\n\n\n\n<p>Okidoki, the required toolset, is now available. The next step is to configure the master node as the Kubernetes control plane and join the worker nodes to it. The scaling of nodes, such as the control plane, is a different issue, as there are other requirements to be considered for the infrastructure used here.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">Master<\/h5>\n\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>We start with the latest version at this point (v.3.5.2) of the Helm installation on the master, set up the Kubernetes directory, install Calico, a pod networking plugin, and create a token to connect the worker to the master. So, the configuration file <code class=\"\" data-line=\"\">tasks\/config-master.yml<\/code> looks like this:<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-yaml\" data-line=\"\">---\n- name: include config file to install Helm on master\n  include_tasks: config-helm.yml\n\n- name: initialize the Kubernetes cluster using kubeadm\n  command: kubeadm init --apiserver-advertise-address={{ kube_api_addr }} --pod-network-cidr={{ kube_pod_cidr }} --token &quot;{{ kube_token }}&quot; --token-ttl &quot;{{ kube_token_ttl }}&quot;\n\n- name: setup kubeconfig directory\n  command: &quot;{{ item }}&quot;\n  with_items:\n   - mkdir -p ~\/.kube\n   - ln -s \/etc\/kubernetes\/admin.conf ~\/.kube\/config\n\n- name: generate Kubernetes token\n  command: kubeadm token create\n  register: &quot;{{ kube_token }}&quot;\n\n...\n\n- name: set kernelmodule for pod network \n  command: &quot;{{ item }}&quot;\n  with_items:\n    - modprobe ip_set\n    - modprobe xt_set\n  when: not package_exist.stat.exists\n\n- name: install Calico pod network\n  command: kubectl create -f https:\/\/docs.projectcalico.org\/archive\/v3.18\/manifests\/calico.yaml\n<\/code><\/pre>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>At the top, the Kubernetes control plane will be initialized with some basic options. Kubernetes executes several tasks in the background, such as a pre-flight check, generating a self-signed CA or creating an <code class=\"\" data-line=\"\">admin.conf<\/code> file, et cetera. [21]<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-bash\" data-line=\"\">---\nrole_action: install\nkube_api_addr: 10.0.100.10\nkube_pod_cidr: 172.16.0.0\/16\nkube_token: &quot;dieswi.rdueb3r5chr1eben&quot;\nkube_token_ttl: &quot;1h2m3s&quot;\n<\/code><\/pre>\n<\/div>\n\n\n\n<p>As we can see, there&#8217;s the IP to advertise the master to the Kubernetes API server, the Pod-CIDR space for allocating the cluster Pod&#8217;s, the token for bootstrapping new clusters[22], and it&#8217;s time to live (TTL) value.<\/p>\n\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>Back to the Kubernetes configuration, where the next step is to create a folder in the home directory with a symlink to the previously created <code class=\"\" data-line=\"\">admin.conf<\/code> file. After the operation, the Kubernetes token is generated and held temporarily in a variable, as tokens are constantly regenerated. The last step is to install Calico. This is necessary so that, for instance, individual pods are assigned to an address to provide or exchange data over the internal decoupled network, managed via Container Network Interface (CNI).[23] Wait for a second, is there a special reason to use Calico instead of another pod network like Flannel? It depends on the specific purpose. In this case, it should cost nothing, the performance should be right, the resources consumption should be low, and it should also offer security features!<\/p>\n<\/div>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/CNI_overview.png\"><img loading=\"lazy\" decoding=\"async\" width=\"651\" height=\"176\" data-attachment-id=\"12993\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/03\/12\/kiss-dry-n-solid-yet-another-kubernetes-system-built-with-ansible-and-observed-with-metrics-server-on-arm64\/cni_overview\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/CNI_overview.png\" data-orig-size=\"651,176\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"CNI_overview\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/CNI_overview.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/CNI_overview.png\" alt=\"\" class=\"wp-image-12993\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/CNI_overview.png 651w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/CNI_overview-300x81.png 300w\" sizes=\"auto, (max-width: 651px) 100vw, 651px\" \/><\/a><figcaption style=\"font-size: 0.7em\">Source :&nbsp; <a href=\"https:\/\/itnext.io\/benchmark-results-of-kubernetes-network-plugins-cni-over-10gbit-s-network-updated-august-2020-6e1b757b9e49\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/itnext.io\/benchmark-results-of-kubernetes-network-plugins-cni-over-10gbit-s-network-updated-august-2020-6e1b757b9e49<\/a><\/figcaption><\/figure>\n\n\n\n<p>Helm, a package manager, is used to installing Kubernetes applications. If you want to read more about helm I recommend this article (<a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2020\/02\/29\/image-editor-on-kubernetes-with-kompose-minikube-k3s-k3sup-and-helm-part-2\/\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2020\/02\/29\/image-editor-on-kubernetes-with-kompose-minikube-k3s-k3sup-and-helm-part-2\/\">click here<\/a>).<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/helm-horizontal-color.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"13004\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/03\/12\/kiss-dry-n-solid-yet-another-kubernetes-system-built-with-ansible-and-observed-with-metrics-server-on-arm64\/helm-horizontal-color\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/helm-horizontal-color.png\" data-orig-size=\"304,351\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"helm-horizontal-color\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/helm-horizontal-color.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/helm-horizontal-color.png\" alt=\"\" class=\"wp-image-13004\" width=\"92\" height=\"108\"><\/a><figcaption style=\"font-size: 0.7em\">Source: <a href=\"https:\/\/cncf-branding.netlify.app\/img\/projects\/helm\/horizontal\/color\/helm-horizontal-color.png\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/cncf-branding.netlify.app\/img\/projects\/helm\/horizontal\/color\/helm-horizontal-color.png<\/a><\/figcaption><\/figure>\n\n\n\n<p>With the installation of Helm, an Ansible task adds the Bitnami repository, which contains a great amount of helmcharts to install the latest version of NGINX. In this tutorial-oriented blog post, NGINX is used just as a Kubernetes default application to present an automatically deployed web application that serves a simple static hello web page. Of course, this procedure can be extended to scale. But it is not a topic currently.<\/p>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-yaml\" data-line=\"\">...\n- name: add helm repository\n  apt_repository:\n    repo: &quot;deb [arch={{ sys_architecture.stdout }}] https:\/\/baltocdn.com\/helm\/stable\/debian\/ all main&quot;\n    state: present\n    filename: helm\n  when: not package_exist.stat.exists\n...\n- name: add bitnami helm repository\n  command: helm repo add bitnami https:\/\/charts.bitnami.com\/bitnami\n...\n<\/code><\/pre>\n<\/div>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/08\/icon_medium.png\" alt=\"\" width=\"97\" height=\"97\"><figcaption style=\"font-size: 0.7em\">Source: <a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/08\/icon_medium.png\">https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/08\/icon_medium.png<\/a><\/figcaption><\/figure>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-yaml\" data-line=\"\">...\n- name: create nginx config\n  template:\n    src: templates\/nginx_values.yml.j2\n    dest: &quot;~\/nginx_values.yml&quot;\n    owner: root\n    group: root\n    mode: 0644\n...\n- name: install nginx\n  command: helm install nginx bitnami\/nginx --values nginx_values.yml\n...\n<\/code><\/pre>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>An excerpt of the <code class=\"\" data-line=\"\">nginx_values.yml<\/code> looks like this:<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-yaml\" data-line=\"\">image:\n  registry: docker.io&lt;b&gt;\n  repository: arm64v8\/nginx &lt;\/b&gt;\n  tag: 1.19.7\n  pullPolicy: IfNotPresent\n  pullSecrets: []\n  debug: false\n\u2026\nservice:\n  type: NodePort\n  port: 80\n  httpsPort: 443\n  nodePorts: &lt;b&gt;\n    http: &quot;30080&quot;&lt;\/b&gt;\n    https: &quot;&quot;\n  targetPort:\n    http: http\n    https: https\n  annotations: {}\n  externalTrafficPolicy: Cluster\n\u2026\n<\/code><\/pre>\n<\/div>\n\n\n\n<h5 class=\"wp-block-heading\">Worker<\/h5>\n\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>To authenticate the worker with the Kubernetes control plane, the command <code class=\"\" data-line=\"\">kubeadm join<\/code> is used in combination with some options and variables:<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-yaml\" data-line=\"\">- name: join worker &quot;{{ ansible_hostname }}&quot; to cluster\n  shell: |\n    kubeadm join --token &quot;{{ kube_token }}&quot; \\\n                 --discovery-token-unsafe-skip-ca-verification \\\n                 &quot;{{ kube_api_addr }}&quot;:6443\n<\/code><\/pre>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Observer \/ Monitor<\/h3>\n\n\n\n<p>The Ansible kubernetes role is now expanded a bit to include Metrics Server for Kubernetes and the Kubernetes Dashboard. Metrics Server is a lightweight tool that gives additional information about what is happening behind Kubernetes magic processes.[24]<\/p>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-yaml\" data-line=\"\">- name: create metrics-server config\n  template:\n    src: templates\/metrics-server.yml.j2\n    dest: &quot;~\/metrics-server.yml&quot;\n    owner: root\n    group: root\n    mode: 0644\n\n- name: install metrics-server\n  command: kubectl apply -f ~\/metrics-server.yml\n<\/code><\/pre>\n<\/div>\n\n\n\n<p>Through the CLI, it is now possible to get additional values about the status and resource consumption from the nodes or a specific pod:<\/p>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-bash\" data-line=\"\">$ kubectl top pod --namespace default\nNAME                     \t\tCPU(cores)   \tMEMORY(bytes)   \nnginx-657f9bdb8b-6cm55   \t1m           \t2Mi\n<\/code><\/pre>\n<\/div>\n\n\n\n<p>That\u2019s not enough. For the fanciness, the next configuration will cover the installation of a web-based scraper. In this guide, it\u2019s the Kubernetes Dashboard, but you may want to have a look at the <a href=\"https:\/\/prometheus.io\/\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/prometheus.io\/\">Prometheus<\/a> monitoring solution, also which has more latency in providing current pod status because of the events harvesting and log aggregation used and stored in a separate influx database. Metrics Server, in turn, is blazing fast and stores intermediate data into its own buffer. But latency is not the scope of this blog post. It is more addressed to Kubernetes hyper scalers. Installing and configuring Kubernetes Dashboard with Ansible is essentially straightforward:<\/p>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><pre style=\"background: #f9f9f9;padding: 13px;font-size: 0.7em\"><code class=\"language-yaml\" data-line=\"\">---\n- name: create kubernetes-dashboard config\n  template:\n    src: templates\/kubernetes-dashboard.yml.j2\n    dest: &quot;~\/kubernetes-dashboard.yml&quot;\n    owner: root\n    group: root\n    mode: 0644\n\n- name: create kubernetes-dashboard config\n  template:\n    src: templates\/kubernetes-dashboard-service-nodeport.yml.j2\n    dest: &quot;~\/kubernetes-dashboard-service-nodeport.yml&quot;\n    owner: root\n    group: root\n    mode: 0644\n\n- name: install kubernetes-dashboard\n  command: kubectl apply -f ~\/kubernetes-dashboard.yml\n\n- name: install kubernetes-dashboard service nodeport\n  command: kubectl apply -f ~\/kubernetes-dashboard-service-nodeport.yml\n...\n<\/code><\/pre>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>For the Metrics Server and the Dashboard scraper, a new Ansible module <code class=\"\" data-line=\"\">template<\/code> is used. The benefit of this module is that a task could use a customized configuration file. Roundabout, the content of the template files are not of interest at this point.<\/p>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Provide<\/h3>\n\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>Nothing is installed and configured yet, so let&#8217;s provide it with: <code class=\"\" data-line=\"\">ansible-playbook -t kubernetes -e role_action=install uls-playbook.yml<\/code>. After a few minutes of pulling multiple applications and containers and making settings, we can see in the Ansible output that everything went well.<\/p>\n<\/div>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Ansible_output.gif\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"360\" data-attachment-id=\"12997\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/03\/12\/kiss-dry-n-solid-yet-another-kubernetes-system-built-with-ansible-and-observed-with-metrics-server-on-arm64\/ansible_output\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Ansible_output.gif\" data-orig-size=\"640,360\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Ansible_output\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Ansible_output.gif\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/Ansible_output.gif\" alt=\"\" class=\"wp-image-12997\"><\/a><figcaption style=\"font-size: 0.7em\">Ansible console output<\/figcaption><\/figure>\n\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>A small check directly on the worker with <code class=\"\" data-line=\"\">kubectl -n kube-system get pods<\/code> shows us that Calico is up and running and thus configured.<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\" style=\"padding: 7px;font-size: 0.7em\"><table>\n<thead>\n<tr>\n<th style=\"text-align:left\">NAME<\/th>\n<th style=\"text-align:left\">READY<\/th>\n<th style=\"text-align:left\">STATUS<\/th>\n<th style=\"text-align:left\">RESTARTS<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align:left\">calico-kube-controllers-hsc7q<\/td>\n<td style=\"text-align:left\">1\/1<\/td>\n<td style=\"text-align:left\">Running<\/td>\n<td style=\"text-align:left\">0<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left\">calico-node-5zk5h<\/td>\n<td style=\"text-align:left\">1\/1<\/td>\n<td style=\"text-align:left\">Running<\/td>\n<td style=\"text-align:left\">0<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left\">calico-node-88cvb<\/td>\n<td style=\"text-align:left\">1\/1<\/td>\n<td style=\"text-align:left\">Running<\/td>\n<td style=\"text-align:left\">0<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left\">calico-node-zz4kx<\/td>\n<td style=\"text-align:left\">1\/1<\/td>\n<td style=\"text-align:left\">Running<\/td>\n<td style=\"text-align:left\">0<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left\">coredns-74ff55c5b-qcfvn<\/td>\n<td style=\"text-align:left\">1\/1<\/td>\n<td style=\"text-align:left\">Running<\/td>\n<td style=\"text-align:left\">0<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left\">coredns-74ff55c5b-tgtxs<\/td>\n<td style=\"text-align:left\">1\/1<\/td>\n<td style=\"text-align:left\">Running<\/td>\n<td style=\"text-align:left\">0<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left\">etcd-uls17m<\/td>\n<td style=\"text-align:left\">1\/1<\/td>\n<td style=\"text-align:left\">Running<\/td>\n<td style=\"text-align:left\">0<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left\">kube-apiserver-uls10m<\/td>\n<td style=\"text-align:left\">1\/1<\/td>\n<td style=\"text-align:left\">Running<\/td>\n<td style=\"text-align:left\">0<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left\">kube-controller-manager-uls10m<\/td>\n<td style=\"text-align:left\">1\/1<\/td>\n<td style=\"text-align:left\">Running<\/td>\n<td style=\"text-align:left\">0<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left\">kube-proxy-95tgp<\/td>\n<td style=\"text-align:left\">1\/1<\/td>\n<td style=\"text-align:left\">Running<\/td>\n<td style=\"text-align:left\">0<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left\">kube-proxy-n4xht<\/td>\n<td style=\"text-align:left\">1\/1<\/td>\n<td style=\"text-align:left\">Running<\/td>\n<td style=\"text-align:left\">0<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left\">kube-proxy-s89bv<\/td>\n<td style=\"text-align:left\">1\/1<\/td>\n<td style=\"text-align:left\">Running<\/td>\n<td style=\"text-align:left\">0<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left\">kube-scheduler-uls10m<\/td>\n<td style=\"text-align:left\">1\/1<\/td>\n<td style=\"text-align:left\">Running<\/td>\n<td style=\"text-align:left\">0<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left\">metrics-server-6c65cfc5d5-wvw5p<\/td>\n<td style=\"text-align:left\">1\/1<\/td>\n<td style=\"text-align:left\">Running<\/td>\n<td style=\"text-align:left\">0<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\">\n<p>This also applies for <code class=\"\" data-line=\"\">kubectl get services<\/code>:<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\" style=\"padding: 7px;font-size: 0.7em\"><table>\n<thead>\n<tr>\n<th style=\"text-align:left\">NAME<\/th>\n<th style=\"text-align:left\">TYPE<\/th>\n<th style=\"text-align:left\">CLUSTER-IP<\/th>\n<th style=\"text-align:left\">EXTERNAL-IP<\/th>\n<th style=\"text-align:left\">PORT(S)<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align:left\">kubernetes<\/td>\n<td style=\"text-align:left\">ClusterIP<\/td>\n<td style=\"text-align:left\">10.96.0.1<\/td>\n<td style=\"text-align:left\">&lt;none&gt;<\/td>\n<td style=\"text-align:left\">443\/TCP<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left\">nginx<\/td>\n<td style=\"text-align:left\">NodePort<\/td>\n<td style=\"text-align:left\">10.104.37.148<\/td>\n<td style=\"text-align:left\">&lt;none&gt;<\/td>\n<td style=\"text-align:left\">80:30080\/TCP<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n\n\n\n<p>Fine, everything is up and running.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/nginx_welcome.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"13001\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/03\/12\/kiss-dry-n-solid-yet-another-kubernetes-system-built-with-ansible-and-observed-with-metrics-server-on-arm64\/nginx_welcome\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/nginx_welcome.png\" data-orig-size=\"611,308\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"nginx_welcome\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/nginx_welcome.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/nginx_welcome.png\" alt=\"\" class=\"wp-image-13001\" width=\"382\" height=\"192\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/nginx_welcome.png 611w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/nginx_welcome-300x151.png 300w\" sizes=\"auto, (max-width: 382px) 100vw, 382px\" \/><\/a><figcaption style=\"font-size: 0.7em\">NGINX welcome page<\/figcaption><\/figure>\n\n\n\n<p>The Kubernetes Dashboard shows us the status of the nodes, pods, services and of course the resources graphically.<\/p>\n\n\n\n<figure class=\"wp-block-gallery columns-3 is-cropped\"><ul class=\"blocks-gallery-grid\"><li class=\"blocks-gallery-item\"><figure><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_0.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" data-attachment-id=\"12998\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/03\/12\/kiss-dry-n-solid-yet-another-kubernetes-system-built-with-ansible-and-observed-with-metrics-server-on-arm64\/kube_dash_0\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_0.png\" data-orig-size=\"1366,768\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"kube_dash_0\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_0-1024x576.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_0-1024x576.png\" alt=\"\" data-id=\"12998\" data-full-url=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_0.png\" data-link=\"https:\/\/blog.mi.hdm-stuttgart.de\/?attachment_id=12998\" class=\"wp-image-12998\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_0-1024x576.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_0-300x169.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_0-768x432.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_0.png 1366w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" data-attachment-id=\"12999\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/03\/12\/kiss-dry-n-solid-yet-another-kubernetes-system-built-with-ansible-and-observed-with-metrics-server-on-arm64\/kube_dash_1\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_1.png\" data-orig-size=\"1366,768\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"kube_dash_1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_1-1024x576.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_1-1024x576.png\" alt=\"\" data-id=\"12999\" data-full-url=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_1.png\" data-link=\"https:\/\/blog.mi.hdm-stuttgart.de\/?attachment_id=12999\" class=\"wp-image-12999\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_1-1024x576.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_1-300x169.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_1-768x432.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_1.png 1366w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" data-attachment-id=\"13000\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/03\/12\/kiss-dry-n-solid-yet-another-kubernetes-system-built-with-ansible-and-observed-with-metrics-server-on-arm64\/kube_dash_2\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_2.png\" data-orig-size=\"1366,768\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"kube_dash_2\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_2-1024x576.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_2-1024x576.png\" alt=\"\" data-id=\"13000\" data-full-url=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_2.png\" data-link=\"https:\/\/blog.mi.hdm-stuttgart.de\/?attachment_id=13000\" class=\"wp-image-13000\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_2-1024x576.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_2-300x169.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_2-768x432.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/03\/kube_dash_2.png 1366w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure><\/li><\/ul><figcaption class=\"blocks-gallery-caption\" style=\"font-size: 0.7em\">Kubernetes Dashboard<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Upshot<\/h2>\n\n\n\n<p>This blog post shows what is possible with the orchestration tool Ansible; what is installed and configured with it is up to each individual. The Cloud Native Computing Foundation (CNCF) describes Ansible as \u201ca radically simple IT automation platform that makes your applications and systems easier to deploy and maintain. Automate everything from code deployment to network configuration to cloud management\u201d.[25] The strengths of Ansible for infrastructure automation and orchestration are simplicity, centralization, repeatability, configurability, and transparency through the inventory, playbooks, roles, and custom modules. Of course, this article is not intended to be, and cannot be, a comprehensive and complete account of Ansible. Much more can be said about each of the points that are mentioned here. But notice, keep it simple, don\u2019t repeat yourself and build a solid system.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Sources<\/h2>\n\n\n\n<p>All links were last accessed on 2021-03-10.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><a href=\"https:\/\/www.raspberrypi.org\/software\/operating-systems\/#raspberry-pi-os-32-bit\">https:\/\/www.raspberrypi.org\/software\/operating-systems\/#raspberry-pi-os-32-bit<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/developer.nvidia.com\/embedded\/jetpack\">https:\/\/developer.nvidia.com\/embedded\/jetpack<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/developer.nvidia.com\/embedded\/downloads#?search=kernel&amp;tx=$product,jetson_nano\">https:\/\/developer.nvidia.com\/embedded\/downloads#?search=kernel&amp;tx=$product,jetson_nano<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/installation_guide\/intro_installation.html#installing-ansible-with-pip\">https:\/\/docs.ansible.com\/ansible\/latest\/installation_guide\/intro_installation.html#installing-ansible-with-pip<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/ecchong\/cowsay_motd\">https:\/\/github.com\/ecchong\/cowsay_motd<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.ansible.com\/ansible\/2.4\/intro_configuration.html\">https:\/\/docs.ansible.com\/ansible\/2.4\/intro_configuration.html<\/a>&nbsp;<\/li>\n\n\n\n<li><a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/cli\/ansible-vault.html\">https:\/\/docs.ansible.com\/ansible\/latest\/cli\/ansible-vault.html<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/user_guide\/intro_inventory.html\">https:\/\/docs.ansible.com\/ansible\/latest\/user_guide\/intro_inventory.html<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/user_guide\/vault.html\">https:\/\/docs.ansible.com\/ansible\/latest\/user_guide\/vault.html<\/a>&nbsp;<\/li>\n\n\n\n<li><a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/reference_appendices\/common_return_values.html\">https:\/\/docs.ansible.com\/ansible\/latest\/reference_appendices\/common_return_values.html<\/a>&nbsp;<\/li>\n\n\n\n<li><a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/reference_appendices\/glossary.html#term-roles\">https:\/\/docs.ansible.com\/ansible\/latest\/reference_appendices\/glossary.html#term-roles<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/user_guide\/playbooks_reuse_roles.html\">https:\/\/docs.ansible.com\/ansible\/latest\/user_guide\/playbooks_reuse_roles.html<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/user_guide\/playbooks_conditionals.html\">https:\/\/docs.ansible.com\/ansible\/latest\/user_guide\/playbooks_conditionals.html<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.ansible.com\/ansible\/2.8\/modules\/list_of_all_modules.html\">https:\/\/docs.ansible.com\/ansible\/2.8\/modules\/list_of_all_modules.html<\/a>&nbsp;<\/li>\n\n\n\n<li><a href=\"https:\/\/kubernetes.io\/blog\/2019\/03\/15\/kubernetes-setup-using-ansible-and-vagrant\/\">https:\/\/kubernetes.io\/blog\/2019\/03\/15\/kubernetes-setup-using-ansible-and-vagrant\/<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/reference_appendices\/glossary.html#term-plays\">https:\/\/docs.ansible.com\/ansible\/latest\/reference_appendices\/glossary.html#term-plays<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.docker.com\/get-started\/overview\/\">https:\/\/docs.docker.com\/get-started\/overview\/<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/kubernetes.io\/docs\/reference\/setup-tools\/kubeadm\/\">https:\/\/kubernetes.io\/docs\/reference\/setup-tools\/kubeadm\/<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/kubernetes.io\/docs\/reference\/command-line-tools-reference\/kubelet\/\">https:\/\/kubernetes.io\/docs\/reference\/command-line-tools-reference\/kubelet\/<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/kubernetes.io\/docs\/reference\/kubectl\/overview\/\">https:\/\/kubernetes.io\/docs\/reference\/kubectl\/overview\/<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/kubernetes.io\/docs\/reference\/setup-tools\/kubeadm\/kubeadm-init\/\">https:\/\/kubernetes.io\/docs\/reference\/setup-tools\/kubeadm\/kubeadm-init\/<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/kubernetes.io\/docs\/reference\/access-authn-authz\/authentication\/\">https:\/\/kubernetes.io\/docs\/reference\/access-authn-authz\/authentication\/<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.projectcalico.org\/archive\/v3.18\/about\/about-calico\">https:\/\/docs.projectcalico.org\/archive\/v3.18\/about\/about-calico<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/kubernetes.io\/docs\/concepts\/cluster-administration\/system-metrics\/\">https:\/\/kubernetes.io\/docs\/concepts\/cluster-administration\/system-metrics\/<\/a>&nbsp;<\/li>\n\n\n\n<li><a href=\"https:\/\/landscape.cncf.io\/?selected=ansible\">https:\/\/landscape.cncf.io\/?selected=ansible<\/a> <\/li>\n<\/ol>\n\n\n\n<div class=\"wp-block-group is-layout-flow wp-block-group-is-layout-flow\"><\/div>\n<\/div>\n<\/div>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This blog post shows how a plain Kubernetes cluster is automatically created and configured on three arm64 devices using an orchestration tool called Ansible. The main focus relies on Ansible; other components that set up and configure the cluster are Docker, Kubernetes, Helm, NGINX, Metrics Server and Kubernetes Dashboard. Individual steps are covered more or [&hellip;]<\/p>\n","protected":false},"author":1001,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1,651,2,223],"tags":[456,459,73,154,460,4,458,457,48],"ppma_author":[818],"class_list":["post-12796","post","type-post","status-publish","format-standard","hentry","category-allgemein","category-system-designs","category-system-engineering","category-ultra-large-scale-systems","tag-ansible","tag-arm64","tag-automation","tag-kubernetes","tag-kubernetes-dashboard","tag-linux","tag-metrics-server","tag-orchestration","tag-scaling"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":6338,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2019\/03\/15\/kubernetesk8s-everywhere-but-how\/","url_meta":{"origin":12796,"position":0},"title":"Kubernetes (K8S) everywhere, but how?","author":"Immanuel Haag","date":"15. March 2019","format":false,"excerpt":"In the last months, nearly everybody has been talking about Kubernetes. It\u2019s incredible! This semester the Stuttgart Media University even held a training course on this topic. For DevOps or \u201ccloud-computing specialist\u201d mastering Kubernetes and the concepts around it is becoming more and more important. This blog post won\u2019t explain\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/08\/up-and-running-with-kubernetes-13-638.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/08\/up-and-running-with-kubernetes-13-638.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/08\/up-and-running-with-kubernetes-13-638.jpg?resize=525%2C300&ssl=1 1.5x"},"classes":[]},{"id":22460,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/28\/himbeer-tarte-und-harte-fakten-im-interview-mit-ansible-k3s-infrastructure-as-code-und-raspberry-pi\/","url_meta":{"origin":12796,"position":1},"title":"\u201cHimbeer Tarte und harte Fakten\u201d: Im Interview mit Ansible, k3s, Infrastructure as Code und Raspberry Pi","author":"Aliena Leonhard","date":"28. February 2022","format":false,"excerpt":"Why so serious? - Ein Artikel von Sarah Schwab und Aliena Leonhard im Rahmen der Vorlesung Systems Engineering and Management. Die Idee, ein fiktives Interview zu erstellen, entstammt daraus komplexe Sachverhalte unterhaltsam und verst\u00e4ndlich zu machen. Wir sind heute zu Gast in der Tech-Sendung \u201cHimbeer Tarte und harte Fakten\u201d.\u00a0 Heute\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stan-slade-BM27BzBrhVM-unsplash-2-scaled.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stan-slade-BM27BzBrhVM-unsplash-2-scaled.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stan-slade-BM27BzBrhVM-unsplash-2-scaled.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stan-slade-BM27BzBrhVM-unsplash-2-scaled.jpg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stan-slade-BM27BzBrhVM-unsplash-2-scaled.jpg?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stan-slade-BM27BzBrhVM-unsplash-2-scaled.jpg?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":10190,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2020\/03\/01\/autoscaling-of-docker-containers-in-google-kubernetes-engine\/","url_meta":{"origin":12796,"position":2},"title":"Autoscaling of Docker Containers  in Google Kubernetes Engine","author":"de032","date":"1. March 2020","format":false,"excerpt":"In this blog post we are taking a look at scaling possibilities within Kubernetes in a cloud environment. We are going to present and discuss various options that all have the same target: increase the availability of a service.","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/03\/1052ebad-d01f-4803-bde6-e943c4598ef9.jpeg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/03\/1052ebad-d01f-4803-bde6-e943c4598ef9.jpeg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/03\/1052ebad-d01f-4803-bde6-e943c4598ef9.jpeg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/03\/1052ebad-d01f-4803-bde6-e943c4598ef9.jpeg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/03\/1052ebad-d01f-4803-bde6-e943c4598ef9.jpeg?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/03\/1052ebad-d01f-4803-bde6-e943c4598ef9.jpeg?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":5175,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2019\/02\/24\/benefiting-kubernetes-part-2-deploy-with-kubectl\/","url_meta":{"origin":12796,"position":3},"title":"Migrating to Kubernetes Part 2 &#8211; Deploy with kubectl","author":"Can Kattwinkel","date":"24. February 2019","format":false,"excerpt":"Written by: Pirmin Gersbacher, Can Kattwinkel, Mario Sallat Migrating from Bare Metal to Kubernetes The interest in software containers is a relatively new trend in the developers world. Classic VMs have not lost their right to exist within a world full of monoliths yet, but the trend is clearly towards\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/02\/pexels-photo-379964.jpeg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/02\/pexels-photo-379964.jpeg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/02\/pexels-photo-379964.jpeg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/02\/pexels-photo-379964.jpeg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/02\/pexels-photo-379964.jpeg?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":6652,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2019\/07\/24\/how-to-create-a-k8s-cluster-with-custom-nodes-in-rancher\/","url_meta":{"origin":12796,"position":4},"title":"How to create a K8s cluster with custom nodes in Rancher","author":"Sarah Schwab","date":"24. July 2019","format":false,"excerpt":"Don't you find it annoying not to be able to manage all your Kubernetes clusters at a glance? Ranger 2.0 offers an ideal solution.\u00a0 The following article is less a scientific post than a how-to guide to creating a new Kubernetes cluster with custom nodes in Ranger 2.0.\u00a0 But before\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/07\/cluster-1.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/07\/cluster-1.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/07\/cluster-1.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/07\/cluster-1.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/07\/cluster-1.png?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":27789,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2025\/07\/24\/beyond-reactive-how-ai-is-revolutionizing-kubernetes-autoscaling\/","url_meta":{"origin":12796,"position":5},"title":"Beyond Reactive: How AI is Revolutionizing Kubernetes Autoscaling","author":"Hannah Holzheu","date":"24. July 2025","format":false,"excerpt":"Note:\u00a0This blog post was written for the module Enterprise IT (113601a) in the summer semester of 2025 Introduction Kubernetes has become the leading open-source platform for managing containerized applications. Its ability to automate deployment, scaling, and operations helps teams efficiently manage microservices architectures and dynamic cloud workloads. A cornerstone of\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/07\/AIvsRuleBased.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/07\/AIvsRuleBased.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/07\/AIvsRuleBased.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/07\/AIvsRuleBased.png?resize=700%2C400&ssl=1 2x"},"classes":[]}],"jetpack_sharing_enabled":true,"authors":[{"term_id":818,"user_id":1001,"is_guest":0,"slug":"ab278","display_name":"Artur Bergen","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/97178db99ff9b30a480245638c770332705fd48d2cfbb924a94a1840e1cf10cb?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/12796","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/users\/1001"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/comments?post=12796"}],"version-history":[{"count":80,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/12796\/revisions"}],"predecessor-version":[{"id":25371,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/12796\/revisions\/25371"}],"wp:attachment":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/media?parent=12796"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/categories?post=12796"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/tags?post=12796"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/ppma_author?post=12796"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}