How to pass them through Jenkins and Kubernetes
Overview
The nice thing about operating system environment variables is they can be reached from practically anywhere (in script files, within Java program, within a Python program, etc.).
One of the factors for a 12 Factor App is Config “Store config in the environment”, as in the operating system (CoreOS Linux, Windows, MacOS, etc.):
“The twelve-factor app stores config in environment variables (often shortened to env vars or env). Env vars are easy to change between deploys without changing any code; unlike config files, there is little chance of them being checked into the code repo accidentally; and unlike custom config files, or other config mechanisms such as Java System Properties, they are a language- and OS-agnostic standard.”
Information likely to vary between deploys (in staging, production, developer environments, etc) include:
- Different resource handles to the database, Memcached, and other backing services
- Credentials to external services such as Amazon S3 or Twitter
- Per-deploy values such as the canonical hostname for the deploy
- Within app programs, flags that have one set of pods serve one type of customer while another set servers another with an alternatve functionality.
Alternative: static files
An alternative to operating system environment variables is writing program code to read a file such as properties.xml
. But that means values from them are static across multiple instances, or a mechnism needs to be crafted to change the contents of the file before use.
MacOS
-
On MacOS Terminals, the path of folders where the operating system searches for executables is defined in an environment variables, listed by this command:
echo $PATH echo $HOME
There are other default environment variables.
-
To set a custom environment variable, edit the script executed whenever a Terminal window is created:
subl ~/.bash_profile
-
To add an environment variable in front of the existing $PATH:
export PATH="$HOME/gits:$PATH"
Windows
On Windows 7, the GUI displaying system variables are MyComputer -> Properties -> Advanced -> Environment Variables -> System Variables
On Windows 10:
- Click the Windows Start logo on the keyboard or on the bottom of the screen.
- In Search, search for and then select: System (Control Panel)
- Click the Advanced system settings link.
- Click Environment Variables. In the section System Variables, find the PATH environment variable and select it.
- Click Edit.
- If the PATH environment variable does not exist, click New.
- In the Edit System Variable (or New System Variable) window, specify the value of the PATH environment variable. Click OK.
- Close all remaining windows by clicking OK.
- Reopen Command prompt window, and run your java code.
Unlike PowerShell, the traditional cmd.exe CLI window doesn’t have its own variables. It only knows about environment variables. To display the environment path:
echo %PATH%
PowerShell also provides access to environment variables through the Env: drive.
Get-ChildItem Env:
PROTIP: (PowerShell extends the concept of drive to store more kinds of data than just files.) PowerShell doesn’t have a built-in way to run a Cmd.exe shell script (batch file) and retain the environment variables set or changed by the script. In addition, in PowerShell there’s no equivalent to the Setlocal
command to revert environment variable changes. The Invoke-CmdScript, Get-Environment, and Save-Environment functions remove these limitations and give PowerShell more power over environment variables.
See http://www.itprotoday.com/management-mobility/take-charge-environment-variables-powershell
In Java programs
-
In Java programs, obtain the name of the variable to read system variables:
System.getenv(String);
The code returns an unmodifiable string map view of the current system environment.
-
Print to stdout the contents of default environment variable JAVA_HOME where Java is installed:
System.out.println(System.getenv("JAVA_HOME"));
PROTIP: A null value is shown if a custom system variable does not exist:
-
If the program should not run unless a custom variable was set, throw an exception:
String ENV_VAR1 = Optional.ofNullable(System.getenv("ENV_VAR1")).orElseThrow( () -> new CustomCheckedException("ENV_VAR1 is not set in the environment"));
See https://docs.oracle.com/javase/tutorial/essential/environment/env.html for Java coding to use environment variables.
PROTIP: Java has both System Environment variables and JVM-based variables.
-
Pass a Java environment variable
pagingDirectory
(containing “C:\temp”) from the command line excuting Java programmyApp.jar
:java.exe -DmaxInMemory=100M -DpagingDirectory=c:\temp -jar myApp.jar
-
PROTIP: A different method is used to read Java system properties:
System.getProperties();
Within Jenkins
The sample Jenkinsfile template from GCP at https://github.com/GoogleCloudPlatform/continuous-deployment-on-kubernetes/blob/master/sample-app/Jenkinsfile
node { def project = 'REPLACE_WITH_YOUR_PROJECT_ID' def appName = 'gceme' def feSvcName = "${appName}-frontend" def imageTag = "gcr.io/${project}/${appName}:${env.BRANCH_NAME}.${env.BUILD_NUMBER}"
The value for project
is specified when the file is adapted for use.
The value for {appName}
is also defined as static values.
The value for env.BRANCH_NAME
is pulled in from environment variables.
Further down the file:
stage "Deploy Application" switch (env.BRANCH_NAME) { // Roll out to canary environment case "canary": // Change deployed image in canary to the one we just built sh("sed -i.bak 's#gcr.io/cloud-solutions-images/gceme:1.0.0#${imageTag}#' ./k8s/canary/*.yaml") sh("kubectl --namespace=production apply -f k8s/services/") sh("kubectl --namespace=production apply -f k8s/canary/") sh("echo http://`kubectl --namespace=production get service/${feSvcName} --output=json | jq -r '.status.loadBalancer.ingress[0].ip'` > ${feSvcName}") break
The sed utility replaces text.
The sed utility is available on practically all Linux and MacOS machines.
The s is for stream. The ed is for editor.
For example, to substitute the word “day” once each line in the “old” file to “night” in the “new” file:
sed s/day/night/ old new
See http://www.grymoire.com/Unix/Sed.html
The kubectl is the Kubernetes command line utility.
Kubernetes deploy files
For example, in
https://github.com/GoogleCloudPlatform/continuous-deployment-on-kubernetes/tree/master/sample-app/k8s
a different folder is used to hold a different set of yaml deployment files for each environment (canary, dev, services, production): frontend-canary.yaml
file and backend-canary.yaml
file.
Within https://github.com/GoogleCloudPlatform/continuous-deployment-on-kubernetes/blob/master/sample-app/k8s/production/frontend-production.yaml note the shell command
command: ["sh", "-c", "app -frontend=true -backend-service=http://gceme-backend:8080 -port=80"]
It contains variable “backend-service” with value “http://gceme-backend:8080”.
Kubernetes Pod files
Within specific Kubernetes kind: Pod
files, page https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/
provides an example of how environment variables are specified under the env:
field header:
apiVersion: v1 kind: Pod metadata: name: dapi-envars-fieldref spec: containers: - name: test-container image: gcr.io/google_containers/busybox command: [ "sh", "-c"] args: - while true; do echo -en '\n'; printenv MY_NODE_NAME MY_POD_NAME MY_POD_NAMESPACE; printenv MY_POD_IP MY_POD_SERVICE_ACCOUNT; sleep 10; done; env: - name: MY_NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName - name: MY_POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: MY_POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: MY_POD_IP valueFrom: fieldRef: fieldPath: status.podIP - name: MY_POD_SERVICE_ACCOUNT valueFrom: fieldRef: fieldPath: spec.serviceAccountName restartPolicy: Never