简洁的

简洁的

我正在使用 Puppet 通过 GlusterFS 文件系统管理一些在服务器之间共享的文件。(具体细节并不重要,但在这种情况下,/etc/httpd/conf.d 和 /var/www/html 等内容通过 GlusterFS 在网络上安装。这是在 RHEL 6 服务器上,带有 Puppet 3.8 和 Gluster 3.5。)

Puppet 处理给定服务器的本地文件没有问题,但当我尝试在此共享文件系统上创建或更新文件时,它几乎从不工作。Puppet 发现需要进行更改,但随后文件无法通过后续的校验和检查。以下是 Puppet 尝试(并失败)创建文件的示例:

从不存在到文件的更改失败:写入磁盘的文件与校验和不匹配;放弃更改({md5} 990680e579211b74e3a8b58a3f4d9814 vs {md5} d41d8cd98f00b204e9800998ecf8427e)

以下是文件编辑的类似示例:

从 {md5}216751de84e40fc247cb02da3944b415 更改为 {md5}261e86c60ce62a99e4b1b91611c1af0e 失败:写入磁盘的文件与校验和不匹配;丢弃更改({md5}261e86c60ce62a99e4b1b91611c1af0e vs {md5}d41d8cd98f00b204e9800998ecf8427e)

这种情况并不总是发生,但在我的 Gluster 文件系统上,我认为它至少 90% 的时间都会发生。

后一个校验和 (d41d8...) 是空文件的校验和。所以我认为情况就是这样的:Puppet 发现需要进行更改,并进行了更改。但它在提交写入之前再次对文件进行校验和,因此它看不到更改已成功完成,因此它会回滚。

那么有两个问题。首先:这看起来合理吗?我该如何测试/确认这种情况?其次:假设这是正在发生的事情,我该如何防止它发生?首先想到的是在文件更改操作后简单地休眠几百毫秒,但我不知道这是否可行,更不用说明智了。

答案1

简洁的

将检查文件的校验和,然后刷新。此校验和将与要写入的文件进行比较。如果存在差异,则写入将失败。

详细

该错误是由以下方法引发的,该方法定义在文件.rb

  # Make sure the file we wrote out is what we think it is.
  def fail_if_checksum_is_wrong(path, content_checksum)
    newsum = parameter(:checksum).sum_file(path)
    return if [:absent, nil, content_checksum].include?(newsum)

    self.fail "File written to disk did not match checksum; discarding changes (#{content_checksum} vs #{newsum})"
  end

并且该方法包含以下方法,该方法位于校验和.rb

  def sum_file(path)
    type = digest_algorithm()
    method = type.to_s + "_file"
    "{#{type}}" + send(method, path).to_s
  end

校验和是如何计算的?

负责此任务的方法也位于 file.rb 中:

  def write(property)
    remove_existing(:file)

    mode = self.should(:mode) # might be nil
    mode_int = mode ? symbolic_mode_to_int(mode, Puppet::Util::DEFAULT_POSIX_MODE) : nil

    if write_temporary_file?
      Puppet::Util.replace_file(self[:path], mode_int) do |file|
        file.binmode
        content_checksum = write_content(file)
        file.flush
        fail_if_checksum_is_wrong(file.path, content_checksum) if validate_checksum?
        if self[:validate_cmd]
          output = Puppet::Util::Execution.execute(self[:validate_cmd].gsub(self[:validate_replacement], file.path), :failonfail => true, :combine => true)
          output.split(/\n/).each { |line|
            self.debug(line)
          }
        end
      end
    else
      umask = mode ? 000 : 022
      Puppet::Util.withumask(umask) { ::File.open(self[:path], 'wb', mode_int ) { |f| write_content(f) } }
    end

    # make sure all of the modes are actually correct
    property_fix
  end

检查校验和的代码片段content_checksum = write_content(file)

  # write the current content. Note that if there is no content property
  # simply opening the file with 'w' as done in write is enough to truncate
  # or write an empty length file.
  def write_content(file)
    (content = property(:content)) && content.write(file)
  end

以下代码片段:

content_checksum = write_content(file)
file.flush
fail_if_checksum_is_wrong(file.path, content_checksum) if validate_checksum?

表示将要写入的文件与实际写入的文件有差异。

讨论

后面的校验和(d41d8...)是空文件的校验和。

您如何检查这一点?


所以我认为事情是这样的:Puppet 发现需要进行更改,并进行了更改。但它在提交写入之前再次对文件进行校验和,因此它看不到更改已成功完成,因此它回滚。

如上所述的代码总是像解释的那样工作,并且根据我的经验,校验和检查是有效的。

结论

看起来 GlusterFS 存在问题,例如,使用 Puppet 部署的文件由于某种原因被 GlusterFS 更改了。

建议

我建议按如下方式调试该问题:

  1. 在 Puppet 上部署包含内容 X 的文件 1
  2. 使用 Puppet 在 GlusterFS 上部署此文件
  3. 手动检查位于 puppetserver 上的文件 1 的校验和
  4. 手动检查 GlusterFS 上文件 1 的校验和
  5. 在 GlusterFS 上运行 Puppet 并检查问题是否发生

相关内容