summaryrefslogtreecommitdiff
path: root/content/posts/manage-multiple-env-vars-in-docker-compose.md
blob: e8e2f8597e6b1870c16cc33db9cd677befc25572 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
---
title: "How I manage multiple development environments in my Django workflow using Docker compose"
date: 2020-05-13T11:36:48-03:00
lastmod: 2020-05-13T11:36:48-03:00
tags : [ "docker", "programming", "django" ]
---

Hi everyone!

Last week I was searching how to manage multiple development environments with the same docker-compose configuration for my Django workflow. I needed to manage a development and a production environment, so this is what I did. 

Some descriptions on my data:

1) I had around 20 env vars, but some of them where shared among environments.
2) I wanted to do it with as little impact as possible.

# First, docker-compose help command

The first thing I did was run a simple `docker-compose --help`, and it returned this:

```bash
Define and run multi-container applications with Docker.

Usage:
  docker-compose [-f <arg>...] [options] [COMMAND] [ARGS...]
  docker-compose -h|--help

Options:
  -f, --file FILE             Specify an alternate compose file
                              (default: docker-compose.yml)
# more not necessary stuff
  --env-file PATH             Specify an alternate environment file
```

I went with the `-f` flag, because I also wanted to run some docker images for development. By using the `-f` flag I could create a base compose file with the shared env vars (docker-compose.yml) and another one for each of the environments (prod.yml and dev.yml)

So I went to town. I kept the shared variables inside `docker-compose.yml` and added the specific variables and configuration to `prod.yml` and `dev.yml`

`docker-compose.yml`:
```yaml
version: "3"

services:
  app:
    build:
      context: .
    ports:
      - "8000:8000"
    volumes:
      - ./app:/app
    command: >
      sh -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000"
    environment:
      - myvar1=myvar1
      - myvar2=myvar2
      ...
      - myvarx=myvarx
```

Since I'm going to connect to a remote RDS and a remote Redis, in my `prod.yml`, I don't need to define a Postgres or Redis image:
```yaml

version: "3"
services:
  app:
    environment:
      # DB connections
      - DB_HOST=my-host
      - DB_NAME=db-name
      - DB_USER=db-user
      - DB_PASS=mysupersecurepassword
      ...
```

For `dev.yml` I added the Postgres database image and Redis image:
```yaml
version: "3"

services:
  app:
    depends_on:
      - db
      - redis
    environment:
      # Basics
      - DEBUG=True
      # DB connections
      - DB_HOST=db
      - DB_NAME=app
      - DB_USER=postgres
      - DB_PASS=supersecretpassword
      ...
  db:
    image: postgres:10-alpine
    environment:
      - POSTGRES_DB=app
      - POSRGRES_USER=postgres
      - POSTGRES_PASSWORD=supersecretpassword
    links:
      - redis:redis

  redis:
    image: redis:5.0.7
    expose:
      - "6379"
```

And to run it between environments, all I have to do is:

```bash
# For production
docker-compose -f docker-compose.yml -f prod.yml up 

# For development
docker-compose -f docker-compose.yml -f dev.yml up 
```

That's it! I have multiple `docker-compose` files for all my environments, but I could go even further.

# Improving the solution

## Improving the base docker-compose.yml file

I liked the way it looked, but I knew I could go deeper. A bunch of vars inside the base `docker-compose.yml` looked weird, and made the file a little unreadable. So again, I went to the `docker-compose` documentation and found what I needed: [env files in docker-compose](https://docs.docker.com/compose/environment-variables/#the-env-file).

So I created a file called `globals.env`, and moved all the global env vars to that file:

```yaml
myvar1=myvar1
mivar2=myvar2
...
```
And on the `docker-compose.yml` file I called the `globals.env` file:

```yaml
  app:
    env_file: globals.env
```

This is the final result:

```yaml
version: "3"

services:
  app:
    build:
      context: .
    ports:
      - "8000:8000"
    volumes:
      - ./app:/app
    command: >
      sh -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000"
    env_file: globals.env
```

## Improving the running command

As I mentioned before, I wanted as little impact as possible, and `docker-compose -f docker-compose.yml -f envfile.yml up` was a bit long for me. So I created a couple of bash files to ease the ingestion of `docker-compose` files:

`prod`: 
```bash
#!/usr/bin/env bash
# Run django as production
docker-compose -f docker-compose.yml -f prod.yml "$@"
```

`dev`:
```bash
#!/usr/bin/env bash
# Run django as development
docker-compose -f docker-compose.yml -f dev.yml "$@"
```

The `"$@"` means "Append all extra arguments here", so if I ran the `dev` command with the `up -d` arguments, the full command would be `docker-compose -f docker-compose.yml -f development.yml up -d`, so it is exactly what I wanted

A quick permissions management:
```bash
chmod +x prod dev 
```

And now I could run my environments as:
```bash
./dev up
./prod up
```

# All good?

I was satisfied with this solution. I could run both environments wherever I want with only one command instead of moving env vars all over the place. I could go even further by moving the environment variables of each file to its own `.env` file, but I don't think that's needed for the time being. At least I like to know that I can do that down the road if it is necessary