--Host environment - macOS Catalina 10.15.6 - VirtualBox 6.1.14 --Guest environment - Ubuntu Server 20.04.1 LTS - Docker 19.03.13 - Docker Compose 1.27.3
So, in order to develop a web application, I would like to build a mass-docker development environment ** Django + MySQL + nginx with Docker.
The initial directory structure is as follows.
app
├── docker-compose.yml
├── mysql
│ ├── Dockerfile
│ └── init.d
│ └── init.sql
├── nginx
│ ├── conf
│ │ └── app_nginx.conf
│ └── uwsgi_params
└── python
├── .dockerignore
├── Dockerfile
└── requirements.txt
From this state
By the way, the following is an example of creating an application called "app" with Django. Replace it with the name of the application you want to create.
Let's write the result first. After trial and error, googled and reading books, and fighting ruthless error messages, I finally arrived at docker-compose.yml. Looking back on the long journey, the inner corner of my eyes gets hot.
docker-compose.yml
version: '3.7'
services:
python:
build:
context: ./python
dockerfile: Dockerfile
command: uwsgi --socket :8001 --module app.wsgi --py-autoreload 1 --logto /tmp/uwsgi.log
restart: unless-stopped
container_name: Django
networks:
- django_net
volumes:
- ./src:/code
- ./static:/static
expose:
- "8001"
depends_on:
- db
db:
build:
context: ./mysql
dockerfile: Dockerfile
restart: unless-stopped
container_name: MySQL
networks:
- django_net
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: "*******"
TZ: "Asia/Tokyo"
volumes:
- app.db.volume:/var/lib/mysql
- ./mysql/init.d:/docker-entrypont-initdb.d
nginx:
image: nginx:1.17
restart: unless-stopped
container_name: nginx
networks:
- django_net
ports:
- "80:80"
volumes:
- ./nginx/conf:/etc/nginx/conf.d
- ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
- ./static:/static
depends_on:
- python
networks:
django_net:
driver: bridge
volumes:
app.db.volume:
name: app.db.volume
If you abbreviate the composition appropriately, it looks like this.
Anyway, from Django's settings. Edit `` `Dockerfile``` directly under the "python" directory as follows.
./python/Dockerfile
FROM python:3.8.5
WORKDIR /code
ADD requirements.txt /code/
RUN pip install --upgrade pip && pip install -r requirements.txt
ADD . /code/
First, create a working directory named `code``` and move to it. Add
requirements.txt``` to that directory, then install the required packages with ``
pip, and finally add
`code``` to the container.
So, the packages to install with `pip``` are listed in`
requirements.txt``` as follows. This time, you'll need Django itself, uWSGI for socket communication with nginx, and mysqlclient for connecting to MySQL.
:./python/requirements.txt
Django==2.2.12
uwsgi==2.0.18
mysqlclient==1.4.6
By the way, let's also edit .dockerignore
. It would be a problem if extra files were put in the container.
:./python/.dockerignore
**/__pycache__
**/.DS_Store
**/.Thumbs.db
**/.git
**/.gitignore
Based on the settings so far, if you set Django in docker-compose.yml
, it will be as follows.
docker-compose.yml (excerpt)
python:
build:
context: ./python # docker-compose.Relative path to Dockerfile from yml
dockerfile: Dockerfile #Explicit specification that the setting is written in "Dockerfile"
command: uwsgi --socket :8001 --module app.wsgi --py-autoreload 1 --logto /tmp/uwsgi.log
restart: unless-stopped #If the container stops abnormally, restart it
container_name: Django #Define container name
networks:
- django_net #Network name "django_Specify "net"
volumes:
- ./src:/code # ./In src/mount code
- ./static:/static # ./static/Mount static
expose:
- "8001" #Keep port 8001 open for uwsgi to socket communicate with nginx
depends_on:
- db
Setting up uWSGI is confusing, but if you keep the `app.wsgi``` part as
`[Django project name] .wsgi```, the rest is magical.
To be honest, I'm not sure about the MySQL settings. Mostly, I hate RDBs at a level where I can't write queries satisfactorily.
As a result of examining various manuals, it seems that this is a best practice. I don't know.
./mysql/Dockerfile
FROM mysql:5.7
COPY init.d/* /docker-entrypoint-initdb.d/
sql:./mysql/init.d/init.sql
CREATE DATABASE IF NOT EXISTS app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER IF NOT EXISTS 'app_user'@'%' IDENTIFIED BY '[password]';
GRANT ALL PRIVILEGES ON app_db.* TO 'app_user'@'%';
FLUSH PRIVILEGES;
By the way, the app.db
(database name) created by the above ``` init.sql``` is required for the setting of
`settings.py``` described later. ,So I will not forget.
Based on this setting, if you configure MySQL with `` `docker-compose.yml```, it will look like this:
docker-compose.yml (excerpt)
db:
build:
context: ./mysql # docker-compose.Relative path to Dockerfile from yml
dockerfile: Dockerfile #Explicit specification that the setting is written in "Dockerfile"
restart: unless-stopped #If the container stops abnormally, restart it
container_name: MySQL #Define container name
networks:
- django_net #Network name "django_Specify "net"
ports:
- "3306:3306" #Specify the port number to communicate
environment:
MYSQL_ROOT_PASSWORD: "*******" #Set root password in environment variable
TZ: "Asia/Tokyo" #Set the time zone with an environment variable
volumes:
- app.db.volume:/var/lib/mysql #Database volume "app".db.Save to "volume"
- ./mysql/init.d:/docker-entrypont-initdb.d
Since we decided to save the database to a volume named "app.db.volume", set "volumes".
docker-compose.yml (excerpt)
volumes:
app.db.volume:
name: app.db.volume
I'm not sure about the uWSGI settings either. To be honest, I didn't feel like investigating uWSGI. sorry.
It seems that you should set the parameters appropriately as follows. I don't know.
./nginx/uwsgi_params
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REQUEST_SCHEME $scheme;
uwsgi_param HTTPS $https if_not_empty;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
So, it is the setting of nginx, but first you need to open port 8001 for socket communication with uWSGI.
Also, set the listening port of nginx. My setting is number 80, but this is the setting of the virtual machine that runs the container, Set the data flowing through number 80 of the guest (virtual machine) to flow to number 8081 of the host. Is.
Now, if you access "127.0.0.1:8081" on the host browser, you can see the web application that runs on the guest.
conf:./nginx/conf/app_nginx.conf
upstream django {
ip_hash;
server python:8001; #A port for uWSGI to communicate with Django and nginx
}
server {
listen 80; #Standby port
server_name 127.0.0.1;
charset utf-8;
location /static {
alias /static;
}
client_max_body_size 75M;
location / {
uwsgi_pass django;
include /etc/nginx/uwsgi_params;
}
}
server_tokens off;
Based on this setting, setting nginx in docker-compose.yml
gives: Similar to Django's settings, we've mounted `./static``` on`
/ static```, so static content that Django doesn't handle can be returned with nginx.
docker-compose.yml (excerpt)
nginx:
image: nginx:1.17 #Get the image of nginx properly
restart: unless-stopped #If the container stops abnormally, restart it
container_name: nginx #Define container name
networks:
- django_net #Network name "django_Specify "net"
ports:
- "80:80" #Specify the port number to communicate
volumes:
- ./nginx/conf:/etc/nginx/conf.d
- ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
- ./static:/static # ./static/Mount static
depends_on:
- python
Also, I said that I will use "django_net" so far, but I have not set it at all, so don't forget to set the "networks" item as well.
docker-compose.yml (excerpt)
networks:
django_net:
driver: bridge
By the way, all the settings of Docker are completed. All you have to do now is leave the Django console settings.
Let's start the project first.
$ docker-compose run python django-admin.py startproject app .
If you look inside `src``` and
`static``` after hitting this command, you should see various files for Django.
Then edit ./src/app/settings.py
.
python:./src/app/settings.py
DATABASES = {
'default': {
# 'ENGINE': 'django.db.backends.sqlite3', #Comment out
'ENGINE': 'django.db.backends.mysql', #add to
# 'NAME': os.path.join(BASE_DIR, 'app_db'), #Comment out
'NAME': 'app_db', # init.Confirm that it is the same as the database name that was CREATE by sql
'USER': 'app_user', #add to
'PASSWORD': '*****', #Add (same as the password specified in the environment variable)
'PORT': '3306', #add to
'HOST': 'db', #add to
}
}
STATIC_URL = '/static/' #add to
STATIC_ROOT = '/static/' #add to
...(Omitted)
LANGUAGE_CODE = 'ja' #Edit
TIME_ZONE = 'Asia/Tokyo' #Edit
All you have to do is hit the familiar Django commands with docker-compose.
$ docker-compose run python ./manage.py migrate #DB migration
$ docker-compose run python ./manage.py createsuperuser #Administrator settings
$ docker-compose run python ./manage.py collectstatic #Copy static files
Now, let's move the container. Get out, Django!
$ docker-compose up -d
And when I access 127.0.0.1:8081 from the host browser ... I did it! !! **Hello! Docker! Hello! Django! ** **
To stop the container, do stop
.
$ docker-compose stop
By the way, if you put it together in an article like this, it seems to be misunderstood as if I set it as crispy, so I will write down how many twists and turns I went through.
First of all, this error message.
ERROR: The Compose file './docker-compose.yml' is invalid because:
services.pithon.volumes contains an invalid type, it should be an array
I was angry and angry about what was wrong with this, but I couldn't find a direct solution.
Suddenly, I noticed that "it should be array" (it should be an array, but it shouldn't be) is the point, and when I examined the YAML syntax ... Express the array by. Please put a half-width space after "-" "](https://magazine.rubyist.net/articles/0009/0009-YAML.html#%E9%85%8D % E5% 88% 97) is stated.
If you look closely, here,
volumes:
- ./src:/code
But,
volumes:
-./src:/code # 「-There is no half-width space after
It was like this. ** It took me 2 hours to notice this **.
** This fucking calculator! !! Are you a relative's annoying man! !! ** ** This is done because there was only one half-width space. I can't do it anymore. In the first place, I don't like the name "Yamuru". Is it Korean food (that's Namul)?
Then this error message.
Unsupported config option for services.nginx: 'deponds_on'
As you can see from a closer look, `depends_on``` is correct, not
`deponds_on```. It's a stupid misspelling, but ** it took me two hours to notice this **.
** This fucking calculator! !! Give back my precious time in life! !! ** ** I don't care about face recognition with deep learning, and I want a smart calculator that gently corrects this kind of human error.
Yes, this error message as well.
ERROR: Service 'nginx' depends on service 'python' which is undefined.
The reason I got this error was because I mistyped `python``` as
`pithon``` (see the first error message). ** It took me an hour to notice this **.
** This fucking calculator! !! Your head is a happy set! !! ** ** I'm surprised at the development like a two-frame cartoon, but the cause is in the Dvorak layout, y and i are next to each other. It seems.
Now, do you understand how much I hate computers, why I decided to major in machine learning in graduate school, and why both researchers and engineers quit (a story completely different from the purpose of this article).
** It's just right to play as a toy, but I don't feel like making it a job **. In short, he is incompetent by any means. I'm really thankful to you.
At the end, it was a very heart-warming punch line, but next time I would like to create a production environment on AWS. stay tuned.
Recommended Posts