Docker Compose快速入门
Docker Compose是Docker官方推出的一种容器编排服务,可以快速在集群中部署分布式应用。本文主要参考官方的快速入门示例来总结一下Docker Compose的简单使用。示例的场景是构建一个运行在Docker Compose上的简单Python Web应用程序。该应用程序使用Flask框架,在Redis中维护一个计数器,并将统计的结果返回。至于Docker Compose的安装,可以参考Docker Compose在Linux上的安装。
一、Docker Compose简介
Docker Compose是Docker官方开源项目,实现了对Docker容器集群的快速编排。Compose定位是定义和运行多个Docker容器的应用程序。通过Compose,可以使用YAML文件来配置应用服务。然后通过一个命令,就可以从配置中创建并启动所有服务。Compose可以应用于开发、测试、CI工作流和生产等所有环境。
Compose中有两个重要的概念:
- 服务(service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。
- 项目(project):由一组关联的应用容器组成的一个完整业务单元,一个YAML文件定义的就是一个项目。
Compose的默认管理对象是项目,通过子命令对项目中的一组容器进行便捷地生命周期管理。而一个项目可以由多个服务关联而成。
Compose的使用基本分为三个步骤:
- 使用
Dockerfile
定义应用环境以便可以在任何地方再现它。 - 在
docker-compose.yml
中定义组成应用程序的服务,以便它们可以在隔离的环境中一起运行。 - 通过
docker-compose up
命令启动并运行整个应用程序。
二、环境信息
本文所使用的环境信息如下:
- 操作系统:CentOS Linux release 8.1.1911
- Docker:19.03.11
- Docker Compose:1.26.0
三、创建Web应用
创建项目目录:
mkdir composetest
cd composetest
在项目目录中创建一个名为app.py
的文件,内容如下:
import time
import redis
from flask import Flask
app = Flask(__name__)
#redis容器的主机名为redis,端口使用默认的6379端口
cache = redis.Redis(host='redis', port=6379)
#如果redis服务不可用,此重试循环使我们可以多次尝试请求
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)
在项目目录中创建另一个名为requirements.txt
的文件,内容如下:
flask
redis
四、创建Dockerfile
在项目目录中创建一个名为Dockerfile
的文件,内容如下:
#指定基础镜像为python:3.7-alpine
FROM python:3.7-alpine
#指定工作目录为/code
WORKDIR /code
#指定环境变量FLASK_APP的值为app.py
ENV FLASK_APP app.py
#指定环境变量FLASK_RUN_HOST的值为0.0.0.0
ENV FLASK_RUN_HOST 0.0.0.0
#安装gcc,以便诸如MarkupSafe和SQLAlchemy之类的Python包可以编译加速
RUN apk add --no-cache gcc musl-dev linux-headers
#复制requirements.txt到容器文件系统的requirements.txt
COPY requirements.txt requirements.txt
#安装Python依赖项
RUN pip install -r requirements.txt
#将项目的当前目录复制到镜像工作目录
COPY . .
#指定容器启动时的默认命令
CMD ["flask", "run"]
五、使用Compose文件定义服务
在项目目录中创建一个名为docker-compose.yml
的文件,内容如下:
#指定docker-compose的版本
version: '3'
#定义项目里所有的服务信息
services:
#服务名
web:
#指定Dockerfile文件的路径,这里指定的docker-compose.yml所在路径
build: .
#指定端口映射,这里将宿主机的5000端口映射到容器的5000端口
ports:
- "5000:5000"
redis:
#指定Docker镜像
image: "redis:alpine"
该Compose文件定义了两个服务:web
和redis
。
-
web
:该服务使用当前目录中的Dockerfile文件构建了镜像,将宿主机的5000
端口映射到容器的5000
端口。 -
redis
:该服务容器使用Docker Hub拉取的官方镜像创建。
六、构建并运行应用
在项目目录中执行以下命令启动应用:
docker-compose up
如果出现下图错误:
错误原因是docker-compose.yml
文件中的缩进部分使用了Tab而非两个空格,改成两个空格即可。YAML格式要求得比较严格,所以要特别注意。然后再次启动。本地没有基础镜像会自动拉取:
如果出现下图错误:
先试一下是不是alpine官方源连接不上的问题,尝试换国内源,可以试试阿里云,在Dockerfile
的安装gcc的指令之前添加以下指令:
RUN echo "https://mirrors.aliyun.com/alpine/v3.12/main/" >> /etc/apk/repositories
RUN echo "https://mirrors.aliyun.com/alpine/v3.12/community/" >> /etc/apk/repositories
如果还是出错,如下图:
更换为国内源还是连接不上可能是容器内访问外部网络出现问题,执行以下命令创建容器并ping一下外网:
docker run -it python:3.7-alpine ping www.baidu.com
docker run -it python:3.7-alpine ping 114.114.114.114
果然ping不通百度,容器内访问外部网络出现问题:
容器要想访问外部网络,需要本地系统的转发支持。所以解决办法是打开IP转发。首先打开/etc/sysctl.conf
文件(或者/usr/lib/sysctl.d/00-system.conf
文件,这个文件没试过):
vim /etc/sysctl.conf
添加以下内容:
net.ipv4.ip_forward=1
重启network服务,CentOS8使用以下命令(CentOS7使用systemctl restart network
):
nmcli c reload
查看是否修改成功:
sysctl net.ipv4.ip_forward
如果返回“net.ipv4.ip_forward = 1”则表示成功了。然后重启Docker:
systemctl restart docker
再次ping百度发现能ping通:
如果还不行可以重启主机系统再试。再次启动:
从启动日志可知Docker Compose完成了镜像的构建和容器的创建运行。切换到另一个终端,输入docker image ls
查看本地镜像:
输入docker ps
查看运行中的容器:
访问http://MACHINE_VM_IP:5000
,其中MACHINE_VM_IP
为Docker主机ip,然后不断刷新,结果如下:
每刷新一次页面,计数就增加了1,说明Docker Compose运行应用成功。在原始终端中按CTRL + C来停止应用,或在另一个终端进入项目目录执行以下命令停止应用:
docker-compose down
七、修改Compose文件以添加目录挂载
修改docker-compose.yml
文件,内容如下:
#指定docker-compose的版本
version: '3'
#定义项目里所有的服务信息
services:
#服务名
web:
#指定Dockerfile文件的路径,这里指定的docker-compose.yml所在路径
build: .
#指定端口映射,这里将宿主机的5000端口映射到容器的5000端口
ports:
- "5000:5000"
#将宿主机项目目录(当前目录)下的/code目录挂载到容器,这样修改代码后不必重新构建镜像
volumes:
- .:/code
#设置FLASK_ENV环境变量,该变量指示flask run在开发模式下运行并在更改时重新加载代码,该模式仅应在开发中使用
environment:
FLASK_ENV: development
redis:
#指定Docker镜像
image: "redis:alpine"
然后使用更新的Compose文件构建应用并运行:
在浏览器中访问应用,刷新计数正常增加。然后修改app.py
文件中的输出内容,修改如下:
return 'Hello Docker Compose! I have been seen {} times.\n'.format(count)
然后在浏览器中刷新访问页面,发现返回的内容已变更,并且计数仍在增加,如下图:
八、Docker Compose基本操作
查看Docker Compose基本命令和用法:
docker-compose --help
查看结果如下:
查看Docker Compose版本信息:
docker-compose version
创建并运行服务:
#交互式启动
docker-compose up
#后台启动
docker-compose up -d
#指定Compose配置文件启动,默认为docker-compose.yml
docker-compose up -f mycompose.yaml
查看当前正在运行的服务:
docker-compose ps
查看结果如下:
列出Compose文件中包含的镜像:
docker-compose images
查看结果如下:
停止运行中的服务容器,这里停止web
服务:
docker-compose stop web
启动已存在的服务容器,这里启动web
服务:
docker-compose start web
重启项目中的服务容器,这里重启web
服务:
docker-compose restart web
暂停服务容器,这里暂停web
服务:
docker-compose pause web
恢复处于暂停状态的服务,恢复暂停的web
服务:
docker-compose unpause web
进入指定服务容器,这里进入web
服务容器并ping百度:
docker-compose exec web ping www.baidu.com
查看服务容器的输出日志,这里查看的web
服务:
docker-compose logs web
查看结果如下:
查看某个服务容器端口所映射的公共端口,这里指定web
服务容器:
docker-compose port web 5000
查看结果如下:
查看各个服务容器内运行的进程:
docker-compose top
查看结果如下:
在指定服务上执行一个命令,这里在web
服务查看环境变量:
docker-compose run web env
查看结果如下:
通过发送SIGKILL
信号来强制停止服务容器,这里发送SIGINT
信号来强制停止服务容器:
docker-compose kill -s SIGINT
验证Compose配置文件格式是否正确,正确会输出文件内容,错误则显示原因:
docker-compose config
验证结果如下:
删除所有停止状态的服务容器:
docker-compose rm
构建或重新构建项目中的容器:
docker-compose build
删除所有服务容器,同时删除网络和挂载目录的数据:
docker-compose down --volumes
置指定服务运行的容器个数,例如:
docker-compose scale web=2 worker=3
不推荐使用该命令,更推荐使用
up
命令的--scale
选项来指定服务运行的容器个数。
拉取服务依赖的镜像,该服务依赖的镜像需要在Compose配置文件中指定,例如:
docker-compose pull db
该命令需在Compose配置文件目录中执行,Compose配置文件中指定db
服务依赖镜像,例如:
services:
db:
image: postgres
推送服务依赖的镜像到镜像仓库,例如:
docker-compose push localhost:5000/yourimage
docker-compose push youruser/yourimage
Compose配置文件中指定服务依赖镜像,例如:
services:
service1:
build: .
image: localhost:5000/yourimage #推送至本地仓库
service2:
build: .
image: youruser/yourimage #推送至你的DockerHub仓库