<p>From my standpoint the best approach would be to:</p>

<ol>
<li>Separate build process using Jenkins that would build NodeJS project into distribution and wrap it into Docker image</li>
<li>Spin up Docker registry that would accumulate Docker images from Jenkins (this registry should be accessible from Kubernetes cluster)</li>
<li>Move environment variables to Jenkins secrets or use separate tool to collect and combine configurations from external Git repo (we use Spring Cloud Config through REST API to collect json/yml definitions for every application in every environment)</li>
</ol>

<p>Using Jenkins you can configure continuous delivery system based on generic Pipelines. Possible flow would be:</p>

<ol>
<li>Developers finish their work in correspondence to GitFlow (latest Pull Request merged into Release branch)</li>
<li>Webhook triggers Jenkins pipeline:

<ul>
<li>Stage 1: collect environment definition</li>
<li>Stage 2: build NodeJS app using npm</li>
<li>Stage 3: build Nginx Docker image with distribution</li>
<li>Stage 4: push Docker image to Docker registry</li>
<li>Stage 5: deploy/update service in Kubernetes using standard definitions</li>
</ul></li>
<li>Notifications send in regard of results</li>
</ol>

<p>This process might be visualized using Rancher. I can answer your questions in chat.</p>

CC BY-SA 3.0
2018-02-02T13:28:36.413Z
So you're proposing to have one image per environment ? That's a workaround consuming a bunch of space for just some variables...
Tensibai
CC BY-SA 3.0
2018-02-02T14:34:25.823Z
@Tensibai u r talking about ~15mb images per each environment. With rotation and additional janitor service 1 Gb would be enough to handle all projects.
Maksim
CC BY-SA 3.0
2018-02-02T14:35:15.377Z
Based on this solution, how would you produce multiple "configs" of an app, for example one for "staging" and one for "prod"? (lets pretend that there's a setting called "BackendApi" that differs, for instance.
Trondh
CC BY-SA 3.0
2018-02-02T14:41:05.657Z
@Trondh as I said we use Spring Cloud Config. What it does is for each application it gets basic config and applies additional configurations from `profiles`. E.g. if you request `https://config/backend.yml` you'll receive configuration from `application.yml` and additional properties from `backend.yml`. For `https://config/backend-stage.yml` it will respond with properties applied by `application.yml` <- `backend.yml` <- `backend-stage.yml`.
Maksim
CC BY-SA 3.0
2018-02-02T14:55:51.047Z
@Maksim 15MB for a few bytes of text is a waste, at some point with hundreds of microservices on various environment with various version on them it will become TB of data for the same few bytes at origin. That's a wrong path in my opinion if I got right what you're proposing, as you can't ensure the image in QA and prod is really the same as they are from two builds (even with same code, they won't be the same id and validation becomes a mess). But maybe I got it rong and you have only one image and configuration at spin up of the pod, if it is so I didn't understood your post properly.
Tensibai
CC BY-SA 3.0
2018-02-02T15:28:50.637Z
@Tensibai so you think that the problem is in architecture, right? In mutability of distribution caused by different configuration options. From my standpoint this problem can be solved by canary releases only. And we need to separate backend configuration from frontend configuration. Backend configuration should be fixed in terms of technologies used but may vary in terms of URLs, credentials, etc. Frontend might be configured during the build or during the bootstrap phase in browser by requesting special configuration REST API. Latter would introduce possible security vulnerabilities.
Maksim
CC BY-SA 3.0
2018-02-02T15:43:17.557Z
@Maksim would be happy to discuss it in [chat] because I've the feeling we're on the same line but not exactly understanding each other :)
Tensibai
CC BY-SA 3.0
2018-02-02T21:15:41.423Z
Thanks for clarifying @Maksim. The pattern you describe with spring cloud is also supported by some JS frameworks such as Aurelia's aurelia-config. I am not familiar with spring cloud config specifically, but we have discussed internally that it might make sense to move the "dynamic" parts of an app's config over to some kind of config api, and your example makes very much sense in that regard. This would require a major rewrite of or apps tho, but there might not be any way around that - simply the fact that we write apps expecting config variables present at "build" seems suboptimal.
Trondh
CC BY-SA 3.0
2018-02-05T10:17:56.693Z
@Trondh if you are satisfied with my answer please mark it as correct. Thanks.
Maksim
CC BY-SA 3.0
2018-02-02T13:13:04.110Z
2018-02-02T13:13:04.110Z
Maksim
<p>This comes up from time to time in several of our dev teams, without us having figured out the "right" way:</p>

