首页 > 编程语言 >使用 Python 优雅地编排 Dockerfile

使用 Python 优雅地编排 Dockerfile

时间:2024-08-08 19:52:49浏览次数:19  
标签:netkiller compose cn service Python nginx 编排 docker Dockerfile

使用 python 替代 docker compose 编排容器

docker compose 是 docker 的容器编排工具,它是基于 YAML 配置,YAML 是一种配置文件格式,支持传递环境变量,但是对于复杂的容器编排显得力不从心。

于是我便开发这个程序,可以像写程序一样编排 docker ,可以充分发挥程序猿的想象力。

pip install netkiller-devops

编排 Dockerfile

from netkiller.docker import *

# 实例化 Dockerfile() 对象
nginx = Dockerfile()

# 基于什么镜像
nginx.image('nginx:latest')

# 配置挂载卷
nginx.volume(['/etc/nginx','/var/log/nginx','/opt'])

# 运行脚本
nginx.run('apt update -y && apt install -y procps')

# 暴漏端口
nginx.expose(['80','443'])

# 设置工作目录
nginx.workdir('/opt')

# 打印 Dockerfile
nginx.show()		

运行结果

FROM nginx:latest
VOLUME ["/etc/nginx","/var/log/nginx","/opt"]
RUN apt update -y && apt install -y procps
EXPOSE 80 443
WORKDIR /opt		

另一种写法

from netkiller.docker import *

nginx = Dockerfile() 
nginx.image('nginx:latest').volume(['/etc/nginx','/var/log/nginx']).run('apt update -y && apt install -y procps').expose(['80','443']).workdir('/opt')
nginx.render()
nginx.save('/tmp/Dockerfile')

构建 Docker 镜像

from netkiller.docker import *

# 编排 Docker 镜像
dockerfile = Dockerfile()
dockerfile.image('openjdk:8').volume(['/srv']).run(
    'apt update -y && apt install -y procps net-tools iputils-ping iproute2 telnet'
).expose(['80', '443']).workdir('/srv')

# 通过 Service 设置镜像名称是 netkiller:openjdk8
image = Services('image')
image.build(dockerfile)
image.image('netkiller:openjdk8')

# 构建镜像
demo = Composes('demo')
demo.version('3.9')
demo.services(image)
demo.build()		

完整演示

#!/usr/bin/python3
#-*- coding: utf-8 -*-
##############################################
# Home	: http://netkiller.github.io
# Author: Neo <[email protected]>
# Upgrade: 2021-11-17
##############################################
try:
	import os,  sys
	module = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
	print(module)
	sys.path.insert(0,module)
	from netkiller.docker import *
except ImportError as err:
	print("%s" %(err))

dockerfile = Dockerfile() 
# dockerfile.label({'org.opencontainers.image.authors':'netkiller'})
dockerfile.image('openjdk:8-alpine')
# dockerfile.image('openjdk:8')
dockerfile.env({'ROCKETMQ_VERSION':'4.9.2','ROCKETMQ_HOME':'/srv/rocketmq', 'PATH':'${ROCKETMQ_HOME}/bin:$PATH'}) # 'JAVA_OPT':'"${JAVA_OPT} -server -Xms512m -Xmx2048m -Xmn128m"'
dockerfile.arg({'user':'rocketmq', 'group':'nogroup'})
dockerfile.run('wget https://dlcdn.apache.org/rocketmq/4.9.2/rocketmq-all-4.9.2-bin-release.zip && unzip rocketmq-all-4.9.2-bin-release.zip')
dockerfile.run('mv rocketmq-4.9.2 /srv/rocketmq-4.9.2 && rm -rf rocketmq-all-4.9.2-bin-release.zip')
dockerfile.run('ln -s /srv/rocketmq-${ROCKETMQ_VERSION} /srv/rocketmq')
dockerfile.run('adduser -S -D ${user}')
dockerfile.run(['chown ${user}:${group} -R /srv/rocketmq-${ROCKETMQ_VERSION}'])
dockerfile.expose(['9876'])
dockerfile.expose(['10909','10911','10912'])
dockerfile.copy('docker-entrypoint.sh','/srv/docker-entrypoint.sh')
dockerfile.run('chmod a+x /srv/docker-entrypoint.sh')
dockerfile.entrypoint('["/srv/docker-entrypoint.sh"]') 
dockerfile.workdir('${ROCKETMQ_HOME}')
# dockerfile.render()
# dockerfile.save('/tmp/Dockerfile')

rocketmq = Services('rocketmq')
rocketmq.build(dockerfile).image('registry.netkiller.cn/rocketmq/rocketmq:4.9.2').container_name('rocketmq')
# rocketmq.entrypoint('/srv/rocketmq/bin/mqnamesrv')
# rocketmq.ports('9876:9876').command('/srv/rocketmq/bin/mqnamesrv')

dockerfile = Dockerfile() 
dockerfile.image('registry.netkiller.cn/rocketmq/rocketmq:4.9.2')
dockerfile.run('ln -s /srv/rocketmq-${ROCKETMQ_VERSION} /srv/mqnamesrv')
dockerfile.cmd('/srv/mqnamesrv/bin/mqnamesrv')
dockerfile.workdir('/srv/mqnamesrv')
dockerfile.user('rocketmq:nogroup')
dockerfile.volume([
 	'/home/rocketmq/logs/rocketmqlogs'
])

mqnamesrv = Services('mqnamesrv')
mqnamesrv.build(dockerfile).image('registry.netkiller.cn/rocketmq/mqnamesrv:4.9.2').container_name('mqnamesrv').ports('9876:9876')
mqnamesrv.command('mqnamesrv')

dockerfile = Dockerfile() 
dockerfile.image('registry.netkiller.cn/rocketmq/rocketmq:4.9.2')
dockerfile.run('ln -s /srv/rocketmq-${ROCKETMQ_VERSION} /srv/mqbroker')
dockerfile.cmd('/srv/rocketmq/bin/mqbroker')
dockerfile.workdir('/srv/mqbroker')
dockerfile.user('rocketmq:nogroup')
dockerfile.volume([
 	'/home/rocketmq/logs/rocketmqlogs'
])

mqbroker = Services('mqbroker')
mqbroker.build(dockerfile).image('registry.netkiller.cn/rocketmq/mqbroker:4.9.2').container_name('mqbroker').ports(['10909:10909','10911:10911','10912:10912'])
mqbroker.command('mqbroker -n mqnamesrv:9876 -c /srv/rocketmq/conf/broker.conf')
mqbroker.volumes(['/tmp/logs:/home/rocketmq/logs/rocketmqlogs'])

composes = Composes('test')
composes.version('3.9')
composes.services(rocketmq)
composes.services(mqnamesrv)
composes.services(mqbroker)


# cat >> /srv/docker-entrypoint.sh <<'EOF'
# EOF

entrypoint='''#!/bin/sh
if [ "$1" = 'mqnamesrv' ]; then
	exec /srv/rocketmq/bin/mqnamesrv
fi
exec "$@"
'''

if __name__ == '__main__':
	try:
		docker = Docker({'DOCKER_HOST':'ssh://[email protected]','NAMESRV_ADDR':'localhost:9876'}) 
		docker.createfile('rocketmq/rocketmq/docker-entrypoint.sh',entrypoint)
		docker.environment(composes)
		docker.main()
	except KeyboardInterrupt:
		print ("Crtl+C Pressed. Shutting down.")

运行

python3 demo.py -e test -b rocketmq

快速入门,首先我们参照这个 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: [email protected]
License: BSD
Location: /usr/local/lib/python3.9/site-packages
Requires: pyttsx3, requests, redis, pyyaml
Required-by: 		

创建一个 Services

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		

创建 Composes

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 <[email protected]>
# 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 <[email protected]>		

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

演示例子

Redis 主从配置

例18.1.Redis Master/Slave

from netkiller.docker import *

image = 'redis:latest'
requirepass='11223344'

compose = Composes('redis-master-slave')
compose.version('3.9')

master =  Services('master')
master.image(image)
master.container_name('master')
master.restart('always')
master.environment(['TZ=Asia/Shanghai'])
master.ports('6379:6379')
master.volumes(['/tmp/master:/data'])
master.sysctls(['net.core.somaxconn=1024'])
master.command([
	'--requirepass '+requirepass,
	'--appendonly yes'])
# master.debug()
# print(master.dump())
compose.services(master)


for i in range(5) :
    slave =  Services('slave-'+str(i))
    slave.image(image).container_name('slave-'+str(i)).restart('always')
    slave.ports(['638{port}:6379'.format(port=i)]).environment(['TZ=Asia/Shanghai'])
    slave.volumes(['/tmp/slave{n}:/data'.format(n=i)])
    slave.sysctls(['net.core.somaxconn=1024']).command([
        '--slaveof master 6379',
        '--masterauth '+requirepass,
        '--requirepass ' + requirepass,
        '--appendonly yes'
    ])

    # print(cluster.dump())
    compose.services(slave)

# print (compose.debug())
print(compose.dump())
# compose.save()
compose.up()					

标签:netkiller,compose,cn,service,Python,nginx,编排,docker,Dockerfile
From: https://blog.csdn.net/u010604770/article/details/141000763

相关文章

  • 学习 Python 语言,它能干什么?
    学习Python语言,它能干什么?为什么要学习编程语言?随着大数据,人工智能盒区块链技术的不断发展和应用,很多领域的创新需要借助一些前沿技术,而软件创建由需要借助编程语言,编程语言是创新与产品之间的桥桥梁,所以创新与编程语言是相辅相成的,人们借助编程语言完成产品的落地。为什......
  • Python的安装环境以及应用
    1.环境python2,Python最新安装3.12可以使用源码安装查看安装包[root@python001~]#yumlistinstalled|grepepel3[root@python001~]#yumlistinstalled|greppython[root@python001~]#yum-yinstallpython3    安装python3查看版本[root@python001......
  • 解锁Python副业密码:从零到日赚1000+的惊人蜕变之路与实战秘籍
    在当今这个数字化时代,掌握一门编程语言如同手握一把开启无限可能的钥匙。Python,以其简洁的语法、丰富的库支持和广泛的应用领域,成为了众多编程爱好者及专业人士的首选。本文将深入探讨如何利用Python技能,开启一条副业之路,并探讨实现日赚1000+的可行策略与实战心得。一、前言:......
  • 【人工智能】【机器学习】-好书推荐之《Python神经网络编程》
    目录内容概览编程环境面向对象学习目标如果你是想要自学机器学习相关知识的读者,我相信看完这篇文章的介绍后,你会对机器学习有更清晰的认识。帮助你走进机器学习的殿堂。《Python神经网络编程》(原书名:MakeYourOwnNeuralNetwork)是一本深度学习领域的入门级书籍,由Tar......
  • 19:Python字符串格式化
    #字符串格式化,用两种方法:百分号和format方式,百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存。#百分号(%)是一个运算符,用于进行字符串格式化、取模运算和格式化输出#百分号方式##%[(name)][flags][width].[precision]typecode##(name)......
  • "阳光高考爬虫项目揭秘:增量爬虫与断点续抓的Python实战"
    阳光高考项目项目要求爬取各大高校基本信息和招生简章(招生简章要求存储为pdf格式并且入库)数据库表设计idtask_urlstatus:0(未抓取),1(抓取中),2(抓取完毕),3(错误),4(更新中),5(数据更新成功),6(数据未更新,保持原样),9(暂无),8(暂无)3:错误,是因为此div下根本没p标签,所以根本等不到......
  • 【Python】excel常用函数操作Python实现,办公入门首选
    常见的Excel函数,在Python中的如何实现:VLOOKUP:可以使用merge或map函数来实现类似的功能。IF:可以使用numpy库的where函数来实现类似的功能。SUMIF:可以使用pandas的query函数来筛选数据,然后使用sum函数来计算总和。COUNTIF:类似于SUMIF,可以使用query函数来筛选数据,然......
  • 通过embeddable版python部署环境
    通过embeddable版python部署环境下载embeddable版本python首先在https://www.python.org/downloads/windows/找到embeddable版本的python压缩包进行下载。解压到指定目录。安装pip安装pip,首先下载get-pip.py文件。放置到解压的目录中。执行pythonget-pip.py安装pip。配......
  • 22.python自定义函数(format,zip)
    python自定义函数一、常见的自定义函数已经学过的函数:list、print、set、str、type、tuple、dict、range、input等今天学的函数:format二、实战讲解(一)format函数1、默认显示案例:hz="{}{}".format("dcs","43")print(hz)#dcs43hz="{}".format("dcs","43"......
  • Depth Anything强大的单目深度估计Python与C++模型部署
    引言最近看到一个叫DepthAnything单目深度估计模型火了,于是抽空把代码和模型下载下来体验了一下,发现确实是很强大。论文链接:https://arxiv.org/pdf/2401.10891.pdf代码链接:https://github.com/LiheYoung/Depth-Anything项目主页:https://depth-anything.github.io/本......