用PyYAML编辑docker-compose.yml

我有一个漂亮的标准docker-compose.yml,我需要以编程方式编辑数据库的密码。

由于它是一个YAML文件,我认为编辑和转储内容很简单。 到目前为止,我尝试了PyYAML,它只是把docker-compose文件搞乱了,我不知道为什么。

加载和转储相同的内容,它打破了结构。

docker-compose.yml的内容:

version: '2' services: web: container_name: xxx ports: - "80:80" volumes: - .:/xxx depends_on: - mysql build: . mysql: ports: - "32768:32768" - "3306:3306" container_name: xxx-mysql restart: always image: mariadb:latest environment: MYSQL_ROOT_PASSWORD: 'thiswillbechangeonsetupscript' MYSQL_DATABASE: 'xxxdb' volumes: - ./database:/var/lib/mysql ports: - "3306:3306" 

这是我如何加载和倾倒的内容:

 import yaml with open("docker-compose.yml", 'r') as ymlfile: docker_config = yaml.load(ymlfile) with open("docker-compose.yml", 'w') as newconf: yaml.dump(docker_config, newconf) 

这就是文件的保存方式。

 services: mysql: container_name: xxx-mysql environment: {MYSQL_DATABASE: xxxdb, MYSQL_ROOT_PASSWORD: thiswillbechangeonsetupscript} image: mariadb:latest ports: ['3306:3306'] restart: always volumes: ['./database:/var/lib/mysql'] web: build: . container_name: xxx depends_on: [mysql] ports: ['80:80'] volumes: ['.:/xxx'] version: '2' 

有没有更好的方法来做到这一点? 我错过了什么?

在编写yaml时需要添加default_flow_style=False

 import yaml with open("docker-compose.yml", 'r') as ymlfile: docker_config = yaml.load(ymlfile) with open("docker-compose_new.yml", 'w') as newconf: yaml.dump(docker_config, newconf, default_flow_style=False) 

然后你会得到下面的输出,除了使用字母顺序写行,是类似于你的input:

 services: mysql: container_name: xxx-mysql environment: MYSQL_DATABASE: xxxdb MYSQL_ROOT_PASSWORD: thiswillbechangeonsetupscript image: mariadb:latest ports: - 3306:3306 restart: always volumes: - ./database:/var/lib/mysql web: build: . container_name: xxx depends_on: - mysql ports: - 80:80 volumes: - .:/xxx version: '2' 

请注意,在你docker-compose.yaml你声明的portsvariables是两次,所以yamlparsing器只会考虑最后一个variables。 要解决该问题,请删除以下行:

 ports: - "3306:3306" 

然后,如上所述运行写操作给出以下输出:

 services: mysql: container_name: xxx-mysql environment: MYSQL_DATABASE: xxxdb MYSQL_ROOT_PASSWORD: thiswillbechangeonsetupscript image: mariadb:latest ports: - 32768:32768 - 3306:3306 restart: always volumes: - ./database:/var/lib/mysql web: build: . container_name: xxx depends_on: - mysql ports: - 80:80 volumes: - .:/xxx version: '2' 

PyYAML的默认转储是为叶节点使用stream风格(序列为[....] ,映射为{...} ),所以您应该做的最小值是指定yaml.dump(....., default_flow_style=False)

然后,YAML规范声明键的顺序是不能保证的,你看到的是PyYAML以sorting顺序转储它们。

我可以推荐使用ruamel.yaml (免责声明:我是这个包的作者),它有一个特定的目标,即允许以最小的变化进行这种往返ruamel.yaml ,而与input相比通常没有变化。 包括按键sorting,stream量与块风格,string上的引号等。

还有一个原因是使用ruamel.yaml :如果你在你的input上运行这个程序:

 import sys import ruamel.yaml yaml = ruamel.yaml.YAML() yaml.preserve_quotes = True yaml.indent(sequence=3, offset=1) with open("docker-compose.yml", 'r') as ymlfile: data = yaml.load(ymlfile) yaml.dump(data, sys.stdout) 

你会得到一个DuplicateKeyError

 ruamel.yaml.constructor.DuplicateKeyError: while constructing a mapping in "docker-compose.yml", line 13, column 5 found duplicate key "ports" with value "[]" (original value: "[]") in "docker-compose.yml", line 24, column 5 

因为在作为密钥mysql的值的映射中ports出现两次。 这根据YAML规范是不允许的(PyYAML休闲和更新的1.2),而PyYAML却默默地处理了第一个键值对,留下的端口32768未映射。

从input中删除最后两行后,程序的输出是:

 version: '2' services: web: container_name: xxx ports: - "80:80" volumes: - .:/xxx depends_on: - mysql build: . mysql: ports: - "32768:32768" - "3306:3306" container_name: xxx-mysql restart: always image: mariadb:latest environment: MYSQL_ROOT_PASSWORD: 'thiswillbechangeonsetupscript' MYSQL_DATABASE: 'xxxdb' volumes: - ./database:/var/lib/mysql 

这是希望足够接近你的预期目的。

请注意,PyYAML删除在- "80:80" ,这是正确的,因为80:80不能被错误地解释为一个六十进制,但如果你做一些端口25变化- 80:80- 25:25 - 80:80当使用像PyYAML这样的YAML 1.1parsing器时(不同于docker docker-compose ),从- "25:25" (the former equalling -1525)

基于此,我制作了一个实用程序ruamel.dcw ,它使用此function预处理ruamel.dcw撰写文件,允许默认环境variables(如果未设置)以及其他一些技巧,写出一个临时文件,然后调用docker-compose -f tmpfile ,你应该使用类似的技术,在运行后处理你的临时文件。