Oracle Functions快速入门

简介

Oracle Function是Oracle Cloud Infrastructure对Fn的实现。
学习Oracle Function,如果要实践的话,Oracle官方提供的Oracle Functions QuickStart是最佳途径。这个QuickStart有网页版,文字版和PDF版。
Oracle Blogs也有一篇对应的文章:Oracle Functions: Serverless On Oracle Cloud - Developer’s Guide To Getting Started (Quickly!)

准备工作

简单来说,这个实验需要一个Oracle云账户和一台Fn客户机。Oracle云账户中我们需要用到Docker Registry和Fn服务,可以认为是服务端;Fn客户机上需要安装Fn Client,Docker Client,以及需要配置到服务端的认证。为了方便,我们使用Oracle云中的计算实例来充当Fn客户端。

开始是一些准备工作,如创建用户,组,Compartment,VCN等。此略。

在OCI中新建一实例,我用的Oracle Cloud Developer Image. 此Image中Docker已经安装,Fn CLI后续安装。

在root compartment,创建Policy FaaSPolicy。定义如下,注意其中指定的compartment不能是child compartment:

Allow service FaaS to read repos in tenancy
Allow service FaaS to use virtual-network-family in compartment cp_mrd1_internal

在这里插入图片描述
创建Auth token,并马上拷贝留存,后续将用于docker login。
创建一个Oracle Docker Registry,本例为fnrepo(名字只能小写), 类型为public。
登录到OCI Docker Registry,其中fra为region code,口令为刚创建的auth token,注意用户名之前必须加上tenancy:

$ docker login fra.ocir.io
Username (tenancy/oracleidentitycloudservice/username):
Password: <your_auth_token>
WARNING! Your password will be stored unencrypted in /home/opc/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

安装Fn CLI:

$ curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
fn version 0.5.92

        ______
       / ____/___
      / /_  / __ \
     / __/ / / / /
    /_/   /_/ /_/`

$ fn version
Client version is latest version: 0.5.92
Server version:  ?

配置认证

接下来需要配置Fn CLI到服务端的认证。
生成Fn私钥:

$ mkdir ~/.oci
# 必须输入pass phrase,假设为1234
$ openssl genrsa -out ~/.oci/fn_key.pem -aes128 2048
$ chmod go-rwx ~/.oci/fn_key.pem
# 生成公钥,使用之前输入相同的pass phrase
$ openssl rsa -pubout -in ~/.oci/fn_key.pem -out ~/.oci/fn_key_public.pem

将公钥输出并拷贝到粘贴板,然后在OCI Console中添加API Key。

$ cat ~/.oci/fn_key_public.pem | pbcopy

编辑配置文件:

$ cat ~/.oci/config
[FaaS]
user=ocid1.user.oc1..aaaa...qfka
fingerprint=...:ce:ae
key_file=/home/opc/.oci/fn_key.pem
tenancy=ocid1.tenancy.oc1..aaaa...6p4q
region=eu-frankfurt-1
pass_phrase=1234

配置Fn Context

# 创建context,provider表示认证方式
$ fn create context FnContext --provider oracle
Successfully created context: FnContext

# 使用context
$ fn use context FnContext
Now using context: FnContext

# 指定认证的profile,FaaS是之前编辑的Profile名
$ fn update context oracle.profile FaaS
Current context updated oracle.profile with FaaS

# 设置compartment ID
$ fn update context oracle.compartment-id ocid1.compartment.oc1..aaaa...w7la
Current context updated oracle.compartment-id with ocid1.compartment.oc1..aaaa...sw7la

# 设置api-url endpoint
$ fn update context api-url https://functions.eu-frankfurt-1.oci.oraclecloud.com
Current context updated api-url with https://functions.eu-frankfurt-1.oci.oraclecloud.com

# 设置Docker Repository,此处为fnrepo,之前已创建
$ fn update context registry fra.ocir.io/ocichina001/fnrepo
Current context updated registry with fra.ocir.io/ocichina001/faasrepo

创建应用,部署函数

在OCI中,Developer Service>Applications中新建一应用,指定应用名为helloworld-app,并指定3个子网即可。后续应用会与function关联。

创建function, 本例使用Oracle自带的示例函数:helloworld-func,详见这里:

$ fn init --runtime java helloworld-func
Creating function at: ./helloworld-func
Function boilerplate generated.
func.yaml created.

然后就是部署function了,此命令会在OCI Registry中创建docker image,会将function与之前建立的应用关联:

$ cd helloworld-func
$ fn deploy --app helloworld-app
$ fn deploy --app helloworld-app
Deploying helloworld-func to app: helloworld-app
Bumped to version 0.0.2
Building image fra.ocir.io/ocichina001/fnrepo/helloworld-func:0.0.2 ...............................................................
Parts:  [fra.ocir.io ocichina001 fnrepo helloworld-func:0.0.2]
Pushing fra.ocir.io/ocichina001/fnrepo/helloworld-func:0.0.2 to docker registry...The push refers to repository [fra.ocir.io/ocichina001/fnrepo/helloworld-func]
0f55b4ee3029: Pushed
5100e8eda33b: Pushed
8893b1d4ff35: Pushed
2ed87b0417a9: Pushed
543913bc2941: Pushed
6f2a80288c03: Pushed
0a0f0213cedd: Pushed
4b7c55f086f0: Pushed
b67d19e65ef6: Pushed
0.0.2: digest: sha256:d057b00a7b6db48f74cbe8a2297cd13218ec2219c465552a4b3e544afe459c5c size: 2206
Updating function helloworld-func using image fra.ocir.io/ocichina001/fnrepo/helloworld-func:0.0.2...
Successfully created function: helloworld-func with fra.ocir.io/ocichina001/fnrepo/helloworld-func:0.0.2

下图显示,应用与function已关联,另外你也可以看到Fn的命令:
在这里插入图片描述

$ docker images
REPOSITORY                                       TAG                 IMAGE ID            CREATED              SIZE
fra.ocir.io/ocichina001/fnrepo/helloworld-func   0.0.2               66d35725ed42        About a minute ago   222MB
<none>                                           <none>              b4c6dc0310e7        About a minute ago   440MB
fnproject/fn-java-fdk                            jre11-1.0.104       928cfe6738e1        2 months ago         222MB
fnproject/fn-java-fdk-build                      jdk11-1.0.104       72dcd80ee5ef        2 months ago         438MB

下图显示docker image已上传到Registry:在这里插入图片描述
手工调用function,第一次时间很长,因为要创建和启动容器(称为warm start时间),后面就快了。所以说的Fn适合间歇性(抽风)的任务:

$ time fn invoke helloworld-app helloworld-func
Hello, world!

real    0m27.694s
user    0m0.092s
sys     0m0.014s

$ time fn invoke helloworld-app helloworld-func
Hello, world!

real    0m0.847s
user    0m0.093s
sys     0m0.010s

$ time fn invoke helloworld-app helloworld-func
Hello, world!

real    0m0.468s
user    0m0.091s
sys     0m0.012s

不过这个容器是会过期的,第二天早上执行又慢了,然后就快了。这也可以理解,毕竟你不用时是不用付费的。

函数代码

来看一下代码,到这里就会发现,配置Fn是简单的,而最难的还是写代码:

$ vi ./main/java/com/example/fn/HelloFunction.java
$ cat ./main/java/com/example/fn/HelloFunction.java
package com.example.fn;

public class HelloFunction {

    public String handleRequest(String input) {
        String name = (input == null || input.isEmpty()) ? "world"  : input;

        return "Hello, " + name + "!";
    }

}

$ cat ./test/java/com/example/fn/HelloFunctionTest.java
package com.example.fn;

import com.fnproject.fn.testing.*;
import org.junit.*;

import static org.junit.Assert.*;

public class HelloFunctionTest {

    @Rule
    public final FnTestingRule testing = FnTestingRule.createDefault();

    @Test
    public void shouldReturnGreeting() {
        testing.givenEvent().enqueue();
        testing.thenRun(HelloFunction.class, "handleRequest");

        FnResult result = testing.getOnlyResult();
        assertEquals("Hello, world!", result.getBodyAsString());
    }

}

部署NodeJS函数

本例参照了此文的最后部分。
之前的helloworld-func是系统自带的Java函数。这里我们自定义一个NodeJS函数。由于一个应用可关联多个函数,因此我们将这个函数部署到与之前Java函数相同的应用中。

$ fn init --runtime node faas-demo-func-1
Creating function at: ./faas-demo-func-1
Function boilerplate generated.
func.yaml created.
$ cd faas-demo-func-1/
$ ls
func.js  func.yaml  package.json

$ cat func.js
const fdk=require('@fnproject/fdk');

fdk.handle(function(input){
  let name = 'World';
  if (input.name) {
    name = input.name;
  }
  return {'message': 'Hello ' + name}
})

$ fn deploy --app helloworld-app
Deploying faas-demo-func-1 to app: helloworld-app
Bumped to version 0.0.2
Building image fra.ocir.io/ocichina001/fnrepo/faas-demo-func-1:0.0.2 ................
Parts:  [fra.ocir.io ocichina001 fnrepo faas-demo-func-1:0.0.2]
Pushing fra.ocir.io/ocichina001/fnrepo/faas-demo-func-1:0.0.2 to docker registry...The push refers to repository [fra.ocir.io/ocichina001/fnrepo/faas-demo-func-1]
c7db0a1328e2: Pushed
7d9c19fb39de: Pushed
22030c3d6056: Pushed
a0d7b4199dce: Pushed
8aed3db29123: Pushed
9c85c117f8f6: Pushed
a464c54f93a9: Pushed
0.0.2: digest: sha256:4c5bdbc5038083fad9da67fd4009eca1d93fd0441222ba997d36b035a584e1b8 size: 1780
Updating function faas-demo-func-1 using image fra.ocir.io/ocichina001/fnrepo/faas-demo-func-1:0.0.2...
Successfully created function: faas-demo-func-1 with fra.ocir.io/ocichina001/fnrepo/faas-demo-func-1:0.0.2

$ time fn invoke helloworld-app faas-demo-func-1
{"message":"Hello World"}

real    0m6.296s
user    0m0.086s
sys     0m0.017s
$ time fn invoke helloworld-app faas-demo-func-1
{"message":"Hello World"}

real    0m0.333s
user    0m0.091s
sys     0m0.011s

再进一步,我们配置一个环境变量,然后让函数读取此环境变量:

$ fn update app helloworld-app --config defaultName=Person
app helloworld-app updated

# 修改func.js代码如下:
$ cat func.js
const fdk=require('@fnproject/fdk');

fdk.handle(function(input){
  let name = process.env.defaultName || 'World';
  if (input.name) {
    name = input.name;
  }
  return {'message': 'Hello ' + name}
})

# 重新部署
$ fn deploy --app helloworld-app
Deploying faas-demo-func-1 to app: helloworld-app
Bumped to version 0.0.3
Building image fra.ocir.io/ocichina001/fnrepo/faas-demo-func-1:0.0.3 ..
Parts:  [fra.ocir.io ocichina001 fnrepo faas-demo-func-1:0.0.3]
Pushing fra.ocir.io/ocichina001/fnrepo/faas-demo-func-1:0.0.3 to docker registry...The push refers to repository [fra.ocir.io/ocichina001/fnrepo/faas-demo-func-1]
2a3cc90cce76: Pushed
e61504cfda16: Pushed
22030c3d6056: Layer already exists
a0d7b4199dce: Layer already exists
8aed3db29123: Layer already exists
9c85c117f8f6: Layer already exists
a464c54f93a9: Layer already exists
0.0.3: digest: sha256:3c5448530391b93aa2b6bc1d0e901351fc26bcce610d7ffa8ee240dcdac3ef44 size: 1780
Updating function faas-demo-func-1 using image fra.ocir.io/ocichina001/fnrepo/faas-demo-func-1:0.0.3...

# 测试
$ time fn invoke helloworld-app faas-demo-func-1
{"message":"Hello Person"}

real    0m3.671s
user    0m0.093s
sys     0m0.012s
$ time fn invoke helloworld-app faas-demo-func-1
{"message":"Hello Person"}

real    0m0.639s
user    0m0.095s
sys     0m0.007s

配置的环境变量在应用中可以为所有函数共享:
在这里插入图片描述

辅助命令

补充一些Fn的命令,详见这里

$ fn list contexts
CURRENT NAME            PROVIDER        API URL                                                 REGISTRY
*       FnContext       oracle          https://functions.eu-frankfurt-1.oci.oraclecloud.com    fra.ocir.io/ocichina001/fnrepo
        default         default         http://localhost:8080

     
$ cat ~/.fn/contexts/FnContext.yaml
api-url: https://functions.eu-frankfurt-1.oci.oraclecloud.com
oracle.compartment-id: ocid1.compartment.oc1..aaaa...sw7la
oracle.profile: FaaS
provider: oracle
registry: fra.ocir.io/ocichina001/fnrepo

$ fn ls app
NAME            ID
helloworld-app  ocid1.fnapp.oc1.eu-frankfurt-1.aaaaaa...itbkviy36nxa

$ fn list fn helloworld-app
NAME            IMAGE                                                   ID
helloworld-func fra.ocir.io/ocichina001/fnrepo/helloworld-func:0.0.2    ocid1.fnfunc.oc1.eu-frankfurt-1.aaaaaaa...jy2rvya

补充一个Docker的命令,以便知道你登录的Docker Registry:

$ cat ~/.docker/config.json
{
        "auths": {
                "fra.ocir.io": {
                        "auth": "b2NpY...OVU="
                }
        },
        "HttpHeaders": {
                "User-Agent": "Docker-Client/18.09.8-ol (linux)"
        }
}$
发布了352 篇原创文章 · 获赞 42 · 访问量 55万+

猜你喜欢

转载自blog.csdn.net/stevensxiao/article/details/103953603
今日推荐