Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions .github/workflows/metrics.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright (c) 2023-2026, Nubificus LTD
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: metrics

on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 1' # every Monday at midnight

jobs:
measure:
runs-on: [self-hosted, linux, amd64]
strategy:
fail-fast: false
matrix:
include:
- unikernel: solo5-hvt
image: harbor.nbfc.io/nubificus/urunc/redis-hvt-rumprun:latest
- unikernel: unikraft-nginx
image: harbor.nbfc.io/nubificus/urunc/nginx-unikraft-fc-initrd:latest

steps:
- name: Checkout code
uses: actions/checkout@v4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if intentional, but the other workflows in this repo all pin actions to a full commit SHA with a version comment ,no floating @v4 references anywhere else in .github/workflows/. Might be worth pinning to match the project pattern:

uses: actions/checkout@ # v4
uses: actions/upload-artifact@ # v4

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed! Pinned both actions to full commit SHAs in commit 2a29d93 to match the project convention.


- name: Setup timestamping shim
run: |
sudo tee /usr/local/bin/containerd-shim-uruncts-v2 > /dev/null << 'EOT'
#!/bin/bash
URUNC_TIMESTAMPS=1 /usr/local/bin/containerd-shim-urunc-v2 $@
EOT
sudo chmod +x /usr/local/bin/containerd-shim-uruncts-v2

- name: Run metrics measurement
run: |
cd script/performance
sudo python3 measure_to_csv.py \
5 \
${{ matrix.image }} \
metrics-${{ matrix.unikernel }}.csv

- name: Upload CSV artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: metrics-${{ matrix.unikernel }}
path: script/performance/metrics-${{ matrix.unikernel }}.csv
if-no-files-found: warn
27 changes: 20 additions & 7 deletions script/performance/__modules__.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ def sorted(self) -> List[Timestamp]:
return temp


def parseSingleContainerTimestamps(filename: str, containerID: str) -> List[str]:
def parseSingleContainerTimestamps(
filename: str, containerID: str
) -> List[str]:
with open(filename, 'r') as f:
data = f.readlines()
return [line for line in data if containerID in line]
Expand All @@ -127,8 +129,19 @@ def emptyFile(filename: str) -> None:
open(filename, "w").close()


def spawnContainer() -> str:
command = "nerdctl run --name redis-test -d --snapshotter devmapper --runtime io.containerd.uruncts.v2 harbor.nbfc.io/nubificus/urunc/redis-hvt-rumprun:latest"
def spawnContainer(
image: str = (
"harbor.nbfc.io/nubificus/urunc/redis-hvt-rumprun:latest"
),
name: str = "redis-test",
snapshotter: str = "devmapper",
runtime: str = "io.containerd.uruncts.v2"
) -> str:
command = (
f"nerdctl run --name {name} -d"
f" --snapshotter {snapshotter}"
f" --runtime {runtime} {image}"
)
cmdParts = command.split(" ")
cmd = run(cmdParts,
stdout=PIPE,
Expand All @@ -138,15 +151,15 @@ def spawnContainer() -> str:
return containerID


def deleteContainer() -> bool:
command = "nerdctl rm --force redis-test"
def deleteContainer(name: str = "redis-test") -> bool:
command = f"nerdctl rm --force {name}"
cmdParts = command.split(" ")
cmd = run(cmdParts,
stdout=PIPE,
text=True)
response = cmd.stdout
containerID = response.splitlines()[-1]
return containerID == "redis-test"
result = response.splitlines()[-1]
return result == name


def myprint(msg: str):
Expand Down
99 changes: 99 additions & 0 deletions script/performance/measure_to_csv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Copyright (c) 2023-2026, Nubificus LTD
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __modules__ import (
emptyFile, spawnContainer, deleteContainer,
parseSingleContainerTimestamps, TimestampSeries, myprint
)
from sys import argv
from time import sleep
import csv

LOGFILE = "/tmp/urunc.zlog"
DELAY = 2

# Key phase definitions: (start_tsID, end_tsID, column_name)
PHASES = [
("TS00", "TS11", "create_ns"), # full create phase
("TS12", "TS19", "start_ns"), # full start phase

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed "TS19" is used as the end timestamp for start_ns, but checking internal/metrics/schema.go the last defined timestamp is TS18 (RX.execve_hypervisor) TimestampCount (value 19) is a sentinel and is never written to the log file. Because get_phase_duration returns "N/A" when the end key isn't found in the map, start_ns would always come out as "N/A" in every CSV row. worth double-checking if TS18 is the intended end of the start phase, the fix would be: ("TS12", "TS18", "start_ns")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed! Changed TS19 to TS18 in commit 2a29d93. Thanks for catching that!

("TS16", "TS17", "network_ns"), # network setup
("TS17", "TS18", "disk_ns"), # disk setup
]


def get_phase_duration(series, start_id, end_id):
ts_map = {ts.tsID: ts for ts in series.sorted}
if start_id in ts_map and end_id in ts_map:
return ts_map[end_id].timestamp - ts_map[start_id].timestamp
return "N/A"


def main():
if len(argv) != 4:
print("Error: Missing arguments!")
print("")
print("Usage:")
print(f"\t{argv[0]} <ITERATIONS> <IMAGE> <OUTPUT_CSV>")
print("")
print("Example:")
print(f"\t{argv[0]} 5 "
"harbor.nbfc.io/nubificus/urunc/"
"redis-hvt-rumprun:latest metrics.csv")
exit(1)

iterations = int(argv[1])
image = argv[2]
output_file = argv[3]
name = "urunc-metrics-test"

myprint(f"Collecting metrics for {iterations} iterations")
myprint(f"Image: {image}")
sleep(2)

emptyFile(LOGFILE)
container_ids = []

for i in range(iterations):
myprint(f"Running iteration {i+1} of {iterations}")
container_id = spawnContainer(image=image, name=name)
container_ids.append(container_id)
sleep(DELAY)
success = deleteContainer(name=name)
if not success:
print("Error removing container.")
exit(1)

myprint("Writing CSV...")

with open(output_file, "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["containerID", "create_ns", "start_ns",
"network_ns", "disk_ns"])
for container_id in container_ids:
data = parseSingleContainerTimestamps(
filename=LOGFILE, containerID=container_id)
if not data:
continue
series = TimestampSeries(data=data)
row = [container_id]
for start_id, end_id, _ in PHASES:
row.append(get_phase_duration(series, start_id, end_id))
writer.writerow(row)

myprint(f"Saved metrics to {output_file}")
emptyFile(LOGFILE)


if __name__ == "__main__":
main()