compose介绍
在Docker中,我们从代码到部署容器需要经过两个步骤:
打包镜像docker build;
部署启动容器docker run;
在真实的企业级应用中,一个服务往往需要和很多个其它的服务进行关联,单个服务也有可能有多个容器实例,如果需要发布,很可能会需要人员手动对每一个容器进行打包和启动的操作,非常繁琐,容易出错。在这种背景下,Docker Compose就有了用武之地。简单来说,Docker Compose是一个用于定义和运行多个容器的工具,通过docker-compose.yml来实现对容器集群的编排工作。
Docker Compose管理如下三个内容:
1、工程,docker-compose运行的目录即为一个工程,在微服务场景下,我们往往都是使用git submodule的方式组建工程的,因此父项目就可以成为一个docker compose的工程;
2、服务,对应子项目,一个工程可以包含多个子项目;
3、容器,对应服务的实例,一个服务可以有多个实例;
Docker Compose当然也存在不足的地方,就是它只能用在单一host上进行容器编排,无法跨节点host对容器进行编排,那是Docker Swarm和K8s的范畴了,后续再讨论。
compose安装
下载地址
sudo curl -L https://github.com/docker/compose/releases/download/1.16.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
或
sudo curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
增加文件可执行权限
sudo chmod +x /usr/local/bin/docker-compose
查看安装状态
[root@localhost ~]# docker-compose --version
docker-compose version 1.25.1, build a82fef07
compose命令介绍
[root@localhost ~]# docker-compose --help
Define and run multi-container applications with Docker.
Usage:
docker-compose [-f <arg>...] [options] [COMMAND] [ARGS...]
docker-compose -h|--help
Options:
-f, --file FILE Specify an alternate compose file
(default: docker-compose.yml)
-p, --project-name NAME Specify an alternate project name
(default: directory name)
--verbose Show more output
--log-level LEVEL Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
--no-ansi Do not print ANSI control characters
-v, --version Print version and exit
-H, --host HOST Daemon socket to connect to
--tls Use TLS; implied by --tlsverify
--tlscacert CA_PATH Trust certs signed only by this CA
--tlscert CLIENT_CERT_PATH Path to TLS certificate file
--tlskey TLS_KEY_PATH Path to TLS key file
--tlsverify Use TLS and verify the remote
--skip-hostname-check Don't check the daemon's hostname against the
name specified in the client certificate
--project-directory PATH Specify an alternate working directory
(default: the path of the Compose file)
--compatibility If set, Compose will attempt to convert keys
in v3 files to their non-Swarm equivalent
--env-file PATH Specify an alternate environment file
Commands:
build Build or rebuild services
bundle Generate a Docker bundle from the Compose file
config Validate and view the Compose file
create Create services
down Stop and remove containers, networks, images, and volumes
events Receive real time events from containers
exec Execute a command in a running container
help Get help on a command
images List images
kill Kill containers
logs View output from containers
pause Pause services
port Print the public port for a port binding
ps List containers
pull Pull service images
push Push service images
restart Restart services
rm Remove stopped containers
run Run a one-off command
scale Set number of containers for a service
start Start services
stop Stop services
top Display the running processes
unpause Unpause services
up Create and start containers
version Show the Docker-Compose version information
docker-compose.yml 详解
YAML模板文件语法
默认的模板文件是docker-compose.yml,其中定义的每个服务都必须通过image指令指定镜像或build指令(需要Dockerfile)来自动构建。
其他大部分都跟docker run 中类似。 如果使用build指令,在Dockerfile中设置的选项(例如:CMD,EXPOSE,VOLUME,ENV等)将自动被获取,无需在docker-compose.yml中再次被设置。
1、image
指定为镜像名称或镜像ID。如果镜像不存在,Compose将尝试从互联网拉取这个镜像,例如: image: ubuntu image: orchardup/postgresql image: a4bc65fd
2、build
指定Dockerfile所在文件夹的路径。Compose将会利用他自动构建这个镜像,然后使用这个镜像。 build: ./dir
3、command
覆盖容器启动后默认执行的命令。 command: bundle exec thin -p 3000
4、links
链接到其他服务容器,使用服务名称(同时作为别名)或服务别名(SERVICE:ALIAS)都可以。
links:
- db
- db:database
- redis
注意:使用别名会自动在服务器中的/etc/hosts 里创建,如:172.17.2.186 db,相应的环境变量也会被创建。
5、external_links
链接到docker-compose.yml外部的容器,甚至并非是Compose管理的容器。参数格式和links类似。 external_links:
- redis_1
- project_db_1:mysql
- project_db_2:sqlserver
6、ports
暴露端口信息。 宿主机器端口:容器端口(HOST:CONTAINER)格式或者仅仅指定容器的端口(宿主机器将会随机分配端口)都可以。
ports:
- "3306"
- "8080:80"
- "127.0.0.1:8090:8001"
注意:当使用 HOST:CONTAINER 格式来映射端口时,如果你使用的容器端口小于 60 你可能会得到错误得结果,因为 YAML 将会解析 xx:yy 这种数字格式为 60 进制。所以建议采用字符串格式。
7、expose
暴露端口,与posts不同的是expose只可以暴露端口而不能映射到主机,只供外部服务连接使用;仅可以指定内部端口为参数。
expose:
- "3000"
- "8000"
8、volumes
设置卷挂载的路径。可以设置宿主机路径:容器路径(host:container)或加上访问模式(host:container:ro)ro就是readonly的意思,只读模式。
volumes:
- /var/lib/mysql:/var/lib/mysql
- /configs/mysql:/etc/configs/:ro
9、volunes_from
挂载另一个服务或容器的所有数据卷。
volumes_from:
- service_name
- container_name
10、environment
设置环境变量。可以属于数组或字典两种格式。 如果只给定变量的名称则会自动加载它在Compose主机上的值,可以用来防止泄露不必要的数据。
environment:
- RACK_ENV=development
- SESSION_SECRET
11、env_file
从文件中获取环境变量,可以为单独的文件路径或列表。 如果通过docker-compose -f FILE指定了模板文件,则env_file中路径会基于模板文件路径。 如果有变量名称与environment指令冲突,则以后者为准。
env_file: .env
env_file:
- ./common.env
- ./apps/web.env
- /opt/secrets.env
环境变量文件中每一行都必须有注释,支持#开头的注释行。
# common.env: Set Rails/Rack environment
RACK_ENV=development
12、extends
基于已有的服务进行服务扩展。例如我们已经有了一个webapp服务,模板文件为common.yml.
# common.yml
webapp:
build: ./webapp
environment:
\ - DEBUG=false
\ - SEND_EMAILS=false
编写一个新的 development.yml 文件,使用 common.yml 中的 webapp 服务进行扩展。 development.yml
web:
extends:
file: common.yml
service:
webapp:
ports:
\ - "8080:80"
links:
\ - db
envelopment:
- DEBUG=true
db:
image: mysql:5.7
后者会自动继承common.yml中的webapp服务及相关的环境变量。
13、net
设置网络模式。使用和docker client 的 --net 参数一样的值。
# 容器默认连接的网络,是所有Docker安装时都默认安装的docker0网络.
net: "bridge"
# 容器定制的网络栈.
net: "none"
# 使用另一个容器的网络配置
net: "container:[name or id]"
# 在宿主网络栈上添加一个容器,容器中的网络配置会与宿主的一样
net: "host"
Docker会为每个节点自动创建三个网络: 网络名称 作用 bridge 容器默认连接的网络,是所有Docker安装时都默认安装的docker0网络 none 容器定制的网络栈 host 在宿主网络栈上添加一个容器,容器中的网络配置会与宿主的一样 附录: 操作名称 命令 创建网络 docker network create -d bridge mynet 查看网络列表 docker network ls
14、pid
和宿主机系统共享进程命名空间,打开该选项的容器可以相互通过进程id来访问和操作。
pid: "host"
docker-compose.yml实例
version: "2"
services:
### console
console:
build:
context: ./images/console
args:
# console 容器 www-data用户密码
- USERPASS=root
- GIT_NAME=yangnan
- GIT_EMAIL=20706149@qq.com
- INSTALL_YARN=false
volumes_from:
- php-fpm
- nginx
- mysql
- redis
volumes:
- ./ssh:/home/www-data/.ssh
links:
- redis
- mysql
tty: true
### php-fpm
php-fpm:
build: ./images/php-fpm
volumes:
- ./app/:/var/www/
### nginx
nginx:
image: nginx
ports:
- "8081:80"
volumes_from:
- php-fpm
volumes:
- ./logs/nginx/:/var/log/nginx/
- ./images/nginx/sites:/etc/nginx/conf.d/
links:
- php-fpm
### mysql
mysql:
image: mysql
ports:
- "7706:3306"
environment:
MYSQL_ROOT_PASSWORD: "123"
MYSQL_DATABASE: "test"
MYSQL_USER: "root"
MYSQL_PASSWORD: "123"
volumes:
- ./data/mysql:/var/lib/mysql
### redis
redis:
image: redis
ports:
- "6379:6379"
volumes:
- ./data/redis:/data
注意事项: 使用compose对Docker容器进行编排管理时,需要编写docker-compose.yml文件,初次编写时,容易遇到一些比较低级的问题,导致执行docker-compose up时先解析yml文件的错误。比较常见的是yml对缩进的严格要求。
yml文件还行后的缩进,不允许使用tab键字符,只能使用空格,而空格的数量也有要求,经过实际测试,发现每一行增加一个空格用于缩进是正常的。 比如:
web:
<空格>build:
<空格><空格>command:
...
否则,很容易引起各种 yaml.scanner.ScannerError:的错误提示。
compose使用
单服务单容器使用
1、新建一个SpringBoot应用,仅仅包含一个Controller:
package com.example.demo.lcfc;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
@RestController
public class HelloController {
@GetMapping("/getHello")
public String getHello(){
return "myapp is running ok!!!";
}
}
请务必保证程序能正常运行,并进行package,打成jar包。
编写Dockerfile:
[root@localhost docker-compose]# cat Dockerfile
FROM openjdk:8
EXPOSE 8080
ADD target/demo-0.0.1-SNAPSHOT.jar /demo.jar
ENTRYPOINT ["java", "-jar", "demo.jar"]
编写docker-compose.yml文件:
[root@localhost docker-compose]# cat docker-compose.yml
# 使用的yml版本
version: "3.3"
services:
# 服务名称,可以自定义
myapp:
# 容器名称,可以自定义
container_name: myapp
# 指定Dockerfile所在的目录
build: .
ports:
- "8080:8080"
运行 docker-compose
docker-compose up -d
主要完成以下的两步操作:
镜像构建docker build;
启动yml中的所有容器docker run;
多服务多容器依赖使用
假设我们的应用需要依赖其它服务,比如需要使用redis,mysql等,那么这种场景下,就需要被依赖的容器先启动。
首先,我们改造上述例子中的myapp代码,需要引入redis的支持依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
redis配置
package com.example.demo.lcfc;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setConnectionFactory(factory);
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
//value hashmap序列化
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
Controller
package com.example.demo.lcfc;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
@RestController
public class HelloController {
@Autowired
private RedisTemplate redisTemplate;
@GetMapping("/getHello")
public String getHello(){
Long counter = redisTemplate.opsForValue().increment("counter");
return "myapp is running " + counter + "times!";
}
}
重新打包上传,并修改docker-compose.yml
[root@localhost docker-compose]# cat docker-compose.yml
# 使用的yml版本
version: "3.3"
services:
# 服务名称,可以自定义
myapp:
# 容器名称,可以自定义
# container_name: myapp
# 指定Dockerfile所在的目录
build: .
ports:
- "8080"
myredis:
image: "redis:latest"
ports:
- "6379:6379"
启动
docker-compose up -d
参考文章
docker-compose.yml 语法说明芝麻教程 (web3.xin)http://www.web3.xin/index/article/182.htmlhttps://www.jianshu.com/p/ee2fea4136f0https://www.jianshu.com/p/ee2fea4136f0