I'm trying to configure a deployed app on an EC2 instance I'm not able to get visit
the application when it's up on ec2 public IP. I've checked the security groups and allowed all
inbound traffic to ports just to see If I can reach the homepage or admin page of django.
Say my ec2 IP address is 34.245.202.112 how do I map my application so nginx serves
The frontend(nuxt) at 34.245.202.112
The backend(django) at 34.245.202.112/admin
The API(DRF) at 34.245.202.112/api
When I try to do this the error I get from nginx is
nginx | 2020-11-14T14:15:35.511973183Z 2020/11/14 14:15:35 [emerg] 1#1: host not found in upstream "nuxt:3000" in /etc/nginx/conf.d/autobets_nginx.conf:9
This is my config
docker-compose
version: "3.4"
services:
db:
restart: always
image: postgres
volumes:
- pgdata:/var/lib/postgresql/data
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
ports:
- "5432:5432"
expose:
- 5432
networks:
- random_name
django:
container_name: django
build:
context: ./backend
env_file: .env
environment:
- DEBUG=True
command: >
sh -c "./wait-for-it.sh db:5432 &&
./autobets/manage.py collectstatic --no-input &&
./autobets/manage.py makemigrations &&
./autobets/manage.py migrate --no-input &&
./autobets/manage.py runserver_plus 0.0.0.0:8001
"
- "8001"
volumes:
- ./backend:/app
depends_on:
- db
restart: on-failure
nginx:
image: nginx
container_name: nginx
ports:
- "80:80"
restart: always
depends_on:
- nuxt
- django
volumes:
- ./nginx/conf:/etc/nginx/conf.d
- ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
- ./backend/static:/static
networks:
- random_name
nuxt:
build:
context: ./frontend
environment:
- API_URI=http://django:8001/api
command: sh -c "npm install && npm run dev"
volumes:
- ./frontend:/app
ports:
- "3000:3000"
depends_on:
- django
networks:
- random_name
volumes:
pgdata:
networks:
random_name:
nginx.conf
# the upstream component nginx needs to connect to
upstream django {
ip_hash;
server django:8001;
}
upstream nuxt {
ip_hash;
server nuxt:3000;
}
# configuration of the server
server {
# the port your site will be served on
listen 8000;
# the domain name it will serve for
server_name 34.245.202.112; # substitute your machine's IP address or FQDN
charset utf-8;
# max upload size
client_max_body_size 75M; # adjust to taste
location /static/ {
alias /static/;
}
# Finally, send all non-media requests to the Django server.
location / {
proxy_pass django;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $host;
}
}
Look at this minimal example:
server {
listen 80;
listen 8000; # from you config, remove if unnecessary
server_name 34.245.202.112;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $host;
location / {
# 'the frontend(nuxt) at 34.245.202.112'
# This is the default route. Requests get here when there's no
# better match to go.
proxy_pass http://nuxt:3000;
}
location /api/ {
# This location will trigger when location in URI begins with '/api/'
# e.g. http://yourserver.org/api/v1/hello/world
proxy_pass http://django:8001;
}
location /admin/ {
# exactly as /api/
proxy_pass http://django:8001;
}
location /static/ {
# same as /api/ but local files instead of proxy
alias /static/;
}
}
As you see from the example, each location has a URI prefix. NGINX will test all these 'prefixes' against location in incoming HTTP requests, finding the best match. Once the best match found NGINX will do whatever you wrote inside the block. In the example above all requests starting with /api/ or /django/ go to the django backend. Requests starting with /static/ are served from local files. Everything else goes to nuxt backend.
I'm not sure if I got your intentions right, probably because I'm missing the original config you've edited, so you have to pick up from here. Just remember that you are not limited to URI prefixes for locations (you may use regex or exact match) and that you can do nested locations. Check out this great beginner's guide from NGINX for more http://nginx.org/en/docs/beginners_guide.html .
UPDATE: After looking at other answers here I though I owe an answer to the question in title instead of just basic configuration. The reason why you got the host not found in upstream error is that you didn't specify a resolver directive. It is necessary when using DNS names in upstream blocks and for NGINX in Docker you may use this: resolver 127.0.0.11 ipv6=off;. Put it in the http block, that is outside of server block.
'127.0.0.11' is the Docker DNS. It resolves service and container names as well as 'normal' DNS records (for that is usesn host's DNS configuration). You don't have to assign an alias to a service or set a container_name because service name is a DNS record on its own. It resolves to all containers of that service. Using resolver wasn't necessary in the basic configuration I've posted because I didn't use upstream blocks.
You are missing the alias section in your network block of the docker-compose file. Aliases that you define will automatically update the /etc/hosts file of the containers and therefore your nginx container will be aware of the nuxt host.
services:
nuxt:
networks:
some-network:
aliases:
- nuxt
more info here. ctrl-f for aliases: https://docs.docker.com/compose/compose-file/
The container name "nuxt" is not defined in the docker-compose file, so the hostname cannot be resolved by the nginx container.
Try to fix the nginx error by adding container_name:nuxt to the nuxt service in the docker-compose file.
Related
I had a perfectly fine Django CMS 3.4.1 setup running behind Nginx as an edge-server with SSL termination. The complete chain was:
nginx (SSL) → nginx (django server) → gunicorn → django
All I did was to replace the first nginx reverse proxy with traefik, for a better integration of other services. Everything is run with docker (compose)
The issue is, that Django now wants to redirect HTTPS calls to admin files (and ajax calls) to HTTP, breaking functionality because those files are blocked by the browser.
I did not change anything with the django installation. In fact, it even is the same docker image as before.
Because it did work with the old setup, I don't think that it is an issue with the Django CMS code using hardcoded http://. SSL was terminated before the django reverse proxy, as well.
Does anyone see something I am missing?
Here are some configuration files, from top to bottom:
traefic.yml:
global:
sendAnonymousUsage: false
api:
dashboard: true
insecure: true
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
watch: true
exposedByDefault: false
log:
level: INFO
format: common
entryPoints:
http:
address: ":80"
http:
redirections:
entryPoint:
to: https
scheme: https
https:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: ***
storage: /etc/acme/acme.json
httpChallenge:
entryPoint: http
relevant parts of django-server docker-compose file:
# ...
services:
cms-nginx:
build: "./nginx"
depends_on:
- postgres
networks:
- proxy
- cms
volumes:
- cms_static:/usr/src/app/static
- cms_media:/usr/src/app/media
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.cms.rule=Host(`***`)"
- "traefik.http.routers.cms.tls=true"
- "traefik.http.routers.cms.tls.certresolver=letsencrypt"
cms:
restart: always
build: ./cms
links:
- postgres:postgres
- static:static
expose:
- "8000"
volumes:
- ./cms:/usr/src/app
- static_out:/usr/src/app/data/generated
- cms_static:/usr/src/app/data/static
- cms_media:/usr/src/app/data/media
depends_on:
- static
env_file:
- .env
- ./cms/.env
command: /bin/sh -c "./docker-init.sh"
networks:
- cms
django server nginx conf:
server {
listen 80;
server_name *** default_server;
charset utf-8;
client_max_body_size 75M;
location ^~ /static/ {
alias /usr/src/app/static/;
}
location ^~ /media/ {
alias /usr/src/app/media/;
}
location / {
proxy_pass http://cms:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Protocol $scheme;
}
error_log /var/log/nginx/deckel_error.log;
}
gunicorn start command:
/usr/local/bin/gunicorn cms.wsgi:application -w 2 -b :8000
django settings part:
SESSION_COOKIE_SECURE = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https')
CSRF_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = True
I am using nginx as a proxy to pass to my django app container. I can successfully access the main url but when it passes control back to the django app and logins in the django app container gives it the local url and it doesn't route back to the correct page.
The redirect uri comes in as http://app
site-enabled
upstream app{
server 0.0.0.0:8888;
}
server {
listen 8080;
server_name app-dev.company.net;
location / {
# django running in uWSGI
proxy_pass http://app;
include uwsgi_params;
uwsgi_read_timeout 300s;
client_max_body_size 320m;
sendfile on;
proxy_read_timeout 1800;
proxy_connect_timeout 1800;
proxy_send_timeout 1800;
send_timeout 1800;
}
}
docker-compose.yml
version: "3.9"
services:
app:
image: ecr/app
container_name: django_app
ports:
- 8888
env_file:
- dev.env
volumes:
- staticfiles:/opt/app/static/
nginx:
build: ./nginx
container_name: django_app
volumes:
- staticfiles:/opt/app/static/
ports:
- 8080:8080
depends_on:
- app
volumes:
staticfiles:
Your docker-compose / nginx configuration file is full with little errors that could cause this kind of problem - so lets try to remove them.
delete the container names if not needed. This will make it easier to understand how to link from one container in another.
Where is your NGINX Dockerfile you need to do build ./nginx.
docker-compose
version: "3.9"
services:
app:
image: ecr/app
ports:
- 8888
env_file:
- dev.env
volumes:
- staticfiles:/opt/app/static/
nginx:
build: ./nginx
volumes:
- staticfiles:/opt/app/static/
ports:
- 8080:8080
depends_on:
- app
volumes:
staticfiles:
NGINX Configuration
You can not use 0.0.0.0 in your upstream block. Normally I use the service name. In your case app. So please change that to server app:8888; and test it one more time.
Location Configuration
You are proxying http traffic to your django app container. There is no need to use uwsgi_read_timeout or include uwsgi_params. In your case a simple http proxy configuration would be enough. For example in all the things you have randomly added to the nginx configuration, one important proxy configuration is missing.
proxy_pass http://django_app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
Make sure your understand the directives you are using in the NGINX configuration as well as in the docker-compose file, clean up the config files and try again.
I am having problems getting the wordpress docker image working with the nginx docker image.
The python/django container works perfectly fine with nginx, but the wordpress/apache one is having problems. I can get to the django site, with https. I cannot get into the wordpress one with https. In fact, when I go to my site site.com/wp, I get back site.com:8080/wp, so for some reason it is coming back through port 8080, and not 443 or 80. I've tried setting the wordpress site as the root location / (in the default.conf file) and it still has the same problem (then I get site.com:8080). The wordpress functionality is normal, I can edit the site as usual.
default.conf file for nginx
disable_symlinks off;
ssl_certificate xxx
ssl_certificate_key xxx
server {
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name site.com;
location / {
proxy_pass http://django:8000; #django container
}
location /static {
alias /path/to/static;
}
location /wp {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_pass http://wordpress:80; #the wordpress container
}
}
docker yml
version: '3.7'
services:
django:
restart: always
build:
context: .
dockerfile: docker-django #django gunicorn server, python image
ports:
- "8000:8000"
wordpress:
restart: always
build:
context: .
dockerfile: docker-wordpress #docker wordpress-apache image
# image: wordpress:latest
volumes:
- ./www/html:/var/www/html
- ./etc/apache2:/etc/apache2
ports:
- "8080:80"
nginx:
restart: always
build:
context: .
dockerfile: docker-nginx #docker nginx image
# image: nginx
volumes:
- ./xx/static:/usr/xx/static
- ./nginx/conf.d:/etc/nginx/conf.d
ports:
- "443:443"
- "80:80"
depends_on:
- django
- wordpress
It seems like the wordpress container is pulling that 8080 port number from mysql, where I had it originally set for local testing.
Overriding the mysql stored site name worked for me, by adding these lines to wp-config.php
define( 'WP_HOME', 'https://' . $_SERVER['HTTP_HOST'] . '/' );
define( 'WP_SITEURL', 'https://' . $_SERVER['SERVER_NAME'] . '/' );
I'm developing a website for my association Here. It uses Django 3.0.7 and PostgreSQL. I follow this tutorial to make it works in good condition in development and in production.
In development mode all is good, site is working perfectly, static files and media files are served by the Django built-in web server.
I test the "vanilla" (coming from the tutorial) production on my local machine it works too. But the tutorial is not complete for me so after completing it, I decided to adapt the code to fit my needs. As I follow the tutorial I created a new docker-compose and a new dockerfile for production. But there are 2 differences between the tutorial and the site I want to set in production:
I want to Use TREAFIK to route the traffic on different URL (appli.amis-simserhof.fr) because I will have other projects in the future on the same server with others subdomains..
I want to secure the website using HTTPS. So I use CERTBOT (let's encrypt) to generate certificates. I add this certificates to TREAFFIK in order to use HTTPS and it works.
So I adapted the docker-compose file with my new stuff :
version: '3.7'
services:
**traefik:
image: traefik:v1.7.12
command: [
"--loglevel=INFO",
"--api",
"--docker",
"--docker.domain=amis-simserhof.fr",
"--entrypoints=name:https address::443 tls:/etc/certs/fullchain.pem,/etc/certs/privkey.pem",
"--entrypoints=name:http address::80 redirect.entrypoint:https",
]
ports:
- 80:80 # http
- 443:443 # https
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /etc/letsencrypt/live/amis-simserhof.fr/fullchain.pem:/etc/certs/fullchain.pem
- /etc/letsencrypt/live/amis-simserhof.fr/privkey.pem:/etc/certs/privkey.pem**
web:
image: registry.gitlab.com/guillaumekoenigtncy/aas-web:latest
command: gunicorn aas.wsgi:application --bind 0.0.0.0:8000
expose:
- 8000
volumes:
- static_volume:/home/app/web/staticfiles
- media_volume:/home/app/web/mediafiles
environment:
- DEBUG=0
- SECRET_KEY=change_me
- DJANGO_ALLOWED_HOSTS=localhost appli.amis-simserhof.fr
- SQL_ENGINE=django.db.backends.postgresql
- SQL_DATABASE=postgres
- SQL_USER=postgres
- SQL_PASSWORD=postgres
- SQL_HOST=db
- SQL_PORT=5432
- DATABASE=postgres
labels:
- traefik.frontend.rule=HostRegexp:appli.amis-simserhof.fr
- traefik.frontend.entryPoints=https
depends_on:
- db
db:
image: postgres:12.0-alpine
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
**nginx:
image: registry.gitlab.com/guillaumekoenigtncy/aas-nginx:latest
volumes:
- static_volume:/home/app/web/staticfiles
- media_volume:/home/app/web/mediafiles
expose:
- 80
- 443
depends_on:
- web**
volumes:
postgres_data:
static_volume:
media_volume:
I also created a dockerfile and a config file for nginx :
FROM nginx:1.19.0
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
upstream website {
server web:8000;
}
server {
listen 80;
listen [::]:80;
access_log off;
server_name appli.amis-simserhof.fr;
location / {
proxy_pass http://website;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /staticfiles/ {
alias /home/app/web/staticfiles/;
}
location /mediafiles/ {
alias /home/app/web/mediafiles/;
}
}
The problem is that now static files are not served in production (I got a 404). When I set the DEBUG to True in the setting.py of the project files are served sot that means static files are present in the correct folder that I have set in the settings.
STATIC_URL = "/staticfiles/"
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
MEDIA_URL = "/mediafiles/"
MEDIA_ROOT = os.path.join(BASE_DIR, "mediafiles")
In production, I have correctly run the python manage.py collectstatic command. I look inside both containers, ngnix and web, and in both the files are present in the correct directory.
So I conclude my configuration is not working but when I made changes in it, I saw this changes in the nginx logs of the containers. There are no errors...
I try everything during the last 2 days: set the ssl in the nginx config and remove it from traefik (site is not accessible), expose or not ports 80 and 443 in nginx or traefik (conflicts on open ports or too many redirect error), add or remove / at the end or at the beginning of blocks or alias in nginx configuration (change nothing), etc.
If you have any hint or tips I would be really grateful...
Have a nice day :)
I'am was able do define static path with following nginx service configuration:
version: '3.7'
services:
server:
container_name: dj
build:
context: ./server/project/
restart: unless-stopped
expose:
- 8000
volumes:
- static_volume:/app/staticfiles
- media_volume:/app/media
command: gunicorn project.wsgi:application --bind 0.0.0.0:8000
networks:
- web
nginx:
container_name: nginx
build:
context: ./
dockerfile: Dockerfile
restart: unless-stopped
volumes:
- static_volume:/app/staticfiles
- media_volume:/app/media
labels:
- "traefik.enable=true"
- "traefik.http.routers.${SERVICE}.rule=(Host(`${DOMAIN_NAME}`) && PathPrefix(`/static`)) || (Host(`${DOMAIN_NAME}`) && PathPrefix(`/media`))"
- "traefik.http.routers.${SERVICE}.tls.certresolver=letsEncrypt"
- "traefik.http.routers.${SERVICE}.entrypoints=web-secure"
- "traefik.http.services.${SERVICE}.loadbalancer.server.port=80"
depends_on:
- server
networks:
- web
volumes:
static_volume:
media_volume:
networks:
web:
external: true
Note this line:
- "traefik.http.routers.static-http.rule=Host(`ex.example.com`) && PathPrefix(`/static`)"
change the ex.example.com to you domain
Here is part of my configuration, The "server" part is not completed yet
UPDATE
To use the same static files between django and nginx use following nxing configuration:
#default.conf
server {
listen 80;
server_name _;
ignore_invalid_headers on;
location / {
root /usr/share/nginx/html;
index index.html;
default_type application/javascript;
try_files $uri $uri/ /index.html =404;
}
location /static {
autoindex on;
alias /app/staticfiles/;
}
location /media {
autoindex on;
alias /app/media/;
}
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
}
Also note that I've updated docker-compose file and added Dockerfile below:
#Dockerfile
FROM nginx:1.19.1-alpine
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
So your Project-Structure should look like:
Project-root/
|-server/
|-nginx/
| |-default.conf
|-Dockerfile
|-docker-compose.yml
I have a dockerized app that works fine in development mode on my host machine. I'm trying to figure out how I can host my app on ec2 using the default ip address created when I launch my instance.
My folder structure is as follows.
backend
|---projectname
|---Dockerfile
|---requirements.txt
|---wait-for-it.sh
config/nginx
|---app.conf
frontend
|---nuxt folders
|---Dockerfile
This is my current docker compose file I'm using
docker-compose.yml
version: '3.4'
services:
db:
restart: always
image: postgres
volumes:
- pgdata:/var/lib/postgresql/data
env_file: .env
ports:
- "5432:5432"
expose:
- 5432
redis:
restart: always
image: redis
volumes:
- redisdata:/data
django:
build:
context: ./backend
env_file: .env
command: >
sh -c "./wait-for-it.sh db:5432 &&
cd autobets && python manage.py collectstatic --noinput &&
gunicorn --workers=2 --bind=0.0.0.0:8000 autobets.wsgi:application"
ports:
- "8000:8000"
volumes:
- ./backend:/app
depends_on:
- db
restart: on-failure
nuxt:
build:
context: ./frontend
environment:
- API_URI=http://django:8000/api
command: bash -c "npm install && npm run dev"
volumes:
- ./frontend:/app
ports:
- "3000:3000"
depends_on:
- django
- redis
volumes:
pgdata:
redisdata:
config/nginx/app.config
upstream django {
ip_hash;
server django:8000;
}
upstream nuxt {
ip_hash;
server nuxt:3000;
}
server {
location ~ /(api|admin|static)/ {
proxy_pass http://django;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $host;
}
location / {
proxy_pass http://nuxt;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $host;
}
listen 8000;
server_name localhost;
}
Say if my ec2 domain name pointer is ec2-52-204-122-132.compute-1.amazonaws.com
How do I set nginx up in my app to accept http connections to the frontend of my app?
On the backend localhost:8000/admin is my admin page I'd like to access this also using the ec2 domain name too.
What's the best way to alter my config so when I push my app after add the domain name pointer I can access my app hosted on ec2?
I've been reading documentation but can't find any helpful info for a dockerized django vue type app running on ec2.
firstly you need to make sure the security group attach to your box is open for incoming connection on port that NGinx listen
For each container you want to put on NGinx config you will need to find their do to so do:
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container_name_or_id
NGinx is not inside a container,docker-compose so, nginx.conf
upstream django {
ip_hash;
server <container IP>:8000; # <-- Change this, with container IP
}
upstream nuxt {
ip_hash;
server <container IP>:3000; # <-- Change this, with container IP
}
server {
location ~ /(api|admin|static)/ {
proxy_pass #django # <-- Change this, with container IP
proxy_pass <container IP>:8000; # <-- OR Change this, with container IP
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $host;
}
location / {
proxy_pass #nuxt # <-- Change this, add port
proxy_pass <container IP>:3000 # <-- OR Change this, add port
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $host;
}
listen 8000; # <-- Change port number, django already use this host port
server_name localhost ec2-52-204-122-132.compute-1.amazonaws.com; # <-- change this line, add EC2 public domain
}
docker-compose.yml
version: '3.4'
services:
db:
restart: always
image: postgres
volumes:
- pgdata:/var/lib/postgresql/data
env_file: .env
ports:
- "5432:5432"
expose:
- 5432
networks: # <-- Add this
- random_name # <-- Add this
redis:
restart: always
image: redis
volumes:
- redisdata:/data
networks: # <-- Add this
- random_name # <-- Add this
django:
build:
context: ./backend
env_file: .env
command: >
sh -c "./wait-for-it.sh db:5432 &&
cd autobets && python manage.py collectstatic --noinput &&
gunicorn --workers=2 --bind=0.0.0.0:8000 autobets.wsgi:application"
ports:
- "8000:8000"
volumes:
- ./backend:/app
depends_on:
- db
restart: on-failure
networks: # <-- Add this
- random_name # <-- Add this
nuxt:
build:
context: ./frontend
environment:
- API_URI=http://django:8000/api # <-- Wrong
# From you Javascript on the client point of view,
# you will request the public server, not the internal name of
# you backend inside a container, that the public will never see
- API_URI=http://ec2-52-204-122-132.compute-1.amazonaws.com/api # <-- Right
command: bash -c "npm install && npm run dev"
volumes:
- ./frontend:/app
ports:
- "3000:3000"
depends_on:
- django # <-- will become useless if change API_URI=
- redis # <-- bad design
networks: # <-- Add this
- random_name # <-- Add this
volumes:
pgdata:
redisdata:
networks: # <-- Add this
- random_name: # <-- Add this wanto make sure container can communicate
Security group on AWS should be open on the port you'll use for listening on NGinx, host 8000 is already used by django, so use another on
From app architecture POV, your backend should do the cache stuff with Redis, not the Frontend. Complexity or backend response caching should be cache on the backend somewhere on the controllers. You client should cache only statis assets. but you here to make you have working on a server, not to speak archi.