<p>I am working on operator-sdk, in the controller, we often need to create a Deployment object, and Deployment resource has a lot of configuration items, such as environment variables or ports definition or others as following. I am wondering what is best way to get these values, I don't want to hard code them, for example, variable_a or variable_b.</p>
<p>Probably, you can put them in the CRD as spec, then pass them to Operator Controller; Or maybe you can put them in the configmap, then pass configmap name to Operator Controller, Operator Controller can access configmap to get them; Or maybe you can put in the template file, then in the Operator Controller, controller has to read that template file.</p>
<p>What is best way or best practice to deal with this situation? Thanks for sharing your ideas or points.</p>
<pre><code>    deployment := &amp;appsv1.Deployment{
        ObjectMeta: metav1.ObjectMeta{
            Name:      m.Name,
            Namespace: m.Namespace,
            Labels:    ls,
        },
        Spec: appsv1.DeploymentSpec{
            Replicas: &amp;replicas,
            Selector: &amp;metav1.LabelSelector{
                MatchLabels: ls,
            },
            Template: corev1.PodTemplateSpec{
                ObjectMeta: metav1.ObjectMeta{
                    Labels: ls,
                },
                Spec: corev1.PodSpec{
                    Containers: []corev1.Container{{
                        Image: &quot;....&quot;,
                        Name: m.Name,
                        Ports: []corev1.ContainerPort{{
                            ContainerPort: port_a,
                            Name:          &quot;tcpport&quot;,
                        }},
                        Env: []corev1.EnvVar{
                            {
                                Name:  &quot;aaaa&quot;,
                                Value: variable_a,
                            },
                            {
                                Name:  &quot;bbbb&quot;,
                                Value: variable_b,
                            },
</code></pre>

Joe
<h2>Using enviroment variables</h2>
<p>It can be convenient that your app gets your data as environment variables.</p>
<h3>Environment variables from <code>ConfigMap</code></h3>
<p>For non-sensitive data, you can store your variables in a <code>ConfigMap</code> and then define container environment variables using the <code>ConfigMap</code> data.</p>
<p><a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#define-container-environment-variables-with-data-from-multiple-configmaps" rel="nofollow noreferrer">Example from Kubernetes docs</a>:</p>
<p>Create the <code>ConfigMap</code> first. File <code>configmaps.yaml</code>:</p>
<pre><code>apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  special.how: very
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
  namespace: default
data:
  log_level: INFO
</code></pre>
<p>Create the ConfigMap:</p>
<pre><code>kubectl create -f ./configmaps.yaml
</code></pre>
<p>Then define the environment variables in the <code>Pod</code> specification, <code>pod-multiple-configmap-env-variable.yaml</code>:</p>
<pre><code>apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox
      command: [ &quot;/bin/sh&quot;, &quot;-c&quot;, &quot;env&quot; ]
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.how
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: env-config
              key: log_level
  restartPolicy: Never
</code></pre>
<p>Create the <code>Pod</code>:</p>
<pre class="lang-sh prettyprint-override"><code>kubectl create -f ./pod-multiple-configmap-env-variable.yaml
</code></pre>
<p>Now in your controller you can read these environment variables <code>SPECIAL_LEVEL_KEY</code> (which will give you <code>special.how</code> value from <code>special-config</code> <code>ConfigMap</code>) and <code>LOG_LEVEL</code> (which will give you <code>log_level</code> value from <code>env-config</code> <code>ConfigMap</code>):</p>
<p>For example:</p>
<pre class="lang-golang prettyprint-override"><code>specialLevelKey := os.Getenv(&quot;SPECIAL_LEVEL_KEY&quot;)
logLevel := os.Getenv(&quot;LOG_LEVEL&quot;)

fmt.Println(&quot;SPECIAL_LEVEL_KEY:&quot;, specialLevelKey)
fmt.Println(&quot;LOG_LEVEL:&quot;, logLevel)
</code></pre>
<h3>Environment variables from <code>Secret</code></h3>
<p>If your data is sensitive, you can store it in a <code>Secret</code> and then use the <code>Secret</code> as environment variables.</p>
<p><a href="https://kubernetes.io/docs/concepts/configuration/secret/#creating-a-secret-manually" rel="nofollow noreferrer">To create a <code>Secret</code> manually</a>:</p>
<p>You'll first need to encode your strings using <code>base64</code>.</p>
<pre><code># encode username
$ echo -n 'admin' | base64
YWRtaW4=

# encode password
$ echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm
</code></pre>
<p>Then create a <code>Secret</code> with the above data:</p>
<pre><code>apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm
</code></pre>
<p>Create a <code>Secret</code> with <code>kubectl apply</code>:</p>
<pre><code>$ kubectl apply -f ./secret.yaml
</code></pre>
<p>Please notice that there are other ways to create a secret, pick one that works best for you:</p>
<ul>
<li><a href="https://kubernetes.io/docs/concepts/configuration/secret/#creating-a-secret-using-kubectl" rel="nofollow noreferrer">Creating a <code>Secret</code> using <code>kubectl</code></a></li>
<li><a href="https://kubernetes.io/docs/concepts/configuration/secret/#creating-a-secret-from-a-generator" rel="nofollow noreferrer">Creating a <code>Secret</code> from a generator</a></li>
<li><a href="https://kubernetes.io/docs/concepts/configuration/secret/#generating-a-secret-from-files" rel="nofollow noreferrer">Creating a <code>Secret</code> from files</a></li>
<li><a href="https://kubernetes.io/docs/concepts/configuration/secret/#generating-a-secret-from-string-literals" rel="nofollow noreferrer">Creating a <code>Secret</code> from string literals</a></li>
</ul>
<p>Now you can <a href="https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets-as-environment-variables" rel="nofollow noreferrer">use this created <code>Secret</code> for environment variables</a>.</p>
<blockquote>
<p>To use a secret in an environment variable in a Pod:</p>
<ol>
<li>Create a secret or use an existing one. Multiple Pods can reference the same secret.</li>
<li>Modify your Pod definition in each container that you wish to consume the value of a secret key to add an environment variable for each secret key you wish to consume. The environment variable that consumes the secret key should populate the secret's name and key in <code>env[].valueFrom.secretKeyRef</code>.</li>
<li>Modify your image and/or command line so that the program looks for values in the specified environment variables.</li>
</ol>
</blockquote>
<p>Here is a <code>Pod</code> example from Kubernetes docs that shows how to use a <code>Secret</code> for environment variables:</p>
<pre><code>apiVersion: v1
kind: Pod
metadata:
  name: secret-env-pod
spec:
  containers:
  - name: mycontainer
    image: redis
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password
  restartPolicy: Never
</code></pre>
<p>Finally, as stated in the docs:</p>
<blockquote>
<p>Inside a container that consumes a secret in an environment variables, the secret keys appear as normal environment variables containing the base64 decoded values of the secret data.</p>
</blockquote>
<p>Now in your controller you can read these environment variables <code>SECRET_USERNAME</code> (which will give you <code>username</code> value from <code>mysecret</code> <code>Secret</code>) and <code>SECRET_PASSWORD</code> (which will give you <code>password</code> value from <code>mysecret</code> <code>Secret</code>):</p>
<p>For example:</p>
<pre class="lang-golang prettyprint-override"><code>username := os.Getenv(&quot;SECRET_USERNAME&quot;)
password := os.Getenv(&quot;SECRET_PASSWORD&quot;)
</code></pre>
<h2>Using volumes</h2>
<p>You can also mount both <code>ConfigMap</code> and <code>Secret</code> as a volume to you pods.</p>
<p><a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#populate-a-volume-with-data-stored-in-a-configmap" rel="nofollow noreferrer">Populate a Volume with data stored in a ConfigMap</a>:</p>
<pre><code>apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox
      command: [ &quot;/bin/sh&quot;, &quot;-c&quot;, &quot;ls /etc/config/&quot; ]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        # Provide the name of the ConfigMap containing the files you want
        # to add to the container
        name: special-config
  restartPolicy: Never
</code></pre>
<p><a href="https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets-as-files-from-a-pod" rel="nofollow noreferrer">Using Secrets as files from a Pod</a>:</p>
<blockquote>
<p>To consume a Secret in a volume in a Pod:</p>
<ol>
<li>Create a secret or use an existing one. Multiple Pods can reference the same secret.</li>
<li>Modify your Pod definition to add a volume under .spec.volumes[]. Name the volume anything, and have a .spec.volumes[].secret.secretName field equal to the name of the Secret object.</li>
<li>Add a <code>.spec.containers[].volumeMounts[]</code> to each container that needs the secret. Specify <code>.spec.containers[].volumeMounts[].readOnly = true</code> and <code>.spec.containers[].volumeMounts[].mountPath</code> to an unused directory name where you would like the secrets to appear.
Modify your image or command line so that the program looks for files in that directory. Each key in the secret <code>data</code> map becomes the filename under <code>mountPath</code>.</li>
</ol>
</blockquote>
<p>An example of a <code>Pod</code> that mounts a <code>Secret</code> in a volume:</p>
<pre><code>apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: &quot;/etc/foo&quot;
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
</code></pre>
