원클릭으로
infrastructure-charm
Workflow for charming infrastructure software (databases, caches, message brokers, proxies, monitoring)
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Workflow for charming infrastructure software (databases, caches, message brokers, proxies, monitoring)
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Known-good Juju bundle shapes (COS Lite, 12-Factor + COS, Identity Platform, Charmed Kubeflow) — canonical app lists and relation edges
Implementing Juju actions for operational tasks in charms. WHEN: add Juju actions to a charm, implement backup / rotate-credentials / restore actions, declare actions in charmcraft.yaml, write action handlers, test actions with Scenario, run actions with juju run.
Adding and validating charm configuration options. WHEN: add charm configuration options, declare config in charmcraft.yaml, validate config values in config-changed, apply config to a Pebble layer, apply config to a machine charm, write Scenario tests for config.
End-to-end workflow for building ops-framework charms for custom applications (K8s and machine). WHEN: build a custom Juju charm, write src/charm.py with Pebble, write a machine charm with systemd, decide K8s vs machine substrate, scaffold a non-paas-charm with charmcraft init, customise the ops framework charm lifecycle.
Workflow for charming infrastructure software (databases, caches, message brokers, proxies, monitoring). WHEN: charm infrastructure software, charm a database / cache / message broker / proxy / monitoring system, implement peer relations, leader election, primary/replica failover, backup and restore actions, clustering.
Expert guidance for developing, building, testing, and publishing Juju charms using charmcraft. WHEN: edit charmcraft.yaml, run charmcraft pack, run charmcraft init, manage charm libraries, publish a charm to Charmhub, set up multi-base builds, configure relations / config options, set up storage or containers.
| name | infrastructure-charm |
| description | Workflow for charming infrastructure software (databases, caches, message brokers, proxies, monitoring) |
This skill covers building Juju charms for infrastructure software — databases, caches, message brokers, proxies, and monitoring systems. These workloads have complex operational requirements (replication, failover, backup/restore, clustering) that go beyond simple application charming.
Use this path (Path C) when the workload is:
Do not use this path for:
twelve-factor skillcustom-charm skillInfrastructure charming always starts with research. Follow this sequence:
Use charmhub_search to check whether a charm already exists:
charmhub_search(query="postgresql", category="databases")
If results are found, use charmhub_info to inspect the best candidate:
charmhub_info(name="postgresql-k8s")
Review the relations, config options, storage, and containers. Check whether the charm covers the user's requirements.
| Situation | Action |
|---|---|
| Existing charm meets all requirements | Use it — deploy directly with juju_deploy |
| Existing charm is close but needs changes | Fork/extend — clone the source, modify, repackage |
| No suitable charm exists | Build new — scaffold from scratch using this skill |
| User explicitly wants a new charm | Build new — even if one exists |
Present the decision to the user with reasoning and wait for confirmation.
When building a new K8s infrastructure charm, search Docker Hub for the upstream image:
registry_search(query="<workload>") — find the official or most-used imageregistry_image_info(image="<name>") — check tags, architectures, and freshnesslatest) and confirm with the userThis image becomes the OCI resource in charmcraft.yaml.
Before building, deeply research the workload. This is even more important for infrastructure software than for applications because operational patterns are complex and getting them wrong is costly.
git_clone with depth: 1 into .source/web_fetch for the project's website and deployment docsWORKLOAD.md — summarise findings at the charm rootInclude these extra sections beyond the standard format:
## Architecture
Single-node / primary-replica / multi-primary / clustered. Replication protocol.
## Operational Patterns
- Failover: automatic or manual? How is the new primary elected?
- Backup: what tools? Snapshots, WAL archiving, logical dumps?
- Scaling: horizontal (add replicas) or vertical (resize)?
- Upgrades: rolling or stop-the-world?
## Client Protocol
Wire protocol, default port, TLS support.
## Observability
Built-in metrics endpoint? What format (Prometheus, StatsD)?
Use these patterns as starting points when implementing infrastructure charms.
The leader unit acts as primary; non-leader units are replicas.
def _on_leader_elected(self, event: ops.LeaderElectedEvent) -> None:
"""This unit became the primary."""
self._configure_as_primary()
# Share connection details with replicas via peer relation.
if self.model.get_relation("replication"):
self.model.get_relation("replication").data[self.app]["primary"] = self._unit_address()
self.unit.status = ops.ActiveStatus("primary")
def _on_peer_relation_changed(self, event: ops.RelationChangedEvent) -> None:
"""Replica reacts to primary election or config changes."""
primary = event.relation.data[self.app].get("primary")
if not primary:
self.unit.status = ops.WaitingStatus("Waiting for primary")
return
if not self.unit.is_leader():
self._configure_as_replica(primary)
self.unit.status = ops.ActiveStatus("replica")
Use the peer relation's app data bag for cluster-wide state. Only the leader can write to it.
def _on_peer_relation_joined(self, event: ops.RelationJoinedEvent) -> None:
"""A new unit joined the cluster."""
if self.unit.is_leader():
# Share cluster configuration with the new member.
event.relation.data[self.app]["cluster-key"] = self._generate_cluster_key()
def _unit_address(self) -> str:
"""Return this unit's address for peer communication."""
binding = self.model.get_binding("replication")
return str(binding.network.ingress_address)
Expose backup/restore as Juju actions:
# actions.yaml / charmcraft.yaml actions section
actions:
create-backup:
description: Create a backup of the database.
params:
destination:
type: string
description: S3 URI or local path for the backup.
restore-backup:
description: Restore from a backup.
params:
source:
type: string
description: S3 URI or local path of the backup to restore.
def _on_create_backup_action(self, event: ops.ActionEvent) -> None:
"""Run a backup and report the result."""
destination = event.params.get("destination", "/backups/latest")
self.unit.status = ops.MaintenanceStatus("Creating backup")
try:
self._run_backup(destination)
event.set_results({"status": "success", "path": destination})
except subprocess.CalledProcessError as exc:
event.fail(f"Backup failed: {exc}")
finally:
self.unit.status = ops.ActiveStatus()
Handle units joining and departing the cluster:
def _on_peer_relation_joined(self, event: ops.RelationJoinedEvent) -> None:
"""Add the new unit to the cluster membership."""
if self.unit.is_leader():
members = self._get_cluster_members(event.relation)
members.append(event.relation.data[event.unit].get("address", ""))
event.relation.data[self.app]["members"] = json.dumps(members)
def _on_peer_relation_departed(self, event: ops.RelationDepartedEvent) -> None:
"""Remove the departing unit from the cluster."""
if self.unit.is_leader():
members = self._get_cluster_members(event.relation)
departing = event.relation.data[event.departing_unit].get("address", "")
members = [m for m in members if m != departing]
event.relation.data[self.app]["members"] = json.dumps(members)
Detect primary failure and promote a replica:
def _on_leader_elected(self, event: ops.LeaderElectedEvent) -> None:
"""Handle leader election — may indicate failover."""
if self._was_replica():
self._promote_to_primary()
self.unit.status = ops.ActiveStatus("primary (promoted)")
else:
self._configure_as_primary()
self.unit.status = ops.ActiveStatus("primary")
When an existing Charmhub charm is close but needs modifications:
web_fetch on the charm's Charmhub page to find the source repositorygit_clone with depth: 1 into the charm directorysrc/charm.py, charmcraft.yaml, testsquick_pack (falls back to charmcraft_pack if the forked charm uses a non-uv plugin or has override-build steps) → juju_deployFor new infrastructure charms, scaffold with charmcraft_init and customise heavily.
Infrastructure charms typically need:
# Peer relation for cluster coordination.
peers:
replication:
interface: <workload>_peers
# Client relation (the interface others use to connect).
provides:
database:
interface: <protocol>_client
# Common infrastructure relations.
requires:
tracing:
interface: tracing
limit: 1
certificates:
interface: tls-certificates
limit: 1
s3-credentials:
interface: s3
limit: 1
# For service-to-service auth (e.g. talking to a downstream API
# behind Hydra), load the `identity-platform` skill and add an
# `oauth-cli` requirer here.
# Persistent storage for data.
storage:
data:
type: filesystem
minimum-size: 10G
location: /var/lib/<workload>
# Actions for operational tasks.
actions:
create-backup:
description: Create a backup.
restore-backup:
description: Restore from a backup.
get-password:
description: Retrieve the admin password.
| Relation | Interface | Purpose |
|---|---|---|
| database | postgresql_client / mysql_client / mongodb_client | Client access |
| replication | <workload>_peers | Peer coordination |
| certificates | tls-certificates | TLS encryption |
| s3-credentials | s3 | Backup storage |
| tracing | tracing | Distributed tracing (ops-tracing) |
| metrics-endpoint | prometheus_scrape | Prometheus metrics |
| grafana-dashboard | grafana_dashboard | Dashboard provisioning |
| logging | loki_push_api | Log forwarding |
Infrastructure charms need tests for operational scenarios that simpler charms do not:
Writing to app data from a non-leader unit — only the leader can write to relation.data[self.app]. Non-leader units write to relation.data[self.unit].
Not handling departing units — when a unit departs, clean up cluster membership and rebalance if needed.
Blocking hooks with long operations — database initialisation, backup, and restore can take minutes. Use actions for long-running operations and set MaintenanceStatus during the work.
Ignoring storage events — storage-attached fires before install on first deploy. Make sure your install handler does not assume storage is already configured.
Hardcoding ports — make the listening port a config option so operators can customise it.
Missing TLS support — infrastructure charms should support TLS via the tls-certificates relation. Do not skip this.
| Symptom | Likely Cause | Fix |
|---|---|---|
Replicas stuck in waiting | Primary address not shared via peer relation | Check leader-elected handler writes to app data |
| Data loss after leader change | Replication not configured | Verify replicas are streaming from primary |
| Backup action fails | Missing S3 credentials or wrong path | Check s3-credentials relation and action params |
| Cluster split-brain | No fencing mechanism | Implement STONITH or use Juju leader as tiebreaker |
| Slow failover | Manual promotion required | Automate promotion in leader-elected handler |
| Client cannot connect after TLS | Client charm using wrong CA | Verify TLS relation data includes the CA certificate |