英文:
How to organize project with multiple Go modules and use Docker compose?
问题
我是你的中文翻译助手,以下是翻译好的内容:
我刚开始学习Go语言,对于如何在一个项目中结构化多个模块并使用docker compose
进行部署感到困惑。目前,该项目是一个包含两个模块的单一git仓库:一个用于API,另一个用于长时间运行的任务处理器,每个模块都在自己的容器中运行。两个模块之间使用RabbitMQ作为工作队列,并且还有一个MySQL数据库。
项目结构的简化视图如下:
├── api
│ ├── Dockerfile
│ └── stuff.go
├── docker-compose.yml
├── go.work
└── processor
├── Dockerfile
└── stuff.go
API的Dockerfile(处理器的Dockerfile除了将"api"替换为"processor"之外是相同的):
FROM golang:1.20
WORKDIR /usr/src/app
COPY go.mod go.sum ./
RUN go mod download && go mod verify
COPY . .
RUN go build -v -o /usr/local/bin/ ./...
CMD ["api"]
简化的docker-compose.yml:
version: '3.8'
services:
api:
build:
context: ./api
...
processor:
build:
context: ./processor
...
rabbitmq:
image: rabbitmq:3.11-management
...
mysql:
image: mysql:8.0
...
我使用docker compose up --build
命令运行这个项目,一切正常。问题是两个模块中存在一些重复的代码,应该放在一个共享模块中,但我不知道如何在保持能够在本地构建和工作的同时实现这一点。
通过研究,我得出的结论是我的模块应该位于它们自己的仓库中。如果我这样做,似乎我需要为docker-compose.yml
创建一个"项目"仓库,然后为共享模块创建另一个仓库。然后,当我在本地工作时,我会更新docker-compose.yml
中的上下文,指向我克隆API和处理器项目的位置。
问题是共享模块将没有Dockerfile
,也不会成为docker-compose.yml
中的一个服务。我假设Docker在构建时会使用go get
命令获取共享模块的内容,但我仍然希望能够在本地开发这段代码,而不必先提交和推送到远程仓库。
英文:
I'm new to Go and am confused about how to structure a project with multiple modules of my own and use them with docker compose
. The project is currently a single git repository containing two modules: an API and a processor for long-running work, each running in their own container. RabbitMQ is used as a work queue between the two and there's a MySQL database as well.
A simplified view of the project structure:
├── api
│   ├── Dockerfile
│   └── stuff.go
├── docker-compose.yml
├── go.work
└── processor
   ├── Dockerfile
   └── stuff.go
API Dockerfile (the processor one is identical except for "api" is "processor"):
FROM golang:1.20
WORKDIR /usr/src/app
COPY go.mod go.sum ./
RUN go mod download && go mod verify
COPY . .
RUN go build -v -o /usr/local/bin/ ./...
CMD [ "api" ]
Simplified docker-compose.yml:
version: '3.8'
services:
api:
build:
context: ./api
...
processor:
build:
context: ./processor
...
rabbitmq:
image: rabbitmq:3.11-management
...
mysql:
image: mysql:8.0
...
I run this with docker compose up --build
and it works fine. The problem is there's some duplicate code in both modules that belongs in a shared module, but I don't know how to do that while still being able to build and work locally.
From researching this I get the impression that my modules should be in their own repositories. If I do that, it seems like I'd need to create a "project" repository just for the docker-compose.yml
, then another for the shared module. I'd then update the docker-compose.yml
contexts to point to wherever I cloned the API and processor projects when working locally.
The problem is the shared module wouldn't have a Dockerfile
or be a service in the docker-compose.yml
. I assume Docker would go get
the contents of the shared module when building, but I still want to be able to work on that code locally without having to commit and push it to origin first.
答案1
得分: 0
我会忽略Docker部分。
按照你正常开发Go应用程序的方式来组织你的文件系统树,以及你计划如何打包和分发它。你应该能够使用正常的go test
和go build
命令进行开发。听起来你有多个顶级命令围绕着一个共享的代码库;将它们放在一个单独的存储库中是有意义的,但请记住每个顶级命令都需要有自己的目录。
如果你有这样的设置,并且想在Docker中运行它,重要的细节是你需要将整个源代码树作为构建上下文目录传递。每个应用程序的Dockerfile都可以,将其放在与其顶级应用程序相同的目录中也是有道理的。但是当你在Compose中声明它时,你必须告诉它项目根目录是构建上下文。
我会将docker-compose.yml
文件放在项目根目录中,与go.mod
文件相邻。它可能如下所示:
version: '3.8'
services:
api:
build:
context: .
dockerfile: ./cmd/api/Dockerfile
ports: ['8000:8000']
processor: { ... }
rabbitmq: { ... }
mysql: { ... }
你的Dockerfile看起来基本上和你已经展示的一样。由于构建上下文是项目根目录,所有的COPY
命令都是相对于项目根目录开始的;另一方面,无论哪个应用程序正在构建,你几乎都需要复制整个源代码树。对于Go,我几乎总是使用多阶段构建来避免在最终镜像中包含工具链。
# cmd/api/Dockerfile; but built with a context of the project root
FROM golang:1.20 AS builder
WORKDIR /usr/src/app
COPY go.mod go.sum ./
RUN go mod download && go mod verify
COPY . .
RUN go build -o api ./cmd/api
FROM ubuntu:22.04 # "alpine" or "not alpine" must match build stage
COPY --from=builder /usr/src/app/api /usr/local/bin/
CMD ["api"]
英文:
I would start off ignoring Docker here.
Arrange your filesystem tree however you would for a normal Go application, however you'd plan to package and distribute it. You should be able to do your development with normal go test
and go build
commands. It sounds like you have multiple top-level commands surrounding a shared code base; putting them in a single repository makes sense, but remember that each top-level command needs its own directory.
If you have a setup like this and you want to run it in Docker, the important detail is that you need to pass the entire source tree as the build-context directory. A Dockerfile for each application is fine, and there's an argument for putting it in the same directory as its top-level application. But when you declare it in Compose you must tell it the project root directory is the build context.
I'd put the docker-compose.yml
file in the project root, next to the go.mod
file. It might look like:
version: '3.8'
services:
api:
build:
context: .
dockerfile: ./cmd/api/Dockerfile
ports: ['8000:8000']
processor: { ... }
rabbitmq: { ... }
mysql: { ... }
Your Dockerfiles look essentially like what you've already shown. Since the build context is the project root, all COPY
commands start relative to the project root; on the other hand, you pretty much need to copy in the entire source tree no matter which application is building. For Go I would almost always use a multi-stage build to avoid including the toolchain in the final image.
# cmd/api/Dockerfile; but built with a context of the project root
FROM golang:1.20 AS builder
WORKDIR /usr/src/app
COPY go.mod go.sum ./
RUN go mod download && go mod verify
COPY . .
RUN go build -o api ./cmd/api
FROM ubuntu:22.04 # "alpine" or "not alpine" must match build stage
COPY --from=builder /usr/src/app/api /usr/local/bin/
CMD ["api"]
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论