<p>We use a lot of react-based webapplications that "compile" into static websites that are just a few html, js and css files.</p>

<p>However, the "building" of these apps take a number of variables that enable/disable feature flags, configure backend urls, etc.
This means that we cannot "build" a binary in the traditional sense and just apply a config file at deploy-time - the "build" itself needs to have these environment-specific variables set, and thus the only time we can "build" is at deploy-time.</p>

<p>For now we solve this by injecting the required environment variables into the Docker container and run a start cmd along the lines of</p>

<pre><code>npm build &amp;&amp; nginx run
</code></pre>

<p>This has a couple of disadvantages:</p>

<ol>
<li>The build process takes a <em>lot</em> of cpu/memory relative to the run-time requirements of the container. That means we need to scale the container for the build process instead of the run-time requirements - which feels wrong</li>
<li>Build failures are hard to "track". We can use healthchecks in Kubernetes, but if a build takes 2 minutes we still have to wait for 3 mins (1 extra for safety) before we can start testing the container's healthcheck endpoint to see if its alive.</li>
<li>Deployments might take a long time: If we configure Kubernetes to do a "serial" deployment, it will start each pod and wait for the "initialDelay" period of 2-3 minutes before starting the next. This means that we're easily looking at a 10-min deploy time if the deployment is scaled to 3-4 pods.</li>
</ol>

<p>This all feels very suboptimal to me. I'd be very interested in hearing how the community solves the "build at deploy-time" conundrum with modern javascript webapps.</p>

<p>I realize that for Kubernetes we could use "init-containers" that perform the build, places the artiacts in persistent storage and then have the app containers simply pull from persistent storage during startup, but this still feels more like "bypassing" the problem than solving the root issue.</p>

