{"id":1144,"date":"2022-06-22T12:29:53","date_gmt":"2022-06-22T09:29:53","guid":{"rendered":"https:\/\/www.bandidor.info\/wp\/?p=1144"},"modified":"2023-05-31T16:57:47","modified_gmt":"2023-05-31T13:57:47","slug":"docker-kubernetes-and-co-part-ii","status":"publish","type":"post","link":"https:\/\/www.bandidor.info\/wp\/?p=1144","title":{"rendered":"Docker, Kubernetes, and Co. &#8211; part II"},"content":{"rendered":"\n<p>Here is our plan.  We will deploy our BPMN model to our Camunda platform from the previous article <a href=\"https:\/\/www.bandidor.info\/wp\/?p=1060\" title=\"Docker, Kubernetes, and Co.\">Docker, Kubernetes, and Co.<\/a> and make it run, simple.<br>You may also want to take a look at one of the articles published in the past: <a href=\"https:\/\/www.bandidor.info\/wp\/?p=857\" title=\"Camunda on Oracle Cloud \u2013 will it work?\">Camunda on Oracle Cloud \u2013 will it work?<\/a> <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">BPMN model<\/h2>\n\n\n\n<p>Let&#8217;s take the BPMN model from the Camunda Quick Start application. In the Modeler 5.0.0 it looks like this:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-7.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"497\" src=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-7-1024x497.png\" alt=\"\" class=\"wp-image-1146\" srcset=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-7-1024x497.png 1024w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-7-300x146.png 300w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-7-768x373.png 768w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-7-1536x746.png 1536w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-7.png 1681w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>Let&#8217;s deploy to our just installed Caminda engine on the k0s cluster, make sure the target IP is right:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-8.png\"><img loading=\"lazy\" decoding=\"async\" width=\"888\" height=\"808\" src=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-8.png\" alt=\"\" class=\"wp-image-1147\" srcset=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-8.png 888w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-8-300x273.png 300w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-8-768x699.png 768w\" sizes=\"auto, (max-width: 888px) 100vw, 888px\" \/><\/a><\/figure>\n\n\n\n<p>Here is the expected outcome:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-9.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"481\" src=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-9-1024x481.png\" alt=\"\" class=\"wp-image-1148\" srcset=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-9-1024x481.png 1024w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-9-300x141.png 300w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-9-768x361.png 768w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-9-1536x722.png 1536w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/04\/image-9.png 1854w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Let&#8217;s start our first process<\/h2>\n\n\n\n<p>We can do this using the Apache JMeter as described in <a href=\"https:\/\/www.bandidor.info\/wp\/?p=857\" title=\"Camunda on Oracle Cloud \u2013 will it work?\">Camunda on Oracle Cloud \u2013 will it work?<\/a> We just have to use the right endpoint and the process ID, which can be found in the Camunda Cockpit:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/05\/image.png\"><img loading=\"lazy\" decoding=\"async\" width=\"925\" height=\"216\" src=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/05\/image.png\" alt=\"\" class=\"wp-image-1153\" srcset=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/05\/image.png 925w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/05\/image-300x70.png 300w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/05\/image-768x179.png 768w\" sizes=\"auto, (max-width: 925px) 100vw, 925px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>And here is the process view in Camunda Cockpit:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/05\/image-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"904\" height=\"586\" src=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/05\/image-1.png\" alt=\"\" class=\"wp-image-1154\" srcset=\"https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/05\/image-1.png 904w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/05\/image-1-300x194.png 300w, https:\/\/www.bandidor.info\/wp\/wp-content\/uploads\/2022\/05\/image-1-768x498.png 768w\" sizes=\"auto, (max-width: 904px) 100vw, 904px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>The process is waiting in the Create Invoice activity because this is a Service Task, that we haven&#8217;t yet implemented.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Service Task Implementation<\/h2>\n\n\n\n<p>There are two parts to it. We will create a working standalone application and then learn how to containerize it and run it in our Kubernetes cluster. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Standalone Create Invoice Application<\/h3>\n\n\n\n<p>We will start with the version of the Service Task application created in <a href=\"https:\/\/www.bandidor.info\/wp\/?p=857\" title=\"Camunda on Oracle Cloud \u2013 will it work?\">Camunda on Oracle Cloud \u2013 will it work?<\/a>. Here is the repository: <a href=\"https:\/\/github.com\/olegme\/create-invoice-worker\">https:\/\/github.com\/olegme\/create-invoice-worker<\/a>.<\/p>\n\n\n\n<p>In addition to some code clean up we will change the following:<\/p>\n\n\n\n<p>Use only one worker, which will connect to the LoadBalancer external IP. If we have more than one Camunda instance deployed, the LoadBalancer will forward our request to the available engine.<\/p>\n\n\n\n<p>Add<code> node.js<\/code> module <code>pg <\/code>to connect to the PostgreSQL database.<\/p>\n\n\n\n<p>Add <code>node.js<\/code> module <code>dotenv<\/code>, which can be used to simulate environment variables via a local file for initial testing. In the Kubernetes environment, we will use these environment variables to pass the information, needed to connect to the database server.<\/p>\n\n\n\n<p>Let&#8217;s take a look at the code.<\/p>\n\n\n\n<p>It starts with the configuration variables, which are either read from the local <code>.env<\/code> file or provided as environment variables. Note line 28, this piece is needed to solve PostgreSQL login issues with password based logins:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; first-line: 19; highlight: [28]; title: ; notranslate\" title=\"\">\nrequire(&#039;dotenv&#039;).config();\n\nconst conf = {\n  host: process.env.PGHOST,\n  user: process.env.PGUSER,     \n  password: process.env.PGPASSWORD,\n  database: process.env.PGDATABASE,\n  port: process.env.PGPORT,\n  ssl: {\n    rejectUnauthorized: false,\n  }\n};\n<\/pre><\/div>\n\n\n<p>Then we create a Client (Camunda client) and subscribe to a topic:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; first-line: 95; title: ; notranslate\" title=\"\">\n\/\/ create a Client instance with custom configuration\nconst client = new Client(config);\nclient.on(&quot;complete:success&quot;, ({ id }) =&gt; {\n  console.log(`${client.options.workerId} --&gt; completed task ${id}`);\n  \n});\n\nclient.on(&quot;complete:error&quot;, ({ id }, e) =&gt; {\n  console.log(`${client.options.workerId} --&gt; couldn&#039;t complete task ${id}, ${e}`);\n});\n\n\n\/\/ susbscribe to the topic: &#039;invoiceCreator&#039;\nclient.subscribe(&quot;invoiceCreator&quot;, handler2);\n<\/pre><\/div>\n\n\n<p>And this handler is at the core of it:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; first-line: 32; highlight: [42,52,60]; title: ; notranslate\" title=\"\">\nconst handler2 = async function ({ task, taskService }) {\n  console.log(&#039;Starting handler...&#039;);\n  const client = new pg.Client(conf);\n\n  client\n  .connect()\n  .then(res =&gt; {\n      console.log(&quot;Database connected, making query&quot;);\n      return client.query(&#039;INSERT INTO invoiceCreator (task_id,worker_id) VALUES ($1,$2)&#039;,&#x5B;task.id,taskService.events.options.workerId]);\n  })\n  .then(res =&gt; {\n      client.end(console.log(&#039;Closed database client connection&#039;));\n      console.log(&quot;Query done, do the business logic and report success back to the engine&quot;);\n      const date = new Date();\n      const invoice = new File({ localPath: &quot;.\/assets\/invoice.txt&quot; }).load();\n      const minute = date.getMinutes();\n      const variables = new Variables().setAll({ invoice, date });\n\n      \/\/Store variables in the process scope\n      taskService.complete(task, variables);\n      console.log(&quot;Success reported to the engine&quot;);\n  })\n  .catch(err =&gt; {\n              console.error(&#039;Database error&#039;);\n              console.log(err.message);\n              client.end();\n              \/\/Report failure back to the engine\n              taskService.handleFailure(task,{\n                errorMessage: &quot;Database error&quot;,\n                errorDetails: &quot;code: &quot; + err.code + &quot;; severity: &quot; + err.severity + &quot;; message: &quot; + err.message,\n                retries: 0,\n                retryTimeout: 0\n              });\n              console.log(&quot;Failure reported back to the engine&quot;);\n\n          }\n      \n  );\n\n  console.log(&quot;Handler done&quot;);\n}\n<\/pre><\/div>\n\n\n<p>In line #42 we insert the data into the database. In lines #46 to #49 we prepare some business-relevant information to be stored in the task variable. In line #52 we report the success back to the Camunda engine and in line #60 we report the failure to the engine in case of database errors.<\/p>\n\n\n\n<p>And that&#8217;s all to it!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Containerize it!<\/h3>\n\n\n\n<p>This is a good starting point: <\/p>\n\n\n\n<p>Let&#8217;s change into the application directory. We already have our <code>package.json<\/code> file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; highlight: [9]; title: ; notranslate\" title=\"\">\n{\n  &quot;name&quot;: &quot;create-invoice-worker&quot;,\n  &quot;type&quot;: &quot;commonjs&quot;,\n  &quot;version&quot;: &quot;1.0.0&quot;,\n  &quot;description&quot;: &quot;&quot;,\n  &quot;main&quot;: &quot;index.js&quot;,\n  &quot;scripts&quot;: {\n    &quot;test&quot;: &quot;echo \\&quot;Error: no test specified\\&quot; &amp;&amp; exit 1&quot;,\n\t&quot;start&quot;: &quot;node index.js&quot;\n  },\n  &quot;author&quot;: &quot;&quot;,\n  &quot;license&quot;: &quot;ISC&quot;,\n  &quot;dependencies&quot;: {\n    &quot;camunda-external-task-client-js&quot;: &quot;^2.2.0&quot;,\n    &quot;dotenv&quot;: &quot;^16.0.0&quot;,\n    &quot;pg&quot;: &quot;^8.7.3&quot;\n  }\n}\n<\/pre><\/div>\n\n\n<p>Line #9 had to be added manually, it will tell the container which command to execute when started.<\/p>\n\n\n\n<p>Next is the <code>Docker <\/code>file. Everything looks the same as in the example from the above URL with one exception, we don&#8217;t need the <code>EXPOSE <\/code>stanza. Our service task application is not a server and doesn&#8217;t expose any ports to the outside. Here is the file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nFROM node:16\n\n# Create app directory\nWORKDIR \/usr\/src\/app\n\n# Install app dependencies\n# A wildcard is used to ensure both package.json AND package-lock.json are copied\n# where available (npm@5+)\nCOPY package*.json .\/\n\nRUN npm install\n# If you are building your code for production\n# RUN npm ci --only=production\n\n# Bundle app source\nCOPY . .\n\n#EXPOSE 8080\nCMD &#x5B; &quot;node&quot;, &quot;index.js&quot; ]\n<\/pre><\/div>\n\n\n<p>Obviously, we need to adjust the last line to run the proper command when started.<\/p>\n\n\n\n<p>Last piece &#8211; .<code>dockerignore<\/code> file. We already have it in our working directory, we will just extend it to make sure we don&#8217;t deploy the copies of all npm modules:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n.gitignore\n.env\nnode_modules\nnpm-debug.log\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">Build the image<\/h4>\n\n\n\n<p>We will use Docker Desktop on the local Windows laptop. It cannot be used in parallel with Oracle VirtualBox and we will need to apply some changes to Windows Registry to be able to switch between the two and the laptop has to be restarted after the changes. Also, the command prompt has to be run in Administrator mode. Here is the cheat sheet:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>bcdedit \/set hypervisorlaunchtype off - VBox\nbcdedit \/set hypervisorlaunchtype auto - Docker<\/code><\/pre>\n\n\n\n<p>Before we start with the build, we need to start Docker, otherwise, we will see an error message. Something like <code>error during connect: This error may indicate that the docker daemon is not running.:<\/code>&#8230;<\/p>\n\n\n\n<p>Let&#8217;s build it:<\/p>\n\n\n\n\n<pre class=\"terminal\"><code><\/p>\n<p>user@computer$ docker build . -t olegme\/create-invoice-worker<br>#2 [internal] load .dockerignore<br>&#8230;<\/p>\n<p>#10 exporting to image<br>#10 sha256:e8c613e07b0b7ff33893b694f7759a10d42e180f2b4dc349fb57dc6b71dcab00<br>#10 exporting layers<br>#10 exporting layers 0.3s done<br>#10 writing image sha256:b27be6ffd05d2b774290022496706f0f07fc9f5bb24c79abd987343a04203d67 0.0s done<br>#10 naming to docker.io\/olegme\/create-invoice-worker done<br>#10 DONE 0.4s<\/p>\n<p>Use &#8216;docker scan&#8217; to run Snyk tests against images to find vulnerabilities and learn how to fix them<\/p>\n<p><\/code><\/pre>\n\n\n\n\n<p>Everything went good, let&#8217;s check the image:<\/p>\n\n\n\n\n<pre class=\"terminal\"><code><\/p>\n<p><code>user@computer$ docker images<\/code><br><code>REPOSITORY&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TAG&nbsp; &nbsp; IMAGE ID&nbsp; &nbsp; &nbsp;CREATED&nbsp; &nbsp; &nbsp; &nbsp; SIZE<\/code><br><code>olegme\/create-invoice-worker latest b27be6ffd05d 22 minutes ago 913MB<\/code><br><code>postgres&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;latest 5b21e2e86aab 3 weeks ago&nbsp; &nbsp; 376MB<\/code><br><code>camunda\/camunda-bpm-platform latest 905a1cbf80c3 2 months ago&nbsp; &nbsp;307MB<\/code><\/p>\n<p><\/code><\/pre>\n\n\n\n\n<p>Let&#8217;s start it and see what happens:<\/p>\n\n\n\n\n<pre class=\"terminal\"><code><br>user@computer$ docker run olegme\/create-invoice-worker<br>\u2713 subscribed to topic invoiceCreator<br>\u2716 polling failed with RequestError: connect ECONNREFUSED 127.0.0.1:8080<br>\u2716 polling failed with RequestError: connect ECONNREFUSED 127.0.0.1:8080<br>\u2716 polling failed with RequestError: connect ECONNREFUSED 127.0.0.1:8080<br>\u2716 polling failed with RequestError: connect ECONNREFUSED 127.0.0.1:8080<br><\/code><\/pre>\n\n\n\n\n<p>What does the above result mean? Everything works as expected. We haven&#8217;t included the .env file in our image and also haven&#8217;t provided any environment variables. So, our small application is using some default values and it tries to connect to the localhost, which obviously fails as we don&#8217;t run any Camunda engine on our local Windows laptop.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Push to the repository<\/h4>\n\n\n\n<p>There are ways to use the image we just created similar to a local file, but it&#8217;s a bit complicated and so we go the simplest way. We will push it to the private remote repository on <a href=\"https:\/\/hub.docker.com\/repository\/docker\/olegme\/create-invoice-worker\">https:\/\/hub.docker.com\/repository\/docker\/olegme\/create-invoice-worker<\/a>. For that, we will create our private repository with the name <code>olegme\/create-invoice-worker<\/code>, which will be protected with the user name and a password. Let&#8217;s push our image there:<\/p>\n\n\n\n\n<pre class=\"terminal\"><code><\/p>\n<p>user@computer$ docker login &#8211;username=olegme -p XXX<br>WARNING! Using &#8211;password via the CLI is insecure. Use &#8211;password-stdin.<br>Login Succeeded<\/p>\n<p>&nbsp;<\/p>\n<p>user@computer$ docker push olegme\/create-invoice-worker<br>Using default tag: latest<br>The push refers to repository [docker.io\/olegme\/create-invoice-worker]<\/p>\n<p>&#8230;<\/p>\n<p>latest: digest: sha256:4d3adae53a09676b1bdc32eb4ba6df1b6b047d3f6f18462d83d4604688c2ccd8 size: 3049<\/p>\n<p><\/code><\/pre>\n\n\n\n\n<p>We are done! In the next step, we will deploy our image to our Kubernetes cluster and it will be pulled from our private repository.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Run it on Kubernetes<\/h3>\n\n\n\n<p>A lot of preparation work ahead&#8230;<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"Create-a-secret-to-pull-the-image-from-the-repository\">Create a secret to pull the image from the repository<\/h4>\n\n\n\n<p>Just follow the steps here: <a href=\"https:\/\/kubernetes.io\/docs\/tasks\/configure-pod-container\/pull-image-private-registry\/\">https:\/\/kubernetes.io\/docs\/tasks\/configure-pod-container\/pull-image-private-registry\/<\/a><\/p>\n\n\n\n\n<pre class=\"terminal\"><code><\/p>\n<p><code>root@master-node:~# k0s kubectl create secret docker-registry regcred --docker-server=https:\/\/index.docker.io\/v1\/ --docker-username=olegme --docker-password=XXX --docker-email=olegme@gmx.de<\/code><br><code>secret\/regcred created<\/code><\/p>\n<p><\/code><\/pre>\n\n\n\n\n<p>Let&#8217;s check what is in the secret:<\/p>\n\n\n\n\n<pre class=\"terminal\"><code><\/p>\n<p><code>root@master-node:~# k0s kubectl get secret regcred --output=yaml<\/code><br><code>apiVersion: v1<\/code><br><code>data:<\/code><br><code>.dockerconfigjson: ey...<\/code><br><code>kind: Secret<\/code><br><code>metadata:<\/code><br><code>creationTimestamp: \"2022-06-23T16:08:31Z\"<\/code><br><code>name: regcred<\/code><br><code>namespace: default<\/code><br><code>resourceVersion: \"226674\"<\/code><br><code>uid: 027c386f-b0fc-47f2-a9cf-8abd61205bb3<\/code><br><code>type: kubernetes.io\/dockerconfigjson<\/code><br><code>root@master-node:~# k0s kubectl get secret regcred --output=\"jsonpath={.data.\\.dockerconfigjson}\" | base64 --decode<\/code><br><code>{\"auths\":{\"https:\/\/index.docker.io\/v1\/\":{\"username\":\"olegme\",\"password\":\"XXX\",\"email\":\"olegme@gmx.de\",\"auth\":\"b...\"}}}<\/code><\/p>\n<p><\/code><\/pre>\n\n\n\n\n<h4 class=\"wp-block-heading\">Deploy the application<\/h4>\n\n\n\n<p>We will deploy our application now. This page is useful: <a href=\"https:\/\/learnk8s.io\/deploying-nodejs-kubernetes\" title=\"Deploying Node.js apps in a local Kubernetes cluster\">Deploying Node.js apps in a local Kubernetes cluster<\/a>. And also this one <a href=\"https:\/\/kubernetes.io\/docs\/tasks\/inject-data-application\/define-environment-variable-container\/\">Define Environment Variables for a Container<\/a><\/p>\n\n\n\n<p>Let&#8217;s take a look at our manifest file now:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: yaml; highlight: [17,20,32]; title: ; notranslate\" title=\"\">\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: create-invoice-worker\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: create-invoice-worker\n  template:\n    metadata:\n      labels:\n        app: create-invoice-worker\n    spec:\n      containers:\n      - name: create-invoice-worker-container\n        image: olegme\/create-invoice-worker\n        imagePullPolicy: Always\n        env:\n        - name: BASE_URL\n          value: &quot;http:\/\/camunda-bpm-platform:8080\/engine-rest&quot;\n        - name: PGUSER\n          value: camunda\n        - name: PGHOST\n          value: acid-pgtest1\n        - name: PGPASSWORD\n          value: KvfW9teVc6SOhQ92Jsrd7AWLBlh4jyDx4uRX4CUBkwnem0ntw2bD1dBWrvJTmFaI\n        - name: PGDATABASE\n          value: camunda\n        - name: PGPORT\n          value: &quot;5432&quot;\n      imagePullSecrets:\n      - name: regcred\n<\/pre><\/div>\n\n\n<p>Line #17 specifies the name of the image, line #32 points to the secret we created and starting from line #20 we provide the environment variables to the container, that are needed to configure our application. Better approach would be to store these values as secret value, but we take a shortcut to save time.<\/p>\n\n\n\n<p>Let&#8217;s deploy it. We try a dry-run first to make sure our syntax is OK:<\/p>\n\n\n\n\n<pre class=\"terminal\"><code><\/p>\n<p><code>root@master-node:\/home\/olegme\/k0s\/manifests# k0s kubectl apply -f create-invoice-worker.yaml --dry-run='server'<\/code><br><code>deployment.apps\/create-invoice-worker configured (server dry run)<\/code><\/p>\n<p><code><\/code><br><code>root@master-node:\/home\/olegme\/k0s\/manifests# k0s kubectl apply -f create-invoice-worker.yaml<\/code><br><code>deployment.apps\/create-invoice-worker configured<\/code><\/p>\n<p><code><\/code><br><code>root@master-node:\/home\/olegme\/k0s\/manifests# k0s kubectl get pods<\/code><br><code>NAME READY STATUS RESTARTS AGE<\/code><br><code>acid-pgtest1-0 1\/1 Running 4 (12d ago) 38d<\/code><br><code>camunda-bpm-platform-755589c9b7-m829b 1\/1 Running 0 158m<\/code><br><code>create-invoice-worker-b95b889bb-gmmk2 1\/1 Running 0 2m4s<\/code><br><code>nfs-subdir-external-provisioner-57c8c954df-n4w6c 1\/1 Running 7 (18h ago) 38d<\/code><br><code>postgres-operator-855655d957-ldqdn 1\/1 Running 10 (12d ago) 38d<\/code><br><code>postgres-operator-ui-5f4f44cf97-bmgq6 1\/1 Running 2 (12d ago) 38d<\/code><\/p>\n<p><\/code><\/pre>\n\n\n\n\n<p>And this works like a charm. We can start one process using Camunda Modeler and it just goes through. This is what we see in the logfile of our <code>create-invoice-worker-b95b889bb-gmmk2<\/code> POD:<\/p>\n\n\n\n\n<pre class=\"terminal\"><code><\/p>\n<p><code>root@master-node:\/home\/olegme\/k0s\/manifests# k0s kubectl logs create-invoice-worker-b95b889bb-gmmk2<\/code><br><code>\u2713 subscribed to topic invoiceCreator<\/code><br><code>Starting handler...<\/code><br><code>Handler done<\/code><br><code>Database connected, making query<\/code><br><code>Closed database client connection<\/code><br><code>Query done, do the business logic and report success back to the engine<\/code><br><code>Success reported to the engine<\/code><br><code>\u2713 completed task cb38f6ae-d5fe-11ec-ace9-7eddd7666a97<\/code><br><code>Client_1 --&gt; completed task cb38f6ae-d5fe-11ec-ace9-7eddd7666a97<\/code><br><\/code><\/pre>\n\n\n\n\n<p>We can see that our Service Task client is a bit chatty, but we leave it as is for now, it does its job and this is it.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Let&#8217;s wrap it up<\/h1>\n\n\n\n<p>This was a two-piece instalment, where we described steps to deploy Camunda 7 Platform on a Kubernetes cluster, including a <code>node.js<\/code>-based Service Task client and a PostgreSQL database cluster. We were able to deploy and run a simple BPMN model, possible improvements and performance tests will be covered in future articles.<\/p>\n\n\n\n<p>Next immediate goal &#8211; do the same with Camunda 8, stay tuned!<\/p>\n\n\n","protected":false},"excerpt":{"rendered":"<p>Here is our plan. We will deploy our BPMN model to our Camunda platform from the previous article Docker, Kubernetes, and Co. and make it run, simple.You may also want to take a look at one of the articles published in the past: Camunda on Oracle Cloud \u2013 will it work? BPMN model Let&#8217;s take&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[34,39],"tags":[42,41],"class_list":["post-1144","post","type-post","status-publish","format-standard","hentry","category-camunda","category-kubernetes","tag-bpmn","tag-kubernetes"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p2EszU-is","_links":{"self":[{"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=\/wp\/v2\/posts\/1144","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1144"}],"version-history":[{"count":27,"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=\/wp\/v2\/posts\/1144\/revisions"}],"predecessor-version":[{"id":1415,"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=\/wp\/v2\/posts\/1144\/revisions\/1415"}],"wp:attachment":[{"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1144"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1144"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bandidor.info\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1144"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}