Docker由nginx,express,letesencrypt SSL组成,获得502错误网关

时间:2019-09-01 03:54:50

标签: docker nginx docker-compose

我正在尝试找到一种使用docker-compose将nginx,express和letencrypt的SSL一起发布的方法。关于此的文档很多,因此我参考了这些文档并尝试进行自己的配置,我成功地从此nginx + ssl配置了https://medium.com/@pentacent/nginx-and-lets-encrypt-with-docker-in-less-than-5-minutes-b4b8a60d3a71

所以现在我想将示例nodejs express应用程序放入nginx + ssl docker-compose中。但是我不知道为什么,我从nginx那里得到502 Bad Gateway而不是express的初始页面。

我正在用我的左侧域和aws ec2 ubuntu16测试此应用。我认为域dns和安全规则设置没有问题。已经打开了80、443、3000个端口。当我在没有Express App的情况下对其进行测试时,它会显示Nginx默认页面。

/p/nginx/conf.d中的

nginx conf

server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com;
    server_tokens off;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

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

}

docker-compose.yml

version: '3'

services:
  app:
    container_name: express
    restart: always
    build: .
    ports: 
      - '3000:3000'
  nginx:
    container_name: nginx
    image: nginx:1.15-alpine
    restart: unless-stopped
    volumes:
      - ./data/nginx:/etc/nginx/conf.d
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    ports:
      - "80:80"
      - "443:443"
    command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
  certbot:
    image: certbot/certbot
    restart: unless-stopped
    volumes:
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

express的Dockerfile

FROM node:12.2-slim
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

我认为SSL可以正常工作,但是express appnginx之间存在一些问题。我该如何解决?

4 个答案:

答案 0 :(得分:1)

proxy_pass http://localhost:3000

正在将请求代理到运行nginx的容器上的3000端口。相反,您想要连接到运行Express的容器的3000端口。为此,我们需要做两件事。

首先,我们使Express容器在预定义的主机名下对Nginx容器可见。我们可以在docker-compose中使用links

nginx:
  links:
    - "app:expressapp"

或者,由于链接现在被认为是旧功能,因此更好的方法是使用用户定义的网络。使用

定义自己的网络
docker network create my-network 

,然后通过在顶层添加以下内容,将您的容器以撰写文件的形式连接到该网络:

networks:
    default:
        external:
            name: my-network

连接到用户定义网络的所有服务都可以通过名称相互访问,而无需显式设置链接。

然后在nginx.conf中,我们使用该主机名代理到express容器:

location / {
    proxy_pass http://app:3000
}

答案 1 :(得分:1)

  

警告--link标志是Docker的旧功能。它最终可能会被删除。除非您绝对需要继续使用它,否则建议您使用user-defined networks来促进两个容器之间的通信,而不要使用--link

docker-compose.yml中定义网络,并使用适当的网络配置服务:

version: '3'

services:
  app:
    restart: always
    build: .
    networks:
      - backend
    expose:
      - "3000"
  nginx:
    image: nginx:1.15-alpine
    restart: unless-stopped
    depends_on:
      - app
    volumes:
      - ./data/nginx:/etc/nginx/conf.d
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    networks:
      - frontend
      - backend
    ports:
      - "80:80"
      - "443:443"
    command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
  certbot:
    image: certbot/certbot
    restart: unless-stopped
    volumes:
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
networks:
  frontend:
  backend:

注意app服务不再发布,因为它是主机的端口,它仅公开端口3000(参考exposing and publishing ports),仅可用于连接到的服务backend网络。 nginx服务在backendfrontend网络中都有据点,可以接受来自frontend的传入流量,并在{中代理到app的连接{1}}(参考multi-host networking)。

使用user-defined networks,您可以解析服务名称:

backend

从您的服务中删除user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { upstream app { server app:3000 max_fails=3; } server { listen 80; server_name example.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl; listen [::]:443 ssl; server_name example.com; server_tokens off; location / { proxy_pass http://app; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } ssl_certificate /etc/letsencrypt/live/sendpi.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/sendpi.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; } } 可以scale的服务:container_name-docker-compose up -d --scale nginx=1 app=3load balance the traffic in round-robin变成3 nginx容器。

答案 2 :(得分:0)

我认为,这可能是造成混淆的根源,原因是docker-compose中正在运行的服务中“ localhost”名称的行为方式。 docker-compose编排容器的方式,每个容器将其自身理解为“ localhost”,因此“ localhost”不引用主机(如果我没记错的话,也无法在容器上运行主机访问主机端口上公开的服务(可能还有一些安全漏洞)。演示:

services:
  app:
    container_name: express
    restart: always
    build: .
    ports: 
      - '2999:3000' # expose app's port on host's 2999

重建

docker-compose build
docker-compose up

运行Express应用程序的告诉容器针对端口3000上其自身正在运行的服务进行卷曲:

$ docker-compose exec app /bin/bash -c "curl http://localhost:3000"

<!DOCTYPE html>
<html>
  <head>
    <title>Express</title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1>Express</h1>
    <p>Welcome to Express</p>
  </body>
</html>

告诉应用尝试使用我们在主机上的端口2999上公开的相同服务:

$ docker-compose exec app /bin/bash -c "curl http://localhost:2999"
curl: (7) Failed to connect to localhost port 2999: Connection refused

我们当然也会在运行容器之间看到相同的行为,因此在您的设置中,nginx尝试代理它在localhost:3000上运行的自己的服务(但您知道其中没有一个)。

答案 3 :(得分:0)

任务

  • 构建NodeJS应用
  • 从框中添加SSL功能(可以自动运行)

解决方案

https://github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion

/ {path_to_the_project} /Docker-compose.yml

version: '3.7'
services:
  nginx-proxy:
    image: jwilder/nginx-proxy:alpine
    restart: always
    container_name: nginx-proxy
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./certs:/etc/nginx/certs:ro
      - ./vhost.d:/etc/nginx/vhost.d
      - ./html:/usr/share/nginx/html
      - ./conf.d:/etc/nginx/conf.d
    ports:
      - "443:443"
      - "80:80"
    labels:
      - "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true"

  letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    container_name: letsencrypt
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./certs:/etc/nginx/certs:rw
      - ./vhost.d:/etc/nginx/vhost.d:rw
      - ./html:/usr/share/nginx/html:rw
    environment:
      - NGINX_PROXY_CONTAINER=nginx-proxy

  api:
    container_name: ${APP_NAME}
    build:
      context: .
      dockerfile: Dockerfile
    command: npm start --port ${APP_PORT}
    expose:
      - ${APP_PORT}
    # ports:
    #   - ${APP_PORT}:${APP_PORT}
    restart: always
    environment:
      VIRTUAL_PORT: ${APP_PORT}
      VIRTUAL_HOST: ${DOMAIN}
      LETSENCRYPT_HOST: ${DOMAIN}
      LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}
      NODE_ENV: production
      PORT: ${APP_PORT}
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./certs:/etc/nginx/certs:ro

/ {path_to_the_project} /.env

APP_NAME=best_api
APP_PORT=3000
DOMAIN=api.site.com
LETSENCRYPT_EMAIL=myemail@gmail.com

在运行容器之前,请不要忘记将DOMAIN连接到服务器

它如何工作?

只需运行docker-compose up --build -d