一、docker-compose两个重要的概念
- 项目(project):有多个服务共同组成的完整的业务单元,定义在docker-compose.yml文件中
- 服务(service):一个服务对应一个应用容器,在一个项目中可以存在多个服务
书写格式:注意yml文件的书写格式,用空格缩进,用-符号写多个值
version: "3.0" # 表示使用的版本,必写
services: # 注意这里写的是复数,表示多个的意思
tomcat: # 这个就是服务了,这里写的是付的名称,就是这个服务叫啥,注意这个开头用了2个空格缩进
image: tomcat:8.0-jre8 # 表示创建当前这个服务所使用的镜像是那个, 注意这个开头也用了2个空格缩进
ports: # 注意这个是端口,是复数
- "8000:8000"
- "4456:4456"
二、docker-compose 模板指令
1.引入两个概念
服务 service: 表示一个服务一个应用的容器,说白了,就是你想要跑的docker。它是写在servcies: 里面的。每一个都需要一个独一无二的名字。
项目 project: 表示,由多个服务构成,具有相同业务逻辑的单元。其实就是整个docker-compose.yml文件
2. 部分指令如下
image
指定 镜像名称或者镜像的ID。 如果镜像在本地不存在,comopse 将会尝试拉取这个镜像
image: ubuntu
image: orchardup/postgresql
image: abcdfe:v3
posts
暴露端口信息
使用 宿主机端口:容器端口(HOST:CONTAINER)格式,或者仅仅指定容器的端口(宿主机将会随机选择端口)都可以
ports:
- "3000"
- "8000:8000"
- "49200:22"
- "127.0.0.1:8001:8001"
*注意: 当使用 "HOST:CONTAINER"格式来映射端口的时候,如果你的端口号小于60,并且没有放到引号里面,可能会得到错误,因为yaml文件会自动解析’ “XX:YY” 这种格式为60进制。为了避免出现这种问题,建议将一组值,翻到引号中 *
volumes
数据卷所挂载的路径设置。可以设置成 宿主机路径(HOST:CONTAINER)或者数据卷名称(VOLUME:CONTAINER)两种格式。。。同时如果需要设置访问权限,就是说只能宿主机修改文件,并且影响到容器里面。不能进容器里面修改数据,从而影响到宿主机。意思就是说:本来双向的数据,现在改成了单向通道,只能宿主机影响容器。格式是:HOST:CONTANIER:ro -->注意这里的:ro 表示read only的意思。
注意: 这个指令支持相对路径
volumes: # 注意这里的复数
- /var/lib/mysql
- cache:/tmp/cache
- ~/configs:/etc/configs/:ro
细节1---------> 如果路径是数据卷的名称,必须要文件中单独配置数据卷。这里就跟network 是一样的。
version: "3.0"
services:
some-one-service:
networks:
- some-network
- other-network
volumes:
- mysql-data:/var/lib/mysql
- data-volume:/srv/data
- /home/hello/:/apps/
volumes: # 注意这里的volumes 是复数,和services 平齐,需要把上面用数据卷的名字都写下来。
mysql-data:
data-volume:
进阶的细节2---------> 上面用数据卷中有一个系统指定的问题。就是我们虽然在底部单独定义了volumes: 然后也把上面用到的数据卷,在里面声明了。但是在系统中单独查找这个数据卷,却找不到。因为docker 给我们添加了一个前缀。。。这个前缀就是docker-compose.yml 所在文件的上层文件夹的名字。
比如我们的docker-compose.yml在hello 这个文件夹中。则我们的数据卷在系统中的完整的名字就是:hello_mysql-data 和hello_data-volume
修改 进阶的细节2---------> 上面我们已经在volumes中单独定义了数据卷,但是 系统给它加上了前缀和下划线。
有没有办法不要这个前缀和下划线哪? 办法是有的。如下:
不过你要确定如果系统里面的数据卷和network 不存在重名的问题,要不然出错。就是名字不要重复。
volumes:
mysql-data:
external: # 注意这里的external: 和true
true
data-volume:
external: # 表示 使用我们自定义的卷名
true # 用true 表示 确认使用指定的卷名
但是这样,直接运行会出错。因为你用我们自定义的数据卷。但是系统在运行的时候,并不知道有这个数据卷,需要提前创建这个数据卷才可以,否则不能用,怎么创建?
在命令行手动创建
docker volume create mysql-data # 执行这个之后,系统里面才有我们上面定义的数据卷,才可用docker-compose.yml 否则会出错。
networks
配置容器连接的网络。
一个容器可以有多个网桥,就是一个容器可以同时用几个网桥,系统会给这个容器分配多个网卡,每一个网卡都绑定不同的IP
可以把一组服务(或者一个工程里面的所有服务,都用一个网桥。这样省的和其他的工程里面的服务产生关系,就是各自在一个独立的局域网里面运行。省的有干扰)
进阶的点------和volumes 一样,都需要单独拿出来定义,然后声明所用到的网桥名字。同时注意 是我们自定义用网桥名字,还是用 系统加前缀的网桥名字。
同时:如果我们用自己的网桥名字的话,需要提前创建,否则运行不通过
docker network create some-network
version: "3.0"
services:
some-one-service:
networks:
- some-network
- other-network
# 注意这里的networks 需要单独定义的,是和上面services 大类是齐平的,也就是说一个docker-compose.yml文件有一组service,那么就有为这一组service服务的网卡,还有为这一组service服务的挂载点volume
networks:
some-network:
external: # 注意这里使用我们自定义的网桥名字,就是some-network这个名字
true
other-network: # 注意这里我们使用系统给我们加前缀的网桥名字
综合volumes 和networks 举例:
version: "3.2"
services:
tomcat-1:
image: tomcat:8.0-jre8
ports:
- "8080:8080"
volumes:
- appdata1:/usr/local/tomcat/webapps
- /home:/home
networks:
- tomcat-net # 这里一样的网络
tomcat-2:
image: tomcat:8.0-jre8
ports:
- 8081:8080
volumes:
- appdate2:/usr/local/tomcat/webapps
networks:
- tomcat-net # 这里一样的网络
-
volumes:
appdata1: # tomcat-1 里面的数据卷
appdate2: # tomcat-2 里面的数据卷
networks:
tomcat-net: # 两者公用一个网桥,也就是说tomcate1服务和tomcate2服务,在一个网段里面,可以互通。
external:
true
container_name
这个是容器的名字,先当于 命令行下面的 --name
重点: 如果设置了container_name那么如果几个容器在一个网桥内,可以使用http://container_name:port来代替http://127.0.0.1:port 来访问同局域网内的其它容器。
version: "3.2"
services:
tomcat-1:
container_name: tomcat-1_name # 这就是给运行的容器一个名字,先当于--name
image: tomcat:8.0-jre8
ports:
- "8080:8080"
volumes:
- appdata1:/usr/local/tomcat/webapps
- /home:/home
networks:
- tomcat-net # 这里一样的网络
volumes:
appdata1:
networks:
tomcat-net:
environment
设置环境变量。你可以使用数组或者字典两种格式。两种格式:字典用:不用-。。。。数组用-用=号
当只给定名称的变量,不赋值。则这个变量会自动获取运行compose主机上对应变量的值,可以用来防止泄露不必要的数据。
environment: # 字典格式,注意第二个变量没有赋值。 也不用 用 - 来开头
RACK_ENV: development
SESSION_SECRET:
environment: # 数组格式,注意第二个变量没有赋值。 需要用 - 来开头
- RACK_NEN=development
- SESSION_SECRET
如果变量名称或者值中用到 true|alse, yes|no
等表达 ··布尔··含义的词汇,最好放到引号里面,避免yaml自动解析某些内容为对应的布尔语义。
这些词汇包括:
y Y yes Yes YES n N no No true True TRUE false False FALSE on On OFF off Off
实例:
mysql:
image: mysql:5.7.23
ports:
- "3307:3306"
volumes:
- mysql_data:/var/lib/mysql
- mysql_config:/etc/mysql
environment:
MYSQL_ROOT_PASSWORD: "123456" # 注意这里 用的是字典格式, :号 后面有空格,需要符合yaml格式
env_file
从文件中获取环境变量,可以是单独的文件路径或者列表。
如果通过docker-compose -f File
的方式来指定compose模板文件, 则env_file中变量的路径会基于模板文件的路径。
如果有变量名称和environment 中的指令冲突,按照惯例,以后者为准
env_file: .env # 指定一个env_file ,文件名字是 .env
或者:
env_file: # 指定多个env_file, 文件名字各式各样,而且还有相对路径和绝对路径
- ./common.env # 相对路径下的
- ./apps/web.env # 相对路径下的
- /app/secrets.env # 绝对路径下的
书写env_file 文件的格式:
环境变量文件中每一行必须符合格式,支持#
开头的注释行。
# common.env Set development environment # 这个是注释行
PROG_ENV=development # 这个是内容
command
覆盖容器启动后默认执行的命令。
相当于在命令行 run 镜像之后用来覆盖容器内部 默认的命令。
command: echo "hello world"
实例:
redis:
image: redis:5.0.10
container_name: redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: "redis-server --appendonly yes" # 原本的redis启动的命令是 redis-server 但是我们要这个是容器,而且要做数据持久化,所以需要覆盖原本的命令,新命令是 command: "redis-server --appendonly yes"
depends_on 比较重要的命令
解决容器的依赖,启动先后的问题。下面的例子会先启动redis db 然后在启动web
注意depends_on 里面写的是服务的名字,不是容器的名字
version: "3"
services:
web:
build: .
depends_on:
- db_container # 注意这里写的是服务名字
- redis_container # 注意这里写的是服务名字,不是服务container_name里面的名字
redis_container:
image: redis
db_container:
image: mysql
注意:web 服务不会等待redis db 完全启动之后才启动,但是确实是晚启动
healthcheck
通过命令检查容器是否健康运行
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 1m30s
timeout: 10s
retries: 3
sysctls
配置容器内核参数。
有两种方式,一种数组,一种字典
用来修改容器内 系统内部参数, 但是这个命令并不是必须的。。有些服务启动的时候,会受到容器内参数的限制导致可能无法成功启动,则必须修改容器内参数才能让这个服务启动成功
sysctls:
net.core.somaxconn: 1024
net.ipv4.tcp_syncookies: 0
sysctls:
- net.core.somaxconn=1024
- net.ipv4.tcp_syncooies=0
ulimits
用来指定容器中最大进程数。
指定容器的ulimits限制值。
例如,指定最大进程数为65535, 指定文件句柄数为20000(软限制,应用可以随时修改,不能超过硬限制)和40000(系统硬限制,只能root用户提高)。
ulimits:
nproc: 65535
nofile:
sofe: 20000
hard: 40000
至此,给一个例子,如下:
version: "3.2"
services:
tomcat-1:
container_name: tomcat-1_name
image: tomcat:8.0-jre8
ports:
- "8080:8080"
volumes:
- appdata1:/usr/local/tomcat/webapps
- /home:/home
networks:
- tomcat-net # 这里一样的网络
depends_on:
- tomcat-2
- mysql
- redis
healthcheck: # 这个是官方给的案例,让系统每1分30秒向本机的docker 引擎发送curl -f http://localhost, 如果能反回,就说明这个tomcat-1的服务是在正常运行中。
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 1m30s
timeout: 10s
retries: 3
sysctls: # 用来修改系统内部参数
- net.core.somaxconn=1024 # 这里是例子
- net.ipv4.tcp_syncookies=0
ulimits: #用来修改系统内进程数限制,日后使用,可以根据当前容器运行服务要求,进行修改,用以满足容器的成功启动
nproc: 65535
nofile:
soft: 20000
hard: 40000
tomcat-2:
image: tomcat:8.0-jre8
ports:
- 8081:8080
volumes:
- appdate2:/usr/local/tomcat/webapps
networks:
- tomcat-net # 这里一样的网络
- tomcat2-net
mysql:
image: mysql:5.7.23
container_name: mysql
ports:
- "3307:3306"
volumes:
- mysql_data:/var/lib/mysql
- mysql_config:/etc/mysql
# environment:
# MYSQL_ROOT_PASSWORD: "123456"
# 第二种方法:用env_file
env_file:
- mysql.env
networks:
- tomcat-net
redis:
image: redis:5.0.10
container_name: redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: "redis-server --appendonly yes" # 原本的redis启动的命令是 redis-server 但是我们要这个是容器,而且要做数据持久化,所以需要覆盖原本的命令,新命令是 command: "redis-server --appendonly yes"
networks:
- tomcat-net
volumes:
appdata1:
appdate2:
mysql_data:
mysql_config:
redis_data:
networks:
tomcat-net:
external:
true
tomcat2-net:
最复杂的一个命令 build 指令
作用:用来将我们指定的自制的Dockerfile 打包成镜像, 然后再运行这个镜像。
build 用来指定Dockerfile 所在目录,现根据build中Dockerfile自动构建镜像,然后自动运行容器
过程就是:先用Dockerfile 生成image,然后在运行成容器。
注意:build 指令下面,要有Dockerfile所在的目录,就是Dockerfile的上下文环境。这里还要注意就是这个目录相对docker-compose.yml的路径
还有就是要指定Dockerfile 文件名
不要忘记把Dockefile中暴露的端口,在这里也写出来
不要忘记volume 挂载点
version: "3.2"
services:
demo:
build: # 启动服务时先将命令中指定dockerfile打包成镜像,然后运行该镜像
context: ./demodir # Dockerfile 的上下文环境,就是Dockerfile所在的目录,里面存放生产镜像所需的文件。这里demodir目录和docker-compose.yaml同级
dockerfile: Dockerfile # 指定Dockefile文件的名字
container_name: demo # 生产容器的名字
ports: # 暴露我们Dockerfile中expose 的端口
- "8081:8081"
depends_on: # 依赖下面的服务 启动之后,这个再启动
- tomcat-1
networks:
- tomcat-net
tomcat-1:
container_name: tomcat-1_name
image: tomcat:8.0-jre8
ports:
- "8080:8080"
volumes:
- appdata1:/usr/local/tomcat/webapps
- /home:/home
networks:
- tomcat-net # 这里一样的网络
depends_on:
- tomcat-2
- mysql
- redis
healthcheck: # 这个是官方给的案例,让系统每1分30秒向本机的docker 引擎发送curl -f http://localhost, 如果能反回,就说明这个tomcat-1的服务是在正常运行中。
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 1m30s
timeout: 10s
retries: 3
sysctls: # 用来修改系统内部参数
- net.core.somaxconn=1024 # 这里是例子
- net.ipv4.tcp_syncookies=0
ulimits: #用来修改系统内进程数限制,日后使用,可以根据当前容器运行服务要求,进行修改,用以满足容器的成功启动
nproc: 65535
nofile:
soft: 20000
hard: 40000
tomcat-2:
image: tomcat:8.0-jre8
ports:
- 8081:8080
volumes:
- appdate2:/usr/local/tomcat/webapps
networks:
- tomcat-net # 这里一样的网络
- tomcat2-net
mysql:
image: mysql:5.7.23
container_name: mysql
ports:
- "3307:3306"
volumes:
- mysql_data:/var/lib/mysql
- mysql_config:/etc/mysql
# environment:
# MYSQL_ROOT_PASSWORD: "123456"
# 第二种方法:用env_file
env_file:
- mysql.env
networks:
- tomcat-net
redis:
image: redis:5.0.10
container_name: redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: "redis-server --appendonly yes" # 原本的redis启动的命令是 redis-server 但是我们要这个是容器,而且要做数据持久化,所以需要覆盖原本的命令,新命令是 command: "redis-server --appendonly yes"
networks:
- tomcat-net
volumes:
appdata1:
appdate2:
mysql_data:
mysql_config:
redis_data:
networks:
tomcat-net:
external:
true
tomcat2-net:
三 docker-compose 命令行指令
up
down
exec
ps
.
.
.
.
翻文挡去吧