A simple and flexible health check library for Go.

Health

A simple and flexible health check library for Go.

Build codecov Go Report Card GolangCI FOSSA Status

Documentation · Report Bug · Request Feature

Table of Contents

  1. Getting started
  2. Synchronous vs. Asynchronous Checks
  3. Caching
  4. Listening to Status Changes
  5. Middleware and Interceptors
  6. Compatibility With Other Libraries
  7. License

Getting Started

This library provides a http.Handler that acts as a health endpoint. It can be used by cloud infrastructure or other services to determine the availability of an application.

Rather than simply returning a response with HTTP status code 200, this library allows building health checks that test the availability of all required dependencies. The HTTP response contains the aggregated health result and details about the health status of each component.

Example

package main

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/alexliesenfeld/health"
	_ "github.com/mattn/go-sqlite3"
	"log"
	"net/http"
	"time"
)

// This is a very simple example that shows the basic features of this library.
func main() {
	db, _ := sql.Open("sqlite3", "simple.sqlite")
	defer db.Close()

	// Create a new Checker.
	checker := health.NewChecker(

		// Set the time-to-live for our cache to 1 second (default).
		health.WithCacheDuration(1*time.Second),

		// Configure a global timeout that will be applied to all checks.
		health.WithTimeout(10*time.Second),

		// A check configuration to see if our database connection is up.
		// The check function will be executed for each HTTP request.
		health.WithCheck(health.Check{
			Name:    "database",      // A unique check name.
			Timeout: 2 * time.Second, // A check specific timeout.
			Check:   db.PingContext,
		}),

		// The following check will be executed periodically every 15 seconds
		// started with an initial delay of 3 seconds. The check function will NOT
		// be executed for each HTTP request.
		health.WithPeriodicCheck(15*time.Second, 3*time.Second, health.Check{
			Name: "search",
			// The check function checks the health of a component. If an error is
			// returned, the component is considered unavailable (or "down").
			// The context contains a deadline according to the configured timeouts.
			Check: func(ctx context.Context) error {
				return fmt.Errorf("this makes the check fail")
			},
		}),

		// Set a status listener that will be invoked when the health status changes.
		// More powerful hooks are also available (see docs).
		health.WithStatusListener(func(ctx context.Context, state health.CheckerState) {
			log.Println(fmt.Sprintf("health status changed to %s", state.Status))
		}),
	)

	// Create a new health check http.Handler that returns the health status
	// serialized as a JSON string. You can pass pass further configuration
	// options to NewHandler to modify default configuration.
	http.Handle("/health", health.NewHandler(checker))
	log.Fatalln(http.ListenAndServe(":3000", nil))
}

Because our search component is down, the request curl -u username:password http://localhost:3000/health would yield a response with HTTP status code 503 (Service Unavailable), and the following JSON response body:

{
  "status": "down",
  "details": {
    "database": {
      "status": "up",
      "timestamp": "2021-07-01T08:05:14.603364Z"
    },
    "search": {
      "status": "down",
      "timestamp": "2021-07-01T08:05:08.522685Z",
      "error": "this makes the check fail"
    }
  }
}

This example shows all features of this library.

Synchronous vs. Asynchronous Checks

With "synchronous" health checks we mean that every HTTP request initiates a health check and waits until all check functions complete before returning an aggregated health result. You can configure synchronous checks using the WithCheck configuration option (see example above).

Synchronous checks can be sufficient for smaller applications but might not scale well for more involved applications. Sometimes an application needs to read a large amount of data, can experience latency issues or make an expensive calculation to tell something about its health. With synchronous health checks the application will not be able to respond quickly to health check requests (see here why this is necessary to avoid service disruptions in modern cloud infrastructure).

Rather than executing health check functions on every HTTP request, periodic (or "asynchronous") health checks execute the check function on a fixed schedule. With this approach, the health status is always read from a local cache that is regularly updated in the background. This allows responding to HTTP requests instantly without waiting for check functions to complete.

Periodic checks can be configured using the WithPeriodicCheck configuration option (see example above).

