# π¬π§ Discovering Fn project - Part I
I decided to understand the concept of "ServerLess" better. So in the coming months I will install and use different platforms to learn how to use them but also see how to integrate them into existing projects (my side bot project for example, which will be born one day here: http://www.jay.chat/) and how to manage the project workflows (management of the source code, CI, CD, ...).
My 1st choice was Fn project.
β οΈ These are my baby steps with Fn project, don't hesitate to comment, improve, contribute, etc. ... π
# What is Fn project?
The description on the homepage of Fn project (opens new window) is very clear; I'll copy it paste here:
The Fn project is an open-source container-native serverless platform that you can run anywhere -- any cloud or on-premise. Itβs easy to use, supports every programming language, and is extensible and performant.
My description would be:
- Fn project is a FaaS platform
- You can write functions in different languages (JavaScript, Java, Go, Python, ...)
# Install Fn project (server side)
I decided to test the Fn project on a virtual machine. For that, I used VirtualBox and Vagrant. So, on the host computer you need:
- Docker (functions are executed inside containers)
- VirtualBox
- Vagrant
# The Vagrant file
First create a directory (e.g.: /fnproject
) and in this directory, create a file name Vagrantfile
:
BOX_IMAGE = "bento/ubuntu-16.04"
VERSION = "1.0"
FN_SERVER_NAME = "fnproject"
# This is the IP you will see on your computer
# eg: add this line to your hosts file:
# 172.16.245.200 fnproject.test
# Then you can access to the server with this url http://fnproject.test:8080/
FN_IP = "172.16.245.200"
# This is the IP you will see from another computer on the same network
FN_PUBLIC_IP = "192.168.1.200"
Vagrant.configure("2") do |config|
config.vm.box = BOX_IMAGE
ENV['LC_ALL']="en_US.UTF-8"
config.vm.define "#{FN_SERVER_NAME}" do |node|
node.vm.hostname = "#{FN_SERVER_NAME}"
node.vm.network "public_network", ip: "#{FN_PUBLIC_IP}", bridge: "en0: Wi-Fi (AirPort)"
node.vm.network "private_network", ip: "#{FN_IP}"
node.vm.provider "virtualbox" do |vb|
vb.memory = 1024
vb.cpus = 2
vb.name = "#{FN_SERVER_NAME}"
end
node.vm.provision :shell, inline: <<-SHELL
apt-get update
# ----- Docker Installation -----
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
apt-key fingerprint 0EBFCD88
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
apt-get update
apt-get install -y docker-ce
# ----- **Fn project** Installation -----
curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
SHELL
node.vm.provision :shell, run: "always", inline: <<-SHELL
nohup sudo fn start > /dev/null 2>&1 &
SHELL
end
end
This Vagrantfile
create a VM with:
- Ubuntu as the OS
- Docker
- Fn project
Also, in the end, it starts the Fn Server.
So, run this command (and wait a moment):
vagrant up
You can see that I provide IP addresses for the virtual machine (you can change the values, it depends of your network):
FN_IP = "172.16.245.200"
FN_PUBLIC_IP = "192.168.1.200"
If you "stay" on your laptop (you are doing your test locally), edit your hosts
file and add this entry:
172.16.245.200 fnproject.test
If you plan to do your tests from another computer on the same local network, use the other IP address:
192.168.1.200 fnproject.test
Then, later, you can access to the server with this url: http://fnproject.test:8080 (opens new window) (8080
is the default port of the Fn server).
Now, your Fn server should be started, you can test it with curl http://fnproject.test:8080
, if all is ok, you'll get: {"goto":"https://github.com/fnproject/fn","hello":"world!"}
.
# Create your first function
You need to install the Fn CLI on your computer (the CLI is the same for the Fn project server and for the client):
# On OSX
brew install fn
# On Linux and OSX
curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
Now, we are going to create our first JavaScript function. Type this command:
fn init --runtime node hello
The command created a directory with the necessary files:
.
βββ hello
βββ func.js
βββ func.yaml
βββ package.json
βββ test.json
The source code is simple and easy to read:
var fdk=require('@fnproject/fdk');
fdk.handle(function(input){
var name = 'World';
if (input.name) {
name = input.name;
}
response = {'message': 'Hello ' + name}
return response
})
# Execute (locally) the function
π Requirements:
- β οΈ You need to have Docker installed on the client computer
- β οΈ You need to have a DockerHub account
To test the function locally, we use the run
command. You need to set before the environment variable FN_REGISTRY
with your DockerHub handle:
cd hello
# use the handle of your DockerHub account
export FN_REGISTRY=k33g
fn run
Wait a moment and you'll get the result:
Building image k33g/hello:0.0.1 .
{"message":"Hello World"}
You wan to "send" arguments to the function?
echo '{"name":"Bob"}' | fn run --content-type application/json
And you'll get a new result:
Building image hello:0.0.1 .
{"message":"Hello Bob"}
Now, it's time to deploy the function to the server.
# Deploy the function to the Fn server
To deploy the function, we use the deploy
command with the --app
argument to name the function. You need to set before the environment variables:
FN_REGISTRY
with your DockerHub handleFN_API_URL
with the url of your Fn server
export FN_REGISTRY=k33g
export FN_API_URL=http://fnproject.test:8080
fn deploy --app hello
Wait some seconds, you should get something like that:
Deploying hello to app: hello at path: /hello
Bumped to version 0.0.2
Building image k33g/hello:0.0.2 .
Pushing k33g/hello:0.0.2 to docker registry...The push refers to repository [docker.io/k33g/hello]
b44153e4998e: Pushed
6642efdc4930: Pushed
0.0.2: digest: sha256:518fad05ad6bd3a86fa990ff444e875327683e47b7605c241a2838630c45555b size: 1572
Updating route /hello using image k33g/hello:0.0.2...
Now, type this command:
fn list apps
You'll get:
NAME
hello
And try this command:
fn list routes hello
You'll get:
PATH IMAGE ENDPOINT
/hello k33g/hello:0.0.2 fnproject.test:8080/r/hello/hello
In the
ENDPOINT
the firsthello
is the function name and the second/hello
is the path
# Call the remote function
You can call your function with the Fn CLI or with an http request
# With fn call
fn call hello /hello
Or like that with arguments:
echo '{"name":"Bob"}' | fn call hello /hello --content-type application/json
Where the first
hello
is the function name and the second/hello
is the path
# With curl
curl -H "Content-Type: application/json" http://fnproject.test:8080/r/hello/hello
Or like that with arguments:
curl -H "Content-Type: application/json" -d '{"name":"Bob"}' http://fnproject.test:8080/r/hello/hello
It's pretty simple π no?
But one more thing. We are going to see how to use a private Docker registry with Fn project
# Use a private Docker registry
First we need to create a Docker registry in a new virtual machine, so somewhere in another directory, create a new Vagrantfile
:
BOX_IMAGE = "bento/ubuntu-16.04"
VERSION = "1.0"
REGISTRY_NAME = "private-registry"
REGISTRY_IP = "172.16.245.160"
REGISTRY_PUBLIC_IP = "192.168.1.160"
Vagrant.configure("2") do |config|
config.vm.box = BOX_IMAGE
ENV['LC_ALL']="en_US.UTF-8"
# === REGISTRY ===
config.vm.define "#{REGISTRY_NAME}" do |node|
node.vm.hostname = "#{REGISTRY_NAME}"
node.vm.network "public_network", ip: "#{REGISTRY_PUBLIC_IP}", bridge: "en0: Wi-Fi (AirPort)"
node.vm.network "private_network", ip: "#{REGISTRY_IP}"
node.vm.provider "virtualbox" do |vb|
vb.memory = 256
vb.cpus = 1
vb.name = "#{REGISTRY_NAME}"
end
node.vm.provision :shell, inline: <<-SHELL
apt-get update
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
apt-key fingerprint 0EBFCD88
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
apt-get update
apt-get install -y docker-ce
# run registry
docker run -d -p 5000:5000 --restart=always --name registry registry:2
SHELL
end
end
This time, I provide again IP addresses for the new virtual machine, so don't forget to add an entry like that in your hosts
file:
172.16.245.160 registry.test
or like that:
192.168.1.160 registry.test
And now, start the new virtual machine by typing this command in the directory of the Vagrant file:
vagrant up
# Update the Fn server VM
We need to update the Vagrantfile
of the first VM (the Fn server), because we need that the first VM could "see" the Docker registry on the network:
BOX_IMAGE = "bento/ubuntu-16.04"
VERSION = "1.0"
FN_SERVER_NAME = "fnproject"
# This is the IP you will see on your computer
# eg: add this line to your hosts file:
# 172.16.245.200 fnproject.test
# Then you can access to the server with this url http://fnproject.test:8080/
FN_IP = "172.16.245.200"
# This is the IP you will see from an other computer on the same network
FN_PUBLIC_IP = "192.168.1.200"
REGISTRY_IP = "172.16.245.160"
REGISTRY_DOMAIN = "registry.test"
REGISTRY = "registry.test:5000"
Vagrant.configure("2") do |config|
config.vm.box = BOX_IMAGE
ENV['LC_ALL']="en_US.UTF-8"
config.vm.define "#{FN_SERVER_NAME}" do |node|
node.vm.hostname = "#{FN_SERVER_NAME}"
node.vm.network "public_network", ip: "#{FN_PUBLIC_IP}", bridge: "en0: Wi-Fi (AirPort)"
node.vm.network "private_network", ip: "#{FN_IP}"
node.vm.provider "virtualbox" do |vb|
vb.memory = 1024
vb.cpus = 2
vb.name = "#{FN_SERVER_NAME}"
end
node.vm.provision :shell, inline: <<-SHELL
apt-get update
# ----- Docker Installation -----
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
apt-key fingerprint 0EBFCD88
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
apt-get update
apt-get install -y docker-ce
# ----- Fn Project Installation -----
curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
# Add entries to hosts file:
echo "" >> /etc/hosts
echo '#{REGISTRY_IP} #{REGISTRY_DOMAIN}' >> /etc/hosts
echo "" >> /etc/hosts
# Add unsecure registry
echo "" >> /etc/docker/daemon.json
echo '{' >> /etc/docker/daemon.json
echo ' "insecure-registries" : ["#{REGISTRY}"]' >> /etc/docker/daemon.json
echo '}' >> /etc/docker/daemon.json
echo "" >> /etc/docker/daemon.json
service docker restart
SHELL
node.vm.provision :shell, run: "always", inline: <<-SHELL
nohup sudo fn start > /dev/null 2>&1 &
SHELL
end
end
I added 3 parts in the Vagrantfile
:
The information about the Docker registry:
REGISTRY_IP = "172.16.245.160"
REGISTRY_DOMAIN = "registry.test"
REGISTRY = "registry.test:5000"
I updated the
hosts
file of the VM, now the Fn server can "ping" the Docker registry:
# Add entries to hosts file:
echo "" >> /etc/hosts
echo '#{REGISTRY_IP} #{REGISTRY_DOMAIN}' >> /etc/hosts
echo "" >> /etc/hosts
I updated the
daemon.json
to allow the use of an insecure registry (without https) and I restart the Docker client:
# Add unsecure registry
echo "" >> /etc/docker/daemon.json
echo '{' >> /etc/docker/daemon.json
echo ' "insecure-registries" : ["#{REGISTRY}"]' >> /etc/docker/daemon.json
echo '}' >> /etc/docker/daemon.json
echo "" >> /etc/docker/daemon.json
service docker restart
We are almost done, before testing, youe have to delete the Fn server virtual machine with this command vagrant destroy -f
(in the same place as the Vagrant file).
Now you can start again your Fn server:
vagrant up
# Create, Deploy, Call a new function
We are going to "export" the environment variables like that:
export FN_REGISTRY=registry.test:5000
export FN_API_URL=http://fnproject.test:8080
π this time, we set
FN_REGISTRY
with the domain name and the port of the Docker registry, instead of using the DockerHub handle.
We initialize and deploy a new function (in Go this time):
fn init --runtime go yo
cd yo
fn deploy --app yoapp
You can check the deployment with the list routes command
:
fn list routes yoapp
PATH IMAGE ENDPOINT
/yo registry.test:5000/yo:0.0.3 fnproject.test:8080/r/yoapp/yo
Where
yoapp
is the function name and/yo
is the path
And, now, you can call yoapp
like that:
echo '{"name":"Bob"}' | fn call yoapp /yo --content-type application/json
Or like that:
curl -H "Content-Type: application/json" -d '{"name":"Bob"}' http://fnproject.test:8080/r/yoapp/yo
That's all for today. The Fn Project community which is very friendly π, helped me a lot with my first steps. So, I can only encourage you to give it a try to the Fn project.
My next experiment with Fn project will be about how to use it with GitLab CI/CD π.
Last Articles
- π«π· Type Result en Kotlin | 2020-10-31 | Kotlin
- π«π· Type Result en Kotlin | 2020-10-31 | Kotlin
- π¬π§ Every GitLab Page deserves a real CI/CD | 2020-07-23 | GitLab CI
- π«π· Lit-Element, commencer doucement | 2020-07-20 | WebComponent
- π¬π§ Build quickly and host easily your Docker images with GitLab and GitLab CI | 2020-06-02 | GitLab CI
- π¬π§ Deploy quickly on Clever Cloud with GitLab CI | 2020-05-31 | GitLab CI
- π«π· Borg Collective, mes jouets pour apprendre Knative | 2020-05-30 | Knative
- π¬π§ Borg Collective, Toys to learn Knative | 2020-05-30 | Knative
- π«π· M5Stack, une petit device IOT bien sympathique, programmable en Python | 2020-05-09 | IOT
- π«π· Knative, l'outil qui rend Kubernetes sympathique | 2020-05-02 | kubernetes