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

dataroaring pushed a commit to branch branch-3.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-3.0 by this push:
     new 40c97296c33 branch-3.0: [feature](doris compose) run docker as host 
user (#43091)
40c97296c33 is described below

commit 40c97296c33c80b3546367e0d804af15045f4735
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Nov 7 14:13:00 2024 +0800

    branch-3.0: [feature](doris compose) run docker as host user (#43091)
    
    PR Body: Our workmates run doris compose on the same dev machine.
    Sometimes some docker doris_be consume high cpu. We want to find out it
    belongs to who. But if show processes with command 'ps -elf' on host
    machine, we only find out that its owner is root.
    What a pitty. So we let doris compose docker run as host user. Then use
    command 'ps -elf' can known who it belongs to.
    
    To achive this goal, we pass host user and uid into docker container,
    and let container add a new user the same with it, then run command with
    this new account.
    
    If user still want to run as root in container, they need to pass
    options '--root' when creating a new doris compose cluster.
    
    ```
    # run as root in container
    python doris-compose.py  up   --root ...
    
    #run as host user in container
    python doris-compose.py up ...
    ```
    
    
     Cherry-picked from #43040
    
    Co-authored-by: yujun <[email protected]>
---
 docker/runtime/doris-compose/Dockerfile            |  6 +-
 docker/runtime/doris-compose/cluster.py            | 92 +++++++++++-----------
 docker/runtime/doris-compose/command.py            |  9 ++-
 .../runtime/doris-compose/resource/entrypoint.sh   | 68 ++++++++++++++++
 4 files changed, 124 insertions(+), 51 deletions(-)

diff --git a/docker/runtime/doris-compose/Dockerfile 
b/docker/runtime/doris-compose/Dockerfile
index 48d94d612df..501574e372e 100644
--- a/docker/runtime/doris-compose/Dockerfile
+++ b/docker/runtime/doris-compose/Dockerfile
@@ -38,7 +38,7 @@ RUN  sed -i s@/deb.debian.org/@/mirrors.aliyun.com/@g 
/etc/apt/sources.list
 RUN  apt-get clean
 
 RUN apt-get update && \
-    apt-get install -y default-mysql-client python lsof tzdata curl unzip 
patchelf jq procps util-linux && \
+    apt-get install -y default-mysql-client python lsof tzdata curl unzip 
patchelf jq procps util-linux gosu && \
     ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
     dpkg-reconfigure -f noninteractive tzdata && \
     apt-get clean
@@ -48,14 +48,14 @@ RUN curl -f 
https://repo1.maven.org/maven2/org/jacoco/jacoco/${JACOCO_VERSION}/j
     unzip jacoco.zip -d /jacoco
 
 # cloud
-COPY cloud/CMakeLists.txt cloud/output* output/ms* /opt/apache-doris/cloud/
+COPY --chmod=777 README.md cloud/output* output/ms* /opt/apache-doris/cloud/
 RUN mkdir /opt/apache-doris/fdb
 RUN if [ -d /opt/apache-doris/cloud/bin ]; then                            \
         sed -i 's/\<chmod\>/echo/g' /opt/apache-doris/cloud/bin/start.sh ; \
     fi
 
 # fe and be
-COPY output /opt/apache-doris/
+COPY --chmod=777 output /opt/apache-doris/
 # in docker, run 'chmod 755 doris_be' first time cost 1min, remove it.
 RUN sed -i 's/\<chmod\>/echo/g' /opt/apache-doris/be/bin/start_be.sh
 
diff --git a/docker/runtime/doris-compose/cluster.py 
b/docker/runtime/doris-compose/cluster.py
index 985ef27113b..abd59bcd0e9 100644
--- a/docker/runtime/doris-compose/cluster.py
+++ b/docker/runtime/doris-compose/cluster.py
@@ -292,32 +292,27 @@ class Node(object):
         enable_coverage = self.cluster.coverage_dir
 
         envs = {
-            "MY_IP":
-            self.get_ip(),
-            "MY_ID":
-            self.id,
-            "MY_TYPE":
-            self.node_type(),
-            "FE_QUERY_PORT":
-            FE_QUERY_PORT,
-            "FE_EDITLOG_PORT":
-            FE_EDITLOG_PORT,
-            "BE_HEARTBEAT_PORT":
-            BE_HEARTBEAT_PORT,
-            "DORIS_HOME":
-            os.path.join(self.docker_home_dir()),
-            "STOP_GRACE":
-            1 if enable_coverage else 0,
-            "IS_CLOUD":
-            1 if self.cluster.is_cloud else 0,
-            "SQL_MODE_NODE_MGR":
-            1 if hasattr(self.cluster, 'sql_mode_node_mgr')
-            and self.cluster.sql_mode_node_mgr else 0
+            "MY_IP": self.get_ip(),
+            "MY_ID": self.id,
+            "MY_TYPE": self.node_type(),
+            "FE_QUERY_PORT": FE_QUERY_PORT,
+            "FE_EDITLOG_PORT": FE_EDITLOG_PORT,
+            "BE_HEARTBEAT_PORT": BE_HEARTBEAT_PORT,
+            "DORIS_HOME": os.path.join(self.docker_home_dir()),
+            "STOP_GRACE": 1 if enable_coverage else 0,
+            "IS_CLOUD": 1 if self.cluster.is_cloud else 0,
+            "SQL_MODE_NODE_MGR": 1 if self.cluster.sql_mode_node_mgr else 0,
         }
 
         if self.cluster.is_cloud:
             envs["META_SERVICE_ENDPOINT"] = self.cluster.get_meta_server_addr()
 
+        # run as host user
+        if not getattr(self.cluster, 'is_root_user', True):
+            envs["HOST_USER"] = getpass.getuser()
+            envs["HOST_UID"] = os.getuid()
+            envs["HOST_GID"] = os.getgid()
+
         if enable_coverage:
             outfile = "{}/coverage/{}-coverage-{}-{}".format(
                 DOCKER_DORIS_PATH, self.node_type(), self.cluster.name,
@@ -332,6 +327,15 @@ class Node(object):
 
         return envs
 
+    def entrypoint(self):
+        if self.start_script():
+            return [
+                "bash",
+                os.path.join(DOCKER_RESOURCE_PATH, "entrypoint.sh")
+            ] + self.start_script()
+        else:
+            return None
+
     def get_add_init_config(self):
         return []
 
@@ -441,8 +445,8 @@ class FE(Node):
     def cloud_unique_id(self):
         return "sql_server_{}".format(self.id)
 
-    def entrypoint(self):
-        return ["bash", os.path.join(DOCKER_RESOURCE_PATH, "init_fe.sh")]
+    def start_script(self):
+        return ["init_fe.sh"]
 
     def docker_ports(self):
         return [FE_HTTP_PORT, FE_EDITLOG_PORT, FE_RPC_PORT, FE_QUERY_PORT]
@@ -539,8 +543,8 @@ class BE(Node):
             storage_root_path = ";".join(dir_descs) if dir_descs else '""'
             f.write("\nstorage_root_path = {}\n".format(storage_root_path))
 
-    def entrypoint(self):
-        return ["bash", os.path.join(DOCKER_RESOURCE_PATH, "init_be.sh")]
+    def start_script(self):
+        return ["init_be.sh"]
 
     def docker_env(self):
         envs = super().docker_env()
@@ -591,12 +595,8 @@ class MS(CLOUD):
             cfg += self.cluster.ms_config
         return cfg
 
-    def entrypoint(self):
-        return [
-            "bash",
-            os.path.join(DOCKER_RESOURCE_PATH, "init_cloud.sh"),
-            "--meta-service"
-        ]
+    def start_script(self):
+        return ["init_cloud.sh", "--meta-service"]
 
     def node_type(self):
         return Node.TYPE_MS
@@ -616,11 +616,8 @@ class RECYCLE(CLOUD):
             cfg += self.cluster.recycle_config
         return cfg
 
-    def entrypoint(self):
-        return [
-            "bash",
-            os.path.join(DOCKER_RESOURCE_PATH, "init_cloud.sh"), "--recycler"
-        ]
+    def start_script(self):
+        return ["init_cloud.sh", "--recycler"]
 
     def node_type(self):
         return Node.TYPE_RECYCLE
@@ -641,8 +638,8 @@ class FDB(Node):
         with open(os.path.join(local_conf_dir, "fdb.cluster"), "w") as f:
             f.write(self.cluster.get_fdb_cluster())
 
-    def entrypoint(self):
-        return ["bash", os.path.join(DOCKER_RESOURCE_PATH, "init_fdb.sh")]
+    def start_script(self):
+        return ["init_fdb.sh"]
 
     def docker_home_dir(self):
         return os.path.join(DOCKER_DORIS_PATH, "fdb")
@@ -659,14 +656,15 @@ class FDB(Node):
 
 class Cluster(object):
 
-    def __init__(self, name, subnet, image, is_cloud, fe_config, be_config,
-                 ms_config, recycle_config, fe_follower, be_disks, be_cluster,
-                 reg_be, coverage_dir, cloud_store_config, sql_mode_node_mgr,
-                 be_metaservice_endpoint, be_cluster_id):
+    def __init__(self, name, subnet, image, is_cloud, is_root_user, fe_config,
+                 be_config, ms_config, recycle_config, fe_follower, be_disks,
+                 be_cluster, reg_be, coverage_dir, cloud_store_config,
+                 sql_mode_node_mgr, be_metaservice_endpoint, be_cluster_id):
         self.name = name
         self.subnet = subnet
         self.image = image
         self.is_cloud = is_cloud
+        self.is_root_user = is_root_user
         self.fe_config = fe_config
         self.be_config = be_config
         self.ms_config = ms_config
@@ -686,9 +684,9 @@ class Cluster(object):
         self.be_cluster_id = be_cluster_id
 
     @staticmethod
-    def new(name, image, is_cloud, fe_config, be_config, ms_config,
-            recycle_config, fe_follower, be_disks, be_cluster, reg_be,
-            coverage_dir, cloud_store_config, sql_mode_node_mgr,
+    def new(name, image, is_cloud, is_root_user, fe_config, be_config,
+            ms_config, recycle_config, fe_follower, be_disks, be_cluster,
+            reg_be, coverage_dir, cloud_store_config, sql_mode_node_mgr,
             be_metaservice_endpoint, be_cluster_id):
         if not os.path.exists(LOCAL_DORIS_PATH):
             os.makedirs(LOCAL_DORIS_PATH, exist_ok=True)
@@ -698,8 +696,8 @@ class Cluster(object):
             if os.getuid() == utils.get_path_uid(lock_file):
                 os.chmod(lock_file, 0o666)
             subnet = gen_subnet_prefix16()
-            cluster = Cluster(name, subnet, image, is_cloud, fe_config,
-                              be_config, ms_config, recycle_config,
+            cluster = Cluster(name, subnet, image, is_cloud, is_root_user,
+                              fe_config, be_config, ms_config, recycle_config,
                               fe_follower, be_disks, be_cluster, reg_be,
                               coverage_dir, cloud_store_config,
                               sql_mode_node_mgr, be_metaservice_endpoint,
diff --git a/docker/runtime/doris-compose/command.py 
b/docker/runtime/doris-compose/command.py
index 267c0de3ab0..99849590435 100644
--- a/docker/runtime/doris-compose/command.py
+++ b/docker/runtime/doris-compose/command.py
@@ -261,6 +261,13 @@ class UpCommand(Command):
             help=
             "Create cloud cluster, default is false. Only use when creating 
new cluster."
         )
+        parser.add_argument(
+            "--root",
+            default=False,
+            action=self._get_parser_bool_action(True),
+            help=
+            "Run cluster as root user, default is false, it will run as host 
user."
+        )
 
         parser.add_argument(
             "--wait-timeout",
@@ -500,7 +507,7 @@ class UpCommand(Command):
                 args.add_recycle_num = 0
 
             cluster = CLUSTER.Cluster.new(
-                args.NAME, args.IMAGE, args.cloud, args.fe_config,
+                args.NAME, args.IMAGE, args.cloud, args.root, args.fe_config,
                 args.be_config, args.ms_config, args.recycle_config,
                 args.fe_follower, args.be_disks, args.be_cluster, args.reg_be,
                 args.coverage_dir, cloud_store_config, args.sql_mode_node_mgr,
diff --git a/docker/runtime/doris-compose/resource/entrypoint.sh 
b/docker/runtime/doris-compose/resource/entrypoint.sh
new file mode 100644
index 00000000000..a3cdaaae8f1
--- /dev/null
+++ b/docker/runtime/doris-compose/resource/entrypoint.sh
@@ -0,0 +1,68 @@
+# 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.
+
+DIR=$(
+    cd $(dirname $0)
+    pwd
+)
+
+source $DIR/common.sh
+
+RUN_USER=root
+
+create_host_user() {
+    if [ -z ${HOST_USER} ]; then
+        health_log "no specific run user, run as root"
+        return
+    fi
+    id ${HOST_USER}
+    if [ $? -eq 0 ]; then
+        health_log "contain user ${HOST_USER}, no create new user"
+        RUN_USER=${HOST_USER}
+        return
+    fi
+    id ${HOST_UID}
+    if [ $? -eq 0 ]; then
+        health_log "contain uid ${HOST_UID}, no create new user"
+        return
+    fi
+    addgroup --gid ${HOST_GID} ${HOST_USER}
+    if [ $? -eq 0 ]; then
+        health_log "create group ${HOST_USER} with gid ${HOST_GID} succ"
+    else
+        health_log "create group ${HOST_USER} with gid ${HOST_GID} failed"
+        return
+    fi
+    adduser --disabled-password --shell /bin/bash --gecos "" --uid ${HOST_UID} 
--gid ${HOST_GID} ${HOST_USER}
+    if [ $? -eq 0 ]; then
+        health_log "create user ${HOST_USER} with uid ${HOST_UID} succ"
+        RUN_USER=${HOST_USER}
+    else
+        health_log "create user ${HOST_USER} with uid ${HOST_UID} failed"
+    fi
+}
+
+create_host_user
+
+if command -v gosu 2>&1 >/dev/null; then
+    if [ -f ${LOG_FILE} ]; then
+        chown ${RUN_USER}:${RUN_USER} ${LOG_FILE}
+    fi
+    gosu ${RUN_USER} bash ${DIR}/${1} ${@:2}
+else
+    bash ${DIR}/${1} ${@:2}
+fi


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to