这里重点介绍Docker Compose的语法,如何写一个正确的docker-compose.yml文件。
我们知道docker-compose
指令(比如docker-compose up
等)是运行它的脚本文件docker-compose.yml,那么docker-compose.yml内部到底有哪些语法要求呢!
docker-compose.yml文件是Docker Compose的核心,用于定义服务、网络和数据卷。
该文件使用YAML格式编写,其默认路径为./docker-compose.yml
,可以使用.yml
或.yaml
作为文件扩展名。在docker-compose.yml文件中,可以定义多个服务,每个服务可以包含一系列配置选项,例如镜像名称、容器端口、环境变量等。
此外,docker-compose.yml文件还可以定义网络和数据卷,以便在多个容器之间共享网络和数据。网络可以设置为公共或私有,数据卷可以设置为持久化或非持久化。
使用Docker Compose的基本步骤如下:
Compose 文件是一个 YAML 文件,用于定义 Docker 应用程序的多个容器化服务。这个文件通常用于定义服务、网络、卷、配置和秘密等。
docker-compose.yml文件有以下6个顶级元素:
这些配置项使 Docker Compose 成为一个功能强大的工具,用于定义、部署和管理复杂的 Docker 应用程序。
version 指定Docker Compose文件的版本。目前最新的版本是3。
参考 https://docs.docker.com/compose/compose-file/04-version-and-name/
参考 https://docs.docker.com/compose/compose-file/05-services/
services 定义各个服务。每个服务都有一个唯一的名称,并且需要指定使用的镜像、端口映射、环境变量等信息。并包含以下属性
指定服务所使用的Docker镜像。
image: redis
image: redis:5
image: redis@sha256:0ed5d5928d4737458944eb604cc8509e245c3e19d02ad83935398bc4b991aac7
image: library/redis
image: docker.io/library/redis
image: my_private.registry:5000/redis
参考 https://docs.docker.com/compose/compose-file/build/
指定Dockerfile的路径,用于构建自定义镜像。
build
选项可以接受不同的参数,其中最常用的是 context
和 dockerfile
。
以下是一些常见的使用方法:
1)指定构建上下文(context):
构建上下文是一个目录,包含了用于构建镜像的所有文件。当使用 build
选项时,Docker 会将该目录下的所有文件和子目录复制到 Docker 守护进程中,并在那里构建镜像。
services:
myservice:
build:
context: .
在上面的例子中,当前目录(.
)被用作构建上下文。这意味着 Docker 将复制当前目录下的所有文件和子目录到 Docker 守护进程中,并在那里构建镜像。
2)指定 Dockerfile:
dockerfile
参数用于指定 Dockerfile 的位置。Dockerfile 是一个文本文件,其中包含了一系列指令,用于定义如何构建 Docker 镜像。
services:
myservice:
build:
dockerfile: ./path/to/Dockerfile
在上面的例子中,Docker 将使用位于 ./path/to/Dockerfile
的 Dockerfile 来构建镜像。
3)使用 Dockerfile 和构建上下文:
当你同时指定 context
和 dockerfile
时,context
是 Dockerfile 的位置的相对路径。这允许你在同一个构建上下文中使用多个 Dockerfile。
services:
myservice:
build:
context: ./path/to/context
dockerfile: ./path/to/Dockerfile
在上面的例子中,Docker 将使用位于 ./path/to/context/path/to/Dockerfile
的 Dockerfile 来构建镜像。
4)使用其他构建选项:
除了 context
和 dockerfile
外,build
选项还可以接受其他一些参数,例如 args
(用于传递构建参数),target
(用于指定 Dockerfile 中的目标),等等。这些参数可以根据需要进行配置。
args 定义构建参数,即 Dockerfile 中的 ARG 值。
以以下 Dockerfile 为例:
ARG GIT_COMMIT
RUN echo "Based on commit: $GIT_COMMIT"
在 Compose 文件的 build 键下,可以使用 args 来定义 GIT_COMMIT。args 可以作为 map 或 list 进行设置:
build:
context: .
args:
GIT_COMMIT: cdc3b19
build:
context: .
args:
- GIT_COMMIT=cdc3b19
在指定构建参数时,可以省略值。在这种情况下,构建时间必须通过用户交互获取其值,否则在构建 Docker 镜像时将无法设置构建参数。
args:
- GIT_COMMIT
5)使用自定义名称:
你可以为构建的镜像指定一个自定义名称。默认情况下,镜像名称与服务的名称相同,但你可以通过设置 image
参数来覆盖默认名称。
services:
frontend:
image: example/webapp
build: ./webapp
backend:
image: example/database
build:
context: backend
dockerfile: ../backend.Dockerfile
custom:
build: ~/custom
指定服务的端口映射。
ports 不能与
network_mode: host
一起使用,否则会出现运行时错误。
Docker Compose中的ports配置项有两种语法格式:短语法和长语法。
1)短语法,比较常见
[HOST:]CONTAINER[/PROTOCOL]
- HOST :
[IP:](port | range)
- CONTAINER:
port | range
- PROTOCOL:将端口限制为指定协议。如 TCP和UDP
如果主机IP未设置,它将绑定到所有网络接口。端口可以是单个值或范围。主机和容器必须使用等效的范围。
要么指定两个端口(HOST:CONTAINER),要么只指定容器端口。在后一种情况下,Compose会自动分配主机上的任何未分配端口。
HOST:CONTAINER应始终作为(带引号的)字符串指定,以避免与yaml基数-60浮点数发生冲突。
"3000"
:主机自动分配未被占用的端口。"3000-3005"
:端口范围规则和上边相同。"8000:8000"
:容器端口8000对应主机端口8000。"9090-9091:8080-8081"
:端口范围规则和上边一天相同。"6060:6060/udp"
:限制为指定的协议udp。ports:
- "9200:9200"
- "9300:9300"
2)长语法格式
长语法可以实现短语法无法实现的功能,配置项如下:
target:80
:容器端口。published:8080
:物理主机的端口。protocol: tcp
:端口协议(tcp或udp)。mode: host
:host和ingress两种模式,host用于在每个节点上发布主机端口,ingress用于被负载平衡的swarm模式端口。ports:
- target: 9200
published: 9200
protocol: tcp
mode: host
- target: 9300
published: 9300
protocol: tcp
mode: host
定义服务容器所连接的网络,关联到顶级networks的配置。
services:
some-service:
networks:
- some-network
- other-network
指定服务的卷挂载。
卷(volumes)定义了可由服务容器访问的主机路径或命名卷。您可以使用volumes来定义多种类型的挂载,如 volume, bind, tmpfs, 或 npipe。
如果挂载是主机路径并且仅由单个服务使用,则可以在服务定义中声明它。要在多个服务之间重复使用卷,必须在顶级 volumes 键中声明一个命名卷。
以下示例显示了由后端服务使用的命名卷(db-data),以及为单个服务定义的绑定挂载。
services:
backend:
image: example/backend
volumes:
- type: volume
source: db-data
target: /data
volume:
nocopy: true
- type: bind
source: /var/run/postgres/postgres.sock
target: /var/run/postgres/postgres.sock
volumes:
db-data:
1)短语法
使用单个字符串与冒号分隔的值来指定卷挂载(VOLUME:CONTAINER_PATH
)或访问模式(VOLUME:CONTAINER_PATH:ACCESS_MODE
)。
VOLUME
:可以是容器所在平台上的主机路径(绑定挂载)或卷名称。CONTAINER_PATH
:卷在容器中挂载的路径。ACCESS_MODE
:逗号分隔的选项列表:
rw
:读写访问。如果没有指定,则为默认值。ro
:只读访问。z
:SELinux选项,指示绑定挂载主机内容在多个容器之间共享。Z
:SELinux选项,指示绑定挂载主机内容对其他容器来说是私有的并且不共享。2)长语法
长语法形式允许配置无法在短语法中表示的其他字段。
type
:挂载类型。可以是 volume、bind、tmpfs、npipe 或 cluster。source
:挂载的源,对于绑定挂载是主机上的路径,或是在顶级 volumes 键中定义的卷的名称。不适用于 tmpfs 挂载。target
:容器中卷挂载的路径。read_only
:将卷设置为只读。bind
:用于配置其他绑定选项:
propagation
:用于绑定的传播模式。create_host_path
:在主机上源路径处创建目录,如果路径不存在。如果路径存在,Compose 不会做任何操作。这是为了向后兼容 docker-compose 遗留版本而自动隐含的短语法。selinux
:SELinux 重新标记选项 z(共享)或 Z(私有)。volume
:配置其他卷选项:
nocopy
:禁用在创建卷时从容器复制数据的标志。tmpfs
:配置其他 tmpfs 选项:
size
:tmpfs 挂载的大小,以字节为单位(可以是数字或字节单位)。mode
:tmpfs 挂载的文件模式,作为 Unix 权限位作为八进制数。consistency
:挂载的一致性要求。可用的值是平台特定的。指定服务的环境变量。
1)Array 语法:
environment:
- VAR1=value1
- VAR2=value2
2)Map 语法
environment:
RACK_ENV: development
SHOW: "true"
USER_INPUT:
定义服务的部署配置。可以设置副本数、更新策略等
参考 https://docs.docker.com/compose/compose-file/deploy/
replicas:设置副本数量,默认是1
services:
frontend:
image: example/webapp
ports:
- "8080:80"
deploy:
mode: replicated
replicas: 2
endpoint_mode: vip
指定服务依赖的其他服务。
services:
web:
build: .
ports:
- "80:80"
environment:
- ENV=production
command 会覆盖容器镜像声明的默认 command ,例如通过Dockerfile的CMD。
command: bundle exec thin -p 3000
该值也可以是列表,与Dockerfile类似:
command: [ "bundle", "exec", "thin", "-p", "3000" ]
如果值为null
,则使用镜像中的默认命令。
如果值为[]
(空列表)或''
(空字符串),则忽略镜像声明的默认命令,即将其覆盖为空。
定义服务的重启策略。可以设置为"no"、“always”、“on-failure"或"unless-stopped”。
restart: "no"
restart: always
restart: on-failure
restart: unless-stopped
networks 定义应用程序使用的网络。可以通过网络名称将不同的服务连接在一起。
Docker Compose Networks 可以不预先创建好网络,默认情况下,这个网络的名字是 “
详细参考 https://docs.docker.com/compose/compose-file/06-networks/
以下是使用 Docker Compose Networks 的基本步骤:
1、使用已有网络
1)创建网络:在开始使用现有网络之前,需要先创建一个 Docker 网络。可以使用以下命令来创建一个网络:
docker network create
2)编写 Docker Compose 文件:接下来,需要编写一个 Docker Compose 文件,来定义服务和网络。在 Compose 文件中,可以定义一个或多个服务,每个服务都可以连接到同一个网络。以下是一个示例的 docker-compose.yml 文件:
version: '3'
services:
web:
image: nginx
networks:
- my-network
db:
image: mysql
networks:
- my-network
networks:
my-network:
external: true
在上述示例中,定义了两个服务,web 和 db。这两个服务都使用同一个网络 my-network。请注意,在 networks 部分,使用了
external: true
,表示使用的是现有的外部网络。
2、自动创建网络
不指定external: true
,在容器启动的时候,会自动创建网络 “
version: '3'
services:
web:
image: nginx:latest
ports:
- "80:80"
networks:
- my-network
db:
image: mysql:latest
ports:
- "3306:3306"
networks:
- my-network
networks:
my-network:
driver: bridge
在上面的示例中,创建了一个名为
my-network
的自定义网络,并将web和db服务连接到该网络。使用driver参数可以指定网络的驱动程序,默认为bridge。
Docker的网络驱动程序有bridge、host、overlay、macvlan等几种,每种驱动程序都有其特点和应用场景。
bridge
(桥接模式):这是Docker默认的网络模式,它将每个容器都连接到本机主机的独立网络中,实现容器之间的网络隔离。Docker创建了一个桥接网络,并为每个容器分配了一个IP地址。容器可以互相通信,也可以与宿主机通信,但不能直接访问其他网络。host
(主机模式):在这种模式下,容器将直接使用宿主机的网络命名空间,因此容器将拥有与宿主机相同的IP地址和端口号。容器可以与宿主机和其他容器通信,但它们之间的通信不受Docker的网络隔离和安全限制。overlay
(覆盖网络模式):这是一种跨主机网络的网络驱动程序,它将多个主机上的容器连接到同一个网络中,并提供了跨主机的容器间通信的能力。它使用VxLAN技术来实现跨主机网络的通信。macvlan
(MACvlan网络模式):这是一种基于MAC地址的容器网络模式,它将每个容器的网络接口绑定到一个MAC地址上,实现了容器之间的网络隔离。这种模式下,每个容器都会分配一个独立的IP地址,并且可以与其他容器进行通信。每种网络驱动程序都有其适用的场景,可以根据具体的需求选择不同的网络驱动程序来实现不同的容器网络配置。
volumes 定义应用程序的卷。可以创建卷,并将其挂载到服务中。
Docker Compose 中使用数据卷(Volumes)可以通过 volumes
关键字在服务定义中进行指定。
详细参考:https://docs.docker.com/compose/compose-file/07-volumes/
具体用法如下:
在 docker-compose.yml
文件中,针对要使用数据卷的服务,添加 volumes
配置块。例如:
version: "3"
services:
web:
image: nginx
volumes:
- ./app:/app
在上面的示例中,我们定义了一个名为 web
的服务,使用 image
关键字指定了镜像 nginx
。然后通过 volumes
关键字将宿主机目录 ./app
挂载到容器内的 /app
目录上。
另外,如果需要在服务中定义卷标(Volume),可以使用 -
开头的短横线表示卷标名称。例如:
version: "3"
services:
web:
image: nginx
volumes:
- data:/var/lib/nginx
volumes:
data:
在上面的示例中,我们定义了一个名为 data
的卷标,并将其挂载到容器内的 /var/lib/nginx
目录上。这样,容器内的 /var/lib/nginx
目录将持久化存储在名为 data
的卷标中。
.env
结合使用环境变量文件 .env
用于定义应用程序所需的环境变量。这些环境变量可以在 docker-compose.yml
文件中引用,以配置容器的行为。
默认情况下,.env
文件必须和 docker-compose.yml
文件在同一个目录中。也可以使用env_file
指定文件。
env_file: .env
将 Docker Compose 和环境变量文件 .env
结合使用,可以让你更方便地管理和配置应用程序的环境。以下是一些步骤,说明如何结合使用这两个工具:
.env
文件,在其中定义你的环境变量。每个变量一行一个,例如:DATABASE_URL=postgres://user:password@localhost/dbname
SECRET_KEY=my-secret-key
docker-compose.yml
文件,并在其中引用 .env
文件中的环境变量。例如:version: '3'
services:
web:
image: my-web-app
ports:
- "8080:80"
environment:
- DATABASE_URL=${DATABASE_URL}
- SECRET_KEY=${SECRET_KEY}
在上面的示例中,${DATABASE_URL}
和 ${SECRET_KEY}
是对 .env
文件中定义的变量的引用。Docker Compose 会自动将这些值填充到容器的环境变量中。
docker-compose up
命令来启动应用程序。Docker Compose 将读取 docker-compose.yml
文件,并使用 .env
文件中定义的环境变量来配置各个容器。.env
文件,然后重新运行 docker-compose up
命令即可。Docker Compose 将自动加载最新的环境变量值。通过这种方式,你可以轻松地管理和配置应用程序的环境,同时利用 Docker Compose 的功能来简化容器化应用程序的部署和管理。
以下是一个简单的 docker-compose.yml
示例,它创建并运行一个基于 Nginx 的 web 服务器和一个基于 Redis 的数据库:
version: '3'
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
networks:
- app-net
command: ["nginx", "-g", "daemon off;"]
redis:
image: redis:latest
networks:
- app-net
command: ["redis-server"]
networks:
app-net:
这个 docker-compose.yml
文件定义了两个服务:web 和 redis。每个服务都有自己的配置。
web
服务使用 nginx:alpine
镜像,这是 Nginx 的一个轻量级版本。它将容器的 80 端口映射到主机的 8080 端口。它还将当前目录下的 nginx.conf
文件挂载到容器的 /etc/nginx/nginx.conf
,这样我们就可以在主机上修改 Nginx 的配置。最后,它使用 daemon off;
命令来确保 Nginx 在容器退出时停止运行。redis
服务使用最新的 Redis 镜像。它连接到名为 app-net
的网络,并使用 redis-server
命令启动 Redis。app-net
网络允许这两个服务相互通信。你可以根据需要修改这个文件来适应你的应用和环境。例如,你可能需要安装不同的软件或挂载不同的卷,这都可以在服务配置中完成。
以下是一个基于 Spring Boot 项目的 docker-compose.yml
示例:
version: '3'
services:
backend:
build:
context: ./backend/
dockerfile: Dockerfile
image: backend:latest
ports:
- "8080:8080"
networks:
- app-net
environment:
- spring-boot-app-port=8080
frontend:
image: frontend:latest
networks:
- app-net
ports:
- "80:80"
environment:
- FRONTEND_URL=http://backend:8080/
networks:
app-net:
这个 docker-compose.yml
文件定义了两个服务:后端(backend)和前端(frontend)。
backend
服务使用 Dockerfile
在 ./backend/
目录下构建镜像。这个 Dockerfile
应该包含构建 Spring Boot 应用程序所需的步骤。构建完成后,该服务将容器的 8080 端口映射到主机的 8080 端口,并连接到名为 app-net
的网络。它还设置了 spring-boot-app-port
环境变量,用于指定 Spring Boot 应用程序监听的端口。frontend
服务直接使用名为 frontend:latest
的镜像。该服务连接到 app-net
网络,并将容器的 80 端口映射到主机的 80 端口。它还设置了 FRONTEND_URL
环境变量,用于指定前端应用程序与后端服务的通信地址。app-net
网络允许这两个服务相互通信。请注意,你需要根据你的项目结构和需求进行相应的修改。例如,你可能需要修改端口映射、环境变量或网络配置。此外,你还需要确保你的 Spring Boot 项目已经正确配置并能够运行。