Docker Compose 是一个方便的工具,可以帮助您创建和运行使用多个容器的应用程序。它通过让您在一个文件中定义所需的一切,使设置和部署复杂应用程序的过程更加简单。使用 Docker Compose,您可以一起管理所有容器,并轻松控制它们的交互方式。
在这篇博客中,我们将探讨使用Docker Compose的好处,以及它如何帮助您简化基于Docker的应用程序开发和部署。我们将介绍Docker Compose的基础知识,包括如何在Compose文件中定义服务、网络和卷,以及如何使用Docker Compose启动、停止和管理应用程序堆栈。
docker-compose是什么?
Docker Compose 是一个工具,可以通过单个命令快速启动多个容器。使用 Docker Compose,我们可以创建一个 YAML 文件,定义如何创建多个服务(容器),并使用单个命令启动或关闭它们。
安装:
- 如何在Linux上安装docker-compose:
- Linux : docs.docker.com/compose/ins...
如何在Windows/Mac上安装docker-compose:
对于Windows/Mac,Docker Compose已经与Docker Desktop预装,因此您需要安装Windows/Mac的Docker Desktop才能使用Docker Compose。
- Windows : docs.docker.com/desktop/ins...
- Mac : docs.docker.com/desktop/ins...
示例的docker-compose文件:
json
version: "3.8"
services:
service-1:
build:
context: pathOfCodeFolder
dockerfile: dockerFile name
image: dockerImageName
ports:
- "hostPort:ContainerPort"
entrypoint: ["/bin/sh","entrypoint.sh"]
command: npm run dev
restart: always
container name: contianer-front-end
environment:
key: value
env_file:
- pathOfEnvFile
networks:
- networkName
volumes: # this is called as volume binding
- volumeName:ContainerPath
volumes: # this is called as host binding
- hostPath:containerPath
deploy:
resources:
limits:
cpus: '2'
memory: 512M
reservations:
cpus: '0.25'
memory: 64M
depends_on:
- serviceName
service-2:
...
networks:
network_name:
driver: <driverType>
voulmes:
volumeName
Docker Compose 文件参数概览:
- 版本:这指定了docker-compose文件的版本。
- 借助Docker Compose中的服务,我们可以创建多个服务,如前端、后端和数据库服务等。
- 使用Docker Compose中的
build
关键字,我们可以为服务创建一个镜像。context
关键字用于指定Dockerfile所在的目录。此外,使用dockerfile
关键字,我们可以指定Dockerfile使用的自定义名称。 - 如果我们想要使用来自Docker仓库(如DockerHub)的预构建镜像,我们只需要在Docker Compose文件中指定镜像名称。
当我们在Docker Compose文件中同时使用
build
和image
关键字时,镜像将使用build
关键字中指定的上下文进行构建,image
关键字将为新镜像分配一个名称。然后,这个新镜像将在docker images
命令下列出。
- 通过端口暴露的帮助,我们可以从本地主机访问运行在容器中的站点。在左边,我们定义主机端口,在右边,我们定义容器端口。例如,如果一个 Node 容器正在 4000 端口运行,并且我们想要从主机机器上的 80 端口访问它,我们可以在 compose 文件中像这样定义端口:(hostPort:containerPort)。
- 入口点和命令:
在Docker Compose中,
entrypoint
和command
之间存在差异。当为一个服务定义了entrypoint
时,它是无法被覆盖的,我们可以使用entrypoint
来运行可执行命令,比如迁移和填充。另一方面,command
是可以被覆盖的,我们可以在单个服务中指定多个要运行的命令。以下是一个示例代码片段。
cmd
command: >
bash -c "python manage.py migrate
&& python manage.py runserver 0.0.0.0:8000"
- 在Docker Compose中,我们可以使用
environment
关键字在运行时指定环境变量,就像上面的示例代码片段中所示。 - environment_file: 在需要指定包含环境变量的整个文件的情况下,我们可以使用上面示例代码片段中所示的
environment_file
关键字。 - container_name: Docker Compose 中的
container_name
关键字允许我们为容器指定自定义名称。 - 重新启动策略:在Docker Compose中,对于每个服务都要指定一个重新启动策略是很重要的。例如,如果重新启动策略设置为"always",那么当主机机器重新启动时,Docker容器将会自动重新启动。如果没有指定,则容器在主机重新启动或重启时不会重新启动。关于重新启动策略的更多信息可以在Docker文档中找到。
- depends_on: 当一个服务依赖于另一个服务时,可以使用
depends_on
关键字。例如,假设后端容器需要数据库容器在其启动之前运行。在这种情况下,我们可以在后端服务中使用depends_on
,以便在后端容器启动之前创建数据库容器。 - 网络:如上面的代码片段所示,我们可以在Docker Compose中创建网络并将其附加到特定的服务。可用的网络类型包括桥接、主机和覆盖网络。有关Docker网络的更多信息,请访问官方Docker文档。
- 我们需要在需要数据持久存储时使用卷挂载,这样数据就可以在容器停止或移除后重新启动。
- 在Docker Compose中,我们可以使用基于主机的挂载来实现在不重新启动容器的情况下立即对网站进行代码更改。例如,如果我们正在运行前端和后端服务,我们可以在主机机器上进行代码更改,借助基于主机的挂载,这些更改将立即反映在容器中。
cmd
volumes:
- 'hostPathOfCode:containerDirName'
卷挂载:为了持久化数据库容器的卷,我们可以创建一个卷并将其附加到数据库服务,如下面的代码片段所示。
cmd
volumes:
- 'backend-db:containerDirName'
volumes:
backend-db:
Docker 的资源限制:
如果在Docker Compose中没有为容器设置资源限制,它将使用主机PC的全部RAM和CPU限制。要检查容器的RAM和CPU使用情况,可以使用docker stats命令。
下面的图片显示了一个没有设置RAM和CPU限制的容器:
没有设置内存和CPU限制的容器:
设置了RAM和CPU限制之后的容器:
cmd
deploy:
resources:
limits:
cpus: '2'
memory: 512M
reservations:
cpus: '0.25'
memory: 64M
如上所示,我已经在每个容器上设置了512MB的限制。这意味着每个容器的最大内存使用量为512MB。
通过限制每个容器的RAM和CPU,我们可以保护主机的RAM和CPU免受过度利用。
让我们使用docker-compose来设置本地开发环境
如何创建一个Docker Compose文件来运行前端、后端和数据库容器。
cmd
version: '3.8'
services:
postgres:
image: postgres:latest
container_name: postgres
ports:
- '5432:5432'
environment:
POSTGRES_PASSWORD: helloworld
POSTGRES_USER: test
POSTGRES_DB: testdb
volumes:
- 'backend-db:/var/lib/postgresql/data'
deploy:
resources:
limits:
cpus: '2'
memory: 512M
reservations:
cpus: '0.25'
memory: 64M
networks:
- application
frontend:
depends_on:
- backend
build:
context: ./boilerplate_next/
dockerfile: Dockerfile
image: compose_next
container_name: frontend
ports:
- '3000:3000'
volumes:
- './boilerplate_next:/app'
- '/app/node_modules'
- '/app/.next'
deploy:
resources:
limits:
cpus: '2'
memory: 512M
reservations:
cpus: '0.25'
memory: 64M
networks:
- application
backend:
depends_on:
- postgres
build:
context: ./server-js/
dockerfile: Dockerfile
image: compose_node
container_name: backend
volumes:
- './server-js/:/app'
ports:
- '8000:8000'
deploy:
resources:
limits:
cpus: '2'
memory: 512M
reservations:
cpus: '0.25'
memory: 64M
networks:
- application
networks:
application:
driver: bridge
volumes:
backend-db:
我们需要挂载前端的 node_modules 和 .next 文件夹,因为在构建过程中卷没有被挂载。
Dockerfiles:
前端Dockerfile:
dockerfile
FROM node:18.16.0-slim
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
EXPOSE 3000
RUN npm run build
CMD ["npm","run","start"]
后端Dockerfile:
dockerfile
FROM node:16.15.0-alpine
WORKDIR /app
COPY package.json ./
RUN npm i
COPY . ./
EXPOSE 8000
RUN ["chmod","+x","entrypoint.sh"]
Entrypoint.sh
npm install
npx sequelize db:migrate --config config/config.js
npx sequelize db:seed --seed ./backend/seeders/20220630094531-create-user.js
npm run dev
对于Postgres数据库,我们使用了预先构建的Docker镜像作为postgres:latest
- 在这里,我们需要运行三个服务:前端、后端和Postgres,因此按照上面所示创建三个服务。
- 最佳实践:在每个服务中,我们应该定义访问该服务的端口,指定是构建还是使用现有的镜像,挂载卷以持久化数据并将代码从主机挂载到容器中,设置资源限制,应用网络,并分配容器名称。
- 查看容器日志:docker logs
- 运行compose文件:docker-compose up -d(-d用于在后台运行容器)
- 关闭并删除文件:docker-compose down(停止和删除容器、网络,不会删除卷;要删除卷,请使用 -v)。
这是上述代码的输出。
运行:docker-compose up
在docker-compose中定义的3000端口访问前端
请注意:要将后端容器与数据库容器连接起来,需要在后端容器的环境变量中使用服务名称作为主机名。
热重载:
重新加载应用程序而不重新启动容器
前端:在不重启容器的情况下,我们需要遵循两个步骤来在主机代码中更改代码并重新加载应用程序。
- 我们需要在compose文件中挂载一个卷。
- 使用开发命令如
npm run dev
。
后端:在主机代码发生更改时更新容器化应用程序而无需重新构建它,我们需要像上面的代码中所示那样挂载一个卷,并将 nodemon 功能添加到 package.json 文件中。
例如,如果您对前端代码进行更改,Docker Compose可以自动在运行的应用程序中反映这些更改,而无需您重新创建容器。
这里,我修改了一行代码。
在不重新启动容器的情况下,更改会反映在运行中的应用程序中
故障排除命令:
- 要删除所有未附加到任何容器的Docker镜像: `` docker rmi -f
docker imaages -aq
- 要删除所有已停止的Docker容器: ``docker rm `docker ps -aq```
- 进入容器内:
docker exec -it <contianerid or name>
- 显示Docker的RAM和CPU使用情况:
docker stats
- 检查容器的日志:
docker logs <container_id_or_name>
- 如果图像不存在,则构建图像并启动容器:
docker-compose up
- 在启动容器之前构建镜像:
docker-compose up --build
- 停止容器并删除由up创建的容器、网络、卷和镜像:
docker-compose down
默认情况下
docker-compose down
不会删除,要删除请使用docker-compose down -v
收尾工作👋
通过在本地环境中设置应用程序,我们可以调试与前端、后端和数据库相关的问题。这使我们能够快速识别和解决开发过程中可能出现的任何问题。
使用Docker Compose 的另一个优势是它能够在本地复制生产环境。通过在Docker Compose 配置文件中定义生产环境,开发人员可以轻松地在本地机器上重新创建它,消除了对昂贵硬件和基础设施的需求。这导致更快的开发周期和更准确的测试,从而产生更稳定可靠的应用程序。