{"id":4405,"date":"2019-01-04T20:19:32","date_gmt":"2019-01-04T19:19:32","guid":{"rendered":"https:\/\/blog.mi.hdm-stuttgart.de\/?p=4405"},"modified":"2023-06-09T14:23:27","modified_gmt":"2023-06-09T12:23:27","slug":"radcup-part-3-automation","status":"publish","type":"post","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2019\/01\/04\/radcup-part-3-automation\/","title":{"rendered":"Radcup Part 3 &#8211; Automation with Gitlab CI\/CD"},"content":{"rendered":"\n<p>Written by: Immanuel Haag, Christian M\u00fcller, Marc R\u00fcttler<\/p>\n\n\n\n<p>The goal of this blog entry is to automate the previously performed steps. At the end all manual steps should be automated when new code changes are added to the repository. The new version of the backend will be made available in the cloud at the end.<br><\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Gitlab Repository Setup<\/h2>\n\n\n\n<p>The first step was to automate the manual execution of testing, image creation and deployment. Especially to prevent mistakes and to define a standardized procedure. We used the external hosted service of <a href=\"https:\/\/gitlab.com\/\">gitlab.com<\/a> instead of the Medienhochschule Stuttgart GitLab. The reason for this is that mi.gitlab pipelines may have some strange behavior when connecting to external IP ranges. Therefore we have created a new repository under the following link: <a href=\"https:\/\/gitlab.com\/system_eng_cup_app\/radcup_backend\">https:\/\/gitlab.com\/system_eng_cup_app\/radcup_backend<\/a> (if you are interested, please contact us, because the project is private). In addition, we decided to use the <a href=\"https:\/\/www.atlassian.com\/git\/tutorials\/comparing-workflows\/feature-branch-workflow\">Git Feature Branch Workflow<\/a> for development. This results in many commits as well as several branches (background: there should not be one large but several small commits for customization&#8217;s). <\/p>\n\n\n\n<p>To push images into the container registry or to deploy them in the cluster, different passwords or tokens are needed. These must not appear in plain text in the pipeline definition. For this reason these are stored under the CI\/CD settings and are called via variable names.<br><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Define the architecture <\/h2>\n\n\n\n<p>After the repository and pipeline were created, we had to think about a rough architecture for our system. The following picture shows a simplified version of this: <\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1062\" height=\"662\" data-attachment-id=\"4612\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2019\/01\/04\/radcup-part-3-automation\/gesamtstruktur-der-architektur-kurzform-1\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/Gesamtstruktur-der-Architektur-Kurzform-1.png\" data-orig-size=\"1062,662\" 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=\"Gesamtstruktur der Architektur Kurzform (1)\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/Gesamtstruktur-der-Architektur-Kurzform-1-1024x638.png\" src=\"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/Gesamtstruktur-der-Architektur-Kurzform-1.png?fit=656%2C409&amp;ssl=1\" alt=\"\" class=\"wp-image-4612\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/Gesamtstruktur-der-Architektur-Kurzform-1.png 1062w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/Gesamtstruktur-der-Architektur-Kurzform-1-300x187.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/Gesamtstruktur-der-Architektur-Kurzform-1-768x479.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/Gesamtstruktur-der-Architektur-Kurzform-1-1024x638.png 1024w\" sizes=\"auto, (max-width: 1062px) 100vw, 1062px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Gitlab CI\/CD Setup<\/h2>\n\n\n\n<p>We defined multiple stages to divide the pipeline into multiple steps.<br>These are based on the steps previously performed manually as described above. <em>Note: In gitlab you have to turn on the CI\/CD Feature in the repository settings. <\/em><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">The pipeline stages<\/h4>\n\n\n\n<p>Our pipeline has the following stages:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>imagegen<\/li><li>test<\/li><li>build<\/li><li>deploy<\/li><li>curlcluster<\/li><\/ol>\n\n\n\n<h4 class=\"wp-block-heading\">.gitlab-ci.yml and runners<\/h4>\n\n\n\n<p>In short, this file defines how the project should be built by the GitLab Runner. It must be located in the root directory of the GitLab repository.<\/p>\n\n\n\n<p>We found the <a href=\"https:\/\/docs.gitlab.com\/ce\/ci\/yaml\/README.html\">Gitlab Documentation<\/a> helpful.<br>And the <a href=\"https:\/\/docs.gitlab.com\/ce\/ci\/quick_start\/README.html\">quick start quide<\/a>, which is also referenced in the documentation, was also interesting.<a href=\"https:\/\/docs.gitlab.com\/ce\/ci\/quick_start\/README.html\"><\/a><\/p>\n\n\n\n<p>To get the pipeline running only the mentioned file has to be present and a GitLab Runner has to be configured.<\/p>\n\n\n\n<p>Please note that the file does not configure the whole project with all branches and throughout all commits. This file is also version controlled like any other file in the repository, which means that this file could be different for every commit and on every branch. We decided to keep this file uniform on all branches.<br><a href=\"https:\/\/about.gitlab.com\/2015\/05\/06\/why-were-replacing-gitlab-ci-jobs-with-gitlab-ci-dot-yml\/\">Interesting notes<\/a>.<a href=\"https:\/\/about.gitlab.com\/2015\/05\/06\/why-were-replacing-gitlab-ci-jobs-with-gitlab-ci-dot-yml\/\"><\/a><\/p>\n\n\n\n<p>We use the free, open for everyone &#8220;Shared Runners&#8221;. Runners can be configured via the website under &#8220;Settings&#8221; -&gt; &#8220;CI \/ CD&#8221; -&gt; &#8220;Runners&#8221;. Own Runners can also be configured there. Now every push\/commit to the repository will cause the pipeline to run.<\/p>\n\n\n\n<p>During development, there was often the problem of delays, so a dedicated runner was set up for longer debugging sessions. Feel free to consolidate the docs for more information about a separated runner.<\/p>\n\n\n\n<p>If any job\/stage returns a non zero value, the job will be interpreted as failed only if everything returns zero it will be considered passed.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"574\" height=\"84\" data-attachment-id=\"4982\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2019\/01\/04\/radcup-part-3-automation\/pipeline-one-failed\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/pipeline-one-failed.png\" data-orig-size=\"574,84\" 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=\"pipeline one failed\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/pipeline-one-failed.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/pipeline-one-failed.png\" alt=\"\" class=\"wp-image-4982\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/pipeline-one-failed.png 574w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/pipeline-one-failed-300x44.png 300w\" sizes=\"auto, (max-width: 574px) 100vw, 574px\" \/><\/figure>\n\n\n\n<p>In the Picture one can see that on a commit two jobs passed and one failed. The failed job caused the whole pipeline to fail as expected. Also note that subsequent jobs were skipped an not even attempted.<\/p>\n\n\n\n<p>In the following sections the setup of each stage in this file is described.<\/p>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><h4>stages, variables and before_script<\/h4>\n<\/div>\n\n\n\n<pre class=\"hljs\" style=\"display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(68, 68, 68); color: rgb(221, 221, 221);\"><span class=\"hljs-attr\">stages:<\/span>\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">-<\/span> imagegen\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">-<\/span> test\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">-<\/span> build\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">-<\/span> deploy\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">-<\/span> curlcluster\n\n<span class=\"hljs-attr\">variables:<\/span>\n<span class=\"hljs-attr\">  MAJOR:<\/span> <span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">'1'<\/span>\n<span class=\"hljs-attr\">  MINOR:<\/span> <span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">'1'<\/span>\n<span class=\"hljs-attr\">  PATCH:<\/span> <span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">'1'<\/span>\n<span class=\"hljs-attr\">  CLUSTERURL:<\/span> <span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">'159.122.181.248:30304'<\/span>\n  \n<span class=\"hljs-attr\">before_script:<\/span>\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">  -<\/span> export BUILD_REF=`export TERM=xterm; git log --pretty=format:<span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">'%h'<\/span> -n <span class=\"hljs-number\">1<\/span>`\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">  -<\/span> export IMAGE_TAG=`export TERM=xterm;  date +%Y%m%d_%H%M`\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">  -<\/span> export BUILD_TAG=${MAJOR}.${MINOR}.${PATCH}-build.${BUILD_REF}\n<\/pre>\n\n\n\n<p><strong>Description:<\/strong><\/p>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p><em>Variables<\/em>:<\/p>\n<p>The variable section is used to introduce the so-called semantic versioning. This gives developers the advantage of listing the current version of the backend in individual sub-releases and can be considered as best practice. Details about that are shown under the following link: <a href=\"https:\/\/semver.org\/\">https:\/\/semver.org\/<\/a>.<br>\nFor example if a developer fixes a small issue he has to increment the <code class=\"\" data-line=\"\">PATCH<\/code> variable by one.<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>Before the pipeline starts executing, multiple variables are generated and exported in the <em>before_script<\/em> section. This part was inserted later because code duplications were available before.<\/p>\n<p>For later image name tagging in the build-stage <code class=\"\" data-line=\"\">BUILD_REF<\/code> and <code class=\"\" data-line=\"\">BUILD_TAG<\/code> creates a string from the semantic-versioning variables and the latest commit id like this:<\/p>\n<p>Successfully tagged registry.eu-de.bluemix.net\/system_engineering_radcup\/radcup_backend:<strong>1.1.1-build.b5ecee4\ufeff<\/strong><\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>The <code class=\"\" data-line=\"\">BUILD_TAG<\/code> variable is used in the <em>imagegen<\/em> stage. This construct creates a nametag like <code class=\"\" data-line=\"\">20190106_1929<\/code> which represent the creation date of the <code class=\"\" data-line=\"\">immae1\/ibmcloudkubectl<\/code> image. Details about that are described below.<\/p>\n<\/div>\n\n\n\n<h4 class=\"wp-block-heading\">1. imagegen stage<\/h4>\n\n\n\n<pre class=\"hljs\" style=\"display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(68, 68, 68); color: rgb(221, 221, 221);\"><span class=\"hljs-attr\">imagegen:<\/span>\n<span class=\"hljs-attr\">  image:<\/span> immae1\/ibmcloudkubectl <span class=\"hljs-comment\" style=\"color: rgb(119, 119, 119);\"># we use this image because here is git installed<\/span>\n<span class=\"hljs-attr\">  services:<\/span>\n<span class=\"hljs-attr\">  - docker:<\/span>dind\n<span class=\"hljs-attr\">  stage:<\/span> imagegen\n<span class=\"hljs-attr\">  only:<\/span>\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">    -<\/span> image_gen\n<span class=\"hljs-attr\">  script:<\/span>\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">  -<\/span> docker login --username ${DOCKERH_USR} --password ${DOCKERH_PWD}\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">  -<\/span> cd ibmcloudkubectlimage\/; docker build -t immae1\/ibmcloudkubectl:latest . ; docker push immae1\/ibmcloudkubectl:latest ; docker tag immae1\/ibmcloudkubectl:latest immae1\/ibmcloudkubectl:${IMAGE_TAG} ; docker push immae1\/ibmcloudkubectl:${IMAGE_TAG} \n<span class=\"hljs-attr\">  environment:<\/span>\n<span class=\"hljs-attr\">    name:<\/span> imagegen\n<\/pre>\n\n\n\n<p><strong>Description:<\/strong><\/p>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>The <em>build<\/em> and <em>deploy<\/em> stage require some tools for their doing. So we created the <em>imagegen<\/em> stage. This stage is responsible for creating a &quot;default tooling-image&quot;.<\/p>\n<p>The <code class=\"\" data-line=\"\">only<\/code> keyword is used because only the imagegen branch of our repository is responsible for this stage and image generation. As we have no control over updates of the CLI tools which are installed in the image, we choose a decoupled approach. And the imagegen branch is executed by the gitlab schedules feature, on every Sunday.<\/p>\n<p>Below you can see the Dockerfile in which is defined that the <code class=\"\" data-line=\"\">IBM-Cloud CLI-Tools<\/code> and <code class=\"\" data-line=\"\">kubectl<\/code> are installed.<\/p>\n<\/div>\n\n\n\n<pre class=\"hljs\" style=\"display: block; overflow-x: auto; padding: 0.5em; background: rgb(68, 68, 68) none repeat scroll 0% 0%; color: rgb(221, 221, 221);\"><span class=\"hljs-keyword\" style=\"color: rgb(255, 255, 255); font-weight: 700;\">FROM<\/span> docker:latest\n<span class=\"hljs-keyword\" style=\"color: rgb(255, 255, 255); font-weight: 700;\">RUN<\/span> <span class=\"bash\">apk add --update --no-cache curl git ca-certificates \\\n&amp;&amp; apk add --update -t deps curl \\\n&amp;&amp; apk add --update gettext \\\n&amp;&amp; curl -fsSL https:\/\/clis.ng.bluemix.net\/install\/linux | sh \\\n&amp;&amp; ibmcloud plugin install container-registry \\\n&amp;&amp; ibmcloud plugin install container-service \\\n&amp;&amp; curl -LO https:\/\/storage.googleapis.com\/kubernetes-release\/release\/$(curl <span class=\"hljs-_\">-s<\/span> https:\/\/storage.googleapis.com\/kubernetes-release\/release\/stable.txt)\/bin\/linux\/amd64\/kubectl  \\\n&amp;&amp; chmod +x .\/kubectl \\\n&amp;&amp; mv .\/kubectl \/usr\/<span class=\"hljs-built_in\" style=\"color: rgb(221, 136, 136);\">local<\/span>\/bin\/kubectl \\\n&amp;&amp; apk del --purge deps \\\n&amp;&amp; rm \/var\/cache\/apk\/*<\/span><\/pre>\n\n\n\n<p>The result of this stage is online under the following link available: <a href=\"https:\/\/hub.docker.com\/r\/immae1\/ibmcloudkubectl\">https:\/\/hub.docker.com\/r\/immae1\/ibmcloudkubectl<\/a><br><\/p>\n\n\n\n<p>Finding: <\/p>\n\n\n\n<p>It was not easy to update an image within the dockerhub. But the trick is to tag twice whilst creating the image with <em>latest<\/em> and the current <em>date<\/em>. So after a second image creation and pushing, the are different versions of our image available in the dockerhub.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">2. test stage<\/h4>\n\n\n\n<pre class=\"hljs\" style=\"display: block; overflow-x: auto; padding: 0.5em; background: rgb(68, 68, 68) none repeat scroll 0% 0%; color: rgb(221, 221, 221);\"><span class=\"hljs-attr\">test:<\/span>\n<span class=\"hljs-attr\">  image:<\/span> node:jessie\n<span class=\"hljs-attr\">  services:<\/span>\n<span class=\"hljs-attr\">  - name:<\/span> mongo:<span class=\"hljs-number\">4.0<\/span>\n<span class=\"hljs-attr\">    alias:<\/span> mongo\n<span class=\"hljs-attr\">  stage:<\/span> test\n<span class=\"hljs-attr\">  except:<\/span>\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">    -<\/span> image_gen\n<span class=\"hljs-attr\">  variables:<\/span>\n<span class=\"hljs-attr\">    MONGO_URI:<\/span> <span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">'mongodb:\/\/mongo\/radtest'<\/span>\n<span class=\"hljs-attr\">    NODE_ENV:<\/span> test\n<span class=\"hljs-attr\">  script:<\/span>\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">  -<\/span> cd src\/ &amp;&amp; npm install --<span class=\"hljs-literal\" style=\"color: rgb(255, 255, 255); font-weight: 700;\">no<\/span>-optional &amp;&amp; npm test\n<span class=\"hljs-attr\">  environment:<\/span>\n<span class=\"hljs-attr\">    name:<\/span> test<\/pre>\n\n\n\n<p><strong>Description:<\/strong><\/p>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>The <em>test<\/em> stage is relativ straight forward. With the <code class=\"\" data-line=\"\">except<\/code> keyword its defined that this stage is not performed in the <em>imagegen<\/em> branch. All other branches will perform this test stage. The other finding what we found here and is interesting to mention is the following fact. If you use shared gitlab runners, you never know which IP-Address a runner uses to talk with the internet. So if you want to use an external service like our managed external hosted MongoDB with IP-Whitelisting security feature you will encounter problems. Because of this the stage uses an additional local MongoDB container and the backend\/test-suite can talk to this new local MongoDB gitlab container trough the <code class=\"\" data-line=\"\">MONGO_URI<\/code> variable.<\/p>\n<\/div>\n\n\n\n<h4 class=\"wp-block-heading\">3. build stage<\/h4>\n\n\n\n<pre class=\"hljs\" style=\"display: block; overflow-x: auto; padding: 0.5em; background: rgb(68, 68, 68) none repeat scroll 0% 0%; color: rgb(221, 221, 221);\"><span class=\"hljs-attr\">build:<\/span>\n<span class=\"hljs-attr\">  image:<\/span> immae1\/ibmcloudkubectl:latest <span class=\"hljs-comment\" style=\"color: rgb(119, 119, 119);\"># or another arbitrary docker image<\/span>\n<span class=\"hljs-attr\">  stage:<\/span> build\n<span class=\"hljs-attr\">  only:<\/span>\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">    -<\/span> master\n<span class=\"hljs-attr\">  services:<\/span>\n<span class=\"hljs-attr\">  - docker:<\/span>dind\n<span class=\"hljs-attr\">  script:<\/span>\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">    -<\/span> ibmcloud login -a https:\/\/api.eu-de.bluemix.net -u ${BLUEMIX_USR} -p ${BLUEMIX_PWD} -c ${BLUEMIX_ORG} &amp;&amp; ibmcloud cr login\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">    -<\/span> ibmcloud cr image-list --format <span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">\"<span class=\"hljs-template-variable\" style=\"color: rgb(221, 136, 136);\">{{ .Created}}<\/span> <span class=\"hljs-template-variable\" style=\"color: rgb(221, 136, 136);\">{{ if gt .Size 1 }}<\/span><span class=\"hljs-template-variable\" style=\"color: rgb(221, 136, 136);\">{{ .Repository }}<\/span>:<span class=\"hljs-template-variable\" style=\"color: rgb(221, 136, 136);\">{{ .Tag }}<\/span> <span class=\"hljs-template-variable\" style=\"color: rgb(221, 136, 136);\">{{end}}<\/span>\"<\/span> | grep <span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">\"system_engineering_radcup\"<\/span> | sort -n | cut -c12<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">-98<\/span> | head -n <span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">-1<\/span> &gt;&gt; deprecated.txt &amp;&amp; chmod +x clean.sh &amp;&amp; sh clean.sh deprecated.txt\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">    -<\/span> docker build -t registry.eu-de.bluemix.net\/system_engineering_radcup\/radcup_backend:${BUILD_TAG} .\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">    -<\/span> docker push registry.eu-de.bluemix.net\/system_engineering_radcup\/radcup_backend:${BUILD_TAG}\n<span class=\"hljs-attr\">  environment:<\/span>\n<span class=\"hljs-attr\">    name:<\/span> build<\/pre>\n\n\n\n<p><strong>Description:<\/strong><\/p>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>In this stage (is only executed on the master branch, see keyword <code class=\"\" data-line=\"\">only<\/code>), the image is built based on the corresponding dockerfile of our backend (<a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2019\/01\/03\/radcup-part-1-refactoring\/\">see first post<\/a>). After the successful login, all images stored in the container registry are first read (the command output is formatted using the specified Go template), filtered and sorted, and the name of the oldest image is stored in a text file. This image is then deleted in the next step.<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><hr>\n<p>Background:<\/p>\n<p>A CleanUp process has been defined to prevent the container registry from overflowing. This process ensures that there are never more than two images in the container registry at the same time. Keyword: resource management.<\/p>\n<\/div>\n\n\n\nclean.sh\n<pre class=\"hljs\" style=\"display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(68, 68, 68); color: rgb(221, 221, 221);\"><span class=\"hljs-meta\" style=\"color: rgb(119, 119, 119);\">#!\/bin\/bash<\/span>\n<span class=\"hljs-keyword\" style=\"color: rgb(255, 255, 255); font-weight: 700;\">while<\/span> IFS= <span class=\"hljs-built_in\" style=\"color: rgb(221, 136, 136);\">read<\/span> -r line; <span class=\"hljs-keyword\" style=\"color: rgb(255, 255, 255); font-weight: 700;\">do<\/span>\n  ibmcloud cr image-rm <span class=\"hljs-variable\" style=\"color: rgb(221, 136, 136);\">$line<\/span>\n<span class=\"hljs-keyword\" style=\"color: rgb(255, 255, 255); font-weight: 700;\">done<\/span> &lt; <span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">\"<span class=\"hljs-variable\" style=\"color: rgb(221, 136, 136);\">$1<\/span>\"<\/span>\n<\/pre>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><hr>\n<p>If the oldest image is deleted, a new one is built and tagged before it is pushed into the container registry.<\/p>\n<\/div>\n\n\n\n<h4 class=\"wp-block-heading\">4. deploy stage<\/h4>\n\n\n\n<pre class=\"hljs\" style=\"display: block; overflow-x: auto; padding: 0.5em; background: rgb(68, 68, 68) none repeat scroll 0% 0%; color: rgb(221, 221, 221);\"><span class=\"hljs-attr\">deploy:<\/span>\n<span class=\"hljs-attr\">  image:<\/span> immae1\/ibmcloudkubectl:latest\n<span class=\"hljs-attr\">  stage:<\/span> deploy\n<span class=\"hljs-attr\">  only:<\/span>\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">    -<\/span> master\n<span class=\"hljs-attr\">  script:<\/span>\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">    -<\/span> ibmcloud login -a https:\/\/api.eu-de.bluemix.net -u ${BLUEMIX_USR} -p ${BLUEMIX_PWD} -c ${BLUEMIX_ORG_FREE} &amp;&amp; ibmcloud cs region-set eu-central &amp;&amp; ibmcloud cs cluster-config radcup\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">    -<\/span> export KUBECONFIG=\/root\/.bluemix\/plugins\/container-service\/clusters\/radcup\/kube-config-mil01-radcup.yml\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">    -<\/span> cd kubernetes\/ &amp;&amp; sed -i s\/version-radcup\/${BUILD_TAG}\/g web-controller.yaml &amp;&amp; kubectl get rc -o=custom-columns=NAME:.metadata.name &gt; tmp.txt &amp;&amp; sed -i <span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">'1d'<\/span> tmp.txt &amp;&amp; RC=`cat tmp.txt`\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">    -<\/span> kubectl rolling-update ${RC} -f web-controller.yaml\n<span class=\"hljs-attr\">  environment:<\/span>\n<span class=\"hljs-attr\">    name:<\/span> deploy<\/pre>\n\n\n\n<p><strong>Description:\ufeff<\/strong><br><\/p>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>In this stage (is only executed on the master branch, see keyword <code class=\"\" data-line=\"\">only<\/code>), the previously built and pushed image is deployed in the cluster. After the successful login, the string <code class=\"\" data-line=\"\">version-radcup<\/code> is replaced by <code class=\"\" data-line=\"\">${BUILD_TAG}<\/code> in the file <code class=\"\" data-line=\"\">web-controller.yaml<\/code>. Then all replication controllers in the cluster are read to get the name of the affected controller and store it in a variable (background: this part has been added because the name of the web-controller has partly changed throughout the project). Finally, a rolling update to the replication controller is performed with the help of this and the image is updated accordingly.<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><hr>\n<p>Rolling update:<\/p>\n<p>Rolling updates allow Deployment updates to take place with zero downtime by incrementally updating Pods instances with new ones.<\/p>\n<ul>\n<li>Example output:<\/li>\n<\/ul>\n<\/div>\n\n\n\n<pre class=\"hljs\" style=\"display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(68, 68, 68); color: rgb(221, 221, 221);\">Created web-controller-1.1.1-build\nScaling up web-controller-1.1.1-build from 0 to 2, scaling down web-controller from 2 to 0 (keep 2 pods available, don't exceed 3 pods)\nScaling web-controller-1.1.1-build up to 1\nScaling web-controller down to 1\nScaling web-controller-1.1.1-build up to 2\nScaling web-controller down to 0\nUpdate succeeded. Deleting old controller: web-controller\nRenaming web-controller-1.1.1-build to web-controller<\/pre>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><hr>\n<\/div>\n\n\n\n<h4 class=\"wp-block-heading\">5. curlcluster stage<\/h4>\n\n\n\n<pre class=\"hljs\" style=\"display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(68, 68, 68); color: rgb(221, 221, 221);\"><span class=\"hljs-attr\">curlcluster:<\/span>\n<span class=\"hljs-attr\">  stage:<\/span> curlcluster\n<span class=\"hljs-attr\">  only:<\/span>\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">    -<\/span> master\n<span class=\"hljs-attr\">  script:<\/span>\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">  -<\/span> result=<span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">\"$(curl -m2 http:\/\/${CLUSTERURL}\/api)\"<\/span>\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">  -<\/span> should=<span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">'{\"message\":\"this will be a beerpong app\"}'<\/span>\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">  -<\/span> if [[ <span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">\"$result\"<\/span> != <span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">\"$should\"<\/span> ]] ; then exit <span class=\"hljs-number\">1<\/span>; fi\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">  -<\/span> resultgame=<span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">\"$(curl -m2 http:\/\/${CLUSTERURL}\/api\/games\/5c2904bf172dbc0023ffdaea)\"<\/span>\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">  -<\/span> shouldgame=`cat gameresult.txt `\n<span class=\"hljs-bullet\" style=\"color: rgb(221, 136, 136);\">  -<\/span> if [[ <span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">\"$resultgame\"<\/span> != <span class=\"hljs-string\" style=\"color: rgb(221, 136, 136);\">\"$shouldgame\"<\/span> ]] ; then exit <span class=\"hljs-number\">1<\/span>; fi\n<span class=\"hljs-attr\">  environment:<\/span>\n<span class=\"hljs-attr\">    name:<\/span> curlcluster<\/pre>\n\n\n\n<p><strong>Description:<\/strong><\/p>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>This stage (is only executed on the master branch, see keyword <code class=\"\" data-line=\"\">only<\/code>) is the last endpoint test. The cli tool <code class=\"\" data-line=\"\">curl<\/code> is used to make a http call against the before updated cluster. If the API is not available &#8211; the return code becomes <em>1<\/em> and because of that the stage will fail. We decided to also test if the api is connected to the <em>mongodbcluster<\/em> &#8211; this is the second curl test which observes a <em>unique gameid<\/em>. This game is present in the mongo database at all times, so that this test won&#8217;t fail.<\/p>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: The whole architecture <\/h2>\n\n\n\n<p>After the integration of the CI\/CD pipeline described above, we can now present the entire architecture of our system. This can be seen in the following picture:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1802\" height=\"1302\" data-attachment-id=\"4818\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2019\/01\/04\/radcup-part-3-automation\/gesamtstruktur-der-architektur-3\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/Gesamtstruktur-der-Architektur-2.png\" data-orig-size=\"1802,1302\" 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=\"Gesamtstruktur der Architektur\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/Gesamtstruktur-der-Architektur-2-1024x740.png\" src=\"https:\/\/i1.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/Gesamtstruktur-der-Architektur-2.png?fit=656%2C474&amp;ssl=1\" alt=\"\" class=\"wp-image-4818\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/Gesamtstruktur-der-Architektur-2.png 1802w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/Gesamtstruktur-der-Architektur-2-300x217.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/Gesamtstruktur-der-Architektur-2-768x555.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/01\/Gesamtstruktur-der-Architektur-2-1024x740.png 1024w\" sizes=\"auto, (max-width: 1802px) 100vw, 1802px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5: Research questions and further project steps <\/h2>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><ul>\n<li>Scan images for vulnerabilities and challenge application logic (API vulnerability)<\/li>\n<li>Eliminate pipeline warnings (rollout for rolling update, credential manager for docker login in imagegen stage)<\/li>\n<li>Changing the ReplicationController to a ReplicaSet (newer version of the ReplicationController)<\/li>\n<li>Endpoint testing and load testing should be pursued further<\/li>\n<li>Set up Hooks in Slack etc.<\/li>\n<li>Apply 12-Factorapp principles to the project<\/li>\n<li>Further improve and simplify the pipeline<\/li>\n<li>Automate npm-check<\/li>\n<\/ul>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Written by: Immanuel Haag, Christian M\u00fcller, Marc R\u00fcttler The goal of this blog entry is to automate the previously performed steps. At the end all manual steps should be automated when new code changes are added to the repository. The new version of the backend will be made available in the cloud at the end.<\/p>\n","protected":false},"author":884,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[659,650,651,2],"tags":[206,144,7,210,61,3,97,98,145,154],"ppma_author":[762],"class_list":["post-4405","post","type-post","status-publish","format-standard","hentry","category-devops","category-scalable-systems","category-system-designs","category-system-engineering","tag-beerpong","tag-ci-pipeline","tag-cloud","tag-cluster","tag-containers","tag-docker","tag-git","tag-gitlab","tag-gitlab-ci","tag-kubernetes"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":3348,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2018\/03\/30\/continuous-integration-pipeline-for-unity-development-using-gitlab-ci-and-aws\/","url_meta":{"origin":4405,"position":0},"title":"Continuous Integration Pipeline for Unity Development using GitLab CI and AWS","author":"Jonas Graf, Christian Gutwein","date":"30. March 2018","format":false,"excerpt":"This blog entry describes the implementation of a Continous Integration (CI) pipeline especially adapted for Unity projects. It makes it possible to automatically execute Unity builds on a configured build server and provide it for a further deployment process if required.","rel":"","context":"In &quot;DevOps&quot;","block_context":{"text":"DevOps","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/devops\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/03\/CI_process.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/03\/CI_process.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/03\/CI_process.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/03\/CI_process.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":5179,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2019\/02\/24\/migrating-to-kubernetes-part-4-create-environments-via-gitlab\/","url_meta":{"origin":4405,"position":1},"title":"Migrating to Kubernetes Part 4 &#8211; Create Environments via Gitlab","author":"Can Kattwinkel","date":"24. February 2019","format":false,"excerpt":"Written by: Pirmin Gersbacher, Can Kattwinkel, Mario Sallat Connect Gitlab with Kubernetes With the Review Apps Gitlab offers an excellent improvement of the Developer Experience. More or less Gitlab enables the management of environments. For each environment, there is a CI task to each set-up and tear down. It is\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":7154,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2019\/08\/31\/setting-up-a-ci-cd-pipeline-in-gitlab\/","url_meta":{"origin":4405,"position":2},"title":"Setting up a CI\/CD pipeline in Gitlab","author":"nr037","date":"31. August 2019","format":false,"excerpt":"Introduction For all my university software projects, I use the HdM Gitlab instance for version control. But Gitlab offers much more such as easy and good ways to operate a pipeline. In this article, I will show how we can use the CI\/CD functionality in a university project to perform\u2026","rel":"","context":"In &quot;Cloud Technologies&quot;","block_context":{"text":"Cloud Technologies","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/cloud-technologies\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/08\/Screenshot-2019-08-26-at-09.53.13.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/08\/Screenshot-2019-08-26-at-09.53.13.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/08\/Screenshot-2019-08-26-at-09.53.13.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/08\/Screenshot-2019-08-26-at-09.53.13.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/08\/Screenshot-2019-08-26-at-09.53.13.png?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/08\/Screenshot-2019-08-26-at-09.53.13.png?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":3513,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2018\/03\/30\/ci-cd-with-gitlab-ci-for-a-web-application-part-3\/","url_meta":{"origin":4405,"position":3},"title":"CI\/CD with GitLab CI for a web application &#8211; Part 3","author":"Nina Schaaf","date":"30. March 2018","format":false,"excerpt":"Hosting your own GitLab server Some users might have concerns regarding security using GitLab for a variety of purposes, including commercial and business applications. That is, because GitLab is commonly used as a cloud-based service - on someone else's computer, so to speak. So setting it up for running it\u2026","rel":"","context":"In &quot;DevOps&quot;","block_context":{"text":"DevOps","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/devops\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":21064,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/09\/11\/how-do-you-get-a-web-application-into-the-cloud\/","url_meta":{"origin":4405,"position":4},"title":"How do you get a web application into the cloud?","author":"af094","date":"11. September 2021","format":false,"excerpt":"by Dominik Ratzel (dr079) and Alischa Fritzsche (af094) For the lecture \"Software Development for Cloud Computing\", we set ourselves the goal of exploring new things and gaining experience. We focused on one topic: \"How do you get a web application into the cloud?\". In doing so, we took a closer\u2026","rel":"","context":"In &quot;Cloud Technologies&quot;","block_context":{"text":"Cloud Technologies","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/cloud-technologies\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/09\/availableRunners-150x118.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":3491,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2018\/03\/31\/continuous-integration-deployment-for-a-cross-platform-application-part-2\/","url_meta":{"origin":4405,"position":5},"title":"Continuous Integration &#038; Deployment for a Cross-Platform Application &#8211; Part 2","author":"Tobias Eberle, Marco Maisel, Tobias Staib, Mario Walz","date":"31. March 2018","format":false,"excerpt":"In the first part we pointed out how we set up the infrastructure for our CI system. Now we would like to explain how we build a pipeline for our cross-platform application and what features of GitLab CI we made use of. Building a Pipeline in GitLab CI Our first\u2026","rel":"","context":"In &quot;DevOps&quot;","block_context":{"text":"DevOps","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/devops\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/03\/flora-app.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/03\/flora-app.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/03\/flora-app.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/03\/flora-app.jpg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/03\/flora-app.jpg?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/03\/flora-app.jpg?resize=1400%2C800&ssl=1 4x"},"classes":[]}],"jetpack_sharing_enabled":true,"authors":[{"term_id":762,"user_id":884,"is_guest":0,"slug":"ih038","display_name":"Immanuel Haag","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/3095f100f75fe977c838303e854bb8cd3ffc7fbf01963610781fcd51bb5a4680?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\/4405","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\/884"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/comments?post=4405"}],"version-history":[{"count":94,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/4405\/revisions"}],"predecessor-version":[{"id":5088,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/4405\/revisions\/5088"}],"wp:attachment":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/media?parent=4405"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/categories?post=4405"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/tags?post=4405"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/ppma_author?post=4405"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}