我有一个包含 4 个容器的 docker compose 文件。 Caddy 、Watchtower 以及前端和后端容器。前端采用React+vite。后端是 Node.js 和 Express。
我在 caddy 容器和前端容器之间有一个共享卷。前端应该使用“npm run build”构建其文件,然后构建文件应该位于 caddy 的 usr/share/caddy 中,以便可以提供它们。
这是我的 docker 撰写:
services:
caddy:
image: caddy
container_name: caddy_container
ports:
- "8080:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- frontend_build:/usr/share/caddy
restart: always
networks:
- default
backend:
image: hashirayaz/rehaish_backend:latest
container_name: backend_container
# build: ./rehaish_backend
environment:
- MONGO_USERNAME=${MONGO_USERNAME}
- MONGO_PASSWORD=${MONGO_PASSWORD}
- MONGO_DATABASE=${MONGO_DATABASE}
- PORT=${PORT}
- JWT_SECRET=${JWT_SECRET}
- JWT_EXPIRES_IN=${JWT_EXPIRES_IN}
- NODE_ENV=${NODE_ENV}
- CORS_ORIGIN=${CORS_ORIGIN}
- NODE_ENV=production
ports:
- "3000:3000"
networks:
- default
frontend:
image: hashirayaz/rehaish_frontend:latest
container_name: frontend_container
# build: ./rehaish_frontend
volumes:
- frontend_build:/app/dist
environment:
- VITE_BACKEND_URL=${VITE_BACKEND_URL}
networks:
- default
depends_on:
- backend
- caddy
labels:
- com.centurylinklabs.watchtower.enable=true
watchtower:
image: containrrr/watchtower
container_name: watchtower
restart: always
privileged: true
networks:
- default
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --interval 5
environment:
- REPO_USER=${DOCKER_USER}
- REPO_PASS=${DOCKER_PAT}
- WATCHTOWER_RETAIN=1
volumes:
caddy_data:
frontend_build:
networks:
default:
driver: bridge
这是我的球童文件:
{
email [email protected]
}
rehaishkikhwaish.site {
# Serve static files from the dist folder (mounted in /usr/share/caddy)
root * /usr/share/caddy
encode gzip
file_server
# Reverse proxy for backend API
handle /api/* {
reverse_proxy backend_container:3000
}
handle /img/* {
reverse_proxy backend_container:3000
}
tls {
issuer acme
}
}
当我运行 docker compose up 时,caddy 自己的 index.html 正在我的网站上提供。当我将shell附加到前端容器并cd到/app/dist时,index.html是caddy自己的index.html而不是我网站的index.html
该行为听起来与 Docker 初始化命名卷的规则一致。 尝试使用命名卷将文件从一个容器发布到另一个容器存在几个重大问题,我会避免这样做。 更一般地说,我建议不要在已包含图像内容的目录上安装任何内容。
您可以构建 Dockerfile
FROM
任何您想要的其他图像。 由于您已经为镜像指定了 image:
名称,因此您可以将 Dockerfile 中的文件从前端镜像添加到代理镜像中;它可以小至COPY
Compose 不知道图像之间的构建依赖关系,因此当您需要重建它时,您需要分两步完成
# Dockerfile.caddy
FROM caddy
COPY --from=hashirayaz/rehaish_frontend:latest /app/dist /usr/share/caddy
通过此设置,您还可以从 Compose 文件中删除
docker-compose build frontend
docker-compose build
卷以及所有提及它的内容。
(看起来没有任何东西会将请求传递到frontend_build
容器。如果这只将前端构建为静态文件,请考虑在多阶段构建中进行。这将允许您从正在运行的容器中删除无操作容器应用程序,只需运行
frontend
一次即可重建所有内容。)
通过您此处的设置,您会遇到两个直接的技术问题。 如果你启动一个挂载了 Docker 命名卷的容器,该卷是空的,你将它挂载到一个有镜像内容的目录上,那么 Docker 会将镜像内容复制到该卷中;然后,无论如何,体积内容都会隐藏原始图像中的内容。如果 Caddy 容器首先启动(您通过
docker-compose build
强制启动),则
Caddy容器启动;depends_on:
frontend_build
文件的目录上;因此文件被复制到卷中。前端容器启动; index.html
frontend_build
容器被体积内容替换。
/app/dist
,强制
depends_on:
在 frontend
之前开始,而不是 反之亦然。 但是以这种方式使用命名卷还存在其他几个问题: 它仅适用于 Docker 命名卷;不是绑定挂载、任何类型的 Kubernetes 卷挂载,也不是任何其他存储。