我正在尝试找到一种使用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 app
和nginx
之间存在一些问题。我该如何解决?
答案 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
服务在backend
和frontend
网络中都有据点,可以接受来自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=3
将load 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)
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