Docker Compose
Docker Compose概述
- 之前使用 Docker,需要定义 Dockerfile 文件,然后使用 docker build、docker run 等命令操作容器。然而微服务架构的应用系统一般包含若干个微服务,每个微服务一般都会部署多个实例,如果每个微服务都要手动启停,那么效率之低,维护量之大可想而知
- 因此使用 Docker Compose 可以轻松、高效的管理容器,它是一个用于定义和运行多容器 Docker 的应用程序工具
- Compose是用于定义和运行多容器Docker应用程序的工具。通过Compose,可以使用YAML文件来配置应用程序的服务。然后使用一个命令,就可以从配置中创建并启动所有服务
使用Compose基本是一个三步过程
- 通过编写
Dockerfile
文件保证我们的项目在任何地方都可以运行 - 通过
docker-compose.yml
文件定义组成应用程序的服务,以便它们在隔离的环境中一起运行 - 运行
docker compose up
,然后Docker compose命令启动并运行整个应用程序。也可以docker-compose up
使用docker-compose二进制文件运行。
作用:
批量容器编排
docker-compose.yml 就是这种:
version: "3.9"
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
- logvolume01:/var/log
links:
- redis
redis:
image: redis
volumes:
logvolume01: {}
Compose一些理解
- Compose 是Docker官方的开源项目,需要手动安装
- 每一个容器就相当于一个服务service,如web、redis、mysql… 将所有服务打包就是一个项目
- 项目project就是由一组关联的容器(服务)组成,如一个wordpress,里面包含了web和mysql
Docker Compose的安装
下载安装compose
curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m) > /usr/local/bin/docker-compose
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 423 100 423 0 0 315 0 0:00:01 0:00:01 --:--:-- 315
100 16.7M 100 16.7M 0 0 3672k 0 0:00:04 0:00:04 --:--:-- 5250k
授权
chmod +x /usr/local/bin/docker-compose
查看版本
docker-compose version
docker-compose version 1.25.5, build 8a1c60f6
docker-py version: 4.1.0
CPython version: 3.7.5
OpenSSL version: OpenSSL 1.1.0l 10 Sep 2019
Docker Compose的使用方法
- 建立一个运行在Docker Compose上的Python Web应用程序。该应用程序使用Flask框架,并在Redis中缓存一个计数器。
- 编写程序
mkdir -p /home/compose/python-redis
cd /home/compose/python-redis
写python应用
vim app.py
#! /usr/bin/python3
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
编写依赖包文件
vim requirements.txt
flask
redis
- 编写Dockerfile文件
vim Dockerfile
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
- 定义一个服务在compose里
vim docker-compose.yml
version: "3"
services:
web:
build: .
ports:
- "5000:5000"
redis:
image: "redis:alpine"
- 通过compose构建和运行应用
目前有四个文件
[root@docker python-redis]# ls
app.py docker-compose.yml Dockerfile requirements.txt
启动compose项目
docker-compose up
Creating python-redis_web_1 ... done
Creating python-redis_redis_1 ... done
最后构建镜像
[root@docker python-redis]# docker-compose build
redis uses an image, skipping
Building web
Step 1/10 : FROM python:3.7-alpine
---> c46f62f378d7
Step 2/10 : WORKDIR /code
---> Using cache
---> 3d09c3c9e5f7
Step 3/10 : ENV FLASK_APP=app.py
---> Using cache
---> 5401ce448bf0
Step 4/10 : ENV FLASK_RUN_HOST=0.0.0.0
---> Using cache
---> cd3cf06ed26e
Step 5/10 : RUN apk add --no-cache gcc musl-dev linux-headers
---> Using cache
---> 5b887aadaeb6
Step 6/10 : COPY requirements.txt requirements.txt
---> Using cache
---> 24c8b54c831f
Step 7/10 : RUN pip install -r requirements.txt
---> Using cache
---> e26516cdef01
Step 8/10 : EXPOSE 5000
---> Using cache
---> 2ffe08dae8ee
Step 9/10 : COPY . .
---> Using cache
---> e1299046671d
Step 10/10 : CMD ["flask", "run"]
---> Using cache
---> ecc8f48e18e5
Successfully built ecc8f48e18e5
Successfully tagged python-redis_web:latest
再次启动会发现
docker-compose up
项目迅速启动
保证启动之后有两个容器
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS
NAMES
e92f6c104c29 redis:alpine "docker-entrypoint.s…" 9 minutes ago Up About a minute 6379/tcp
python-redis_redis_1
ad9ab1f0220d python-redis_web "flask run" 9 minutes ago Up About a minute 0.0.0.0:5000->5000/tcp, :::500
0->5000/tcp python-redis_web_1
测试
[root@docker ~]# curl localhost:5000
Hello World! I have been seen 1 times.
[root@docker ~]# curl localhost:5000
Hello World! I have been seen 2 times.
网络规则
我们查看网络,会发现compose会单独创建一个网络
查看网络详情
docker network inspect python-redis_default
[
{
"Name": "python-redis_default",
"Id": "0e1cc417dc99dc66c337823ed1cbda0b680e7543732444d1120d54a50b72989c",
"Created": "2021-04-25T03:03:35.019219454-04:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": true,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"ad9ab1f0220dda2638747f03852e547a0154514574b65930720ead939fc66048": {
"Name": "python-redis_web_1",
"EndpointID": "8c23797f7d6e4ea8464cddc12a0757c119a41381a45d936b8d8c5beeb2978e85",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
},
"e92f6c104c29e79459e0c5eaebbd0d98c674c273cc1354bd4e18704f0c41e2b2": {
"Name": "python-redis_redis_1",
"EndpointID": "68c762f33ebf63a77cbde1bc6af15484f1458a43c50d7384a748cbdfe1033a33",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {
"com.docker.compose.network": "default",
"com.docker.compose.project": "python-redis",
"com.docker.compose.version": "1.25.5"
}
}
]
里面包含python-redis_redis_1、python-redis_web_1,因为在同一个网络就可以通过容器名直接访问
- 这里我们看之前python代码
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)
这里host='redis' 没有写host=‘localhost’或者127.0.0.1
就是因为docker容器在同一个网络里面可以通过容器名访问
yaml编写规则
官方详解
耐心查看官方的教程,会有很大收获的
- 编写规则
3层 第一层:版本 第二层:服务 第三层:其他配置
version: # 版本
services: # 服务
服务1:web
# 服务配置
images
build
netwokr
.....
服务2:redis
服务3:redis
...
# 其他配置 网络/卷、全局规则
volumes:
networks:
configs:
如以下一个样本
version: "3.9"
services:
redis:
image: redis:alpine
ports:
- "6379"
networks:
- frontend
deploy:
replicas: 2
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
db:
image: postgres:9.4
volumes:
- db-data:/var/lib/postgresql/data
networks:
- backend
deploy:
placement:
max_replicas_per_node: 1
constraints:
- "node.role==manager"
vote:
image: dockersamples/examplevotingapp_vote:before
ports:
- "5000:80"
networks:
- frontend
depends_on:
- redis
deploy:
replicas: 2
update_config:
parallelism: 2
restart_policy:
condition: on-failure
result:
image: dockersamples/examplevotingapp_result:before
ports:
- "5001:80"
networks:
- backend
depends_on:
- db
deploy:
replicas: 1
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
worker:
image: dockersamples/examplevotingapp_worker
networks:
- frontend
- backend
deploy:
mode: replicated
replicas: 1
labels: [APP=VOTING]
restart_policy:
condition: on-failure
delay: 10s
max_attempts: 3
window: 120s
placement:
constraints:
- "node.role==manager"
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8080:8080"
stop_grace_period: 1m30s
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
placement:
constraints:
- "node.role==manager"
networks:
frontend:
backend:
volumes:
db-data:
depends_on
version: "3.9"
services:
web:
build: .
depends_on: web依赖于db和redis
- db
- redis
redis:
image: redis
db:
image: postgres
容器运行顺序就是1.db 2.redis 3.web
deploy
version: "3.9"
services:
redis:
image: redis:alpine
deploy:
replicas: 6
placement:
max_replicas_per_node: 1
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
replicas是副本
使用compose 一键部署wordpress博客
mkdir /home/wordpress
cd /home/wordpress
编写yaml
vim docker-compose.yml
version: '3.3'
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: 123
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: 123
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: 123
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
docker-compose up
通过docker-compose使微服务上线
package com.example.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
StringRedisTemplate redisTemplate;
@GetMapping("/hello")
public String hello(){
Long views = redisTemplate.opsForValue().increment("views");
return "hello,xiaotian,thank you,views:"+views;
}
}
application.properties
# 应用名称
spring.application.name=demo
# 应用服务 WEB 访问端口
server.port=8080
spring.redis.host=redis
dockerfile
FROM java:8
COPY *.jar /app.jar
CMD ["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
docker-compose.yml
version: '3.8'
services:
tianapp:
build: .
image: tianapp
depends_on:
- redis
ports:
- "8080:8080"
redis:
image: "library/redis:alpine"
将jar包上传至服务器后启动项目
[root@docker javaapp]# ls
docker-compose.yml Dockerfile maomao-0.0.1-SNAPSHOT.jar
测试
[root@docker python-redis]# curl localhost:8080/hello
hello,xiaotian,thank you,views:1
[root@docker python-redis]# curl localhost:8080/hello
hello,xiaotian,thank you,views:2