Epinio is a next generation PaaS built with existing CNCF projects (LinkerD, Cert-Manager, Traefik and others) which aims to provide a great developer experience. Epinio is opinionated and runs on Kubernetes to take you from code to URL in one step.
If you want to learn more about it go to https://epinio.io.
Kubernetes is becoming the de-facto standard for container orchestration. Developers may want to use Kubernetes for all the benefits it provides or may have to do so because that's what their Ops team has chosen. Whatever the case, using Kubernetes is not simple. It has a steep learning curve and doing it right is a full time job. Developers should spend their time working on their applications, not doing operations.
Epinio is adding the needed abstractions and intelligence to allow Developers to use Kubernetes as a PaaS (Platform as a Service).
In order to use Epinio in a quickly manner, we are going to use some tools to make our lives easier being:
https://kubernetes.io/docs/tasks/tools/install-kubectl-macos/
https://github.com/epinio/epinio/releases/tag/v1.5.1
P.S - I've got some problems when installing CLI via Homebrew (
brew install epinio) so I installed using the latest release binary on Github (see link above).
Assuming you already have Docker installed in your machine (I'm using a Macbook M1) let's install our local Kubernetes cluster. We are going to use K3D for our local Kubernetes cluster. Follow the instructions on K3D page to install it in your machine.
Open your Terminal and type:
k3d cluster create epinio -p '80:80@loadbalancer' -p '443:443@loadbalancer'to create the local Kubernetes cluster with K3D.
Epinio has to connect to pods inside the cluster. The default installation uses the internal Docker IP for this. If Docker is running in a VM, e.g. with Docker Desktop for Mac (which is my case), that IP will not be reachable.
We have to create the cluster in a way, that the internal port 80 (where the traefik ingress controller is listening on) is exposed on the host system.
That's why we used -p '80:80@loadbalancer' -p '443:443@loadbalancer' which means we are mapping the ingress port 80 to localhost:80 and we are also mapping port 443 to localhost:443.
The port-mapping construct 80:80@loadbalancer means:
-
“map port
80from the host to port 80 on the container which matches the nodefilterloadbalancer“ -
The loadbalancer nodefilter matches only the serverlb that’s deployed in front of a cluster’s server nodes
-
All ports exposed on the
serverlbwill be proxied to the same ports on all server nodes in the cluster
After the cluster is created, we need to install cert-manager there:
Create the cert-manager namespace:
kubectl create ns cert-managerAdd the Jetstack Helm repo:
helm repo add jetstack https://charts.jetstack.ioDo a Helm update:
helm repo updateInstall cert-manager:
helm install cert-manager --namespace cert-manager jetstack/cert-manager \
--set installCRDs=true \
--set extraArgs[0]=--enable-certificate-owner-ref=trueBefore deploying Epinio, we must have an ingress controller available in our cluster. NGINX and Traefik provide two popular options.
Ingresses let you expose your applications using URLs instead of raw hostnames and ports. Epinio requires your apps to be deployed with a URL, so it won’t work without an Ingress Controller. New deployments automatically generate a URL, but you can manually assign one instead.
Most popular single-node Kubernetes distributions such as K3s,minikube and Rancher Desktop come with one either built-in or as a bundled add-on.
Luckily, we are using K3D which deploys Traefik as the default Ingress Controller.
To see the Traefik deployment rollout status in our local K3D cluster, run:
kubectl rollout status deployment traefik -n kube-systemA message like this one below will appear in your Terminal:
deployment "traefik" successfully rolled outwhich means we are good to proceed installing Epinio as our next step.
Add the Epinio Helm repo:
helm repo add epinio https://epinio.github.io/helm-chartsInstall Epinio via Helm:
helm install epinio -n epinio --create-namespace --version 1.4.0 epinio/epinio \
--set global.domain=127.0.0.1.sslip.io \
--set api.users[0].role=admin \
--set api.users[0].username=admin \
--set api.users[0].password=password \
--set api.users[1].role=user \
--set api.users[1].username=epinio \
--set api.users[1].password=passwordNote that we are using the flag --set global.domain=127.0.0.1.sslip.io which means we are using a "Magic DNS". Our choice for "Magic DNS" was https://sslip.io/.
A working system domain is needed for Epinio to work. We are just trying out Epinio, so we can use a "magic" domain to get running faster. By "magic" we mean a domain that will always resolve to the IP address that is part of the domain itself.
After the installation is done you should see:
NAME: epinio
LAST DEPLOYED: Tue Nov 30 22:03:57 2022
NAMESPACE: epinio
STATUS: deployed
REVISION: 1
NOTES:
To interact with your Epinio installation download the latest epinio binary from https://github.com/epinio/epinio/releases/latest.
Login to the cluster with any of
`epinio login -u admin https://epinio.127.0.0.1.sslip.io`
`epinio login -u epinio https://epinio.127.0.0.1.sslip.io`
or go to the dashboard at: https://epinio.127.0.0.1.sslip.io
If you didn't specify a password the default one is `password`.
For more information about Epinio, feel free to checkout https://epinio.io/ and https://docs.epinio.io/.You can now go to your browser and visit the URL https://epinio.127.0.0.1.sslip.io
Type your credentials (admin/password) and Login into Epinio UI
Let's create an application via Epinio UI. Click on the Create button on top right corner.
Select Git URL as the Source Type, https://githuc.com/andrealmar/pokemon-api as URL and master as a Branch as you can see in the picture below:
In the next step, type a name for the application (pokemon-api in my case) and select the number of desired instances (1 in my case):
Next step is to select a Service to bind to our application but in this case it is not necessary so we can just click "Create" in the bottom right corner.
You will see Epinio doing 4 steps in the next screen (Create App, Fetch, Build and Deploy):
Also you can see the application logs using Epinio UI:
We can see that Epinio created a new pod for our application:
➜ kubectl get pods -n workspace
NAME READY STATUS RESTARTS AGE
rpokemon-api-61c8fa3c31e6c14570da1778b9e5bfa9b63fb669-56cdvvqlm 1/1 Running 0 5sIf you go back to applications in Epinio UI you will see our pokemon-api successfully deployed:
Go to https://pokemon-api.127.0.0.1.sslip.io and you will be greated with:
Welcome to POKEMON API from 10.42.0.48P.S - 10.42.0.48 is the IP of our Pod
Now go to the URL https://pokemon-api.127.0.0.1.sslip.io/pikachu and you will see:
quu..__
$$$b `---.__
"$$b `--. ___.---uuudP
`$$b `.__.------.__ __.---' $$$$" .
"$b -' `-.-' $$$" .'|
". d$" _.' |
`. / ..." .' |
`./ ..::-' _.' |
/ .:::-' .-' .'
: ::''\ _.' |
.' .-. .-. `. .' |
: /'$$| .@"$\ `. .' _.-'
.'|$u$$| |$$,$$| | < _.-'
| `:$$:' :$$$$$: `. `. .-'
: `"--' | `-. \
:##. == .###. `. `. `\
|##: :###: | > >
|#' `..'`..' `###' x: / /
\ xXX| / ./
\ xXXX'| / ./
/`-. `. / /
: `- ..........., | / .'
| ``:::::::' . |< `.
| ``` | x| \ `.:``.
| .' /' xXX| `:`M`M':.
| | ; /:' xXXX'| -'MMMMM:'
`. .' : /:' |-'MMMM.-'
| | .' /' .'MMM.-'
`'`' : ,' |MMM<
| `' |tbap\
\ :MM.-'
\ | .''
\. `. /
/ .:::::::.. : /
| .:::::::::::`. /
| .:::------------\ /
/ .'' >::' /
`',: : .'
`:.:' Tim Park
You can also try with the following endpoints:
- https://pokemon-api.127.0.0.1.sslip.io/bulbasaur
- https://pokemon-api.127.0.0.1.sslip.io/squirtle
- https://pokemon-api.127.0.0.1.sslip.io/charmander
P.S - In order to make the pokemon-api work with Epinio I had to add a
Procfile. You can check that in the repo.
We are going to install the Rails App inside the /example-rails folder.
Make sure the Ruby version in your machine matches the Ruby version of the application (2.7.5):
brew install rbenv
rbenv install 2.7.5
rbenv global 2.7.5Add the Bitnami Helm repo:
helm repo add bitnami https://charts.bitnami.com/bitnamiUpdate Helm repos:
helm repo updateInstalling the PostgreSQL database in our K3D cluster:
helm install postgres bitnami/postgresql --set volumePermissions.enabled=true,postgresqlUsername=myuser,postgresqlPassword=mypassword,postgresqlDatabase=productionOutput:
NAME: postgres
LAST DEPLOYED: Thu Dec 1 17:11:43 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: postgresql
CHART VERSION: 12.1.2
APP VERSION: 15.1.0
** Please be patient while the chart is being deployed **
PostgreSQL can be accessed via port 5432 on the following DNS names from within your cluster:
postgres-postgresql.default.svc.cluster.local - Read/Write connection
To get the password for "postgres" run:
export POSTGRES_PASSWORD=$(kubectl get secret --namespace default postgres-postgresql -o jsonpath="{.data.postgres-password}" | base64 -d)
To connect to your database run the following command:
kubectl run postgres-postgresql-client --rm --tty -i --restart='Never' --namespace default --image docker.io/bitnami/postgresql:15.1.0-debian-11-r0 --env="PGPASSWORD=$POSTGRES_PASSWORD" \
--command -- psql --host postgres-postgresql -U postgres -d postgres -p 5432
> NOTE: If you access the container using bash, make sure that you execute "/opt/bitnami/scripts/postgresql/entrypoint.sh /bin/bash" in order to avoid the error "psql: local user with ID 1001} does not exist"
To connect to your database from outside the cluster execute the following commands:
kubectl port-forward --namespace default svc/postgres-postgresql 5432:5432 &
PGPASSWORD="$POSTGRES_PASSWORD" psql --host 127.0.0.1 -U postgres -d postgres -p 5432
which means the installation was successful.
You can double check with:
➜ kubectl get pods
NAME READY STATUS RESTARTS AGE
postgres-postgresql-0 1/1 Running 0 50sNow let's create our Rails App in Epinio using the Epinio CLI:
epinio apps create rails-exampleOutput:
🚢 Create application
Namespace: workspace
Application: rails-example
⚠️ Epinio server version (v1.4.0) doesn't match the client version (v1.5.0)
⚠️ Update the client manually or run `epinio client-sync`
✔️ Ok
Check if the Rails app was created with:
epinio apps listOutput:
🚢 Listing applications
Namespace: workspace
⚠️ Epinio server version (v1.4.0) doesn't match the client version (v1.5.0)
⚠️ Update the client manually or run `epinio client-sync`
✔️ Epinio Applications:
| NAME | CREATED | STATUS | ROUTES | CONFIGURATIONS | STATUS DETAILS |
|---------------|-------------------------------|--------|--------------------------------|-------------------------------------|----------------|
| pokemon-api | 2022-12-01 15:51:55 -0300 -03 | 1/1 | pokemon-api.127.0.0.1.sslip.io | | |
| rails-example | 2022-12-01 17:13:49 -0300 -03 | n/a | n/a | | |
| wordpress | 2022-12-01 10:01:40 -0300 -03 | 1/1 | wordpress.127.0.0.1.sslip.io | xcca9aa0f19a036fb6389474a7be0-mysql | |
You can see that rails-example app was created.
Create a service with Epinio to allow the application to access the database:
epinio service create postgresql-dev mydb Bind the Service to the Application
epinio service bind mydb rails-exampleCreate an environment variable for RAILS_MASTER_KEY:
epinio apps env set rails-example RAILS_MASTER_KEY $(cat config/master.key)Now push the rails application with Epinio CLI:
epinio push -n rails-exampleP.S - I had to modify the Ruby version on
Gemfileofrails-exampleapp because Paketo Buildpacks are only compatible with the following Ruby versions:
Supported versions are: [2.7.6, 2.7.7, 3.0.4, 3.0.5, 3.1.2, 3.1.3] It will take a while to spin up the Rails application so grab a coffee, sit back and relax.
If everything works as expected, the application deployment should finish soon and the command should print the url of your app (something like https://rails-example.127.0.0.1.sslip.io).
Visit that in your browser and see the greeting message.
- had to move python to dependencies and change the version to match the version on my machine
- had to install postgres in order to have the pg_config utility required by some dependency when doing the
conda env createcommand - had to install Rust compiler
- Tried to install miniconda via Homebrew but I wasn't successful because of Architecture problems (ARM vs x86_64) so I had to install miniconda by downloading the
pkgfile compatible with the python version I was using (installing via homebrew didn't work) - had to type this command:
pip install psycopg2-binary --force-reinstall --no-cache-dirto force reinstall ofpsycopg2-binaryin my local machine and fix thelibpq5 not founderror.
- I had to include the postgresql package in the main Dockerfile
- I've installed
RUN pip install psycopg2 --force-reinstall --no-cache-diron orders service Dockerfile - on docker-compose I changed the postgres container version to 11 (it was getting the latest version)