[英]How to setup Docker Compose without a Dockerfile?
I have spend the last few hours trying to setup 2 default images of nodejs 14 and rethinkdb 2.3.5, so sorry if the tone is a little frustrated but I am currently frustrated.我花了过去几个小时尝试设置 nodejs 14 和 rethinkdb 2.3.5 的 2 个默认图像,如果语气有点沮丧,我很抱歉,但我目前很沮丧。
My requirements are seemingly super simple.我的要求看似超级简单。
npm ci
and npm test
. npm ci
和npm test
。I want the tests to be reproducible across all developer machines - currently no CI.我希望测试能够在所有开发人员机器上重现——目前没有 CI。 Regardless of where the developer has the project on their hard drive.无论开发人员将项目放在硬盘上的哪个位置。
I have a single docker-compose.yml
file.我有一个docker-compose.yml
文件。
version: "3"
services:
tests:
image: node:14
ports:
- "3000:3000"
# command:
# - npm ci
# - npm test
volumes:
- ".:/cli-app"
depends_on:
- rethinkdb
rethinkdb:
image: rethinkdb
ports:
- "28015:28015"
- "8080:8080"
volumes:
- "./data: /data"
command: rethinkdb --bindall --data /data
The point of this answer is not to give the must succinct explanation (briefly and clearly expressed) as possible but to highlight all the confusion that the current documentation at docs.docker.com and hub.docker.com create. The point of this answer is not to give the must succinct explanation (briefly and clearly expressed) as possible but to highlight all the confusion that the current documentation at docs.docker.com and hub.docker.com create. Eventually I/we will get it right and a succinct answer can be written.最终我/我们会做对,并且可以写出简洁的答案。
The corrected docker-compose.yml
:更正后的docker-compose.yml
:
version: "3"
services:
tests:
image: "node:14"
user: "node"
working_dir: /home/node/app
volumes:
- ./:/home/node/app
container_name: nodejs
depends_on:
- rethinkdb
command: bash -c "npm ci && npm test"
rethinkdb:
image: rethinkdb:2.3.5
container_name: rethinkdb
Right off the bat, the documentation at docs.docker.com and hub.docker.com is arguably the worse documentation ever written, since it is a) wrong , b) assumes prior knowledge . Right off the bat, the documentation at docs.docker.com and hub.docker.com is arguably the worse documentation ever written, since it is a) wrong , b) assumes prior knowledge .
If any of the following is wrong - blame the horrible documentation.如果以下任何一项是错误的 - 归咎于可怕的文档。
No, you do not need a Dockerfile unless you plan to built your own image.不,您不需要Dockerfile ,除非您计划构建自己的映像。
So after wasting an hour or so on different outdated examples , you might be lucky to discover that everything you have tried with context
to get around the absolute path examples... does not matter at all unless you are creating your own image from scratch (which 90% of docker users, do not need).因此,在浪费了一个小时左右的不同过时示例之后,您可能会幸运地发现您尝试使用context
来绕过绝对路径示例的所有内容......根本不重要,除非您从头开始创建自己的图像(其中 90% 的 docker 用户,不需要)。
Tip use
docker system prune
to delete all those unfortunate useless docker containers you've creating by following example.提示使用docker system prune
来删除您通过以下示例创建的所有那些不幸的无用 docker 容器。
Next up, find the correct docker containers.接下来,找到正确的 docker 容器。
Presenting: nodejs official docker image!赠送: nodejs官方docker镜像!
Not one place does it say image
.没有一个地方说image
。 You just have to know.你只需要知道。
docker-compose.yml
逐行解释docker-compose.yml
version: "3"
Uses version 3.x of the syntax.使用 3.x 版本的语法。 The syntax varies from Docker engine versions as listed at Compose and Docker compatibility matrix .语法与Compose 和 Docker 兼容性矩阵中列出的 Docker 引擎版本不同。
services:
Each container image is a service in Docker Compose terminology.每个容器镜像都是 Docker Compose 术语中的一个服务。 tests
and rethinkdb
are my names for 2 images. tests
和rethinkdb
是我的 2 张图片的名字。 You can name them as you want but we will use this name later to create a dependency between the 2 images (one needs to be online before the other).您可以根据需要命名它们,但稍后我们将使用此名称在 2 个图像之间创建依赖关系(一个需要在另一个之前在线)。
services:
tests:
...
rethinkdb:
...
tests
and rethinkdb
are the two services that we will get Docker Compose to run for us. tests
和rethinkdb
是我们将让 Docker Compose 为我们运行的两个服务。
image: "node:14"
Luckily the nodejs Docker developers included extremely nice documentation in lieu of proper standard documentation at hub.docker.com/_/node .幸运的是,nodejs Docker 开发人员在hub.docker.com/_/node上提供了非常好的文档来代替适当的标准文档。
Image variants: The defacto image
node:<version>
andnode:<version>-alpine
node:<version>-alpine
image is based on the popular Alpine Linux project, which is much smaller than most distribution base images (~5MB), and thus leads to much slimmer images in general..镜像变体:事实上的镜像node:<version>
和node:<version>-alpine
node:<version>-alpine
镜像基于流行的 Alpine Linux 项目,它比大多数发行版基础镜像 (~5MB) 小得多,因此通常会导致图像更苗条..
image: rethinkdb:2.3.5
Unfortunately, the less funded RethinkDB project, Docker Hub page不幸的是,资金较少的 RethinkDB 项目, Docker Hub 页面does not have such information.没有这样的信息。 Actually if you scroll all the way down to the bottom of the page, you will find Image Variants (sorry, I can not link to headings on hub.docker.com since they do not have an id
or name
attribute).实际上,如果您一直向下滚动到页面底部,您会发现Image Variants (抱歉,我无法链接到 hub.docker.com 上的标题,因为它们没有id
或name
属性)。 The 2 versions you will want to use is either rethinkdb:<version>
or rethinkdb:<version>-slim
.您将要使用的 2 个版本是rethinkdb:<version>
或rethinkdb:<version>-slim
。
Q: So what about the much more dominant Supported tags and respective Dockerfile links section?问:那么更占主导地位的支持标签和相应的 Dockerfile 链接部分呢?
A: They are less frequently needed and are specialized docker images.答:它们不太常用,是专门的 docker 映像。 In the RethinkDB case, it is the RethinkDB database install on Debian Buster and CentOS.在 RethinkDB 案例中,它是安装在 Debian Buster 和 CentOS 上的 RethinkDB 数据库。 There is also a link to some versions but not all.还有一些版本的链接,但不是全部。 So it's a selected list of images, you probably do not want.所以这是一个选定的图像列表,你可能不想要。 Remember you have to click on Tags or the small grey link;请记住,您必须单击标签或灰色的小链接; View Available Tags (see the image above with the red squares - sorry no anchor linking support on SO either).查看可用标签(见上图红色方块 - 抱歉,SO 上也没有锚链接支持)。
user: "node"
working_dir: /home/node/app
volumes:
- ./:/home/node/app
If you look up user
or working_dir
on docs.docker.com , you are out of luck without prior knowledge with docker
.如果您在docs.docker.com上查找user
或working_dir
,那么在没有docker
的先验知识的情况下,您将不走运。 It only states that:它只指出:
Each of these is a single value, analogous to its docker run counterpart.其中每一个都是单个值,类似于其 docker 运行对应项。 Note that mac_address is a legacy option.请注意, mac_address 是一个旧选项。
The goal here is to copy the current directory (where docker-compose.yml
is located) to the tests
service which pulls the node:14
image.这里的目标是将当前目录( docker-compose.yml
所在的位置)复制到提取node:14
图像的tests
服务。 We need to create a directory in our container where our current directory on our machine will be copied to.我们需要在我们的容器中创建一个目录,我们机器上的当前目录将被复制到该目录中。 Later on, we want to execute some commands in that directory and we do not want to run stuff as sudo
, so the natural placement is in our user home directory ( ~/
).稍后,我们想在该目录中执行一些命令,我们不想以sudo
运行东西,所以自然放置在我们的用户主目录( ~/
)中。
We need:我们需要:
nodejs
container.我们的nodejs
容器中的用户。nodejs
container.在我们的nodejs
容器的用户主目录中创建一个目录。nodejs
container. Map 我们机器上的当前目录到我们的nodejs
容器中的新目录。 After reading How to use this image that the nodejs developers wrote, we can see in the example that there is a node
user in the image.在阅读了nodejs开发者写的How to use this image之后,我们可以在示例中看到图像中有一个node
用户。 The example also shows which Docker Compose configurations we need.该示例还显示了我们需要哪些 Docker Compose 配置。
user: "node"
Presumably use the node user defined in image: "node:14"
.大概使用image: "node:14"
中定义的节点用户。
working_dir: /home/node/app
Create an app directory inside the node
user's home directory.在node
用户的主目录中创建一个app目录。
volumes:
- ./:/home/node/app
Map our current directory on our machine to the /home/node/app directory inside the tests
container (which uses image: "node:14"
). Map 我们机器上的当前目录到tests
容器内的/home/node/app目录(使用image: "node:14"
)。
container_name: nodejs
Turns out that defining container_name
is purely cosmetic.事实证明,定义container_name
纯粹是装饰性的。 It does not help you to link network or start one container before the other.它不能帮助您链接网络或先启动一个容器。 It is just a name.这只是一个名字。 While your containers are running you can use docker ps
to see them or docker ps -a
to see all containers, even shut downed ones.当您的容器正在运行时,您可以使用docker ps
查看它们或使用docker ps -a
查看所有容器,甚至是关闭的容器。 In the NAMES column the container_name
is written.在NAMES列中写入了container_name
。 If you do not define container_name
, then they will be called your directory name and the service name, post-fixed with a incremental number.如果您没有定义container_name
,那么它们将被称为您的目录名称和服务名称,后缀为增量编号。
docker ps
with container_name
while running: docker ps
运行时带有container_name
:
CONTAINER ID IMAGE PORTS NAMES
5272576f8555 node:14 nodejs
fb11d5ce049b rethinkdb:2.3.5 8080/tcp, 28015/tcp, 29015/tcp rethinkdb
docker ps
without container_name
while running: docker ps
运行时没有container_name
:
CONTAINER ID IMAGE PORTS NAMES
528e5ee37956 node:14 data_access_layer_tests_1
e80682b806fc rethinkdb:2.3.5 8080/tcp, 28015/tcp, 29015/tcp data_access_layer_rethinkdb_1
depends_on:
- rethinkdb
depends_on
is the magic one! depends_on
是神奇的! It tells Docker Compose that the rethinkdb
container must be它告诉 Docker Compose rethinkdb
容器必须是online在线的started before this container.在这个容器之前开始。
This in a rare case of the Docker Compose documentation ( depends_on
) actually being good.这在Docker Compose 文档( depends_on
)的极少数情况下实际上很好。
Unfortunately depends_on
does not guarantee that the depending image is online, as you would expect, but the documentation is also very clear in this case and offer another solution.不幸的是, depends_on
不能保证依赖图像在线,如您所料,但在这种情况下,文档也非常清楚,并提供了另一种解决方案。 In our case, it does not matter much since the rethinkdb
container starts fast enough ( npm ci
in nodejs
will take way longer than the startup time of rethinkdb
).在我们的例子中,这并不重要,因为rethinkdb
容器启动得足够快(nodejs 中的nodejs
npm ci
将花费比rethinkdb
的启动时间更长的时间)。
command: "npm ci && npm test"
Now we can run some commands inside our docker container,现在我们可以在 docker 容器中运行一些命令, as if we where running them on our machine in our project directory.就好像我们在我们的项目目录中的机器上运行它们一样。 Of course not.当然不是。 The above will only execute npm ci
.以上只会执行npm ci
。 See the SO answer Using Docker-Compose, how to execute multiple commands .请参阅使用 Docker-Compose 的 SO 答案,如何执行多个命令。 Docker uses an obscure difficult to fathom variant of shell script that can execute one, and only one command, with parameters. Docker 使用了 shell 脚本的一种晦涩难懂的变体,该脚本可以执行一个且只有一个带有参数的命令。 The documentation says it is similar to the docker CMD
key but neither sites explain why some POSIX shell commands work and others don't but we assume that it made life easier for Docker developers:) The documentation says it is similar to the docker CMD
key but neither sites explain why some POSIX shell commands work and others don't but we assume that it made life easier for Docker developers:)
The correct way to execute multiple commands, is:执行多个命令的正确方法是:
command: bash -c "npm ci && npm test"
Please see "Using Docker-Compose, how to execute multiple commands" .请参阅“使用 Docker-Compose,如何执行多个命令” 。
npm ci
installs all npm packages from package-lock.json and npm test
runs our "test"
script in package.json . npm ci
installs all npm packages from package-lock.json and npm test
runs our "test"
script in package.json .
You might think that you can write multiple commands in an YAML array but doing so will, in this case apparently, tell node to require
them as modules inside the container and you will get errors like:您可能认为您可以在 YAML 数组中编写多个命令,但在这种情况下,这样做显然会告诉节点require
它们作为容器内的模块,您将收到如下错误:
nodejs | internal/modules/cjs/loader.js:883
nodejs | throw err;
nodejs | ^
nodejs |
nodejs | Error: Cannot find module '/home/node/app/npm ci'
nodejs | at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15)
nodejs | at Function.Module._load (internal/modules/cjs/loader.js:725:27)
nodejs | at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
nodejs | at internal/main/run_main_module.js:17:47 {
nodejs | code: 'MODULE_NOT_FOUND',
nodejs | requireStack: []
nodejs | }
command:
- "npm ci"
- "npm test"
The above will not work!以上是不行的!
Instead use the usual Again, Using Docker-Compose, how to execute multiple commands is the correct documentation.同样,使用 Docker-Compose,如何执行多个命令是正确的文档。&&
to execute a series of successful commands.而是使用通常的&&
来执行一系列成功的命令。
Actually, forget the above and use entrypoint
to point to a bash file where you write all of your commands.实际上,忘记上面的内容并使用entrypoint
点指向您编写所有命令的 bash 文件。
DO NOT USE docker-compose up -d
!不要使用docker-compose up -d
!
You do not have to do anything here.您不必在这里做任何事情。 Even though every Docker Compose tutorial will tell you to run in detached mode.即使每个 Docker Compose 教程都会告诉您以分离模式运行。 You will not see stdout or stderr.您将看不到 stdout 或 stderr。 Just loose the -d
.只需松开-d
。
docker-compose up
is the way to go! docker-compose up
是要走的路!
If you really have to run in detached mode, you can see the output via docker logs [container id]
and get the container id via docker ps
or docker ps -a
. If you really have to run in detached mode, you can see the output via docker logs [container id]
and get the container id via docker ps
or docker ps -a
. The latter is used if your containers are not currently running.如果您的容器当前未运行,则使用后者。
docker-compose.yml
keys that look like they are needed看起来像是需要的docker-compose.yml
密钥Turns out that Docker comes with a lot of sane defaults which you should not mess with unless you really have to.事实证明,Docker 带有很多正常的默认值,除非你真的必须这样做,否则你不应该乱用它们。
ports:
- "28015:28015"
- "8080:8080"
You do not need to expose ports in a docker-compose.yml
unless you need to expose the ports to your local machine or outside network, even though every example out there does it.您不需要在docker-compose.yml
中公开端口,除非您需要将端口公开给本地计算机或外部网络,即使那里的每个示例都这样做。 For example, if you need access to localhost:8080
(in RethinkDB that is the dashboard), then you have to add:例如,如果您需要访问localhost:8080
(在 RethinkDB 中,即仪表板),那么您必须添加:
ports:
- "8080:8080"
Your other services/containers will have access to both port 28015
and 8080
, using the service name as hostname, without you have to specify anything in your docker-compose.yml
.您的其他服务/容器将可以访问端口28015
和8080
,使用服务名称作为主机名,而无需在docker-compose.yml
中指定任何内容。 Eg in this case rethinkdb:28015
.例如在这种情况下rethinkdb:28015
。 See below for more information.请参阅下面的详细信息。
links:
- rethinkdb
links
seems like a way to connect two distinct containers but it turns out that all containers share network, so you do not have to. links
似乎是一种连接两个不同容器的方法,但事实证明所有容器共享网络,所以你不必这样做。 The official documentation comes with a big red WARNING, which suggest that you use user-defined networks . 官方文档附带一个大大的红色WARNING,建议你使用用户自定义网络。 Which in turn says that:反过来说:
By default Compose sets up a single network for your app.默认情况下,Compose 会为您的应用程序设置一个网络。 Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by them at a hostname identical to the container name.服务的每个容器都加入默认网络,并且可以被该网络上的其他容器访问,并且可以通过与容器名称相同的主机名被它们发现。
That's a convoluted way of saying that inside your Docker Compose containers, you can connect to ports and IP address just as you would, had it been on your local machine.这是一种令人费解的说法,在 Docker Compose 容器中,您可以像在本地计算机上一样连接到端口和 IP 地址。
So if container A's image exposes Scratch that!刮那个! The exposed IP addresses are not stable.暴露的 IP 地址不稳定。 Docker will change them, for various (unknown) reasons.由于各种(未知)原因,Docker 将更改它们。172.18.0.2:28015
then you can connect from container B using that exact address.因此,如果容器 A 的图像暴露172.18.0.2:28015
,那么您可以使用该确切地址从容器 B 连接。 Ei IP: 172.18.0.2
and port: 28015
. Ei IP: 172.18.0.2
和端口: 28015
。
Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by them at a hostname identical to the container name.服务的每个容器都加入默认网络,并且可以被该网络上的其他容器访问,并且可以通过与容器名称相同的主机名被它们发现。
Means that your service name is used as hostname, in the same way you would define it in your /etc/hosts file.意味着您的服务名称用作主机名,就像您在/etc/hosts文件中定义它一样。 Or similar to how DNS links stackoverflow.com to 151.101.193.69.或类似于 DNS 如何将 stackoverflow.com 链接到 151.101.193.69。
So if container rethinkdb
exposes port 28015
then it will be accessible from the nodejs
container via rethinkdb:28015
.因此,如果容器rethinkdb
暴露了端口28015
,那么它将可以从nodejs
容器通过rethinkdb:28015
访问。
BEWARE that the documentation says:请注意文档中说:
version: "3.9"
services:
web:
build: .
ports:
- "8000:8000"
db:
image: postgres
ports:
- "8001:5432"
Each container can now look up the hostname
web
ordb
and get back the appropriate container's IP address.每个容器现在可以查找主机名web
或db
并取回相应容器的 IP 地址。 For example,web
's application code could connect to the URLpostgres://db:5432
and start using the Postgres database.例如,web
的应用程序代码可以连接到 URLpostgres://db:5432
并开始使用 Postgres 数据库。
In the example for Networking in Compose @jonrsharpe points out that the protocol ( postgres
is the image
, db
is the service name and 5432
is the port number.在Compose 中的网络示例中, postgres
是image
, db
是服务名称, 5432
是端口号。 This is does not work in my experience.根据我的经验,这是行不通的。 You need to use the service name and never the image
name.您需要使用服务名称,而不是image
名称。 So the correct way to connect from web
to db
is to use the URL db:5232
.因此,从web
连接到db
的正确方法是使用 URL db:5232
。postgres://
) just happens to match the image name. @jonrsharpe 指出协议( postgres://
)恰好与图像名称匹配。
The following is a correction of my previous understanding of the example.以下是我之前对例子理解的修正。
In the example for Networking in Compose postgres
is the protocol (similar to https
), db
is the service name and 5432
is the port number.在Compose 中的 Networking 示例中, postgres
是协议(类似于https
), db
是服务名称, 5432
是端口号。 You need to use the service name, to get the correct IP address.您需要使用服务名称来获取正确的 IP 地址。 So the correct way to connect from web
to db
is to use the URL [protocol]://db:5232
.所以从web
连接到db
的正确方法是使用 URL [protocol]://db:5232
。 Where protocol
can be http
, https
, progres
, etc.其中protocol
可以是http
、 https
、 progres
等。
Since all containers are within the same network, you do not need the ports
key unless you need to expose a service to your local machine (or outside network).由于所有容器都在同一个网络中,因此您不需要ports
密钥,除非您需要向本地计算机(或外部网络)公开服务。
volumes:
- "./data: /data"
Every RethinkDB Dockerfile
has this but this is only needed if you want to copy files from your local machine into the container.每个 RethinkDB Dockerfile
都有此功能,但仅当您要将文件从本地计算机复制到容器中时才需要此功能。 In this case we do not want to preload the database with anything, so we do not have any files to seed the database with, hence the volumes
key is not needed.在这种情况下,我们不想用任何东西预加载数据库,所以我们没有任何文件来为数据库播种,因此不需要volumes
键。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.