You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by ch...@apache.org on 2019/10/07 12:34:52 UTC
[openwhisk] branch master updated: Standalone OpenWhisk
KubernetesContainerFactory support (#4671)
This is an automated email from the ASF dual-hosted git repository.
chetanm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openwhisk.git
The following commit(s) were added to refs/heads/master by this push:
new 75d8507 Standalone OpenWhisk KubernetesContainerFactory support (#4671)
75d8507 is described below
commit 75d8507cc4264da3f57efe7433a45278ecbc578e
Author: Chetan Mehrotra <ch...@apache.org>
AuthorDate: Mon Oct 7 18:04:35 2019 +0530
Standalone OpenWhisk KubernetesContainerFactory support (#4671)
* Add support for port forwarding to remote pod
* Add support to use KCF in Standalone mode
---
core/invoker/src/main/resources/application.conf | 4 ++
.../kubernetes/KubernetesClient.scala | 14 +++-
.../kubernetes/KubernetesContainer.scala | 11 +--
core/standalone/README.md | 84 +++++++++++++++++++++-
.../src/main/resources/standalone-kcf.conf | 36 ++++++++++
.../openwhisk/standalone/StandaloneOpenWhisk.scala | 12 +++-
6 files changed, 150 insertions(+), 11 deletions(-)
diff --git a/core/invoker/src/main/resources/application.conf b/core/invoker/src/main/resources/application.conf
index ce0bcef..9afba4d 100644
--- a/core/invoker/src/main/resources/application.conf
+++ b/core/invoker/src/main/resources/application.conf
@@ -77,6 +77,10 @@ whisk {
key: "openwhisk-role"
value: "invoker"
}
+
+ # Enables forwarding to remote port via a local random port. This mode is mostly useful
+ # for development via Standalone mode
+ port-forwarding-enabled = false
}
# Timeouts for runc commands. Set to "Inf" to disable timeout.
diff --git a/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/kubernetes/KubernetesClient.scala b/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/kubernetes/KubernetesClient.scala
index 777f069..31c7678 100644
--- a/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/kubernetes/KubernetesClient.scala
+++ b/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/kubernetes/KubernetesClient.scala
@@ -74,7 +74,8 @@ case class KubernetesInvokerNodeAffinity(enabled: Boolean, key: String, value: S
*/
case class KubernetesClientConfig(timeouts: KubernetesClientTimeoutConfig,
invokerAgent: KubernetesInvokerAgentConfig,
- userPodNodeAffinity: KubernetesInvokerNodeAffinity)
+ userPodNodeAffinity: KubernetesInvokerNodeAffinity,
+ portForwardingEnabled: Boolean)
/**
* Serves as an interface to the Kubernetes API by proxying its REST API and/or invoking the kubectl CLI.
@@ -219,13 +220,20 @@ class KubernetesClient(
protected def toContainer(pod: Pod): KubernetesContainer = {
val id = ContainerId(pod.getMetadata.getName)
- val addr = ContainerAddress(pod.getStatus.getPodIP)
+
+ val portFwd = if (config.portForwardingEnabled) {
+ Some(kubeRestClient.pods().withName(pod.getMetadata.getName).portForward(8080))
+ } else None
+
+ val addr = portFwd
+ .map(fwd => ContainerAddress("localhost", fwd.getLocalPort))
+ .getOrElse(ContainerAddress(pod.getStatus.getPodIP))
val workerIP = pod.getStatus.getHostIP
// Extract the native (docker or containerd) containerId for the container
// By convention, kubernetes adds a docker:// prefix when using docker as the low-level container engine
val nativeContainerId = pod.getStatus.getContainerStatuses.get(0).getContainerID.stripPrefix("docker://")
implicit val kubernetes = this
- new KubernetesContainer(id, addr, workerIP, nativeContainerId)
+ new KubernetesContainer(id, addr, workerIP, nativeContainerId, portFwd)
}
}
diff --git a/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/kubernetes/KubernetesContainer.scala b/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/kubernetes/KubernetesContainer.scala
index d37139b..be73bd4 100644
--- a/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/kubernetes/KubernetesContainer.scala
+++ b/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/kubernetes/KubernetesContainer.scala
@@ -25,6 +25,7 @@ import akka.stream.StreamLimitReachedException
import akka.stream.scaladsl.Framing.FramingException
import akka.stream.scaladsl.Source
import akka.util.ByteString
+import io.fabric8.kubernetes.client.PortForward
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
@@ -100,10 +101,11 @@ object KubernetesContainer {
class KubernetesContainer(protected[core] val id: ContainerId,
protected[core] val addr: ContainerAddress,
protected[core] val workerIP: String,
- protected[core] val nativeContainerId: String)(implicit kubernetes: KubernetesApi,
- override protected val as: ActorSystem,
- protected val ec: ExecutionContext,
- protected val logging: Logging)
+ protected[core] val nativeContainerId: String,
+ portForward: Option[PortForward] = None)(implicit kubernetes: KubernetesApi,
+ override protected val as: ActorSystem,
+ protected val ec: ExecutionContext,
+ protected val logging: Logging)
extends Container {
/** The last read timestamp in the log file */
@@ -120,6 +122,7 @@ class KubernetesContainer(protected[core] val id: ContainerId,
override def destroy()(implicit transid: TransactionId): Future[Unit] = {
super.destroy()
+ portForward.foreach(_.close())
kubernetes.rm(this)
}
diff --git a/core/standalone/README.md b/core/standalone/README.md
index d74a554..0dbdf09 100644
--- a/core/standalone/README.md
+++ b/core/standalone/README.md
@@ -75,7 +75,7 @@ $ java -jar openwhisk-standalone.jar -h
\ \ / \/ \___/| .__/ \___|_| |_|__/\__|_| |_|_|___/_|\_\
\___\/ tm |_|
- -m, --manifest <arg> Manifest JSON defining the supported runtimes
+ -m, --manifest <arg> Manifest json defining the supported runtimes
-c, --config-file <arg> application.conf which overrides the default
standalone.conf
--api-gw Enable API Gateway support
@@ -91,6 +91,8 @@ $ java -jar openwhisk-standalone.jar -h
--api-gw-port <arg> API Gateway Port
--clean Clean any existing state like database
-d, --data-dir <arg> Directory used for storage
+ --dev-kcf Enables KubernetesContainerFactory for local
+ development
--dev-mode Developer mode speeds up the startup by
disabling preflight checks and avoiding
explicit pulls.
@@ -271,6 +273,84 @@ Launched service details
[ 3000 ] http://localhost:3000 (whisk-grafana)
```
+#### Using KubernetesContainerFactory
+
+Standalone OpenWhisk can be configured to use KubernetesContainerFactory (KCF) via `--dev-kcf` option. This mode can be used to
+simplify developing KubernetesContainerFactory.
+
+Below mentioned steps are based on [Kind][9] tool for running local Kubernetes clusters using Docker container "nodes".
+However this mode should work against any Kubernetes cluster if the the `KUBECONFIG` is properly set.
+
+##### 1. Install and configure Kind
+
+We would use Kind to setup a local k8s. Follow the steps [here][10] to create a simple cluster.
+
+```bash
+$ kind create cluster --wait 5m
+
+# Export the kind config for kubectl usage
+$ export KUBECONFIG="$(kind get kubeconfig-path)"
+
+# Configure the default namespace
+$ kubectl config set-context --current --namespace=default
+
+# See the config path
+$ kind get kubeconfig-path
+/Users/example/.kube/kind-config-kind
+```
+
+##### 2. Launch Standalone
+
+```bash
+# Launch it with `kubeconfig` system property set to kind config
+$ java -Dkubeconfig="$(kind get kubeconfig-path)" -jar bin/openwhisk-standalone.jar --dev-kcf
+```
+
+Once started and required `.wskprops` configured to use the standalone server create a `hello.js` function
+
+```js
+function main(params) {
+ greeting = 'hello, world'
+ var hello = {payload: greeting}
+ var result = {...hello, ...process.env}
+ console.log(greeting);
+ return result
+}
+```
+
+```bash
+$ wsk action create hello hello.js
+$ wsk action invoke hello -br
+```
+
+This shows an output like below indicating that KubernetesContainerFactory based invocation is working properly.
+
+```
+{
+ "HOME": "/root",
+ "HOSTNAME": "wsk0-2-prewarm-nodejs10",
+ "KUBERNETES_PORT": "tcp://10.96.0.1:443",
+ "KUBERNETES_PORT_443_TCP": "tcp://10.96.0.1:443",
+ "KUBERNETES_PORT_443_TCP_ADDR": "10.96.0.1",
+ "KUBERNETES_PORT_443_TCP_PORT": "443",
+ "KUBERNETES_PORT_443_TCP_PROTO": "tcp",
+ "KUBERNETES_SERVICE_HOST": "10.96.0.1",
+ "KUBERNETES_SERVICE_PORT": "443",
+ "KUBERNETES_SERVICE_PORT_HTTPS": "443",
+ "NODE_VERSION": "10.15.3",
+ "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+ "PWD": "/nodejsAction",
+ "YARN_VERSION": "1.13.0",
+ "__OW_ACTION_NAME": "/guest/hello",
+ "__OW_ACTIVATION_ID": "71e48d2d62e142eca48d2d62e192ec2d",
+ "__OW_API_HOST": "http://host.docker.internal:3233",
+ "__OW_DEADLINE": "1570223213407",
+ "__OW_NAMESPACE": "guest",
+ "__OW_TRANSACTION_ID": "iSOoNklk6V7l7eh8KJnvugidKEmaNJmv",
+ "payload": "hello, world"
+}
+```
+
[1]: https://github.com/apache/incubator-openwhisk/blob/master/docs/cli.md
[2]: https://github.com/apache/incubator-openwhisk/blob/master/docs/samples.md
[3]: https://github.com/apache/incubator-openwhisk-apigateway
@@ -279,3 +359,5 @@ Launched service details
[6]: https://github.com/obsidiandynamics/kafdrop
[7]: https://github.com/apache/openwhisk/blob/master/docs/metrics.md#user-specific-metrics
[8]: https://github.com/apache/openwhisk/blob/master/core/monitoring/user-events/README.md
+[9]: https://kind.sigs.k8s.io/
+[10]: https://kind.sigs.k8s.io/docs/user/quick-start/
diff --git a/core/standalone/src/main/resources/standalone-kcf.conf b/core/standalone/src/main/resources/standalone-kcf.conf
new file mode 100644
index 0000000..0608be0
--- /dev/null
+++ b/core/standalone/src/main/resources/standalone-kcf.conf
@@ -0,0 +1,36 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+include classpath("standalone.conf")
+
+whisk {
+ spi {
+ ContainerFactoryProvider = "org.apache.openwhisk.core.containerpool.kubernetes.KubernetesContainerFactoryProvider"
+ LogStoreProvider = "org.apache.openwhisk.core.containerpool.logging.DockerToActivationLogStoreProvider"
+ }
+ kubernetes {
+ timeouts {
+ # Use higher timeout for run as in local dev the required Docker images may not be pre pulled
+ run = 10 minute
+ logs = 1 minute
+ }
+ user-pod-node-affinity {
+ enabled = false
+ }
+ port-forwarding-enabled = true
+ }
+}
diff --git a/core/standalone/src/main/scala/org/apache/openwhisk/standalone/StandaloneOpenWhisk.scala b/core/standalone/src/main/scala/org/apache/openwhisk/standalone/StandaloneOpenWhisk.scala
index e677faa..92ae3fc 100644
--- a/core/standalone/src/main/scala/org/apache/openwhisk/standalone/StandaloneOpenWhisk.scala
+++ b/core/standalone/src/main/scala/org/apache/openwhisk/standalone/StandaloneOpenWhisk.scala
@@ -51,7 +51,7 @@ class Conf(arguments: Seq[String]) extends ScallopConf(Conf.expandAllMode(argume
this.printedName = "openwhisk"
val configFile =
opt[File](descr = "application.conf which overrides the default standalone.conf", validate = _.canRead)
- val manifest = opt[File](descr = "Manifest json defining the supported runtimes", validate = _.canRead)
+ val manifest = opt[File](descr = "Manifest JSON defining the supported runtimes", validate = _.canRead)
val port = opt[Int](descr = "Server port", default = Some(3233))
val verbose = tally()
@@ -62,7 +62,7 @@ class Conf(arguments: Seq[String]) extends ScallopConf(Conf.expandAllMode(argume
val devMode = opt[Boolean](
descr = "Developer mode speeds up the startup by disabling preflight checks and avoiding explicit pulls.",
noshort = true)
- val apiGwPort = opt[Int](descr = "Api Gateway Port", default = Some(3234), noshort = true)
+ val apiGwPort = opt[Int](descr = "API Gateway Port", default = Some(3234), noshort = true)
val dataDir = opt[File](descr = "Directory used for storage", default = Some(StandaloneOpenWhisk.defaultWorkDir))
val kafka = opt[Boolean](descr = "Enable embedded Kafka support", noshort = true)
@@ -99,6 +99,8 @@ class Conf(arguments: Seq[String]) extends ScallopConf(Conf.expandAllMode(argume
descr = "Enables all the optional services supported by Standalone OpenWhisk like CouchDB, Kafka etc",
noshort = true)
+ val devKcf = opt[Boolean](descr = "Enables KubernetesContainerFactory for local development")
+
mainOptions = Seq(manifest, configFile, apiGw, couchdb, userEvents, kafka, kafkaUi)
verify()
@@ -270,7 +272,11 @@ object StandaloneOpenWhisk extends SLF4JLogging {
require(f.exists(), s"Config file $f does not exist")
System.setProperty("config.file", f.getAbsolutePath)
case None =>
- System.setProperty("config.resource", "standalone.conf")
+ val config = if (conf.devKcf()) {
+ "standalone-kcf.conf"
+ } else "standalone.conf"
+ System.setProperty("config.resource", config)
+
}
}