nginx_k8s¶
Nginx sidecar container management abstractions.
The nginx_k8s charm library provides:
Nginx: A class to manage a nginx sidecar container.Includes regular nginx config file generation, tls configuration, and reload logic.
NginxPrometheusExporter: A class to manage a nginx-prometheus-exportersidecar container.
NginxConfig: A nginx config file generation wrapper.
- class Nginx(
- container: Container,
- update_ca_certificates_on_restart: bool = True,
- liveness_check_endpoint_getter: Callable[[bool], str] | None = None,
Bases:
objectHelper class to manage the nginx workload.
- NGINX_DIR = '/etc/nginx'¶
- NGINX_CONFIG = '/etc/nginx/nginx.conf'¶
- KEY_PATH = '/etc/nginx/certs/server.key'¶
- CERT_PATH = '/etc/nginx/certs/server.cert'¶
- CA_CERT_PATH = '/usr/local/share/ca-certificates/ca.crt'¶
- reconcile( )¶
Configure pebble layer and restart if necessary.
- class NginxConfig(
- server_name: str,
- upstream_configs: list[NginxUpstream],
- server_ports_to_locations: dict[int, list[NginxLocationConfig]],
- map_configs: Sequence[NginxMapConfig] | None = None,
- enable_health_check: bool = False,
- enable_status_page: bool = False,
- supported_tls_versions: list[str] | None = None,
- ssl_ciphers: list[str] | None = None,
- worker_processes: int = 5,
- worker_connections: int = 4096,
- proxy_read_timeout: int = 300,
- proxy_connect_timeout: str = '5s',
Bases:
objectNginxConfig.
- To generate an Nginx configuration for a charm, instantiate the NginxConfig class with the
required inputs:
server_name: The name of the server (e.g. charm fqdn), which is used to identify the server in Nginx configurations.
upstream_configs: List of NginxUpstream used to generate Nginx upstream directives.
server_ports_to_locations: Mapping from server ports to a list of NginxLocationConfig.
Any charm can instantiate NginxConfig to generate an Nginx configuration as follows:
- Example::
>>> # illustrative purposes only >>> import socket >>> from ops import CharmBase >>> from charmlibs.nginx_k8s import NginxConfig, NginxUpstream, NginxLocationConfig ... #[...] >>> class AnyCharm(CharmBase): >>> def __init__(self, *args): >>> super().__init__(*args) ... #[...] >>> self._container = self.unit.get_container("nginx") >>> self._nginx = NginxConfig( >>> server_name=self.hostname, >>> upstream_configs=self._nginx_upstreams(), >>> server_ports_to_locations=self._server_ports_to_locations(), >>> ) ... #[...] >>> self._reconcile() ... #[...] >>> @property >>> def hostname(self) -> str: >>> return socket.getfqdn() ... >>> @property >>> def _nginx_locations(self) -> List[NginxLocationConfig]: >>> return [ >>> NginxLocationConfig(path="/api/v1", backend="upstream1",modifier="~"), >>> NginxLocationConfig(path="/status", backend="upstream2",modifier="="), >>> ] ... >>> @property >>> def _upstream_addresses(self) -> Dict[str, Set[str]]: >>> # a mapping from an upstream "role" to the set of addresses >>> # that belong to this upstream >>> return { >>> "upstream1": {"address1", "address2"}, >>> "upstream2": {"address3", "address4"}, >>> } ... >>> @property >>> def _tls_available(self) -> bool: >>> # return if the Nginx config should have TLS enabled >>> pass ... >>> def _reconcile(self): >>> if self._container.can_connect(): >>> new_config: str = self._nginx.get_config(self._upstream_addresses, >>> self._tls_available) >>> should_restart: bool = self._has_config_changed(new_config) >>> self._container.push(self.config_path, new_config, make_dirs=True) >>> self._container.add_layer("nginx", self.layer, combine=True) >>> self._container.autostart() ... >>> if should_restart: >>> logger.info("new nginx config: restarting the service") >>> self.reload() ... >>> def _nginx_upstreams(self) -> List[NginxUpstream]: >>> # UPSTREAMS is a list of backend services that we want to route traffic to >>> for upstream in UPSTREAMS: >>> # UPSTREAMS_PORT is the port the backend services are running on >>> upstreams.append(NginxUpstream(upstream, UPSTREAMS_PORT, upstream)) >>> return upstreams ... >>> def _server_ports_to_locations(self) -> Dict[int, List[NginxLocationConfig]]: >>> # NGINX_PORT is the port an nginx server is running on >>> # Note that you can define multiple server directives, >>> # each running on a different port >>> return {NGINX_PORT: self._nginx_locations}
- otel_module_path = '/etc/nginx/modules/ngx_otel_module.so'¶
- get_config(
- upstreams_to_addresses: dict[str, set[str]],
- listen_tls: bool,
- root_path: str | None = None,
- tracing_config: NginxTracingConfig | None = None,
Render the Nginx configuration as a string.
- Parameters:
upstreams_to_addresses – A dictionary mapping each upstream name to a set of addresses associated with that upstream.
listen_tls – Whether Nginx should listen for incoming traffic over TLS.
root_path – If provided, it is used as a location where static files will be served.
tracing_config – Tracing configuration.
- class NginxLocationConfig(
- path: str,
- backend: str | None = None,
- backend_url: str = '',
- headers: dict[str,
- str] = <factory>,
- modifier: ~typing.Literal['',
- '=',
- '~',
- '~*',
- '^~'] = '',
- is_grpc: bool = False,
- upstream_tls: bool | None = None,
- rewrite: list[str] | None = None,
- extra_directives: dict[str,
- ~typing.Any] = <factory>,
Bases:
objectRepresents a location block in a Nginx configuration file.
For example:
NginxLocationConfig( '/', 'foo', backend_url="/api/v1" headers={'a': 'b'}, modifier=EXACT, is_grpc=True, use_tls=True, )
would result in the nginx config:
location = / { set $backend grpcs://foo/api/v1; grpc_pass $backend; proxy_connect_timeout 5s; proxy_set_header a b; }- backend: str | None = None¶
The name of the upstream service to route requests to (e.g. an upstream block).
- backend_url: str = ''¶
An optional URL path to append when forwarding to the upstream (e.g., ‘/v1’).
- upstream_tls: bool | None = None¶
//) If None, it will inherit the TLS setting from the server block that the location is part of.
- Type:
Whether to connect to the upstream over TLS (e.g., https
- Type:
// or grpcs
- class NginxMapConfig( )¶
Bases:
objectRepresents a map block of the Nginx config.
Example:
NginxMapConfig( source_variable="$http_upgrade", target_variable="$connection_upgrade", value_mappings={ "default": ["upgrade"], "": ["close"], }, )
will result in the following map block:
map $http_upgrade $connection_upgrade { default upgrade; '' close; }
- class NginxPrometheusExporter(
- container: Container,
- nginx_port: int = 8080,
- nginx_insecure: bool = False,
- nginx_tls_port: int = 443,
- nginx_prometheus_exporter_port: int = 9113,
- nginx_serves_tls: bool = False,
Bases:
objectHelper class to manage the nginx prometheus exporter workload.
- property are_certificates_on_disk: bool¶
Return True if the certificates files are on disk.
This is used to determine whether the exporter should serve metrics over HTTP or HTTPS by checking whether the certificates are present on THIS container’s FS. It has no effect on whether or not the exporter will attempt to scrape nginx over HTTP or HTTPS. That is determined by the nginx_serves_tls parameter passed to the reconciler.
- class NginxTracingConfig(
- endpoint: str,
- service_name: str,
- resource_attributes: dict[str,
- str] = <factory>,
Bases:
objectConfiguration for OTel tracing in Nginx.
- class NginxUpstream(
- name: str,
- port: int,
- address_lookup_key: str | None = None,
- ignore_address_lookup_key: bool = False,
Bases:
objectRepresents metadata needed to construct an Nginx upstream block.
- port: int¶
Port number that all backend servers in this upstream listen on.
Our coordinators assume that all servers under an upstream share the same port.
- address_lookup_key: str | None = None¶
Group that this upstream belongs to.
Used for mapping multiple upstreams to a single group of backends (loadbalancing between all). If you leave it None, this upstream will be routed to all available backends (loadbalancing between them).
- ignore_address_lookup_key: bool = False¶
If True, overrides address_lookup_key and routes to all available backend servers.
Use this when the upstream should be generic and include any available backend.
TODO: This class is now used outside of the context of pure coordinated-workers. This arg hence must be renamed to have a more generic name for eg. ignore_address_lookup. See: https://github.com/canonical/cos-coordinated-workers/issues/105