在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的范畴了,后续再讨论。
下载地址
- 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
- [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
YAML模板文件语法
默认的模板文件是docker-compose.yml,其中定义的每个服务都必须通过image指令指定镜像或build指令(需要Dockerfile)来自动构建。
其他大部分都跟docker run 中类似。 如果使用build指令,在Dockerfile中设置的选项(例如:CMD,EXPOSE,VOLUME,ENV等)将自动被获取,无需在docker-compose.yml中再次被设置。
指定为镜像名称或镜像ID。如果镜像不存在,Compose将尝试从互联网拉取这个镜像,例如: image: ubuntu image: orchardup/postgresql image: a4bc65fd
指定Dockerfile所在文件夹的路径。Compose将会利用他自动构建这个镜像,然后使用这个镜像。 build: ./dir
覆盖容器启动后默认执行的命令。 command: bundle exec thin -p 3000
- 链接到其他服务容器,使用服务名称(同时作为别名)或服务别名(SERVICE:ALIAS)都可以。
- links:
- - db
- - db:database
- - redis
- 注意:使用别名会自动在服务器中的/etc/hosts 里创建,如:172.17.2.186 db,相应的环境变量也会被创建。
- 链接到docker-compose.yml外部的容器,甚至并非是Compose管理的容器。参数格式和links类似。 external_links:
- - redis_1
- - project_db_1:mysql
- - project_db_2:sqlserver
- 暴露端口信息。 宿主机器端口:容器端口(HOST:CONTAINER)格式或者仅仅指定容器的端口(宿主机器将会随机分配端口)都可以。
- ports:
- - "3306"
- - "8080:80"
- - "127.0.0.1:8090:8001"
- 注意:当使用 HOST:CONTAINER 格式来映射端口时,如果你使用的容器端口小于 60 你可能会得到错误得结果,因为 YAML 将会解析 xx:yy 这种数字格式为 60 进制。所以建议采用字符串格式。
- 暴露端口,与posts不同的是expose只可以暴露端口而不能映射到主机,只供外部服务连接使用;仅可以指定内部端口为参数。
- expose:
- - "3000"
- - "8000"
- 设置卷挂载的路径。可以设置宿主机路径:容器路径(host:container)或加上访问模式(host:container:ro)ro就是readonly的意思,只读模式。
- volumes:
- - /var/lib/mysql:/var/lib/mysql
- - /configs/mysql:/etc/configs/:ro
- 挂载另一个服务或容器的所有数据卷。
- volumes_from:
- - service_name
- - container_name
- 设置环境变量。可以属于数组或字典两种格式。 如果只给定变量的名称则会自动加载它在Compose主机上的值,可以用来防止泄露不必要的数据。
- environment:
- - RACK_ENV=development
- - SESSION_SECRET
- 从文件中获取环境变量,可以为单独的文件路径或列表。 如果通过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
- 基于已有的服务进行服务扩展。例如我们已经有了一个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服务及相关的环境变量。
- 设置网络模式。使用和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
- 和宿主机系统共享进程命名空间,打开该选项的容器可以相互通过进程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:的错误提示。
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.bootgroupId>
- <artifactId>spring-boot-starter-data-redisartifactId>
- dependency>
- <dependency>
- <groupId>org.apache.commonsgroupId>
- <artifactId>commons-pool2artifactId>
- 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/ee2fea4136f0
https://www.jianshu.com/p/ee2fea4136f0