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: 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_prometheus_exporter_port: int = 9113,
Bases:
objectHelper class to manage the nginx prometheus exporter workload.
- reconcile()¶
Configure pebble layer and restart if necessary.
- 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