如何使用 Nginx 和 Traefik 将静态文件提供给同一域中的 Dockerized Angular 应用程序?
How to serve static files to Dockerized Angular apps on the same domain using Nginx and Traefik?
我想做的是在同一个域中有 2 个 Angular 应用程序,但为不同的路径提供不同的文件。例如,浏览到“app1/”将显示与“app1/test”路径上不同的内容。注意:这些是默认的 Angular 应用程序,它们没有做任何特别的事情。我唯一将登录页面更改为“app1”或“app2”以验证路径是否命中了正确的容器。
此外,我希望 app1/test 路径上的任何内容都解析为同一主机。因此像“app1/test”和“app1/test/page1”这样的路径将指向同一个容器。我可以毫无问题地访问 app1/,但我似乎无法弄清楚如何正确路由到 app1/test。我可以浏览到它,但它提供 app1/ 的内容而不是它应该提供的内容。我已经确认我正在创建的所有文件也在容器内,只是没有被访问。
最后,URL 信息需要处于允许 nginx 路由到正确容器并允许 angular 应用程序处理自己的虚拟路由的状态,而不会受到这两者的干扰彼此。
这是我的配置文件:
docker-compose.yml
services:
traefik:
image: "traefik:v2.4"
container_name: "traefik"
command:
- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
app1:
build:
context: ./app1
ports:
- "8081:80"
labels:
- "traefik.enable=true"
- "traefik.http.routers.app1.entrypoints=web"
- "traefik.http.routers.app1.rule=Host(`localhost`)"
- "traefik.http.routers.app1.middlewares=app1-stripprefix"
- "traefik.http.routers.app1.middlewares=app1-autodetect"
- "traefik.http.middlewares.app1-stripprefix.stripprefix.prefixes=/"
- "traefik.http.middlewares.app1-autodetect.contenttype.autodetect=false"
- "traefik.port=80"
app2:
build:
context: ./app2
ports:
- "8082:80"
labels:
- "traefik.enable=true"
- "traefik.http.routers.app2.entrypoints=web"
- "traefik.http.routers.app2.rule=Host(`localhost`) && PathPrefix(`/test{regex:$$|/.*}`)"
- "traefik.http.routers.app2.middlewares=app2-stripprefix"
- "traefik.http.routers.app2.middlewares=app2-autodetect"
- "traefik.http.middlewares.app2-stripprefix.stripprefix.prefixes=/test"
- "traefik.http.middlewares.app2-autodetect.contenttype.autodetect=false"
- "traefik.port=80"
两个 Angular 应用程序的 Dockerfile
# set working directory
WORKDIR /app
# add `/app/node_modules/.bin` to $PATH
ENV PATH /app/node_modules/.bin:$PATH
# install and cache app dependencies
COPY package.json /app/package.json
RUN npm install
RUN npm install -g @angular/cli@11.0.7
# add app
COPY . /app
# generate build
RUN ng build --output-path=dist
# base image
FROM nginx:1.16.0-alpine
# copy artifact build from the 'build environment'
COPY --from=build /app/dist /usr/share/nginx/html
COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf
# expose port 80
EXPOSE 80
# run nginx
CMD ["nginx", "-g", "daemon off;"]
nginx.conf 对于 app1
include /etc/nginx/mime.types;
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
nginx.conf 对于 app2
include mime.types;
include /etc/nginx/mime.types;
server {
listen 80;
server_name localhost;
location /test {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
您应该从更易于管理的子域提供服务,有一个普遍接受的模式 sandbox|dev|test|staging.example.com,example.com 正在生产。
您将能够在整个堆栈(反向代理、Web 服务器、angular)中正确路由、删除 cookie、管理单独的身份等。
不幸的是,您遇到的情况是因为 traefik 的路由规则以及用于描述路由的正则表达式中缺少否定。基本上,第一个路由器正在以贪婪的方式接管 Host('localhost) 。
你可以做的是按照 Traefik 的文档并尝试设置优先级:
请参阅设置优先级部分——使用文件提供程序
https://doc.traefik.io/traefik/routing/routers/#priority
我解决了我的问题。我沿途进行了一些编辑,但解开谜题的最后关键是我的 nginx.conf 文件中的一行“try_files”。在这里阅读:https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
更新文件如下:
docker-compose.yml
services:
traefik:
image: "traefik:v2.4"
container_name: "traefik"
command:
- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
app1:
build:
context: ./app1
ports:
- "8081:80"
labels:
- "traefik.enable=true"
- "traefik.http.routers.app1.entrypoints=web"
- "traefik.http.routers.app1.rule=Host(`localhost`)"
- "traefik.http.routers.app1.middlewares=app1-stripprefix"
- "traefik.http.routers.app1.middlewares=app1-autodetect"
- "traefik.http.middlewares.app1-stripprefix.stripprefix.prefixes=/"
- "traefik.http.middlewares.app1-autodetect.contenttype.autodetect=false"
- "traefik.port=80"
app2:
build:
context: ./app2
ports:
- "8082:80"
labels:
- "traefik.enable=true"
- "traefik.http.routers.app2.entrypoints=web"
- "traefik.http.routers.app2.rule=PathPrefix(`/test{regex:$$|/.*}`)"
- "traefik.http.routers.app2.middlewares=app2-stripprefix"
- "traefik.http.routers.app2.middlewares=app2-autodetect"
- "traefik.http.middlewares.app2-stripprefix.stripprefix.prefixes=/test"
- "traefik.http.middlewares.app2-autodetect.contenttype.autodetect=false"
- "traefik.port=80"
Dockerfile(针对 app1)
FROM node:14.15.4 as build
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json /app/package.json
RUN npm install
RUN npm install -g @angular/cli@11.0.7
COPY . /app
RUN ng build --output-path=dist
FROM nginx:1.16.0-alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
app2 的 Dockerfile
FROM node:14.15.4 as build
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json /app/package.json
RUN npm install
RUN npm install -g @angular/cli@11.0.7
COPY . /app
RUN npm install
RUN npm install --save-dev @angular-devkit/build-angular
RUN ng build --base-href /test --deploy-url /test/ --output-path=dist
FROM nginx:1.16.0-alpine
COPY --from=build /app/dist /usr/share/nginx/html/test
COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
nginx.conf 对于 app1
include mime.types;
include /etc/nginx/mime.types;
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
nginx.conf 对于 app2
include mime.types;
include /etc/nginx/mime.types;
server {
listen 80;
server_name localhost;
location /test {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /test/index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location /.ico {
root /usr/share/nginx/html;
add_header Content-Type image/x-icon;
}
}
我想做的是在同一个域中有 2 个 Angular 应用程序,但为不同的路径提供不同的文件。例如,浏览到“app1/”将显示与“app1/test”路径上不同的内容。注意:这些是默认的 Angular 应用程序,它们没有做任何特别的事情。我唯一将登录页面更改为“app1”或“app2”以验证路径是否命中了正确的容器。
此外,我希望 app1/test 路径上的任何内容都解析为同一主机。因此像“app1/test”和“app1/test/page1”这样的路径将指向同一个容器。我可以毫无问题地访问 app1/,但我似乎无法弄清楚如何正确路由到 app1/test。我可以浏览到它,但它提供 app1/ 的内容而不是它应该提供的内容。我已经确认我正在创建的所有文件也在容器内,只是没有被访问。
最后,URL 信息需要处于允许 nginx 路由到正确容器并允许 angular 应用程序处理自己的虚拟路由的状态,而不会受到这两者的干扰彼此。
这是我的配置文件:
docker-compose.yml
services:
traefik:
image: "traefik:v2.4"
container_name: "traefik"
command:
- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
app1:
build:
context: ./app1
ports:
- "8081:80"
labels:
- "traefik.enable=true"
- "traefik.http.routers.app1.entrypoints=web"
- "traefik.http.routers.app1.rule=Host(`localhost`)"
- "traefik.http.routers.app1.middlewares=app1-stripprefix"
- "traefik.http.routers.app1.middlewares=app1-autodetect"
- "traefik.http.middlewares.app1-stripprefix.stripprefix.prefixes=/"
- "traefik.http.middlewares.app1-autodetect.contenttype.autodetect=false"
- "traefik.port=80"
app2:
build:
context: ./app2
ports:
- "8082:80"
labels:
- "traefik.enable=true"
- "traefik.http.routers.app2.entrypoints=web"
- "traefik.http.routers.app2.rule=Host(`localhost`) && PathPrefix(`/test{regex:$$|/.*}`)"
- "traefik.http.routers.app2.middlewares=app2-stripprefix"
- "traefik.http.routers.app2.middlewares=app2-autodetect"
- "traefik.http.middlewares.app2-stripprefix.stripprefix.prefixes=/test"
- "traefik.http.middlewares.app2-autodetect.contenttype.autodetect=false"
- "traefik.port=80"
两个 Angular 应用程序的 Dockerfile
# set working directory
WORKDIR /app
# add `/app/node_modules/.bin` to $PATH
ENV PATH /app/node_modules/.bin:$PATH
# install and cache app dependencies
COPY package.json /app/package.json
RUN npm install
RUN npm install -g @angular/cli@11.0.7
# add app
COPY . /app
# generate build
RUN ng build --output-path=dist
# base image
FROM nginx:1.16.0-alpine
# copy artifact build from the 'build environment'
COPY --from=build /app/dist /usr/share/nginx/html
COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf
# expose port 80
EXPOSE 80
# run nginx
CMD ["nginx", "-g", "daemon off;"]
nginx.conf 对于 app1
include /etc/nginx/mime.types;
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
nginx.conf 对于 app2
include mime.types;
include /etc/nginx/mime.types;
server {
listen 80;
server_name localhost;
location /test {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
您应该从更易于管理的子域提供服务,有一个普遍接受的模式 sandbox|dev|test|staging.example.com,example.com 正在生产。 您将能够在整个堆栈(反向代理、Web 服务器、angular)中正确路由、删除 cookie、管理单独的身份等。
不幸的是,您遇到的情况是因为 traefik 的路由规则以及用于描述路由的正则表达式中缺少否定。基本上,第一个路由器正在以贪婪的方式接管 Host('localhost) 。 你可以做的是按照 Traefik 的文档并尝试设置优先级:
请参阅设置优先级部分——使用文件提供程序 https://doc.traefik.io/traefik/routing/routers/#priority
我解决了我的问题。我沿途进行了一些编辑,但解开谜题的最后关键是我的 nginx.conf 文件中的一行“try_files”。在这里阅读:https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
更新文件如下:
docker-compose.yml
services:
traefik:
image: "traefik:v2.4"
container_name: "traefik"
command:
- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
app1:
build:
context: ./app1
ports:
- "8081:80"
labels:
- "traefik.enable=true"
- "traefik.http.routers.app1.entrypoints=web"
- "traefik.http.routers.app1.rule=Host(`localhost`)"
- "traefik.http.routers.app1.middlewares=app1-stripprefix"
- "traefik.http.routers.app1.middlewares=app1-autodetect"
- "traefik.http.middlewares.app1-stripprefix.stripprefix.prefixes=/"
- "traefik.http.middlewares.app1-autodetect.contenttype.autodetect=false"
- "traefik.port=80"
app2:
build:
context: ./app2
ports:
- "8082:80"
labels:
- "traefik.enable=true"
- "traefik.http.routers.app2.entrypoints=web"
- "traefik.http.routers.app2.rule=PathPrefix(`/test{regex:$$|/.*}`)"
- "traefik.http.routers.app2.middlewares=app2-stripprefix"
- "traefik.http.routers.app2.middlewares=app2-autodetect"
- "traefik.http.middlewares.app2-stripprefix.stripprefix.prefixes=/test"
- "traefik.http.middlewares.app2-autodetect.contenttype.autodetect=false"
- "traefik.port=80"
Dockerfile(针对 app1)
FROM node:14.15.4 as build
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json /app/package.json
RUN npm install
RUN npm install -g @angular/cli@11.0.7
COPY . /app
RUN ng build --output-path=dist
FROM nginx:1.16.0-alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
app2 的 Dockerfile
FROM node:14.15.4 as build
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json /app/package.json
RUN npm install
RUN npm install -g @angular/cli@11.0.7
COPY . /app
RUN npm install
RUN npm install --save-dev @angular-devkit/build-angular
RUN ng build --base-href /test --deploy-url /test/ --output-path=dist
FROM nginx:1.16.0-alpine
COPY --from=build /app/dist /usr/share/nginx/html/test
COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
nginx.conf 对于 app1
include mime.types;
include /etc/nginx/mime.types;
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
nginx.conf 对于 app2
include mime.types;
include /etc/nginx/mime.types;
server {
listen 80;
server_name localhost;
location /test {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /test/index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location /.ico {
root /usr/share/nginx/html;
add_header Content-Type image/x-icon;
}
}