This library allows you to mix synchronous and asynchronous check functions, so you can start out simple and easily transition into a more scalable and robust health check implementation later.

Caching

Health check responses are cached to avoid sending too many request to the services that your program checks and to mitigate "denial of service" attacks. The TTL is set to 1 second by default. If you do not want to use caching altogether, you can disable it using the health.WithDisabledCache() configuration option.

Listening to Status Changes

It can be useful to react to health status changes. For example, you might want to log status changes, so you can easier correlate logs during root cause analysis or perform actions to mitigate the impact of an unhealthy component.

This library allows you to configure listener functions that will be called when either the overall/aggregated health status changes, or that of a specific component.

Example

health.WithPeriodicCheck(5*time.Second, 0, health.Check{
    Name:   "search",
    Check:  myCheckFunc,
    StatusListener: func (ctx context.Context, name string, state CheckState) ) {
	    log.Printf("status of component '%s' changed to %s", name, state.Status)
    },
}),

health.WithStatusListener(func (ctx context.Context, state CheckerState)) {
    log.Printf("overall system health status changed to %s", state.Status)
}),

Middleware and Interceptors

It can be useful to hook into the checking lifecycle to do some processing before and after a health check. For example, you might want to add some tracing information to the Context before the check function executes, do some logging or modify the check result before sending the HTTP response (e.g., removing details on failed authentication).

This library provides two mechanisms that allow you to hook into processing:

  • Middleware gives you the possibility to intercept all calls of Checker.Check, which corresponds to every incoming HTTP request. In contrary to the usually used middleware pattern, this middleware allows you to access check related information and post-process a check result before sending it in an HTTP response.

    Middleware Description
    BasicAuth Reduces exposed health details based on authentication success. Uses basic auth for authentication.
    CustomAuth Same as BasicAuth middleware, but allows using an arbitrary function for authentication.
    FullDetailsOnQueryParam Disables health details unless the request contains a previously configured query parameter name.
    BasicLogger Basic request-oriented logging functionality.
  • Interceptors make it possible to intercept all calls to a check function. This is useful if you have cross-functional code that needs to be reusable and should have access to check state information.

    Interceptor Description
    BasicLogger Basic component check function logging functionality

Compatibility With Other Libraries

Most existing Go health check libraries come with their own implementations of tool specific check functions (such as for Redis, memcached, Postgres, etc.). Rather than reinventing the wheel and come up with yet another library specific implementation of check functions, the goal was to design this library in a way that makes it easy to reuse existing solutions. The following (non-exhaustive) list of health check implementations should work with this library without or minimal adjustments:

  • github.com/hellofresh/health-go ( see full example here)
    import httpCheck "github.com/hellofresh/health-go/v4/checks/http"
    ...
    health.WithCheck(health.Check{
       Name:    "google",
       Check:   httpCheck.New(httpCheck.Config{
          URL: "https://www.google.com",
       }),
    }),
  • github.com/etherlabsio/healthcheck ( see full example here)
    import "github.com/etherlabsio/healthcheck/v2/checkers"
    ...
    health.WithCheck(health.Check{
      Name:    "database",
      Check:   checkers.DiskSpace("/var/log", 90).Check,
    })
  • github.com/heptiolabs/healthcheck ( see full example here)
    import "github.com/heptiolabs/healthcheck"
    ...
    health.WithCheck(health.Check{
        Name: "google",
        Check: func(ctx context.Context) error {
           deadline, _ := ctx.Deadline()
           timeout := time.Now().Sub(deadline)
           return healthcheck.HTTPGetCheck("https://www.google.com", timeout)()
        },
    }),
  • github.com/InVisionApp/go-health ( see full example here)
      import "github.com/InVisionApp/go-health/checkers"
      ...
      // Create check as usual (no error checking for brevity)
      googleURL, err := url.Parse("https://www.google.com")
      check, err := checkers.NewHTTP(&checkers.HTTPConfig{
          URL: googleURL,
      })
      ...
      // Add the check in the Checker configuration.
      health.WithCheck(health.Check{
          Name: "google",
          Check: func(_ context.Context) error {
              _, err := check.Status() 
              return err
          },
      })

License

health is free software: you can redistribute it and/or modify it under the terms of the MIT Public License.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MIT Public License for more details.

FOSSA Status

Owner
Comments
  • How to differentiate output based on auth status?

    How to differentiate output based on auth status?

    Commit 739c4a012ae74f2a6b7b2c9efebc7dcee916116a removed WithCustomAuth and WithBasicAuth.

    What's the recommended way to obtain the same behaviour now?

  • Add panic recovery mechanism

    Add panic recovery mechanism

    When a user created health check function panics, this health check library currently does not provide a means to recover from it automatically and continue. Instead, the application terminates. This behaviour is most likely is not wanted. Therefore, this library should provide a way to recover from panics right after the check function call (e.g.: here). The user should

    • be able to able to opt out of this by configuration (e.g. by disabling it with a configuration option) and
    • instead of panicking, the library should instead return an error.
  • Add SMTP server health check

    Add SMTP server health check

    It would be useful to have a library of commonly used health checks. In the scope of this issue, a health check should be implemented that checks if a mail server (SMTP) is alive and can be used to send emails, e.g. by using Golangs https://pkg.go.dev/net/smtp package.

    A few constraints to keep things clean and simple:

    • The check should be added to the checks module that you can find in the development branch.
    • As with all health check functions of this library, the check implementation should support accepting and forwarding context.Context that is being passed to each health check function.
    • If the health check requires external external modules or other dependencies, the check should be implemented as a separate Go module(similarly how it is done for the tests, that require dependencies to other packages as well). This is required to avoid that when people import the base library, they also need to import all dependencies of all provided checks.
    • The check should have a README.md in its root directory that describes how to use it and maybe what to look out for.
    • You should provide a test that checks at least the happy path. If a docker container is required to properly test the check, the docker container can be added to the docker-compose.yaml file in the checks base package.

    Should you have questions or require help, please join our discord chat room.

  • Fix startup `MaxContiguousFails` and add contiguous success Check options

    Fix startup `MaxContiguousFails` and add contiguous success Check options

    Currently, when Check.MaxContiguousFails is set to n the first n-1 checks will return StatusUp even if it and all prior checks were a failure. This is because evaluateCheckStatus only returns StatusDown if the number of consecutive failures is >= MaxContigiousFails. The result is that on startup the status may be reported as Up when it has never actually had a successful check. The fix for this is to also return StatusDown if there were no previous successes (check that LastSuccessAt is not nil).

    Additionally, this implements Check.MinContiguousSuccesses and Check.MinTimeInSuccess which function as the inverse of Check.MaxContiguousFails and Check.MaxTimeInError, respectively.

    • Checks must be successful for MinContiguousSuccesses consecutive checks before it's considered healthy.
    • Checks must be successful for MinTimeInSuccess duration before it's considered healthy.

    If either threshold is not satisfied then the most recent check.Status is returned; this is so that on startup StatusUnknown will continue to be returned until the threshold(s) are reached to avoid returning a misleading StatusDown.

  • feat: Added diskspace check for checking unix filesystem usage

    feat: Added diskspace check for checking unix filesystem usage

    Closes #50.

    Note: I did not see much in the project regarding Microsoft WIndows, so implemented this only for Linux as I am unfamiliar with developing Go for windows and not sure how you want cross platform building and packaging to work.

  • ADD redis check

    ADD redis check

    This will add a redis check. I used this library: https://github.com/go-redis/redis Yes I know same thing as in imap check but I think with over 15k stars this is a very common library for redis with go

    closes #44

  • add a go.sum

    add a go.sum

    Hi,

    We at crowdsec use github.com/alexliesenfeld/health as one of our dependencies. Thank you for that, this is useful. To ease the task of packaging the whole stuff, it would be very helpful to add a go.sum. This Pr is proposed to add this file. Any thought on this ?

    Regards, Manuel Sabban

  • Add IMAP server health check

    Add IMAP server health check

    It would be useful to have a library of commonly used health checks. In the scope of this issue, a health check should be implemented that checks if a mail server (IMAP) is alive and can be used to receive/read emails, e.g. by using Golangs https://pkg.go.dev/net/smtp package.

    A few constraints to keep things clean and simple:

    • The check should be added to the checks module that you can find in the development branch.
    • As with all health check functions of this library, the check implementation should support accepting and forwarding context.Context that is being passed to each health check function.
    • If the health check requires external external modules or other dependencies, the check should be implemented as a separate Go module(similarly how it is done for the tests, that require dependencies to other packages as well). This is required to avoid that when people import the base library, they also need to import all dependencies of all provided checks.
    • The check should have a README.md in its root directory that describes how to use it and maybe what to look out for.
    • You should provide a test that checks at least the happy path. If a docker container is required to properly test the check, the docker container can be added to the docker-compose.yaml file in the checks base package.

    Should you have questions or require help, please join our discord chat room.

  • Add Mongo DB check

    Add Mongo DB check

    It would be useful to have a library of commonly used health checks. In the scope of this issue, a health check should be implemented that checks if Mongo DB is alive (e.g. by pinging Mongo DB and see if the ping result is successful).

    A few constraints to keep things clean and simple:

    • The check should be added to the checks module that you can find in the development branch.
    • As with all health check functions of this library, the check implementation should support accepting and forwarding context.Context that is being passed to each health check function.
    • If the health check requires external external modules or other dependencies, the check should be implemented as a separate Go module(similarly how it is done for the tests, that require dependencies to other packages as well). This is required to avoid that when people import the base library, they also need to import all dependencies of all provided checks.
    • The check should have a README.md in its root directory that describes how to use it and maybe what to look out for.
    • You should provide a test that checks at least the happy path. If a docker container is required to properly test the check, the docker container can be added to the docker-compose.yaml file in the checks base package.

    Should you have questions or require help, please join our discord chat room.

  • In actual response content type is text/plain even for (default) http.JSONResultWriter

    In actual response content type is text/plain even for (default) http.JSONResultWriter

  • Bump github.com/stretchr/testify from 1.8.0 to 1.8.1

    Bump github.com/stretchr/testify from 1.8.0 to 1.8.1

    Bumps github.com/stretchr/testify from 1.8.0 to 1.8.1.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
  • Add GRPC check

    Add GRPC check

    It would be useful to have a library of commonly used health checks. In the scope of this issue, a health check should be implemented that checks if a GRPC endpoint is reachable, e.g. by using a HealthClient instance from google.golang.org/grpc/health/grpc_health_v1 to retrieve its status and see if it is "serving" (I.e. HealthCheckResponse_SERVING).

    A few constraints to keep things clean and simple:

    • The check should be added to the checks module that you can find in the development branch.
    • As with all health check functions of this library, the check implementation should support accepting and forwarding context.Context that is being passed to each health check function.
    • If the health check requires external external modules or other dependencies, the check should be implemented as a separate Go module(similarly how it is done for the tests, that require dependencies to other packages as well). This is required to avoid that when people import the base library, they also need to import all dependencies of all provided checks.
    • The check should have a README.md in its root directory that describes how to use it and maybe what to look out for.
    • You should provide a test that checks at least the happy path. If a docker container is required to properly test the check, the docker container can be added to the docker-compose.yaml file in the checks base package.

    Should you have questions or require help, please join our discord chat room.

  • Add Kafka producer check

    Add Kafka producer check

    It would be useful to have a library of commonly used health checks. In the scope of this issue, a health check should be implemented that checks if a Kafka procuder is alive. Strategies on how to do this can be discussed here prior to implementation.

    A few constraints to keep things clean and simple:

    • The check should be added to the checks module that you can find in the development branch.
    • As with all health check functions of this library, the check implementation should support accepting and forwarding context.Context that is being passed to each health check function.
    • If the health check requires external external modules or other dependencies, the check should be implemented as a separate Go module(similarly how it is done for the tests, that require dependencies to other packages as well). This is required to avoid that when people import the base library, they also need to import all dependencies of all provided checks.
    • The check should have a README.md in its root directory that describes how to use it and maybe what to look out for.
    • You should provide a test that checks at least the happy path. If a docker container is required to properly test the check, the docker container can be added to the docker-compose.yaml file in the checks base package.

    Should you have questions or require help, please join our discord chat room.

  • Add Kafka Consumer check

    Add Kafka Consumer check

    It would be useful to have a library of commonly used health checks. In the scope of this issue, a health check should be implemented that checks if a Kafka consumer is alive. Strategies on how to do this can be discussed here prior to implementation.

    A few constraints to keep things clean and simple:

    • The check should be added to the checks module that you can find in the development branch.
    • As with all health check functions of this library, the check implementation should support accepting and forwarding context.Context that is being passed to each health check function.
    • If the health check requires external external modules or other dependencies, the check should be implemented as a separate Go module(similarly how it is done for the tests, that require dependencies to other packages as well). This is required to avoid that when people import the base library, they also need to import all dependencies of all provided checks.
    • The check should have a README.md in its root directory that describes how to use it and maybe what to look out for.
    • You should provide a test that checks at least the happy path. If a docker container is required to properly test the check, the docker container can be added to the docker-compose.yaml file in the checks base package.

    Should you have questions or require help, please join our discord chat room.

  • Add Elasticsearch check

    Add Elasticsearch check

    It would be useful to have a library of commonly used health checks. In the scope of this issue, a health check should be implemented that checks if Elasticsearch is alive (e.g. calling an sql.PingContext first and e.g. execute a query, such as 'SELECT 1 ' afterwards to see if MySQL is ready for query execution.

    A few constraints to keep things clean and simple:

    • The check should be added to the checks module that you can find in the development branch.
    • As with all health check functions of this library, the check implementation should support accepting and forwarding context.Context that is being passed to each health check function.
    • If the health check requires external external modules or other dependencies, the check should be implemented as a separate Go module(similarly how it is done for the tests, that require dependencies to other packages as well). This is required to avoid that when people import the base library, they also need to import all dependencies of all provided checks.
    • The check should have a README.md in its root directory that describes how to use it and maybe what to look out for.
    • You should provide a test that checks at least the happy path. If a docker container is required to properly test the check, the docker container can be added to the docker-compose.yaml file in the checks base package.

    Should you have questions or require help, please join our discord chat room.

  • Add MySQL check

    Add MySQL check

    It would be useful to have a library of commonly used health checks. In the scope of this issue, a health check should be implemented that checks if MySQL is alive (e.g. calling an sql.PingContext first and e.g. execute a query, such as 'SELECT 1 ' afterwards to see if MySQL is ready for query execution.

    A few constraints to keep things clean and simple:

    • The check should be added to the checks module that you can find in the development branch.
    • As with all health check functions of this library, the check implementation should support accepting and forwarding context.Context that is being passed to each health check function.
    • If the health check requires external external modules or other dependencies, the check should be implemented as a separate Go module(similarly how it is done for the tests, that require dependencies to other packages as well). This is required to avoid that when people import the base library, they also need to import all dependencies of all provided checks.
    • The check should have a README.md in its root directory that describes how to use it and maybe what to look out for.
    • You should provide a test that checks at least the happy path. If a docker container is required to properly test the check, the docker container can be added to the docker-compose.yaml file in the checks base package.

    Should you have questions or require help, please join our discord chat room.

An extensible tool for creating your own in cluster health endpoints

healthyk8s an extensible tool for creating your own "in cluster" health endpoints Why? allows for creating a health endpoint for anything - external r

Oct 26, 2021
This is Reperio Health's GoLang backend assessment

reperio-backend-assessment This is Reperio Health's GoLang backend assessment. N

Dec 22, 2021
This library provides a metrics package which can be used to instrument code, expose application metrics, and profile runtime performance in a flexible manner.

This library provides a metrics package which can be used to instrument code, expose application metrics, and profile runtime performance in a flexible manner.

Jan 18, 2022
Nomad is an easy-to-use, flexible, and performant workload orchestrator that can deploy a mix of microservice, batch, containerized, and non-containerized applications
Nomad is an easy-to-use, flexible, and performant workload orchestrator that can deploy a mix of microservice, batch, containerized, and non-containerized applications

Nomad is an easy-to-use, flexible, and performant workload orchestrator that can deploy a mix of microservice, batch, containerized, and non-containerized applications. Nomad is easy to operate and scale and has native Consul and Vault integrations.

Jan 5, 2023
Flexible HTTP command line stress tester for websites and web services
Flexible HTTP command line stress tester for websites and web services

Pewpew Pewpew is a flexible command line HTTP stress tester. Unlike other stress testers, it can hit multiple targets with multiple configurations, si

Dec 27, 2022
Kubei is a flexible Kubernetes runtime scanner, scanning images of worker and Kubernetes nodes providing accurate vulnerabilities assessment, for more information checkout:
Kubei is a flexible Kubernetes runtime scanner, scanning images of worker and Kubernetes nodes providing accurate vulnerabilities assessment, for more information checkout:

Kubei is a vulnerabilities scanning and CIS Docker benchmark tool that allows users to get an accurate and immediate risk assessment of their kubernet

Dec 30, 2022
Connect, Subscribe and Publish over MQTT broker to check its status.

MQTT Blackbox Exporter Introduction In each probe it sends a message over MQTT broker and then wait for getting it over subscription. By measuring thi

Aug 27, 2022
A set of tests to check compliance with the Prometheus Remote Write specification

Prometheus Remote Write Compliance Test This repo contains a set of tests to check compliance with the Prometheus Remote Write specification. The test

Dec 4, 2022
Kubedd – Check migration issues of Kubernetes Objects while K8s upgrade

Kubedd – Check migration issues of Kubernetes Objects while K8s upgrade

Dec 19, 2022
scenario system to check the behavior of kube-scheduler

kube-scheduler-simulator-cli: Kubernetes Scheduler simulator on CLI and scenario system. Hello world. This repository is scenario system for kube-sche

Jan 25, 2022
GitHub Action: Compose multiple (conditional) checks into a single check based on file paths in a pull request
GitHub Action: Compose multiple (conditional) checks into a single check based on file paths in a pull request

GitHub Action: Composite Example Usage --- name: All Checks on: pull_request: branches: - main jobs: meta: runs-on: - ubuntu-20.

Dec 29, 2022
Just a playground with some interesting concepts like pipelines aka middleware, handleFuncs, request validations etc. Check it out.

Pipeline a.k.a middleware in Go Just a playground with some interesting concepts like pipelines aka middleware, handleFuncs, request validations etc.

Dec 9, 2021
A tool that scans archives to check for vulnerable log4j versions

log4j-sniffer log4j-sniffer crawls for all instances of log4j that are earlier t

Dec 14, 2022
A command-line debugging tool to check the latency of SSL handshake

ssl-handshake A command-line tool for testing SSL handshake latency, written in

Nov 13, 2022
Controller-check - Run checks against K8s controllers to verify if they meets certain conventions

controller-check Run checks against K8s controllers to verify if they meets cert

Jan 4, 2022
Sensu-go-postgres-metrics - The sensu-go-postgres-metrics is a sensu check that collects PostgreSQL metrics

sensu-go-postgres-metrics Table of Contents Overview Known issues Usage examples

Jan 12, 2022
A tool to check whether docker images exist in the remote registry.

Check Docker Image A tool to check whether docker images exist in the remote registry. Build project: go build -o check-image . Example usage: REGISTR

Jul 26, 2022
Gohalt 👮‍♀🛑: Fast; Simple; Powerful; Go Throttler library
Gohalt 👮‍♀🛑: Fast; Simple; Powerful; Go Throttler library

Gohalt ??‍♀ ?? : Fast; Simple; Powerful; Go Throttler library go get -u github.com/1pkg/gohalt Introduction Gohalt is simple and convenient yet powerf

Nov 27, 2022
A simple project (which is visitor counter) on kubernetesA simple project (which is visitor counter) on kubernetes

k8s playground This project aims to deploy a simple project (which is visitor counter) on kubernetes. Deploy steps kubectl apply -f secret.yaml kubect

Dec 16, 2022