我正在使用Google的计算引擎开发一个应用程序,希望使用可抢占实例。
我需要让我的代码响应谷歌发送的ACPI G2软关机信号,该信号在他们要取走您的VM时,会提前30秒发出警告,如此处所述:https://cloud.google.com/compute/docs/instances/preemptible。
我该如何在正在运行的Python代码中检测到此事件并相应地进行反应(在我的情况下,我需要将VM正在处理的作业重新放回一个打开的作业队列,以便另一台机器可以接管)。
我正在使用Google的计算引擎开发一个应用程序,希望使用可抢占实例。
我需要让我的代码响应谷歌发送的ACPI G2软关机信号,该信号在他们要取走您的VM时,会提前30秒发出警告,如此处所述:https://cloud.google.com/compute/docs/instances/preemptible。
我该如何在正在运行的Python代码中检测到此事件并相应地进行反应(在我的情况下,我需要将VM正在处理的作业重新放回一个打开的作业队列,以便另一台机器可以接管)。
gcloud instances stop
命令(或它调用的相应API)生成;GCE不会发送带有电源按钮事件的“30秒终止警告”。它只发送正常、诚实的电源按钮软关事件,立即启动系统关闭。
与之相伴随的“警告”部分很简单:“这是您的电源按钮事件,请尽快关闭操作系统,因为您还有30秒的时间,否则我们将从墙上插座上拔掉插头。你已经被警告了!”
您有两个系统服务,可以以不同的方式组合,以获得所需的行为。
Type=oneshot
服务的ExecStop=
命令调用(请参见systemd.service(8))。该脚本相对较晚地运行于关机序列中。After
和Before
在关闭时被反转:如果X在Y之后启动,则在Y之前停止。After
依赖性确保在运行关闭脚本之前告诉序列中的服务终止。它不能确保服务已经终止。google-shutdown-scripts.service
时运行。sudo systemctl edit google-shutdown-scripts.service
。这将创建一个空的配置覆盖文件并打开您的$EDITOR
,在那里您可以放置您的After
和Before
依赖项,例如,[Unit]
# Make sure that shutdown script is run (synchronously) *before* mysvc1.service is stopped.
After=mysvc1.service
# Make sure that mysvc2.service is sent a command to stop before the shutdown script is run
Before=mysvc2.service
您可以指定任意数量的After或Before子句,每个子句可以为0个或多个。有关更多信息,请阅读systemd.unit(8)。
有一个实例元数据v1/instance/preempted
。如果实例被抢占,则其值为TRUE
,否则为FALSE
。
GCP有一份详尽的有关使用实例元数据的文档。简而言之,您可以使用两种方法来使用此(或任何其他)元数据值:
Query its value at any time, e. g. in the shutdown script. curl(1) equivalent:
curl -sfH 'Metadata-Flavor: Google' \
'http://169.254.169.254/computeMetadata/v1/instance/preempted'
Run an HTTP request that will complete (200) when the metadatum changes. The only change that can ever happen to it is from FALSE
to TRUE
, as preemption is irreversible.
curl -sfH 'Metadata-Flavor: Google' \
'http://169.254.169.254/computeMetadata/v1/instance/preempted?wait_for_change=true'
?wait_for_change=true
),由于挂起的请求可能随时以代码503返回,因此您的代码应该准备好处理并重新启动查询。curl不直接返回HTTP错误代码,但如果您使用了x=$(curl ....)
表达式,则可以利用返回空字符串的事实来脚本化;在这种情况下,您的抢占正检测标准为[[ $x == TRUE ]]
。
gcloud instance stop <vmname>
(它也会发送电源按钮事件!),则在关机脚本中查询 preempted
元数据。preempted
元数据,如果需要区分不同的关闭原因。有可能真正的决策点是你是否有一个想要重新加入“队列”的“活动工作”:如果你在处理一个活动工作时被要求停止服务,无论为什么原因,都应该返回它。但我不知道你实际的设计,无法发表评论。
我认为处理GCP预抢占的最简单方法是使用SIGTERM信号。
SIGTERM信号是一种通用信号,用于导致程序终止。与SIGKILL不同,这个信号可以被阻塞、处理和忽略。这是一种正常的方式,礼貌地要求程序终止。https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html
这取决于关机脚本,这些脚本是基于“最佳努力”的原则运行的。实际上,对于短脚本而言,关机脚本非常可靠。
在你的关机脚本中:
echo "Running shutdown script"
preempted = curl "http://metadata.google.internal/computeMetadata/v1/instance/preempted" -H "Metadata-Flavor: Google"
if $preempted; then
PID="$(pgrep -o "python")"
echo "Send SIGTERM to python"
kill "$PID"
sleep infinity
fi
echo "Shutting down"
import signal
import os
def sigterm_handler(sig, frame):
print("Got SIGTERM")
os.environ["IS_PREEMPTED"] = True
# Call cleanup functions
signal.signal(signal.SIGTERM, sigterm_handler)
if __name__ == "__main__":
print("Main")