简体   繁体   English

Docker-compose + Nginx + Certbot + Simple Django Rest 框架应用

[英]Docker-compose + Nginx + Certbot + Simple Django Rest Framework app

I am trying to deploy a simple Django Rest Framework app to the production server using Docker. My aim is to install Nginx with a proxy and Certbot for a regular Let'sEncrypt SSL at the same time.我正在尝试使用 Docker 将一个简单的 Django Rest Framework 应用程序部署到生产服务器。我的目标是同时为常规的 Let'sEncrypt SSL 安装带有代理和 Certbot 的 Nginx。 I manage my dependencies in DockerFiles and docker-compose.我在 DockerFiles 和 docker-compose 中管理我的依赖项。

So the folder structure has the following view:因此文件夹结构具有以下视图:

  • app应用程序
    • DockerFile DockerFile
  • nginx nginx
    • DockerFile DockerFile
    • init-letsencrypt.sh init-letsencrypt.sh
    • nginx.conf nginx.conf
  • docker-compose.yml docker-compose.yml

My idea is to hold all the configs in app/docker-compose.yml and start many different instances from the same source.我的想法是将所有配置保存在 app/docker-compose.yml 中,并从同一源启动许多不同的实例。 But I do not have any nginx or certbot config in app/DockerFile - that's only for Django Rest Framework and that works well.但我在 app/DockerFile 中没有任何 nginx 或 certbot 配置 - 这仅适用于 Django Rest 框架并且效果很好。 But in docker-compose.yml I have the following code:但是在docker-compose.yml中我有以下代码:

version: '3'

'services':
    app:
      container_name: djangoserver
      command: gunicorn prototyp.wsgi:application --env DJANGO_SETTINGS_MODULE=prototyp.prod_settings --bind 0.0.0.0:8000 --workers=2 --threads=4 --worker-class=gthread
      build:
        context: ./api
        dockerfile: Dockerfile
      restart: always
      ports:
        - "8000:8000"
      depends_on:
        - otherserver
    otherserver:
      container_name: otherserver
      build:
        context: ./otherserver
        dockerfile: Dockerfile
      restart: always
    nginx:
      build: ./nginx
      ports:
         - 80:80
      depends_on:
         - app
      command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
    certbot:
      image: certbot/certbot
      entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

This makes me to build "app", "otherserver", "nginx" and "certbot".这让我构建“app”、“otherserver”、“nginx”和“certbot”。 The most important parts are in "nginx" folder.最重要的部分在“nginx”文件夹中。 I used this manual and cloned file "init-letsencrypt.sh" from the source just the way it was described.我按照描述的方式使用了本手册并从源代码中克隆了文件“init-letsencrypt.sh”。 Then I tried to bash it:然后我试了bash吧:

nginx/DockerFile : nginx/码头文件

FROM nginx:1.19.0-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
RUN mkdir -p /usr/src/app
COPY init-letsencrypt.sh /usr/src/app
WORKDIR /usr/src/app
RUN chmod +x init-letsencrypt.sh
ENTRYPOINT ["/usr/src/app/init-letsencrypt.sh"]

In nginx/nginx.conf I have the following code:nginx/nginx.conf我有以下代码:

upstream django {
    server app:8000;
}

server {
    listen 80;
    server_name app.com www.app.com;
    location / {
        return 301 https://$host$request_uri;
    }
}

server {

    listen 443 ssl;
    server_name app.com www.app.com;
    access_log /var/log/nginx-access.log;
    error_log /var/log/nginx-error.log;

    ssl_certificate /etc/letsencrypt/live/app.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location ^/static/rest_framework/((img/|css/|js/|fonts).*)$ {
        autoindex on;
        access_log off;
        alias /usr/src/app/static/rest_framework/$1;
    }

    location / {
        proxy_pass http://django;
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        client_body_buffer_size 256k;

        proxy_connect_timeout 120;
        proxy_send_timeout 120;
        proxy_read_timeout 120;

        proxy_buffer_size 64k;
        proxy_buffers 4 64k;
        proxy_busy_buffers_size 64k;
        proxy_temp_file_write_size 64k;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        client_max_body_size 100M;
    }
}

So, with this configuration when I do "docker-compose build", the build works without any errors and everything is successfully built.因此,当我执行“docker-compose build”时使用此配置,构建工作没有任何错误并且一切都已成功构建。 But as soon as I do "docker-compose up" I have the problem that certbot and nginx are not connect and the app is working only when I use http://app.com:8000 instead of https://app.com .但是,一旦我执行“docker-compose up”,我就会遇到 certbot 和 nginx 未连接的问题,并且该应用程序仅在我使用http://app.com:8000而不是https://app.com 时才工作. In console I do not have any errors.在控制台中我没有任何错误。 What do I do wrong?我做错了什么? What have I missed?我错过了什么? Any help will be appreciated.任何帮助将不胜感激。

I see in your setup you try to run let's encrypt from within the nginx container.我在您的设置中看到您尝试从 nginx 容器中运行 let's encrypt。 But I believe there are two better way that I describe in details here and here .但我相信我在这里这里详细描述了两种更好的方法。

The idea behind the first method is to have a docker-compose file to initiate the letsencrypt certificate, and another docker-compose file to run the system and renew the certificate.第一种方法背后的想法是有一个 docker-compose 文件来启动 letsencrypt 证书,另一个 docker-compose 文件来运行系统和更新证书。

So without further ado, here is the file structure and content that is working really well for me (you still need to adapt the files to suit your needs)因此,事不宜迟,这是对我来说非常有效的文件结构和内容(您仍然需要调整文件以满足您的需要)

  • ./setup.sh ./setup.sh
  • ./docker-compose-initiate.yaml ./docker-compose-initiate.yaml
  • ./docker-compose.yaml ./docker-compose.yaml
  • ./etc/nginx/templpates/default.conf.template ./etc/nginx/templpates/default.conf.template
  • ./etc/nginx/templpates-initiation/default.conf.template ./etc/nginx/templpates-initiation/default.conf.template

The setup in 2 phases:设置分 2 个阶段:

In the first phase "the initiation phase" we will run an nginx container, and a certbot container just to obtain the ssl certificate for the first time and store it on the host./etc/letsencrypt folder在第一阶段“启动阶段”,我们将运行一个 nginx 容器和一个 certbot 容器,以便第一次获取 ssl 证书并将其存储在主机上的 /etc/letsencrypt 文件夹中

I the second phase "the operation phase" we run all necessary services for the app including nginx that will use the letsencrypt folder this time to serve https on port 443, a certbot container will also run (on demand) to renew the certificate.在第二阶段“操作阶段”,我们为应用程序运行所有必要的服务,包括 nginx,这次将使用 letsencrypt 文件夹在端口 443 上为 https 提供服务,certbot 容器也将(按需)运行以更新证书。 We can add a cron job for that.我们可以为此添加一个 cron 作业。 So the setup.sh script is a simple convenience script that runs the commands one after another:所以 setup.sh 脚本是一个简单的便捷脚本,可以一个接一个地运行命令:

#!/bin/bash
# the script expects two arguments:
# - the domain name for which we are obtaining the ssl certificatee
# - the Email address associated with the ssl certificate
echo DOMAIN=$1 >> .env
echo EMAIL=$2 >> .env

# Phase 1 "Initiation"
docker-compose -f ./docker-compose-first.yaml up -d nginx
docker-compose -f ./docker-compose-first.yaml up certbot
docker-compose -f ./docker-compose-first.yaml down

# Phase 2 "Operation"    
crontab ./etc/crontab
docker-compose -f ./docker-compose.yaml up -d

Phase 1: The ssl certificate initiation phase:第一阶段:ssl证书发起阶段:

./docker-compose-initiate.yaml ./docker-compose-initiate.yaml

version: "3"
services:
  nginx:
    container_name: nginx
    image: nginx:latest
    environment:
      - DOMAIN
    ports:
      - 80:80
    volumes:
      - ./etc/nginx/templates-initiate:/etc/nginx/templates:ro
      - ./etc/letsencrypt:/etc/letsencrypt:ro
      - ./certbot/data:/var/www/certbot
  certbot:
    container_name: certbot
    image: certbot/certbot:latest
    depends_on:
      - nginx
    command: >- 
             certonly --reinstall --webroot --webroot-path=/var/www/certbot
             --email ${EMAIL} --agree-tos --no-eff-email
             -d ${DOMAIN}
    volumes:
      - ./etc/letsencrypt:/etc/letsencrypt
      - ./certbot/data:/var/www/certbot

./etc/nginx/templates-initiate/default.conf.template ./etc/nginx/templates-initiate/default.conf.template

server {
    listen [::]:80;
    listen 80;
    server_name $DOMAIN;
    location ~/.well-known/acme-challenge {
        allow all;
        root /var/www/certbot;
    }
}

Phase 2: The operation phase第二阶段:运营阶段

./docker-compose.yaml ./docker-compose.yaml

services:
  app:
    {{your_configurations_here}}
  {{other_services...}}:
    {{other_services_configuraitons}}
  nginx:
    container_name: nginx
    image: nginx:latest
    restart: always
    environment:
      - DOMAIN
    depends_on:
      - app
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./etc/nginx/templates:/etc/nginx/templates:ro
      - ./etc/letsencrypt:/etc/letsencrypt
      - ./certbot/data:/var/www/certbot
      - /var/log/nginx:/var/log/nginx
  certbot:
    container_name: certbot
    image: certbot/certbot:latest
    depends_on:
      - nginx
    command: >-
             certonly --reinstall --webroot --webroot-path=/var/www/certbot
             --email ${EMAIL} --agree-tos --no-eff-email
             -d ${DOMAIN} 
    volumes:
      - ./etc/letsencrypt:/etc/letsencrypt
      - ./certbot/data:/var/www/certbot

./etc/nginx/templates/default.conf.template ./etc/nginx/templates/default.conf.template

server {
    listen [::]:80;
    listen 80;

    server_name $DOMAIN;

    return 301 https://$host$request_uri;
}
server {
    listen [::]:443 ssl http2;
    listen 443 ssl http2;

    server_name $DOMAIN; 

    ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;

    location ~ /.well-known/acme-challenge {
        allow all;
        root /var/www/html;
    }

    access_log  /var/log/nginx/access.log;
    error_log   /var/log/nginx/error.log;

    location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-Host $host;
      proxy_set_header X-Forwarded-Proto https;
      proxy_pass http://app:80;
  }
}

The second method uses two docker images: http-proxy and http-proxy-acme-companion that were developed specifically for this reason.第二种方法使用两个 docker 图像:http-proxy 和 http-proxy-acme-companion 是专门为此开发的。 I suggest looking at the blog post for further details.我建议查看博客文章以获取更多详细信息。

As I see, you havenot exposed port 443 for nginx container:如我所见,您尚未为 nginx 容器公开端口 443:

nginx:
      build: ./nginx
      ports:
         - 80:80
         - 443:443
      depends_on:

Add more 443 port.添加更多443端口。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM