Skip to content

Instantly share code, notes, and snippets.

@zlwu
Created June 11, 2025 16:50
Show Gist options
  • Save zlwu/f56b87b14468168bd7f99c49d8f07984 to your computer and use it in GitHub Desktop.
Save zlwu/f56b87b14468168bd7f99c49d8f07984 to your computer and use it in GitHub Desktop.
simple docker stack cli tool
#!/usr/bin/env python3
import os
import sys
import subprocess
import re
def load_env_vars(env_file):
"""从.env文件加载环境变量"""
if not os.path.isfile(env_file):
return {}
env_vars = {}
with open(env_file, 'r') as f:
for line in f:
line = line.strip()
# 跳过空行和注释
if not line or line.startswith('#') or '=' not in line:
continue
key, value = line.split('=', 1)
env_vars[key] = value
return env_vars
def interpolate_variables(content, env_vars):
"""使用环境变量替换内容中的变量引用"""
# 处理${VAR}形式的变量
def replace_var(match):
var_name = match.group(1)
return env_vars.get(var_name, '')
content = re.sub(r'\$\{(\w+)\}', replace_var, content)
# 处理$VAR形式的变量
def replace_simple_var(match):
var_name = match.group(1)
return env_vars.get(var_name, '')
return re.sub(r'\$(\w+)', replace_simple_var, content)
def deploy_stack(app_name):
"""部署指定的应用栈"""
stack_root = os.environ.get('STACK_ROOT', '/opt/stacks')
app_dir = os.path.join(stack_root, app_name)
if not os.path.isdir(app_dir):
print(f"Error: Application '{app_name}' not found in {stack_root}")
sys.exit(1)
stack_file = os.path.join(app_dir, 'stack.yml')
if not os.path.isfile(stack_file):
print(f"Error: stack.yml not found in {app_dir}")
sys.exit(1)
# 加载环境变量
env_vars = os.environ.copy()
env_vars.update(load_env_vars(os.path.join(stack_root, '.env')))
env_vars.update(load_env_vars(os.path.join(app_dir, '.env')))
# 读取并处理stack.yml内容
with open(stack_file, 'r') as f:
content = f.read()
env_vars['PWD'] = app_dir
processed_content = interpolate_variables(content, env_vars)
# 在应用目录执行部署命令
cmd = ['docker', 'stack', 'deploy', '-d', '-c', '-', app_name]
try:
subprocess.run(
cmd,
input=processed_content.encode('utf-8'),
cwd=app_dir,
check=True
)
except subprocess.CalledProcessError as e:
print(f"Deployment failed: {e}")
sys.exit(1)
def main():
if len(sys.argv) < 2:
print("Usage: dstack <command> [app_name] [options]")
print("Commands:")
print(" deploy <app_name> Deploy an application stack")
print(" Other docker stack commands (ls, services, ps, rm, etc.)")
sys.exit(1)
command = sys.argv[1]
if command == 'deploy':
if len(sys.argv) < 3:
print("Error: Missing application name for deploy")
sys.exit(1)
app_name = sys.argv[2]
deploy_stack(app_name)
else:
# 直接传递命令给docker stack
cmd = ['docker', 'stack'] + sys.argv[1:]
try:
subprocess.run(cmd, check=True)
except subprocess.CalledProcessError as e:
sys.exit(e.returncode)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment