How many times have you been facing a situation when you have some dockerized application which stores its configuration on the filesystem?
What's a problem you may ask.
In a usual Docker world we would prefer to override or even initialize configuration parameters over environment variables.
Why is that ?
By design all Docker containers are stateless.
That means, by default, working with stateful techniques in Docker (filesystem is one of those) increases complexity and operational costs.
Let's assume we have a single-page-application (ReactJS). That application consists of some usual static content
:
- HTML
- CSS
- JavaScript
- Assets (images, icons, etc.)
We have 3
environments:
- development
- qa
- production
And using Adobe Analytics
and Google Analytics
for collecting data of user activities.
Important Note
We must segregate user activities data between environments, meaning each environment has its unique Adobe Analytics URL
and Google Analytics URL
defined in index.html
file.
...
<script
src="//assets.adobedtm.com/some/path/analytics-for-environment.min.js"
async
></script>
<script
src="test.url.com/analytics.js?utm_source=newsletter&utm_medium=banner&utm_campaign=spring_sale"
async
></script>
...
For usual web applications we normally use nginx
Docker image and populate it with static content
.
FROM nginx:alpine
# copy nginx configuration
COPY nginx/default.conf /etc/nginx/conf.d/
# copy static content
COPY dist/ /html
# run nginx
CMD ["/bin/sh", "-c", "nginx -g 'daemon off;'"]
How would you deploy that Docker image
and adjust Adobe Analytics URL
and Google Analytics URL
according to the specifc environment
?
Option 1
Compile static content
per environment
.
- very time consuming and inefficient
Option 2
Set environment variables
and utilize bash
.
- tricky, complex and messy
Option 3
Many other options
are available, but with operational and complexity overheads
.
This is where the sev
comes handy.
sev
is a CLI utility written in Golang.
...
<script
src="_{ADOBE_ANALYTICS_URL}_"
async
></script>
<script
src="_{GOOGLE_ANALYTICS_URL}_"
async
></script>
...
# First, let's define and initialize environment variables:
export ADOBE_ANALYTICS_URL=//assets.adobedtm.com/sc99ai/a9da01/launch-dif9-staging.min.js
export GOOGLE_ANALYTICS_URL=//test.url.com/?utm_source=newsletter&utm_medium=banner&utm_campaign=spring_sale
export VAR_NAMES_STORAGE=ADOBE_ANALYTICS_URL,GOOGLE_ANALYTICS_URL
# execute sev
sev /html/index.html
##### START SEV ANNOUNCEMENT #####
## Target environment variables
ADOBE_ANALYTICS_URL=//assets.adobedtm.com/sc99ai/a9da01/launch-dif9-staging.min.js
GOOGLE_ANALYTICS_URL=//test.url.com/?utm_source=newsletter&utm_medium=banner&utm_campaign=spring_sale
## Sets of values
N/A
## Mode
Pulling values for variables from environment
###### END SEV ANNOUNCEMENT ######
2020/04/20 13:01:51 INFO: sev succeeded: /html/index.html
...
<script
src="//assets.adobedtm.com/sc99ai/a9da01/launch-dif9-staging.min.js"
async
></script>
<script
src="//test.url.com/?utm_source=newsletter&utm_medium=banner&utm_campaign=spring_sale"
async
></script>
...
The final Docker image implemented with sev
makes maintenance of our application's parameters simple, straight-forward and informative
FROM nginx:alpine
# copy nginx configuration
COPY nginx/default.conf /etc/nginx/conf.d/
# copy static content
COPY dist/ /html
# developers explicitly define and maintain VAR_NAMES_STORAGE in Dockerfile so that operations engineers could always use it as a reference and source of truth for the list of configuration parameters
ENV VAR_NAMES_STORAGE ADOBE_ANALYTICS_URL,GOOGLE_ANALYTICS_URL
# run sev and then run nginx
CMD ["/bin/sh", "-c", "sev /html && nginx -g 'daemon off;'"]
sev
/html/index.html
/html/index.html
isdestination
destination
can bepath to a file
- or
path to a directory
In this mode we simply replace placeholders
in files
with the corresponding environment variables values
.
We have a sevice environment variables
which are used to initialize sev
.
Stores the list of environment variables names
which should be processed by sev
.
export VAR_NAMES_STORAGE=ADOBE_ANALYTICS_URL,GOOGLE_ANALYTICS_URL
# you can separate environment variable names with comma
sev
recursively processes each file provided indestination
- in each file
sev
replaces placeholders like_{ENVIRONMENT_VARIABLE}_
with the corresponding value ofENVIRONMENT_VARIABLE
In this mode we provide a JSON structure
which contains sets of values of environment variables
segregated by identifier
.
Serves the same purpose as in simple mode
export environment-variable-4=some-value-4
export check=in_environment
export VAR_NAMES_STORAGE=environment-variable-4,check
Stores the JSON structure
which contains the sets of values
.
{
"set-of-values-identifier-1": {
"variable-1": "the-value-v1-mk1"
},
"set-of-values-identifier-2": {
"variable-1": "the-value-v1-mk2",
"variable-2": "the-value-v2"
},
"set-of-values-identifier-3": {
"variable-1": "the-value-v1-mk3",
"variable-3": "the-value-v3"
}
}
{
"akamai-prod":{
"check":"in-structure",
"cntf_space":"9bnj36vfwq8e",
"cntf_token":"asd-AsdAsdAsdAsdAsdD-_AsdAsdAsdAsdAsd-As60",
"cntf_env":"master",
"chtr_log_level":0
},
"akamai":{
"cntf_space":"asd234asd123asd",
"cntf_token":"asd-AsdAsdAsdAsdAsdD-_AsdAsdAsdAsdAsd-As60",
"cntf_env":"chatr-qa1",
"chtr_log_level":1
},
"dev":{
"cntf_space":"asd456asd123asd",
"cntf_token":"asd-AsdAsdAsdAsdAsdD-_AsdAsdAsdAsdAsd-As60",
"cntf_env":"chatr-dev",
"chtr_log_level":1
}
}
Contains the identifier
for preferred set of values
.
export VAR_VALUES_SETS_CHOSEN_ID=akamai-prod
##### START SEV ANNOUNCEMENT #####
## Target environment variables
environment-variable-4 = some-value-4
check = in_environment
## Sets of values
► akamai-prod
akamai
dev
## Fetched values
chtr_log_level = 0
check = in-structure
cntf_space = asd234asd123asd
cntf_token = asd-AsdAsdAsdAsdAsdD-_AsdAsdAsdAsdAsd-As60
cntf_env = master
## Mode
Pulling sets of values for variables from JSON structure saved at {env.VAR_VALUES_SETS_STORAGE}
###### END SEV ANNOUNCEMENT ######
2020/04/21 15:01:00 INFO: sev succeeded: test-dir\sample_to_process
2020/04/21 15:01:00 INFO: sev succeeded: test-dir\sub\sample_to_process
Process finished with exit code 0
If you have environment variable
intialized both in JSON struct
and in the environment
then the value
taken from environment
will prevail and get higher priority.