docker compose 是 docker 的容器编排工具,它是基于 YAML 配置,YAML 是一种配置文件格式,支持传递环境变量,但是对于复杂的容器编排显得力不从心。
于是我便开发这个程序,可以像写程序一样编排 docker ,可以充分发挥程序猿的想象力。
快速入门,首先我们参照这个 docker-compose.yaml 脚本,转换成 python 脚本。
version: '3.9'
services:
nginx:
container_name: nginx
environment:
- TZ=Asia/Shanghai
extra_hosts:
- db.netkiller.cn:127.0.0.1
- cache.netkiller.cn:127.0.0.1
- api.netkiller.cn:127.0.0.1
hostname: www.netkiller.cn
image: nginx:latest
ports:
- 80:80
- 443:443
restart: always
volumes:
- /tmp:/tmp
转换成 python 语言之后
from netkiller.docker import *
service = Services('nginx')
service.image('nginx:latest')
service.container_name('nginx')
service.restart('always')
service.hostname('www.netkiller.cn')
service.extra_hosts(['db.netkiller.cn:127.0.0.1','cache.netkiller.cn:127.0.0.1','api.netkiller.cn:127.0.0.1'])
service.environment(['TZ=Asia/Shanghai'])
service.ports(['80:80','443:443'])
service.volumes(['/tmp:/tmp'])
# service.debug()
# print(service.dump())
compose = Composes('development')
compose.version('3.9')
compose.services(service)
# print (compose.debug())
print(compose.dump())
compose.save()
怎么样,只是换了另一种写法,并没有难度。下面我们就系统学习,如何使用 python 编排 docker 容器
实际上程序最终还是会转化做 docker-compose 脚本执行。这种写法的有点是更灵活,你可以在程序中使用 if, while, 链接数据库,等等操作,可以做更复杂的容器编排。
neo@MacBook-Pro-Neo ~ % pip install netkiller-devops
确认是否安装成功
neo@MacBook-Pro-Neo ~ % pip show netkiller-devops
Name: netkiller-devops
Version: 0.2.4
Summary: DevOps of useful deployment and automation
Home-page: https://github.com/oscm/devops
Author: Neo Chen
Author-email: netkiller@msn.com
License: BSD
Location: /usr/local/lib/python3.9/site-packages
Requires: pyttsx3, requests, redis, pyyaml
Required-by:
from netkiller.docker import *
service = Services('nginx')
service.image('nginx:latest')
service.container_name('nginx')
service.restart('always')
service.hostname('www.netkiller.cn')
service.extra_hosts(['db.netkiller.cn:127.0.0.1','cache.netkiller.cn:127.0.0.1','api.netkiller.cn:127.0.0.1'])
service.environment(['TZ=Asia/Shanghai'])
service.ports(['80:80','443:443'])
service.volumes(['/tmp:/tmp'])
# service.debug()
print(service.dump())
运行结果
nginx:
container_name: nginx
environment:
- TZ=Asia/Shanghai
extra_hosts:
- db.netkiller.cn:127.0.0.1
- cache.netkiller.cn:127.0.0.1
- api.netkiller.cn:127.0.0.1
hostname: www.netkiller.cn
image: nginx:latest
ports:
- 80:80
- 443:443
restart: always
volumes:
- /tmp:/tmp
来一个复杂的演示
for i in range(10) :
cluster = Services('nginx-'+str(i))
cluster.image('nginx:latest').container_name('nginx-'+str(i)).restart('always').hostname('www'+str(i)+'.netkiller.cn')
cluster.ports(['8{port}:80'.format(port=i)])
print(cluster.dump())
运行结果
nginx-0:
container_name: nginx-0
hostname: www0.netkiller.cn
image: nginx:latest
ports:
- 80:80
restart: always
nginx-1:
container_name: nginx-1
hostname: www1.netkiller.cn
image: nginx:latest
ports:
- 81:80
restart: always
nginx-2:
container_name: nginx-2
hostname: www2.netkiller.cn
image: nginx:latest
ports:
- 82:80
restart: always
nginx-3:
container_name: nginx-3
hostname: www3.netkiller.cn
image: nginx:latest
ports:
- 83:80
restart: always
nginx-4:
container_name: nginx-4
hostname: www4.netkiller.cn
image: nginx:latest
ports:
- 84:80
restart: always
nginx-5:
container_name: nginx-5
hostname: www5.netkiller.cn
image: nginx:latest
ports:
- 85:80
restart: always
nginx-6:
container_name: nginx-6
hostname: www6.netkiller.cn
image: nginx:latest
ports:
- 86:80
restart: always
nginx-7:
container_name: nginx-7
hostname: www7.netkiller.cn
image: nginx:latest
ports:
- 87:80
restart: always
nginx-8:
container_name: nginx-8
hostname: www8.netkiller.cn
image: nginx:latest
ports:
- 88:80
restart: always
nginx-9:
container_name: nginx-9
hostname: www9.netkiller.cn
image: nginx:latest
ports:
- 89:80
restart: always
Services 对象创建服务,让服务工作还需要 Composes 对象。
from netkiller.docker import *
service = Services('nginx')
service.image('nginx:latest')
service.container_name('nginx')
service.restart('always')
service.hostname('www.netkiller.cn')
service.extra_hosts(['db.netkiller.cn:127.0.0.1','cache.netkiller.cn:127.0.0.1','api.netkiller.cn:127.0.0.1'])
service.environment(['TZ=Asia/Shanghai'])
service.ports(['80:80','443:443'])
service.volumes(['/tmp:/tmp'])
compose = Composes('development')
compose.version('3.9')
compose.services(service)
# print (compose.debug())
print(compose.dump())
compose.save()
# compose.save('/tmp/docker-compose.yaml')
运行结果
services:
nginx:
container_name: nginx
environment:
- TZ=Asia/Shanghai
extra_hosts:
- db.netkiller.cn:127.0.0.1
- cache.netkiller.cn:127.0.0.1
- api.netkiller.cn:127.0.0.1
hostname: www.netkiller.cn
image: nginx:latest
ports:
- 80:80
- 443:443
restart: always
volumes:
- /tmp:/tmp
version: '3.9'
这已经是一个完善的 docker-compose 脚本了。使用 save 可以保存为 yaml 文件,这是使用 docker-compose -f development.yaml up 就可以启动容器了。
Composes 对象同时也携带了完善的 docker-compose 命令和参数,用于自我管理容器。
compose.up() 创建容器
compose = Composes('development')
compose.version('3.9')
compose.services(service)
compose.up()
compose.start() 启动已存在的容器
compose = Composes('development')
compose.version('3.9')
compose.services(service)
compose.start()
compose.stop() 停止已存在的容器
compose = Composes('development')
compose.version('3.9')
compose.services(service)
compose.stop()
compose.restart() 重启已存在的容器
compose = Composes('development')
compose.version('3.9')
compose.services(service)
compose.restart()
compose.rm() 销毁已存在的容器
compose = Composes('development')
compose.version('3.9')
compose.services(service)
compose.rm()
compose.logs() 查看容器日志
compose = Composes('development')
compose.version('3.9')
compose.services(service)
compose.logs()
compose.ps() 查看容器运行状态
compose = Composes('development')
compose.version('3.9')
compose.services(service)
compose.ps()
Docker 对象是让我们摆脱 docker-compose 这个命令,它将接管 docker-compose 这个命令,进行自我管理。
#!/usr/bin/python3
#-*- coding: utf-8 -*-
##############################################
# Home : http://netkiller.github.io
# Author: Neo <netkiller@msn.com>
# Upgrade: 2021-09-05
##############################################
try:
import os, sys
module = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0,module)
from netkiller.docker import *
except ImportError as err:
print("%s" %(err))
nginx = Services('nginx')
nginx.image('nginx:latest')
nginx.container_name('nginx')
nginx.restart('always')
nginx.hostname('www.netkiller.cn')
nginx.environment(['TA=Asia/Shanghai'])
nginx.ports(['80:80'])
compose = Composes('development')
compose.version('3.9')
compose.services(nginx)
compose.workdir('/tmp/compose')
if __name__ == '__main__':
try:
docker = Docker()
docker.environment(compose)
docker.main()
except KeyboardInterrupt:
print ("Crtl+C Pressed. Shutting down.")
运行结果
neo@MacBook-Pro-Neo ~ % python3 docker.py
Usage: docker.py [options] up|rm|start|stop|restart|logs|top|images|exec <service>
Options:
-h, --help show this help message and exit
--debug debug mode
-d, --daemon run as daemon
--logfile=LOGFILE logs file.
-l, --list following logging
-f, --follow following logging
-c, --compose show docker compose
-e, --export export docker compose
Homepage: http://www.netkiller.cn Author: Neo <netkiller@msn.com>
Docker 对象提供了与 docker-compose 对等的参数,用法也基本相通。例如
python3 docker.py up = docker-compose up
python3 docker.py up -d nginx = docker-compose up -d nginx
python3 docker.py restart nginx = docker-compose restart nginx
python3 docker.py ps = docker-compose ps
python3 docker.py logs nginx = docker-compose logs nginx
使用 -c 可以查看 compose yaml 脚本,使用 -e 可以导出 docker compose yaml