CC BY-SA 3.0
2018-02-02T15:41:03.773Z
Sorry, I don't understand - why are you running `npm build` at runtime instead of during the container build?
Xiong Chiamiov
CC BY-SA 3.0
2018-02-02T15:47:23.843Z
@XiongChiamiov I assume frontend configuration is done during the build and is not modifiable without rebuilding (I'd argue it should be separated in an included file which, at worst, could be 'sed' at container startup)
Tensibai
CC BY-SA 3.0
2018-02-02T21:20:08.857Z
exactly. `npm build` is just an arbitrary command. Could be `react build` or `sanity build` or whatever the JS framework expects. We're forced to do it at container deploy because that's when we know the environment to `build` for.
Trondh
<docker><deployment><kubernetes><javascript>
Strategy for deploying/hosting javascript-based static websites in containers
Trondh
# Strategy for deploying/hosting javascript-based static websites in containers

(Asked by: username_0 on 2018-02-02)

This comes up from time to time in several of our dev teams, without us having figured out the "right" way:
We use a lot of react-based webapplications that "compile" into static websites that are just a few html, js and css files.
However, the "building" of these apps take a number of variables that enable/disable feature flags, configure backend urls, etc.
This means that we cannot "build" a binary in the traditional sense and just apply a config file at deploy-time - the "build" itself needs to have these environment-specific variables set, and thus the only time we can "build" is at deploy-time.
For now we solve this by injecting the required environment variables into the Docker container and run a start cmd along the lines of
npm build && nginx run

This has a couple of disadvantages:

The build process takes a lot of cpu/memory relative to the run-time requirements of the container. That means we need to scale the container for the build process instead of the run-time requirements - which feels wrong
Build failures are hard to "track". We can use healthchecks in Kubernetes, but if a build takes 2 minutes we still have to wait for 3 mins (1 extra for safety) before we can start testing the container's healthcheck endpoint to see if its alive.
Deployments might take a long time: If we configure Kubernetes to do a "serial" deployment, it will start each pod and wait for the "initialDelay" period of 2-3 minutes before starting the next. This means that we're easily looking at a 10-min deploy time if the deployment is scaled to 3-4 pods.

This all feels very suboptimal to me. I'd be very interested in hearing how the community solves the "build at deploy-time" conundrum with modern javascript webapps.
I realize that for Kubernetes we could use "init-containers" that perform the build, places the artiacts in persistent storage and then have the app containers simply pull from persistent storage during startup, but this still feels more like "bypassing" the problem than solving the root issue.

**Tags:** docker, deployment, kubernetes, javascript

> **Comments:**
> **username_1**: Sorry, I don't understand - why are you running `npm build` at runtime instead of during the container build?  
> **username_2**: @XiongChiamiov I assume frontend configuration is done during the build and is not modifiable without rebuilding (I'd argue it should be separated in an included file which, at worst, could be 'sed' at container startup)  
> **username_0**: exactly. `npm build` is just an arbitrary command. Could be `react build` or `sanity build` or whatever the JS framework expects. We're forced to do it at container deploy because that's when we know the environment to `build` for.  

---

## 1 answers

### Answer by username_3 on 2018-02-02. Score: 5

From my standpoint the best approach would be to:

1. Separate build process using Jenkins that would build NodeJS project into distribution and wrap it into Docker image
2. Spin up Docker registry that would accumulate Docker images from Jenkins (this registry should be accessible from Kubernetes cluster)
3. Move environment variables to Jenkins secrets or use separate tool to collect and combine configurations from external Git repo (we use Spring Cloud Config through REST API to collect json/yml definitions for every application in every environment)

Using Jenkins you can configure continuous delivery system based on generic Pipelines. Possible flow would be:

1. Developers finish their work in correspondence to GitFlow (latest Pull Request merged into Release branch)
2. Webhook triggers Jenkins pipeline:
   * Stage 1: collect environment definition
   * Stage 2: build NodeJS app using npm
   * Stage 3: build Nginx Docker image with distribution
   * Stage 4: push Docker image to Docker registry
   * Stage 5: deploy/update service in Kubernetes using standard definitions
3. Notifications send in regard of results

This process might be visualized using Rancher. I can answer your questions in chat.

> **Comments:**
> **username_2**: So you're proposing to have one image per environment ? That's a workaround consuming a bunch of space for just some variables...  
> **username_3**: @username_2 u r talking about ~15mb images per each environment. With rotation and additional janitor service 1 Gb would be enough to handle all projects.  
> **username_0**: Based on this solution, how would you produce multiple "configs" of an app, for example one for "staging" and one for "prod"? (lets pretend that there's a setting called "BackendApi" that differs, for instance.  
> **username_3**: @username_0 as I said we use Spring Cloud Config. What it does is for each application it gets basic config and applies additional configurations from `profiles`. E.g. if you request `https://config/backend.yml` you'll receive configuration from `application.yml` and additional properties from `backend.yml`. For `https://config/backend-stage.yml` it will respond with properties applied by `application.yml` <- `backend.yml` <- `backend-stage.yml`.  
> **username_2**: @username_3 15MB for a few bytes of text is a waste, at some point with hundreds of microservices on various environment with various version on them it will become TB of data for the same few bytes at origin. That's a wrong path in my opinion if I got right what you're proposing, as you can't ensure the image in QA and prod is really the same as they are from two builds (even with same code, they won't be the same id and validation becomes a mess). But maybe I got it rong and you have only one image and configuration at spin up of the pod, if it is so I didn't understood your post properly.  
> **username_3**: @username_2 so you think that the problem is in architecture, right? In mutability of distribution caused by different configuration options. From my standpoint this problem can be solved by canary releases only. And we need to separate backend configuration from frontend configuration. Backend configuration should be fixed in terms of technologies used but may vary in terms of URLs, credentials, etc. Frontend might be configured during the build or during the bootstrap phase in browser by requesting special configuration REST API. Latter would introduce possible security vulnerabilities.  
> **username_2**: @username_3 would be happy to discuss it in [chat] because I've the feeling we're on the same line but not exactly understanding each other :)  
> **username_0**: Thanks for clarifying @username_3. The pattern you describe with spring cloud is also supported by some JS frameworks such as Aurelia's aurelia-config. I am not familiar with spring cloud config specifically, but we have discussed internally that it might make sense to move the "dynamic" parts of an app's config over to some kind of config api, and your example makes very much sense in that regard. This would require a major rewrite of or apps tho, but there might not be any way around that - simply the fact that we write apps expecting config variables present at "build" seems suboptimal.  
> **username_3**: @username_0 if you are satisfied with my answer please mark it as correct. Thanks.  

---

