
Running database migrations
We are still receiving errors about missing tables, and the reason is because we have not run database migrations to establish the required database schema the application expects to be in place. Recall that we used the python3 manage.py migrate command locally to run these migrations, so we need to do the same in our Docker environment.
If you tear down the environment again by pressing Ctrl + C and running docker-compose down -v, one approach would be to use the docker-compose run command:
> docker-compose down -v
...
...
> docker-compose run release python3 manage.py migrate
Creating network "todobackend_default" with the default driver
Creating todobackend_db_1 ... done
Traceback (most recent call last):
File "/usr/lib/python3.6/site-packages/mysql/connector/network.py", line 515, in open_connection
self.sock.connect(sockaddr)
ConnectionRefusedError: [Errno 111] Connection refused
...
...
In the preceding example, note that when you use the docker-compose run command, Docker Compose does NOT support the health check behavior we previously observed when we ran docker-compose up. This means you can take one of two approaches:
- Ensure you run docker-compose up release first, and then run docker-compose run python3 manage.py migrate - this will leave your application in a state where it will raise errors until the migrations complete.
- Define the migrations as a separate service, called migrate, with a dependency on the db service, bring up the migrate service, which will execute the migrations and exit, and then bring up the application.
Although as you will soon see, option 1 is simpler, option 2 is more robust as it ensures the database is in the correct state before starting the application. Option 2 also aligns with the approach we will take later on in this book when we have to orchestrate running database migrations in AWS, so we will implement option 2 now.
The following example demonstrates the changes we need to make to run the migrations as a separate service:
version: '2.4'
services:
test:
build:
context: .
dockerfile: Dockerfile
target: test
release:
build:
context: .
dockerfile: Dockerfile
environment:
DJANGO_SETTINGS_MODULE: todobackend.settings_release
MYSQL_HOST: db
MYSQL_USER: todo
MYSQL_PASSWORD: password
app:
extends:
service: release
depends_on:
db:
condition: service_healthy
ports:
- 8000:8000
command:
- uwsgi
- --http=0.0.0.0:8000
- --module=todobackend.wsgi
- --master
migrate:
extends:
service: release
depends_on:
db:
condition: service_healthy
command:
- python3
- manage.py
- migrate
- --no-input
db:
image: mysql:5.7
healthcheck:
test: mysqlshow -u $$MYSQL_USER -p$$MYSQL_PASSWORD
interval: 3s
retries: 10
environment:
MYSQL_DATABASE: todobackend
MYSQL_USER: todo
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: password
In the preceding example, note that in addition to the migrate service, we have added a new service, called app, as well. The reason is that we want to extend migrate from the release service (as defined by the extends parameter) so it will inherit the release image and release service settings, however, one limitation of extending another service is that you cannot extend a service that has a depends_on statement. This requires us to use the release service as more of a base configuration that other services inherit from, and shift the depends_on, ports, and command parameters from the release service to the new app service.
With this configuration in place, we can tear down the environment and stand up our new environment, as demonstrated in the following example:
> docker-compose down -v
...
...
> docker-compose up migrate
Creating network "todobackend_default" with the default driver
Building migrate
Step 1/24 : FROM alpine AS test
---> 3fd9065eaf02
...
...
Successfully built 5b20207e3e9c
Successfully tagged todobackend_migrate:latest
WARNING: Image for service migrate was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating todobackend_db_1 ... done
Creating todobackend_migrate_1 ... done
Attaching to todobackend_migrate_1
migrate_1 | Operations to perform:
migrate_1 | Apply all migrations: admin, auth, contenttypes, sessions, todo
migrate_1 | Running migrations:
migrate_1 | Applying contenttypes.0001_initial... OK
migrate_1 | Applying auth.0001_initial... OK
migrate_1 | Applying admin.0001_initial... OK
migrate_1 | Applying admin.0002_logentry_remove_auto_add... OK
migrate_1 | Applying contenttypes.0002_remove_content_type_name... OK
migrate_1 | Applying auth.0002_alter_permission_name_max_length... OK
migrate_1 | Applying auth.0003_alter_user_email_max_length... OK
migrate_1 | Applying auth.0004_alter_user_username_opts... OK
migrate_1 | Applying auth.0005_alter_user_last_login_null... OK
migrate_1 | Applying auth.0006_require_contenttypes_0002... OK
migrate_1 | Applying auth.0007_alter_validators_add_error_messages... OK
migrate_1 | Applying auth.0008_alter_user_username_max_length... OK
migrate_1 | Applying auth.0009_alter_user_last_name_max_length... OK
migrate_1 | Applying sessions.0001_initial... OK
migrate_1 | Applying todo.0001_initial... OK
todobackend_migrate_1 exited with code 0
> docker-compose up app
Building app
Step 1/24 : FROM alpine AS test
---> 3fd9065eaf02
...
...
Successfully built 5b20207e3e9c
Successfully tagged todobackend_app:latest
WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
todobackend_db_1 is up-to-date
Creating todobackend_app_1 ... done
Attaching to todobackend_app_1
app_1 | *** Starting uWSGI 2.0.17 (64bit) on [Thu Jul 5 11:21:00 2018] ***
app_1 | compiled with version: 6.4.0 on 04 July 2018 11:33:09
app_1 | os: Linux-4.9.93-linuxkit-aufs #1 SMP Wed Jun 6 16:55:56 UTC 2018
...
...
In the preceding example, note that Docker Compose builds new images for each service, however these builds complete very quickly as they are identical to the release image, given each service extends the release service. You will observe a 15-30 second delay when you bring up the migrate service waiting for the db service health check to pass, after which the migrations are run, creating the appropriate schema and tables the todobackend application expects. After starting the app service, you should be able to interact with the todobackend API without receiving any errors:
> curl -s localhost:8000/todos | jq
[]