This is an automated email from the ASF dual-hosted git repository.

baiyangtx pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/amoro.git


The following commit(s) were added to refs/heads/master by this push:
     new 6bd2416e6 [AMORO-1958] Implement KubernetesOptimizerContainer to 
support running Optimizer natively on K8S (#2695)
6bd2416e6 is described below

commit 6bd2416e6c7d95c7d7d2c4303c7c44fb38bb780b
Author: ConradJam <[email protected]>
AuthorDate: Mon Apr 29 21:33:51 2024 +0800

    [AMORO-1958] Implement KubernetesOptimizerContainer to support running 
Optimizer natively on K8S (#2695)
    
    * [AMORO-1958] Implement KubernetesOptimizerContainer to support running 
Optimizer natively on K8S
    
    * fix override
---
 amoro-ams/amoro-ams-server/pom.xml                 |   5 +
 .../manager/KubernetesOptimizerContainer.java      | 163 +++++++++++++++++++++
 .../server/manager/LocalOptimizerContainer.java    |   2 +-
 ...org.apache.amoro.api.resource.ResourceContainer |   3 +-
 .../dist/src/main/arctic-bin/bin/optimizer.sh      |  63 +++++++-
 .../dist/src/main/arctic-bin/conf/config.yaml      |   8 +
 charts/amoro/templates/_optimizer.tpl              |   8 +
 charts/amoro/templates/amoro-configmap.yaml        |   4 +
 charts/amoro/values.yaml                           |   7 +
 docker/amoro/entrypoint.sh                         |   2 +-
 docs/admin-guides/managing-optimizers.md           |  28 ++++
 pom.xml                                            |   8 +
 12 files changed, 293 insertions(+), 8 deletions(-)

diff --git a/amoro-ams/amoro-ams-server/pom.xml 
b/amoro-ams/amoro-ams-server/pom.xml
index e88a6f908..481fc72c8 100644
--- a/amoro-ams/amoro-ams-server/pom.xml
+++ b/amoro-ams/amoro-ams-server/pom.xml
@@ -58,6 +58,11 @@
             <version>${project.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>io.fabric8</groupId>
+            <artifactId>kubernetes-client</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.apache.spark</groupId>
             <artifactId>spark-core_2.12</artifactId>
diff --git 
a/amoro-ams/amoro-ams-server/src/main/java/org/apache/amoro/server/manager/KubernetesOptimizerContainer.java
 
b/amoro-ams/amoro-ams-server/src/main/java/org/apache/amoro/server/manager/KubernetesOptimizerContainer.java
new file mode 100644
index 000000000..dd18ec6e2
--- /dev/null
+++ 
b/amoro-ams/amoro-ams-server/src/main/java/org/apache/amoro/server/manager/KubernetesOptimizerContainer.java
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ */
+
+package org.apache.amoro.server.manager;
+
+import io.fabric8.kubernetes.api.model.Quantity;
+import io.fabric8.kubernetes.api.model.ResourceRequirementsBuilder;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.KubernetesClientBuilder;
+import org.apache.amoro.api.resource.Resource;
+import org.apache.commons.io.IOUtils;
+import org.apache.curator.shaded.com.google.common.base.Preconditions;
+import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
+import org.apache.iceberg.relocated.com.google.common.collect.Maps;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Map;
+
+/** Kubernetes Optimizer Container with Standalone Optimizer */
+public class KubernetesOptimizerContainer extends AbstractResourceContainer {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(KubernetesOptimizerContainer.class);
+
+  public static final String MEMORY_PROPERTY = "memory";
+  public static final String CPU_FACTOR_PROPERTY = "cpu.factor";
+  public static final String NAMESPACE = "namespace";
+  public static final String IMAGE = "image";
+  public static final String KUBE_CONFIG_PATH = "kube-config-path";
+
+  private static final String NAME_PREFIX = "amoro-optimizer-";
+
+  private static final String KUBERNETES_NAME_PROPERTIES = "name";
+
+  private KubernetesClient client;
+
+  @Override
+  public void init(String name, Map<String, String> containerProperties) {
+    super.init(name, containerProperties);
+    // start k8s job using k8s client
+    String kubeConfigPath = checkAndGetProperty(containerProperties, 
KUBE_CONFIG_PATH);
+    Config config = 
Config.fromKubeconfig(getKubeConfigContent(kubeConfigPath));
+    this.client = new KubernetesClientBuilder().withConfig(config).build();
+  }
+
+  @Override
+  protected Map<String, String> doScaleOut(Resource resource) {
+    Map<String, String> groupProperties = Maps.newHashMap();
+    groupProperties.putAll(getContainerProperties());
+    groupProperties.putAll(resource.getProperties());
+
+    // generate pod start args
+    long memoryPerThread = Long.parseLong(checkAndGetProperty(groupProperties, 
MEMORY_PROPERTY));
+    long memory = memoryPerThread * resource.getThreadCount();
+    // point at amoro home in docker image
+    String startUpArgs =
+        String.format(
+            "/entrypoint.sh optimizer %s %s",
+            memory, super.buildOptimizerStartupArgsString(resource));
+    LOG.info("Starting k8s optimizer using k8s client with start command : 
{}", startUpArgs);
+
+    String namespace = groupProperties.getOrDefault(NAMESPACE, "default");
+    String image = checkAndGetProperty(groupProperties, IMAGE);
+    String cpuLimitFactorString = 
groupProperties.getOrDefault(CPU_FACTOR_PROPERTY, "1.0");
+    double cpuLimitFactor = Double.parseDouble(cpuLimitFactorString);
+    int cpuLimit = (int) (Math.ceil(cpuLimitFactor * 
resource.getThreadCount()));
+
+    String resourceId = resource.getResourceId();
+    String groupName = resource.getGroupName();
+    String kubernetesName = NAME_PREFIX + resourceId;
+    Deployment deployment =
+        new DeploymentBuilder()
+            .withNewMetadata()
+            .withName(NAME_PREFIX + resourceId)
+            .endMetadata()
+            .withNewSpec()
+            .withReplicas(1)
+            .withNewTemplate()
+            .withNewMetadata()
+            .addToLabels("app", NAME_PREFIX + resourceId)
+            .addToLabels("AmoroOptimizerGroup", groupName)
+            .addToLabels("AmoroResourceId", resourceId)
+            .endMetadata()
+            .withNewSpec()
+            .addNewContainer()
+            .withName("optimizer")
+            .withImage(image)
+            .withCommand("sh", "-c", startUpArgs)
+            .withResources(
+                new ResourceRequirementsBuilder()
+                    .withLimits(
+                        ImmutableMap.of(
+                            "memory",
+                            new Quantity(memory + "Mi"),
+                            "cpu",
+                            new Quantity(cpuLimit + "")))
+                    .withRequests(
+                        ImmutableMap.of(
+                            "memory",
+                            new Quantity(memory + "Mi"),
+                            "cpu",
+                            new Quantity(cpuLimit + "")))
+                    .build())
+            .endContainer()
+            .endSpec()
+            .endTemplate()
+            .withNewSelector()
+            .addToMatchLabels("app", NAME_PREFIX + resourceId)
+            .endSelector()
+            .endSpec()
+            .build();
+    
client.apps().deployments().inNamespace(namespace).resource(deployment).create();
+    Map<String, String> startupProperties = Maps.newHashMap();
+    startupProperties.put(NAMESPACE, namespace);
+    startupProperties.put(KUBERNETES_NAME_PROPERTIES, kubernetesName);
+    return startupProperties;
+  }
+
+  @Override
+  public void releaseOptimizer(Resource resource) {
+    String resourceId = resource.getResourceId();
+    LOG.info("release Kubernetes Optimizer Container {}", resourceId);
+    String namespace = resource.getProperties().get(NAMESPACE);
+    String name = resource.getProperties().get(KUBERNETES_NAME_PROPERTIES);
+    client.apps().deployments().inNamespace(namespace).withName(name).delete();
+  }
+
+  private static String checkAndGetProperty(Map<String, String> properties, 
String key) {
+    Preconditions.checkState(
+        properties != null && properties.containsKey(key), "Cannot find %s in 
properties", key);
+    return properties.get(key);
+  }
+
+  private String getKubeConfigContent(String path) {
+    try {
+      return IOUtils.toString(Files.newInputStream(Paths.get(path)), 
StandardCharsets.UTF_8);
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+}
diff --git 
a/amoro-ams/amoro-ams-server/src/main/java/org/apache/amoro/server/manager/LocalOptimizerContainer.java
 
b/amoro-ams/amoro-ams-server/src/main/java/org/apache/amoro/server/manager/LocalOptimizerContainer.java
index 737bbc2b9..2d40e6d36 100644
--- 
a/amoro-ams/amoro-ams-server/src/main/java/org/apache/amoro/server/manager/LocalOptimizerContainer.java
+++ 
b/amoro-ams/amoro-ams-server/src/main/java/org/apache/amoro/server/manager/LocalOptimizerContainer.java
@@ -55,7 +55,7 @@ public class LocalOptimizerContainer extends 
AbstractResourceContainer {
     long memoryPerThread = 
Long.parseLong(resource.getRequiredProperty(JOB_MEMORY_PROPERTY));
     long memory = memoryPerThread * resource.getThreadCount();
     return String.format(
-        "%s/bin/optimizer.sh %s %s",
+        "%s/bin/optimizer.sh start %s %s",
         amsHome, memory, super.buildOptimizerStartupArgsString(resource));
   }
 
diff --git 
a/amoro-ams/amoro-ams-server/src/main/resources/META-INF/services/org.apache.amoro.api.resource.ResourceContainer
 
b/amoro-ams/amoro-ams-server/src/main/resources/META-INF/services/org.apache.amoro.api.resource.ResourceContainer
index d80e47ea6..19a63d196 100644
--- 
a/amoro-ams/amoro-ams-server/src/main/resources/META-INF/services/org.apache.amoro.api.resource.ResourceContainer
+++ 
b/amoro-ams/amoro-ams-server/src/main/resources/META-INF/services/org.apache.amoro.api.resource.ResourceContainer
@@ -18,4 +18,5 @@
 
 org.apache.amoro.server.manager.SparkOptimizerContainer
 org.apache.amoro.server.manager.FlinkOptimizerContainer
-org.apache.amoro.server.manager.LocalOptimizerContainer
\ No newline at end of file
+org.apache.amoro.server.manager.LocalOptimizerContainer
+org.apache.amoro.server.manager.KubernetesOptimizerContainer
diff --git a/amoro-ams/dist/src/main/arctic-bin/bin/optimizer.sh 
b/amoro-ams/dist/src/main/arctic-bin/bin/optimizer.sh
index 2d025f2e5..5d2af0f76 100755
--- a/amoro-ams/dist/src/main/arctic-bin/bin/optimizer.sh
+++ b/amoro-ams/dist/src/main/arctic-bin/bin/optimizer.sh
@@ -23,12 +23,12 @@ CURRENT_DIR="$( cd "$(dirname "$0")" ; pwd -P )"
 
 LIB_PATH=$AMORO_HOME/lib
 export CLASSPATH=$AMORO_CONF_DIR/optimize:$LIB_PATH/:$(find $LIB_PATH/ -type f 
-name "*.jar" | paste -sd':' -)
-if [ -z $(find $LIB_PATH/ -type f -name "*.jar" | paste -sd':' -) ]; then
+if [ -z "$(find $LIB_PATH/ -type f -name "*.jar" | paste -sd':' -)" ]; then
   echo "Launching the localOptimize task lacks relevant jars, please check" >&2
   exit 1
 fi
 
-ARGS=${@:2}
+ARGS=${@:3}
 
 if [ -z "$OPTIMIZER_LOG_DIR_NAME" ]; then
   OPTIMIZER_LOG_DIR=$AMORO_LOG_DIR/optimizer-$(date +%s)
@@ -37,14 +37,67 @@ else
 fi
 STDERR_LOG=${OPTIMIZER_LOG_DIR}/optimizer.log.err
 
-if [ ! -f "${OPTIMIZER_LOG_DIR}" ]; then
+if [ ! -d "${OPTIMIZER_LOG_DIR}" ]; then
   mkdir -p "${OPTIMIZER_LOG_DIR}"
 fi
 if [ ! -f $STDERR_LOG ];then
     touch $STDERR_LOG
 fi
 
-JAVA_OPTS="-Xmx$1m -Dlog.home=${OPTIMIZER_LOG_DIR}"
+JAVA_OPTS="-Xmx$2m -Dlog.home=${OPTIMIZER_LOG_DIR}"
 RUN_SERVER="org.apache.amoro.optimizer.standalone.StandaloneOptimizer"
 CMDS="$JAVA_RUN $JAVA_OPTS $RUN_SERVER $ARGS"
-nohup ${CMDS} >/dev/null 2>${STDERR_LOG} &
+
+start() {
+  nohup $CMDS > /dev/null 2>"$STDERR_LOG" &
+  echo "Amoro Optimizer started in background with PID $!"
+}
+
+start-foreground() {
+  $CMDS
+}
+
+#0:pid bad and proc OK;   1:pid ok and proc bad;    2:pid bad
+function status(){
+    test -e ${PID} || return 2
+    test -n "$(ps -p $(cat ${PID}) -o pid=)" && return 0 || return 1
+}
+
+case "$1" in
+  start)
+    start
+    ;;
+  start-foreground)
+    start-foreground
+    ;;
+  stop)
+    status;
+    if [ $? -ne 0 ]; then
+        echo "proc not running."
+    else
+        echo 'stopping app server.'
+        stop
+    fi
+    ;;
+  status)
+    status;
+    if [ $? -eq 0 ];then
+        echo 'running.'
+    else
+        echo 'not running.'
+        exit 1
+    fi
+    ;;
+  pid)
+    status
+    if [ $? -eq 0 ]; then
+      cat $PID
+    else
+      echo "not running"
+    fi
+    ;;
+  *)
+    echo "Usage: $0 {start|start-foreground|stop|status|pid} 
<memory_limit_in_MB> [additional_args]"
+    exit 1
+    ;;
+esac
\ No newline at end of file
diff --git a/amoro-ams/dist/src/main/arctic-bin/conf/config.yaml 
b/amoro-ams/dist/src/main/arctic-bin/conf/config.yaml
index 02218a5c9..d6cbb7ff2 100644
--- a/amoro-ams/dist/src/main/arctic-bin/conf/config.yaml
+++ b/amoro-ams/dist/src/main/arctic-bin/conf/config.yaml
@@ -137,6 +137,14 @@ containers:
       export.JAVA_HOME: "/opt/java"   # JDK environment
 
 #containers:
+
+# - name: KubernetesContainer
+#   container-impl: 
com.netease.arctic.server.manager.KubernetesOptimizerContainer
+#    properties:
+#     kube-config-path: ~/.kube/config
+#     image: arctic163/amoro:0.6
+#     namespace: default
+
 #  - name: flinkContainer
 #    container-impl: org.apache.amoro.server.manager.FlinkOptimizerContainer
 #    properties:
diff --git a/charts/amoro/templates/_optimizer.tpl 
b/charts/amoro/templates/_optimizer.tpl
index 48847ed96..6bfb1a2ac 100644
--- a/charts/amoro/templates/_optimizer.tpl
+++ b/charts/amoro/templates/_optimizer.tpl
@@ -84,3 +84,11 @@ properties:
   {{- end -}}
 {{- end -}}
 
+{{- define "amoro.optimizer.container.kubernetes" -}}
+container-impl: org.apache.amoro.server.manager.KubernetesOptimizerContainer
+properties:
+  {{- with .Values.optimizer.kubernetes.properties -}}
+    {{- toYaml . | nindent 2 }}
+  {{- end -}}
+{{- end -}}
+
diff --git a/charts/amoro/templates/amoro-configmap.yaml 
b/charts/amoro/templates/amoro-configmap.yaml
index bbc02fe0b..e413ed977 100644
--- a/charts/amoro/templates/amoro-configmap.yaml
+++ b/charts/amoro/templates/amoro-configmap.yaml
@@ -113,6 +113,10 @@ data:
       - name: {{ .Values.optimizer.local.name | default "local" }}
         {{- include "amoro.optimizer.container.local" . | nindent 8 }}
       {{ end }}
+      {{- if .Values.optimizer.kubernetes.enabled }}
+      - name: {{ .Values.optimizer.local.name | default "kubernetes" }}
+        {{- include "amoro.optimizer.container.kubernetes" . | nindent 8 }}
+      {{ end }}
 
 
     {{- with .Values.amoroConf.amoroDefaults }}
diff --git a/charts/amoro/values.yaml b/charts/amoro/values.yaml
index e364980e3..aa01ca4d3 100644
--- a/charts/amoro/values.yaml
+++ b/charts/amoro/values.yaml
@@ -171,6 +171,13 @@ optimizer:
     name: ~
     # properties for optimizer container.
     properties: ~
+  kubernetes:
+    # enable the kubernetes optimizer container
+    enabled: true
+    properties:
+      namespace: "default"
+      kube-config-path: "~/.kube/config"
+      image: "arctic163/amoro:0.6"
   extra: []
 
 
diff --git a/docker/amoro/entrypoint.sh b/docker/amoro/entrypoint.sh
index 2b857a40e..57d867373 100644
--- a/docker/amoro/entrypoint.sh
+++ b/docker/amoro/entrypoint.sh
@@ -56,7 +56,7 @@ elif [ "$1" == "ams" ]; then
 elif [ "$1" == "optimizer" ]; then
   args=("${args[@]:1}")
   echo "Start Amoro Optimizer"
-  exec "$AMORO_HOME/bin/optimizer.sh " "${args[@]}"
+  exec "$AMORO_HOME/bin/optimizer.sh" "start-foreground" "${args[@]}"
 fi
 
 # Running command in pass-through mode
diff --git a/docs/admin-guides/managing-optimizers.md 
b/docs/admin-guides/managing-optimizers.md
index 982d9db3c..613e70f21 100644
--- a/docs/admin-guides/managing-optimizers.md
+++ b/docs/admin-guides/managing-optimizers.md
@@ -30,6 +30,34 @@ containers:
       export.JAVA_HOME: "/opt/java"   # JDK environment
 ```
 
+### Kubernetes container
+
+Kubernetes Optimizer Container is a way to start Optimizer On K8s with 
standalone Optimizer.
+To use flink container, you need to add a new container configuration. 
+with container-impl as 
`org.apache.amoro.server.manager.KubernetesOptimizerContainer`
+
+KubernetesOptimizerContainer support the following properties:
+
+
+| Property Name             | Required | Default Value | Description           
                                                                                
        |
+|---------------------------|----------|---------------|---------------------------------------------------------------------------------------------------------------|
+| kube-config-path          | true     | N/A           | Kubernetes config 
location.                                                                       
            |
+| image                     | true     | N/A           | Optimizer Image name  
                                                                                
        |
+| namespace                 | false    | "default"     | The namespace of 
optimizer to deploy                                                             
             |
+| ams-optimizing-uri        | false    | N/A           | uri of AMS thrift 
self-optimizing endpoint. This could be used if the ams.server-expose-host is 
not available |
+| cpu.factor                | false    | "1.0"         | Cpu factor when 
request kubernetes resource. Default 1 Cpu pre thread                           
              |
+| memory                    | true     | N/A           | Memory usage for pre 
thread                                                                          
         |
+
+
+```yaml
+containers:
+  - name: KubernetesContainer
+    container-impl: 
org.apache.amoro.server.manager.KubernetesOptimizerContainer
+    properties:
+      kube-config-path: ~/.kube/config
+      image: apache/amoro:0.6
+```
+
 ### Flink container
 Flink container is a way to start Optimizer through Flink jobs. With Flink, 
you can easily deploy Optimizer
 on yarn clusters or kubernetes clusters to support large-scale data scenarios. 
To use flink container, 
diff --git a/pom.xml b/pom.xml
index 480f68767..8f90ffc12 100644
--- a/pom.xml
+++ b/pom.xml
@@ -128,6 +128,7 @@
         <bitmap.version>1.0.1</bitmap.version>
         <prometheus.version>0.16.0</prometheus.version>
         <flink.version>1.18.1</flink.version>
+        
<fabric8-kubernetes-client.version.version>6.12.1</fabric8-kubernetes-client.version.version>
     </properties>
 
     <dependencies>
@@ -793,6 +794,13 @@
                 <version>${flink.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>io.fabric8</groupId>
+                <artifactId>kubernetes-client</artifactId>
+                <version>${fabric8-kubernetes-client.version.version}</version>
+            </dependency>
+
+
             <dependency>
                 <groupId>junit</groupId>
                 <artifactId>junit</artifactId>

Reply via email to